我正在做一个WPF应用程序,该应用程序从USB LoRaWAN加密狗读取信息并向其中发送消息。
以下是该应用程序应做的快速恢复:
使用此应用程序,用户应该能够连接“服务器”,也就是说,打开串行端口并开始使用消息(目前,我只是试图在控制台上显示它们)。为此,用户单击菜单选项。
然后,用户还应该能够将消息或“命令”发送到加密狗。发生这种情况时,加密狗会发送“已收到消息”信号(也以消息的形式)。
最后,用户还必须能够通过另一个菜单选项关闭服务器。
每条消息都必须以换行符结尾。
这是我的“启动服务器”按钮单击命令和读取异步命令的代码:
private async void ConnectServerCommand_Executed(object sender, ExecutedRoutedEventArgs e)
{
#if DEBUG
Debug.Print("ConnectServer");
#endif
cts = new CancellationTokenSource();
//CancellationToken ct = cts.Token;
// Se llama al método OpenPort() de MySerialPort que intenta abrir el puerto, hace un SET del VERBOSITY y controla las excepciones
MySerialPort.OpenPort();
await ConnectServer(cts.Token);
}
private async Task ConnectServer(CancellationToken ct)
{
// ENTRAR AL BUCLE PARA RECIBIR/ENVIAR MENSAJES
byte[] recBuffer = new byte[1024];
int result;
while ((!ct.IsCancellationRequested) && (MySerialPort.SerialPort.IsOpen))
{
try
{
result = await MySerialPort.SerialPort.BaseStream.ReadAsync(recBuffer, 0, 1024, ct);
if (result > 0)
{
Debug.Print("{0} - {1} ", DateTime.Now, Encoding.Default.GetString(recBuffer));
}
Debug.Print("IsCancellationRequested: {0}", ct.IsCancellationRequested);
/*if (Encoding.Default.GetString(recBuffer).Contains("\n")) {
msg = (Encoding.Default.GetString(recBuffer)).Split('\n')[0];
return msg;
}*/
if (ct.IsCancellationRequested)
{
MySerialPort.ClosePort();
return;
}
}
catch (Exception ex)
{
MessageBox.Show("Error in ReadSerialBytesAsync: " + ex.ToString());
}
}
}
这是我的“关闭服务器”按钮命令:
private void DisconnectServerCommand_Executed(object sender, ExecutedRoutedEventArgs e)
{
#if DEBUG
Debug.Print("DisconnectServer");
#endif
// ROMPER EL BUCLE DE CONNECTSERVER
if (cts != null)
{
cts.Cancel();
}
}
目前,我能够连接服务器并开始异步读取一些消息,但是目前有两个问题。
1。每当我尝试取消任务时,它都不会立即发生。我必须尝试向应用发送另一个命令,以了解请求了取消令牌,然后它确实关闭了端口。我试图在我的关闭服务器命令中关闭端口,但随后抛出IOException,我不知道这是否是正确的方法。
2。我多次收到消息。每当我发送命令时,加密狗都会发送接收到的信号超过一次。我不明白为什么会这样。
我也只想在找到换行符后才处理消息,但是我仍然不明白这样做的逻辑。
这是示例输出:
ConnectServer
Trying to open SerialPort COM3.
03/11/2019 11:14:30 - RIsCancellationRequested: False
03/11/2019 11:14:30 - SET 0 SUCCESS VERBOSE=LONG,DEVPORT,OFF,OFF
IsCancellationRequested: False
LoadDeviceListFile
03/11/2019 11:14:32 - RET 0 SUCCESS VERBOSE=LONG,DEVPORT,OFF,OFF
IsCancellationRequested: False
03/11/2019 11:14:32 - GET 0 SUCCESS DEV_PROV_LIST=\
0,70B3D5E75E00IsCancellationRequested: False
03/11/2019 11:14:32 - 4ET 0 SUCCESS DEV_PROV_LIST=\
0,70B3D5E75E00IsCancellationRequested: False
03/11/2019 11:14:32 - 275,ABP,C,0,00004275,2B7E151628AED2A6ABF7158809CF4F3C,2B7E151628AED2A6ABF7158809CF4F3C,70B3D5E75F600000,2B7E151628AED2A6ABF7158809CF4F3CIsCancellationRequested: False
03/11/2019 11:14:32 -
75,ABP,C,0,00004275,2B7E151628AED2A6ABF7158809CF4F3C,2B7E151628AED2A6ABF7158809CF4F3C,70B3D5E75F600000,2B7E151628AED2A6ABF7158809CF4F3CIsCancellationRequested: False
GetFromDeviceCommand
COMPort de MySerialPort: COM3
03/11/2019 11:14:38 - R75,ABP,C,0,00004275,2B7E151628AED2A6ABF7158809CF4F3C,2B7E151628AED2A6ABF7158809CF4F3C,70B3D5E75F600000,2B7E151628AED2A6ABF7158809CF4F3CIsCancellationRequested: False
03/11/2019 11:14:38 - GET 0 SUCCESS FIRMWARE_INFO=LWCServer:0.3(beta), Kernel:3.4.0.3924,\
FWName:3.4.0.3924.lwc-server.lwcs.ClassC.lrctm.EU.chkpt.wdt2-dfp-br-F5437A
IsCancellationRequested: False
DisconnectServer
GetFromDeviceCommand
COMPort de MySerialPort: COM3
03/11/2019 11:14:44 - RET 0 SUCCESS FIRMWARE_INFO=LWCServer:0.3(beta), Kernel:3.4.0.3924,\
FWName:3.4.0.3924.lwc-server.lwcs.ClassC.lrctm.EU.chkpt.wdt2-dfp-br-F5437A
IsCancellationRequested: True
SerialPort COM3 open. Closing it.
The thread 0x3b14 has exited with code 0 (0x0).
The program '[10580] LWCConfig_02.exe' has exited with code 0 (0x0).
答案 0 :(得分:0)
您可以引入扩展方法来等待取消或 任务完成。
public static async Task<T> WhenFinishedOrCancelled<T>(this Task<T> task, CancellationToken cancellationToken)
{
var tcs = new TaskCompletionSource<byte>();
using (cancellationToken.Register(s => (s as TaskCompletionSource<byte>).TrySetResult(1), tcs))
{
if (tcs.Task == await Task.WhenAny(task, tcs.Task))
{
throw new OperationCanceledException(cancellationToken);
}
tcs.SetCanceled();
}
return await task;
}
然后像这样使用它
while(...)
{
try
{
var readTask = await <...>.ReadAsync(...)
.WhenFinishedOrCancelled(ct);
}
}
编辑:感谢@PauloMorgado提供了改进的WaitForCancel()
功能。
Edit2:解决了一个问题,如果未取消WaitForCancel()
,则CancellationToken
函数产生的任务将永远无法完成。
Edit3:调查了ReadAsync()
未正确取消的原因,并调查了source code。而且方法只检查一次是否应该取消操作
public virtual Task<int> ReadAsync(Byte[] buffer, int offset, int count, CancellationToken cancellationToken)
{
// If cancellation was requested, bail early with an already completed task.
// Otherwise, return a task that represents the Begin/End methods.
return cancellationToken.IsCancellationRequested
? Task.FromCancellation<int>(cancellationToken)
: BeginEndReadAsync(buffer, offset, count);
}