Skip to content

Commit 3466460

Browse files
committed
feat: IMaaAgentClient.IsAlive & SetTimeout()
- Implement Cancel() and CancelWith() to trigger SetTimeout(0) for operation cancellation and restore the original timeout after completion. MaaXYZ/MaaFramework#671 Update Test_IMaaAgentClient.cs Update Test_IMaaAgentClient.cs Update Test_IMaaAgentClient.cs
1 parent 3dbef4f commit 3466460

3 files changed

Lines changed: 186 additions & 33 deletions

File tree

src/MaaFramework.Binding.Native/MaaAgentClient.cs

Lines changed: 92 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,14 @@ namespace MaaFramework.Binding;
1313
[DebuggerDisplay("{DebuggerDisplay,nq}")]
1414
public class MaaAgentClient : MaaDisposableHandle<MaaAgentClientHandle>, IMaaAgentClient<MaaAgentClientHandle>
1515
{
16+
private long _timeout = -1;
1617
private Process? _agentServerProcess;
1718

1819
[ExcludeFromCodeCoverage(Justification = "Debugger display.")]
1920
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
2021
private string DebuggerDisplay => IsInvalid
2122
? $"Invalid {GetType().Name}"
22-
: $"{GetType().Name} {{ {nameof(Id)} = {Id ?? "<null>"}, {nameof(IsConnected)} = {IsConnected} }}";
23+
: $"{GetType().Name} {{ {nameof(Id)} = {Id ?? "<null>"}, {nameof(IsConnected)} = {IsConnected}, {nameof(IsAlive)} = {IsAlive}, Timeout = {_timeout} }}";
2324

2425
/// <summary>
2526
/// Creates a <see cref="MaaAgentClient"/> instance.
@@ -162,11 +163,11 @@ public async Task<bool> LinkStartUnlessProcessExit(Process process, Cancellation
162163

163164
try
164165
{
166+
_ = Cancel(waitTask: linkStartTask);
165167
cts.Token.ThrowIfCancellationRequested();
166-
if (completedTask == serverExitTask)
167-
return false;
168168

169-
return linkStartTask.Result;
169+
return completedTask != serverExitTask
170+
&& linkStartTask.Result;
170171
}
171172
finally
172173
{
@@ -178,36 +179,106 @@ public async Task<bool> LinkStartUnlessProcessExit(Process process, Cancellation
178179
}
179180
}
180181

181-
private int _stopping;
182-
183182
/// <inheritdoc/>
184183
/// <remarks>
185184
/// Wrapper of <see cref="MaaAgentClientDisconnect"/>.
186185
/// </remarks>
187186
public bool LinkStop()
187+
=> MaaAgentClientDisconnect(Handle);
188+
189+
/// <inheritdoc/>
190+
/// <remarks>
191+
/// Wrapper of <see cref="MaaAgentClientConnect"/>.
192+
/// </remarks>
193+
public bool IsConnected => MaaAgentClientConnected(Handle);
194+
195+
/// <inheritdoc/>
196+
/// <remarks>
197+
/// Wrapper of <see cref="MaaAgentClientAlive"/>.
198+
/// </remarks>
199+
public bool IsAlive => MaaAgentClientAlive(Handle);
200+
201+
/// <inheritdoc/>
202+
/// <remarks>
203+
/// Wrapper of <see cref="MaaAgentClientSetTimeout"/>.
204+
/// </remarks>
205+
public bool SetTimeout(long millisecondsDelay)
206+
{
207+
_timeout = millisecondsDelay;
208+
return MaaAgentClientSetTimeout(Handle, _timeout);
209+
}
210+
211+
/// <inheritdoc/>
212+
/// <remarks>
213+
/// Wrapper of <see cref="MaaAgentClientSetTimeout"/>.
214+
/// </remarks>
215+
public bool SetTimeout(TimeSpan delay)
216+
=> SetTimeout((long)delay.TotalMilliseconds);
217+
218+
private static bool NeedToCancel(Func<bool>? waitFunc, Task<bool>? waitTask, MaaJob? waitJob)
219+
{
220+
if (waitFunc is not null)
221+
return true;
222+
if (waitTask is not null && !waitTask.IsCompleted)
223+
return true;
224+
if (waitJob is not null && !waitJob.Status.IsDone())
225+
return true;
226+
227+
return false;
228+
}
229+
230+
private static bool WaitForCancellation(Func<bool>? waitFunc = null, Task<bool>? waitTask = null, MaaJob? waitJob = null)
231+
{
232+
var ret = true;
233+
if (waitFunc is not null)
234+
ret &= waitFunc.Invoke();
235+
if (waitTask is not null)
236+
ret &= waitTask.GetAwaiter().GetResult();
237+
if (waitJob is not null)
238+
ret &= waitJob.Wait().IsSucceeded();
239+
return ret;
240+
}
241+
242+
/// <inheritdoc/>
243+
/// <remarks>
244+
/// Wrapper of <see cref="MaaAgentClientSetTimeout"/>.
245+
/// </remarks>
246+
public bool CancelWith(CancellationToken cancellationToken, Func<bool>? waitFunc = null, Task<bool>? waitTask = null, MaaJob? waitJob = null)
188247
{
189-
if (IsConnected && Interlocked.CompareExchange(ref _stopping, 1, 0) == 0)
248+
if (!NeedToCancel(waitFunc, waitTask, waitJob))
249+
return true;
250+
251+
var ctr = cancellationToken.Register(() => SetTimeout(0));
252+
try
190253
{
191-
return MaaAgentClientDisconnect(Handle);
192-
#pragma warning disable CS0162 // 检测到无法访问的代码
193-
try
194-
{
195-
return MaaAgentClientDisconnect(Handle);
196-
}
197-
finally
198-
{
199-
_stopping = 0;
200-
}
201-
#pragma warning restore CS0162 // 检测到无法访问的代码
254+
return WaitForCancellation(waitFunc, waitTask, waitJob);
255+
}
256+
finally
257+
{
258+
ctr.Dispose();
259+
_ = SetTimeout(_timeout).ThrowIfFalse();
202260
}
203-
return true;
204261
}
205262

206263
/// <inheritdoc/>
207264
/// <remarks>
208-
/// Wrapper of <see cref="MaaAgentClientConnect"/>.
265+
/// Wrapper of <see cref="MaaAgentClientSetTimeout"/>.
209266
/// </remarks>
210-
public bool IsConnected => MaaAgentClientConnected(Handle);
267+
public bool Cancel(Func<bool>? waitFunc = null, Task<bool>? waitTask = null, MaaJob? waitJob = null)
268+
{
269+
if (!NeedToCancel(waitFunc, waitTask, waitJob))
270+
return true;
271+
272+
_ = SetTimeout(0).ThrowIfFalse();
273+
try
274+
{
275+
return WaitForCancellation(waitFunc, waitTask, waitJob);
276+
}
277+
finally
278+
{
279+
_ = SetTimeout(_timeout).ThrowIfFalse();
280+
}
281+
}
211282

212283
/// <inheritdoc/>
213284
public Process AgentServerProcess => _agentServerProcess

src/MaaFramework.Binding.UnitTests/Test_IMaaAgentClient.cs

Lines changed: 61 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -58,12 +58,21 @@ public void Interface_Id_Resource(MaaTypes type, IMaaAgentClient maaAgentClient)
5858

5959
[TestMethod]
6060
[MaaData(MaaTypes.All, nameof(Data))]
61-
public void Interface_LinkStart_LinkStop_AgentServerProcess(MaaTypes type, IMaaAgentClient maaAgentClient)
61+
public void Interface_LinkStart_LinkStop_AgentServerProcess_IsConnected_IsAlive_SetTimeout(MaaTypes type, IMaaAgentClient maaAgentClient)
6262
{
6363
_ = Assert.ThrowsExactly<InvalidOperationException>(() =>
6464
maaAgentClient.AgentServerProcess);
65+
Assert.IsFalse(
66+
maaAgentClient.IsConnected);
67+
Assert.IsFalse(
68+
maaAgentClient.IsAlive);
69+
6570
Assert.IsTrue(
66-
maaAgentClient.IsConnected); // IsConnected 的值有问题,先改了一下LinkStop()实现,记得改回来
71+
maaAgentClient.SetTimeout(TimeSpan.MaxValue));
72+
Assert.IsTrue(
73+
maaAgentClient.SetTimeout(TimeSpan.MinValue));
74+
Assert.IsTrue(
75+
maaAgentClient.SetTimeout(TimeSpan.FromMinutes(2)));
6776

6877
var ret = maaAgentClient.LinkStart(StartupAgentServer);
6978
Assert.IsTrue(
@@ -74,6 +83,8 @@ public void Interface_LinkStart_LinkStop_AgentServerProcess(MaaTypes type, IMaaA
7483
maaAgentClient.AgentServerProcess.HasExited);
7584
Assert.IsTrue(
7685
maaAgentClient.IsConnected);
86+
Assert.IsTrue(
87+
maaAgentClient.IsAlive);
7788

7889
Assert.IsTrue(
7990
maaAgentClient.LinkStop());
@@ -82,25 +93,63 @@ public void Interface_LinkStart_LinkStop_AgentServerProcess(MaaTypes type, IMaaA
8293
Task.Delay(100).Wait(); // wait for process exit
8394
Assert.IsTrue(
8495
maaAgentClient.AgentServerProcess.HasExited);
85-
Assert.IsTrue(
96+
Assert.IsFalse(
8697
maaAgentClient.IsConnected);
98+
Assert.IsFalse(
99+
maaAgentClient.IsAlive);
100+
}
101+
102+
[TestMethod]
103+
[MaaData(MaaTypes.All, nameof(Data))]
104+
public void Interface_Cancel_CancelWith(MaaTypes type, IMaaAgentClient maaAgentClient)
105+
{
106+
using var res = new MaaResource();
107+
using var agent = MaaAgentClient.Create(res);
108+
var ct = new CancellationToken(true);
109+
var job = new MaaJob(0, res);
110+
Assert.AreEqual(MaaJobStatus.Invalid, job.Wait());
111+
112+
Assert.IsFalse(
113+
agent.Cancel(waitFunc: agent.LinkStart));
114+
Assert.IsFalse(
115+
agent.Cancel(waitTask: Task.Run(agent.LinkStart)));
116+
Assert.IsFalse(
117+
agent.Cancel(waitJob: job));
118+
119+
Assert.IsFalse(
120+
agent.CancelWith(ct, waitFunc: agent.LinkStart));
121+
Assert.IsFalse(
122+
agent.CancelWith(ct, waitTask: Task.Run(agent.LinkStart)));
123+
Assert.IsFalse(
124+
agent.CancelWith(ct, waitJob: job));
87125
}
88126

89127
[TestMethod]
90128
[MaaData(MaaTypes.All, nameof(Data))]
91-
public void RunTask(MaaTypes type, IMaaAgentClient maaAgentClient)
129+
public void Case_RunTask(MaaTypes type, IMaaAgentClient maaAgentClient)
92130
{
93-
using var maa = new MaaTasker
131+
using var maa = type switch
94132
{
95-
Controller = new MaaAdbController(Common.AdbPath, Common.Address, AdbScreencapMethods.Encode, AdbInputMethods.AdbShell, Common.AdbConfig, Common.AgentPath),
96-
Resource = new MaaResource(),
97-
DisposeOptions = DisposeOptions.All,
133+
#if MAA_NATIVE
134+
MaaTypes.Native => new MaaTasker
135+
{
136+
Controller = new MaaAdbController(Common.AdbPath, Common.Address, AdbScreencapMethods.Encode, AdbInputMethods.AdbShell, Common.AdbConfig, Common.AgentPath),
137+
Resource = new MaaResource(),
138+
DisposeOptions = DisposeOptions.All,
139+
},
140+
#endif
141+
_ => throw new NotImplementedException(),
98142
};
99-
Assert.IsTrue(
100-
maa.IsInitialized);
143+
Assert.IsTrue(maa.IsInitialized);
101144

102-
using var agent = MaaAgentClient.Create("6CDC213A-085C-40C8-8665-635820D10425", maa.Resource);
103-
using (var cts = new CancellationTokenSource(10000))
145+
using var agent = type switch
146+
{
147+
#if MAA_NATIVE
148+
MaaTypes.Native => MaaAgentClient.Create("6CDC213A-085C-40C8-8665-635820D10425", maa.Resource),
149+
#endif
150+
_ => throw new NotImplementedException(),
151+
};
152+
using (var cts = new CancellationTokenSource(10 * 1000))
104153
{
105154
Assert.IsTrue(
106155
// agent.LinkStart());

src/MaaFramework.Binding/IMaaAgentClient.cs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,39 @@ public interface IMaaAgentClient : IMaaDisposable
7979
/// <returns><see langword="true"/> if the <see cref="IMaaAgentClient"/> is connected; otherwise, <see langword="false"/>.</returns>
8080
bool IsConnected { get; }
8181

82+
/// <summary>
83+
/// Gets whether the connection is alive.
84+
/// </summary>
85+
/// <returns><see langword="true"/> if the connection is alive; otherwise, <see langword="false"/>.</returns>
86+
bool IsAlive { get; }
87+
88+
/// <summary>
89+
/// Sets the timeout for the agent server to respond.
90+
/// </summary>
91+
/// <param name="millisecondsDelay">The time span to wait before the agent server response.</param>
92+
/// <returns><see langword="true"/> if the timeout was set successfully; otherwise, <see langword="false"/>.</returns>
93+
bool SetTimeout(long millisecondsDelay);
94+
95+
/// <param name="delay">The time span to wait before the agent server response.</param>
96+
/// <inheritdoc cref="SetTimeout(long)"/>
97+
bool SetTimeout(TimeSpan delay);
98+
99+
/// <summary>
100+
/// Cancels the agent operation with a specified token.
101+
/// </summary>
102+
/// <param name="cancellationToken">The token used to cancel the waiting operation.</param>
103+
/// <param name="waitFunc">The func that needs to be canceled.</param>
104+
/// <param name="waitTask">The task that needs to be canceled.</param>
105+
/// <param name="waitJob">The job that needs to be canceled.</param>
106+
/// <returns>The return value of <paramref name="waitFunc"/> AND <paramref name="waitTask"/>.Result AND <paramref name="waitJob"/>.IsSucceeded().</returns>
107+
bool CancelWith(CancellationToken cancellationToken, Func<bool>? waitFunc = null, Task<bool>? waitTask = null, MaaJob? waitJob = null);
108+
109+
/// <summary>
110+
/// Cancels the agent operation.
111+
/// </summary>
112+
/// <inheritdoc cref="CancelWith"/>
113+
bool Cancel(Func<bool>? waitFunc = null, Task<bool>? waitTask = null, MaaJob? waitJob = null);
114+
82115
/// <summary>
83116
/// Represents a method that starts the agent server process.
84117
/// </summary>

0 commit comments

Comments
 (0)