我正在使用Winform GUI与串口上的微控制器通信,我正在开发。
我根据预定义的协议发送一组命令,并从微控制器接收反馈字符串。
我想知道在发送命令后是否有一种简单的方法可以等待某个反馈。
E.g。
如果未及时收到反馈,将触发超时并显示失败消息。如果数据及时返回,则应立即停止等待方法并继续执行下一步操作。我也不想在等待反馈的同时阻止用户界面。
我使用以下代码接收数据。
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);
}
}
我有一个单击按钮的方法,我一直在努力解决延迟(///开始等待)。
private void buttonDet_Click(object sender, EventArgs e)
{
ResetPositionMark();
serialPort1.DiscardInBuffer();
//Position 1 at Initial Stage
textBoxStatus.AppendText("Position 1: Init/Home." + Environment.NewLine);
SendCommand(0,1); //Trigger LED1
textBoxStatus.AppendText("LED1 triggered." + Environment.NewLine);
Thread.Sleep(200);
///Camera Capture
//============== Position 2 ==============
SendCommand(2,0); //Move to Position 2
textBoxStatus.AppendText("Moving to Position 2..." + Environment.NewLine);
**///Start waiting**
if (timeout)
{
textBoxStatus.AppendText("Position 2 Timeout reached." + Environment.NewLine);
}
else
{
textBoxStatus.AppendText("Data received in time." + Environment.NewLine);
textBoxStatus.AppendText("Position 2 OK." + Environment.NewLine);
SendCommand(0, 2); //Trigger LED2 once the motor reaches the position 2
textBoxStatus.AppendText("LED2 triggered." + Environment.NewLine);
}
///Camera Capture
//============== Position 3 ==============
SendCommand(3,0); //Move to Position 3
textBoxStatus.AppendText("Moving to Position 3..." + Environment.NewLine);
**///Start waiting**
if (timeout)
{
textBoxStatus.AppendText("Position 3 Timeout reached." + Environment.NewLine);
}
else
{
textBoxStatus.AppendText("Data received in time." + Environment.NewLine);
textBoxStatus.AppendText("Position 3 OK." + Environment.NewLine);
SendCommand(0, 3); //Trigger LED3 once the motor reaches the position 2
textBoxStatus.AppendText("LED3 triggered." + Environment.NewLine);
}
///Camera Capture
SendCommand(1, 0); //Move back to Home position (position 1)
textBoxStatus.AppendText("Moving Home to Position 1..." + Environment.NewLine);
**///Start waiting**
if (timeout)
{
textBoxStatus.AppendText("Back to Home Timeout!" + Environment.NewLine);
}
else
{
textBoxStatus.AppendText("Data received in time." + Environment.NewLine);
textBoxStatus.AppendText("Home now." + Environment.NewLine);
}
}
我不熟悉线程和ManualResetEvent等。
请帮助了解如何最好地等待数据,最好是使用代码示例。
非常感谢。
答案 0 :(得分:1)
这是一个简单的解决方案:https://1drv.ms/u/s!AnSTW4R3pQ5uitAnyiAKTscGPHpxYw
这个想法是你在开始发送命令时创建一个单独的线程, 这是通过:
完成的 Task.Factory.StartNew(() => {
}, _canecellationTokenSource.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default);
这里的参数主要是为自己说话:
接下来,您需要创建一个AutoResetEvent实例。它的工作原理你可以在MSDN上阅读。但简而言之,它是一个具有两个状态,开放和关闭的开关。默认情况下,您希望它被关闭,这是构造函数中的false参数。 在来自串口(DataReceived)的事件处理程序中,您希望"打开"的AutoResetEvent。所以你这样做:
dataReceivedEvent.Set();
现在,当您发出命令时,等待AutoResetEvent被打开"你指定一个你愿意等待的时间,如下:
var succeeded = dataReceivedEvent.WaitOne(TimeSpan.FromSeconds(3));
这意味着如果在3秒内未打开AutoResetEvent,则停止等待并报告失败。基本上,如果它没有在给定的时间范围内打开,则返回false,如果没有,则返回true。因为它是" Auto"重置事件它将自动"关闭"完成等待后,您不必手动重置。
其余的都是你已经拥有的。使用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);
}
}
}
UI保留在自己的线程上,不会受到影响。 由于我没有可用的微控制器,我不得不写一个虚拟模拟器:)