C#等待serialPort_DataReceived事件的反馈
我正在使用我正在开发的WinFormGUI与串行端口上的微控制器通信 我根据预定义的协议发送一组命令,并从微控制器接收反馈字符串 我想知道是否有一种简单的方法可以在发送命令后等待某个反馈 例如C#等待serialPort_DataReceived事件的反馈,c#,winforms,user-interface,serial-port,timeout,C#,Winforms,User Interface,Serial Port,Timeout,我正在使用我正在开发的WinFormGUI与串行端口上的微控制器通信 我根据预定义的协议发送一组命令,并从微控制器接收反馈字符串 我想知道是否有一种简单的方法可以在发送命令后等待某个反馈 例如 发出命令 等待设定的时间(可能是几秒钟到几分钟) 及时显示反馈,并继续发出下一个命令/行动 如果未及时收到反馈,将触发超时并显示故障消息。如果数据及时返回,应立即停止等待方法,并继续执行下一个操作过程。我不想在等待反馈时阻塞UI 我使用以下代码来接收数据 delegate void SetText
delegate void SetTextCallback(string text);
private void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
try
{
string receivedData = serialPort1.ReadExisting();
SetText(receivedData);
}
catch (IOException exception)
{
MessageBox.Show(exception.Message, "Message", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
catch (Exception exception)
{
MessageBox.Show(exception.Message, "Message", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
private void SetText(string text)
{
if (this.textBoxReceive.InvokeRequired)
{
SetTextCallback d = SetText;
this.Invoke(d, new object[] { text });
//this.Invoke(new Action(() => { this.textBoxReceive.AppendText(text); }));
}
else
{
if (text.Length > 15)
{
CheckPosition(text); //To check for a position number from the feedback string
}
this.textBoxReceive.AppendText(text);
}
}
这是我的写作方法
private void SendCommand(int move, int trigger)
{
try
{
if (serialPort1.IsOpen)
{
string cmd = string.Empty;
//Format: { “move”: 0, “trigger”: 0}
cmd = "{ " + "\"move\": " + move + ","
+ " \"trigger\": " + trigger
+ " }"
+ Environment.NewLine;
textBoxReceive.AppendText("Send:" + cmd + Environment.NewLine); // Display sent command
serialPort1.DiscardOutBuffer();
serialPort1.DiscardInBuffer();
serialPort1.Write(cmd);
}
else if (serialPort1.IsOpen != true)
{
MessageBox.Show(@"Lost COM Port.", "Message", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
catch (IOException e)
{
MessageBox.Show(e.Message, "Message", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "Message", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
我有一个点击按钮的方法,我一直在和延迟(///开始等待)斗争,就像这样
我对线程和手动重置事件等不太熟悉
请帮助了解等待数据的最佳方式,最好是使用代码示例
非常感谢。这里有一个简单的解决方案: 这样做的目的是在开始发送命令时创建一个单独的线程, 这是通过以下方式实现的:
Task.Factory.StartNew(() => {
}, _canecellationTokenSource.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default);
这里的参数主要是为自己说话:
dataReceivedEvent.Set();
现在,当您发出命令时,您将等待AutoResetEvent被“打开”,并指定您愿意等待的时间,如下所示:
var succeeded = dataReceivedEvent.WaitOne(TimeSpan.FromSeconds(3));
这意味着,如果在3秒内AutoResetEvent未打开,请停止等待并报告故障。基本上,如果在给定的时间范围内没有打开它,则返回false;如果在给定的时间范围内打开,则返回true。由于它是“自动”重置事件,在完成等待后,它将自动“关闭”自身,因此您不必手动重置
剩下的都是你已经拥有的。使用invoke与UI交互并读取/发送命令
public class Communicator
{
CancellationTokenSource _canecellationTokenSource = new CancellationTokenSource();
List<Command> _scenario = new List<Command>(6)
{
Command.Trigger(1),
Command.MoveTo(2),
Command.Trigger(2),
Command.MoveTo(3),
Command.Trigger(3),
Command.MoveTo(1)
};
public void Start(ListBox feedbackControl)
{
Task.Factory.StartNew(() => {
var dataReceivedEvent = new AutoResetEvent(false);
var ct = _canecellationTokenSource.Token;
var controller = new DummyMicrocontroller();
DataReceivedEventHandler onDataReceived = (cmd) => { dataReceivedEvent.Set(); };
controller.DataReceived += onDataReceived;
foreach (var cmd in _scenario)
{
if (ct.IsCancellationRequested)
{
AddItemSafe(feedbackControl, $"Operation cancelled...");
break;
}
AddItemSafe(feedbackControl, cmd.GetMessage(Command.MessageType.Info));
controller.Send(cmd);
var succeeded = dataReceivedEvent.WaitOne(TimeSpan.FromSeconds(3));
var messageType = succeeded ? Command.MessageType.Success : Command.MessageType.Error;
AddItemSafe(feedbackControl, cmd.GetMessage(messageType));
}
AddItemSafe(feedbackControl, $"Finished executing scenario.");
controller.DataReceived -= onDataReceived;
}, _canecellationTokenSource.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default);
}
public void Stop(ListBox feedbackControl)
{
AddItemSafe(feedbackControl, $"Attempting to cancel...");
_canecellationTokenSource.Cancel();
}
private void AddItemSafe(ListBox feedbackControl, object item)
{
if (feedbackControl.InvokeRequired)
{
feedbackControl.Invoke((MethodInvoker)delegate { AddItemSafe(feedbackControl, item); });
}
else
{
feedbackControl.Items.Add(item);
}
}
}
公共类通信器
{
CancellationTokenSource _canecellationTokenSource=新的CancellationTokenSource();
列表_场景=新列表(6)
{
命令触发器(1),
命令。移动到(2),
命令触发器(2),
命令。移动到(3),
指令触发器(3),
命令。移动到(1)
};
公共无效启动(列表框反馈控制)
{
Task.Factory.StartNew(()=>{
var dataReceivedEvent=新的自动存储事件(假);
var ct=\u canecellationTokenSource.Token;
var控制器=新的Dummy微控制器();
DataReceivedEventHandler onDataReceived=(cmd)=>{dataReceivedEvent.Set();};
controller.DataReceived+=onDataReceived;
foreach(在_场景中为var cmd)
{
如果(ct.IsCancellationRequested)
{
AddItemSafe(反馈控制,$“操作已取消…”);
打破
}
AddItemSafe(feedbackControl,cmd.GetMessage(Command.MessageType.Info));
控制器发送(cmd);
var succeed=dataReceivedEvent.WaitOne(TimeSpan.FromSeconds(3));
var messageType=successed?Command.messageType.Success:Command.messageType.Error;
AddItemSafe(feedbackControl,cmd.GetMessage(messageType));
}
AddItemSafe(反馈控制,$“已完成执行场景”);
controller.DataReceived-=onDataReceived;
},_canecellationTokenSource.Token、TaskCreationOptions.LongRunning、TaskScheduler.Default);
}
公共无效停止(列表框反馈控制)
{
AddItemSafe(反馈控制,$“尝试取消…”);
_canecellationTokenSource.Cancel();
}
私有void AddItemSafe(列表框反馈控件,对象项)
{
if(feedbackControl.invokererequired)
{
调用((MethodInvoker)委托{AddItemSafe(feedbackControl,item);});
}
其他的
{
反馈控制.项目.添加(项目);
}
}
}
UI保留在自己的线程上,不受影响。
由于我没有可用的微控制器,我不得不编写一个虚拟模拟器:)非常感谢您的帮助,狂暴者网络!由于我不熟悉所有这些多线程术语/概念,请您将其简化为新手级别,说明哪个方法在做什么,哪个方法调用什么,以及如何修改我的方法。我已经更新了我的答案,使其更具解释性,希望能有所帮助。更新代码以使用我提供的解决方案对您来说应该不难。用真正的串行端口驱动控制器替换DimMyCube,并考虑在手之前有一个命令数组,这样就不必重复代码并发出另一个命令。谢谢你对我有耐心。我从你身上学到了很多。这很有帮助。另一个问题:这里的超时时间固定为3秒。如果我需要发送一个命令,并且PCB/uC在完成任何操作后半小时向我报告,该怎么办?因为每个命令都会导致uC执行不同的操作,因此会有所不同
public class Communicator
{
CancellationTokenSource _canecellationTokenSource = new CancellationTokenSource();
List<Command> _scenario = new List<Command>(6)
{
Command.Trigger(1),
Command.MoveTo(2),
Command.Trigger(2),
Command.MoveTo(3),
Command.Trigger(3),
Command.MoveTo(1)
};
public void Start(ListBox feedbackControl)
{
Task.Factory.StartNew(() => {
var dataReceivedEvent = new AutoResetEvent(false);
var ct = _canecellationTokenSource.Token;
var controller = new DummyMicrocontroller();
DataReceivedEventHandler onDataReceived = (cmd) => { dataReceivedEvent.Set(); };
controller.DataReceived += onDataReceived;
foreach (var cmd in _scenario)
{
if (ct.IsCancellationRequested)
{
AddItemSafe(feedbackControl, $"Operation cancelled...");
break;
}
AddItemSafe(feedbackControl, cmd.GetMessage(Command.MessageType.Info));
controller.Send(cmd);
var succeeded = dataReceivedEvent.WaitOne(TimeSpan.FromSeconds(3));
var messageType = succeeded ? Command.MessageType.Success : Command.MessageType.Error;
AddItemSafe(feedbackControl, cmd.GetMessage(messageType));
}
AddItemSafe(feedbackControl, $"Finished executing scenario.");
controller.DataReceived -= onDataReceived;
}, _canecellationTokenSource.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default);
}
public void Stop(ListBox feedbackControl)
{
AddItemSafe(feedbackControl, $"Attempting to cancel...");
_canecellationTokenSource.Cancel();
}
private void AddItemSafe(ListBox feedbackControl, object item)
{
if (feedbackControl.InvokeRequired)
{
feedbackControl.Invoke((MethodInvoker)delegate { AddItemSafe(feedbackControl, item); });
}
else
{
feedbackControl.Items.Add(item);
}
}
}