C#串口,在允许新消息之前等待正确的响应
C# Serial Port, wait for correct Response before allowing a new message
首先,我想对双倍posting 表示抱歉。恐怕我 post 之前提出的上下文和问题还不够清楚,尽管其中一个解决方案有效,但我仍在努力掌握这个概念,这就是我决定制作一个新解决方案的原因。也因为对于如何以正确的方式完成它显然有很多不同的意见。
首先这是一个干净的 运行 代码示例,我用它来解决这个问题:
namespace serialInterfaceTest
{
public partial class Form1 : Form
{
string serialDataIn;
bool sent = false;
public Form1()
{
InitializeComponent();
serialPort.PortName = "COM3";
serialPort.BaudRate = 9600;
serialPort.Open();
}
private void serialPort_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
serialDataIn = serialPort.ReadExisting();
this.Invoke(new EventHandler(saveData));
}
public void saveData(object sender, EventArgs e)
{
string trimmedMsg = serialDataIn;
richTextBox.Text += trimmedMsg;
if(trimmedMsg.Contains("*")) button_sendMsg.Enabled = true;
}
private void richTextBox_TextChanged(object sender, EventArgs e)
{
richTextBox.SelectionStart = richTextBox.Text.Length;
richTextBox.ScrollToCaret();
}
private void button_sendMsg_Click(object sender, EventArgs e)
{
send(textBox_message.Text);
button_sendMsg.Enabled = false;
//WAIT FOR RESPONSE BEFORE ALLOWING THE USER TO SEND ANOTHER MESSAGE
}
private void button_loopMsg_Click(object sender, EventArgs e)
{
button_loopMsg.Enabled = false;
for (int i = 0; i < 10; i++)
{
send(textBox_message.Text);
//WAIT FOR RESPONSE BEFORE CONTINUING THE LOOP
}
button_loopMsg.Enabled = true;
}
void send(String message)
{
serialPort.Write(message);
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
if (serialPort.IsOpen)
{
try
{
serialPort.Close();
}
catch (Exception error)
{
MessageBox.Show(error.Message);
}
}
}
}
}
这是一个非常简单的 GUI,带有一个用于接收消息的 richTextBox、一个用于输入消息的文本框和两个以两种不同方式发送消息的按钮。
第一种情况,用户手动发送消息很简单。只需停用该按钮,这样您就无法向其发送垃圾邮件。在第二种情况下,我试图在一个循环中发送多条消息,这似乎比我最初想象的要复杂得多。
我试着概述我的需求:
- richTextBox 总是在消息通过串行到达时得到更新
- 当用户想要发送一条消息时,他必须等待回复才能发送下一条
- 连续发送消息时,发送消息的方法需要停止等待回复再继续
下一条消息或发送超时并停止执行
方法
- 来自串行端口的答案始终以“*”结尾
- 答案可以如上所述恢复循环或从我的 GUI
触发一个方法
- 所有串口通信都是 ASCII。
- GUI 应保持响应
在我的另一个问题的已批准解决方案中使用 localHandler 是可行的,对于一个简单的案例来说很好,但我很快意识到它不够灵活。我试图弄明白,但一无所获。
在我上面 post 编写的代码示例中,我将 serialPort.Write 分隔在它自己的方法中。我在想这样的事情:
UI 在它自己的线程中是 运行,而 serialPort_DataReceived 在它自己的线程中是 运行,正如我的研究显示的那样,它无论如何都会这样做。所以现在当我接收数据时一切都很好,每次我从串行端口收到消息时 UI 都会更新。现在对于发送部分,我想最好的方法是也给它自己的线程?所以我可以简单地暂停发送消息的线程,等待主线程的回复,然后继续。或者哪个其他概念可以满足我的需求?
我是面向对象编程的新手,到目前为止我所做的大部分工作都是基于 C 的,所以我可以在这里得到任何帮助。感谢您的考虑,再次对双倍 post 感到抱歉。我只是希望我的问题现在更清楚了。
在 4 天几乎没有睡觉后,我想出了办法,并想 post 为任何试图在串行通信中进行某种流量控制的人提供解决方案。最后我使用了异步方法。我认为对于没有很多 C# 经验的人来说,这是最简单的了。这是表单的代码:
namespace serialInterfaceTest
{
public partial class Form1 : Form
{
string serialDataIn;
public Form1()
{
InitializeComponent();
serialPort.PortName = "COM3";
serialPort.BaudRate = 9600;
serialPort.Open();
}
private void serialPort_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
serialDataIn = serialPort.ReadExisting();
this.Invoke(new EventHandler(saveData));
}
public void saveData(object sender, EventArgs e)
{
string trimmedMsg = serialDataIn;
richTextBox.Text += trimmedMsg;
}
private void richTextBox_TextChanged(object sender, EventArgs e)
{
richTextBox.SelectionStart = richTextBox.Text.Length;
richTextBox.ScrollToCaret();
}
private async void button_sendMsg_Click(object sender, EventArgs e)
{
button_sendMsg.Enabled = false;
await Task.Run(() => send(textBox_message.Text));
button_sendMsg.Enabled = true;
//WAIT FOR RESPONSE BEFORE ALLOWING THE USER TO SEND ANOTHER MESSAGE
}
private async void button_loopMsg_Click(object sender, EventArgs e)
{
button_loopMsg.Enabled = false;
for (int i = 0; i < 10; i++)
{
await Task.Run(() => send(textBox_message.Text));
//WAIT FOR RESPONSE BEFORE CONTINUING THE LOOP
}
button_loopMsg.Enabled = true;
}
private async Task send(String message)
{
serialDataIn = "";
serialPort.Write(message);
while (!serialDataIn.Contains("*"))
{
//PROCESS ANSWERS HERE
serialDataIn = serialPort.ReadExisting();
if (serialDataIn.Contains("*"))
{
this.Invoke(new EventHandler(saveData));
}
}
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
if (serialPort.IsOpen)
{
try
{
serialPort.Close();
}
catch (Exception error)
{
MessageBox.Show(error.Message);
}
}
}
}
}
我有异步方法发送数据,两个按钮也是异步的。当我按下它们时,我只是在等待任务完成,然后才允许另一个输入。我认为这也应该是其他项目的良好起点。 UI 保持响应,消息不会排队。 UI 上的 richTextBox 通过 Invoke 更新,因此消息一到达就显示。
这里是arduino的测试代码:
#define println Serial.println
char serialIn;
String appendSerialData;
void setup()
{
Serial.begin(9600);
}
void loop()
{
appendSerialData = "";
while (Serial.available() > 0)
{
serialIn = Serial.read();
appendSerialData += serialIn;
}
/* asterisk functions as message identifier */
delay(1000);
if (appendSerialData != "") println(appendSerialData + " *");
for (int i = 0; i < 5; i ++)
{
println(i);
}
delay(100);
}
如果我可以对此做出任何改进,我很高兴听到。
首先,我想对双倍posting 表示抱歉。恐怕我 post 之前提出的上下文和问题还不够清楚,尽管其中一个解决方案有效,但我仍在努力掌握这个概念,这就是我决定制作一个新解决方案的原因。也因为对于如何以正确的方式完成它显然有很多不同的意见。
首先这是一个干净的 运行 代码示例,我用它来解决这个问题:
namespace serialInterfaceTest
{
public partial class Form1 : Form
{
string serialDataIn;
bool sent = false;
public Form1()
{
InitializeComponent();
serialPort.PortName = "COM3";
serialPort.BaudRate = 9600;
serialPort.Open();
}
private void serialPort_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
serialDataIn = serialPort.ReadExisting();
this.Invoke(new EventHandler(saveData));
}
public void saveData(object sender, EventArgs e)
{
string trimmedMsg = serialDataIn;
richTextBox.Text += trimmedMsg;
if(trimmedMsg.Contains("*")) button_sendMsg.Enabled = true;
}
private void richTextBox_TextChanged(object sender, EventArgs e)
{
richTextBox.SelectionStart = richTextBox.Text.Length;
richTextBox.ScrollToCaret();
}
private void button_sendMsg_Click(object sender, EventArgs e)
{
send(textBox_message.Text);
button_sendMsg.Enabled = false;
//WAIT FOR RESPONSE BEFORE ALLOWING THE USER TO SEND ANOTHER MESSAGE
}
private void button_loopMsg_Click(object sender, EventArgs e)
{
button_loopMsg.Enabled = false;
for (int i = 0; i < 10; i++)
{
send(textBox_message.Text);
//WAIT FOR RESPONSE BEFORE CONTINUING THE LOOP
}
button_loopMsg.Enabled = true;
}
void send(String message)
{
serialPort.Write(message);
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
if (serialPort.IsOpen)
{
try
{
serialPort.Close();
}
catch (Exception error)
{
MessageBox.Show(error.Message);
}
}
}
}
}
这是一个非常简单的 GUI,带有一个用于接收消息的 richTextBox、一个用于输入消息的文本框和两个以两种不同方式发送消息的按钮。
第一种情况,用户手动发送消息很简单。只需停用该按钮,这样您就无法向其发送垃圾邮件。在第二种情况下,我试图在一个循环中发送多条消息,这似乎比我最初想象的要复杂得多。
我试着概述我的需求:
- richTextBox 总是在消息通过串行到达时得到更新
- 当用户想要发送一条消息时,他必须等待回复才能发送下一条
- 连续发送消息时,发送消息的方法需要停止等待回复再继续 下一条消息或发送超时并停止执行 方法
- 来自串行端口的答案始终以“*”结尾
- 答案可以如上所述恢复循环或从我的 GUI 触发一个方法
- 所有串口通信都是 ASCII。
- GUI 应保持响应
在我的另一个问题的已批准解决方案中使用 localHandler 是可行的,对于一个简单的案例来说很好,但我很快意识到它不够灵活。我试图弄明白,但一无所获。
在我上面 post 编写的代码示例中,我将 serialPort.Write 分隔在它自己的方法中。我在想这样的事情:
UI 在它自己的线程中是 运行,而 serialPort_DataReceived 在它自己的线程中是 运行,正如我的研究显示的那样,它无论如何都会这样做。所以现在当我接收数据时一切都很好,每次我从串行端口收到消息时 UI 都会更新。现在对于发送部分,我想最好的方法是也给它自己的线程?所以我可以简单地暂停发送消息的线程,等待主线程的回复,然后继续。或者哪个其他概念可以满足我的需求?
我是面向对象编程的新手,到目前为止我所做的大部分工作都是基于 C 的,所以我可以在这里得到任何帮助。感谢您的考虑,再次对双倍 post 感到抱歉。我只是希望我的问题现在更清楚了。
在 4 天几乎没有睡觉后,我想出了办法,并想 post 为任何试图在串行通信中进行某种流量控制的人提供解决方案。最后我使用了异步方法。我认为对于没有很多 C# 经验的人来说,这是最简单的了。这是表单的代码:
namespace serialInterfaceTest
{
public partial class Form1 : Form
{
string serialDataIn;
public Form1()
{
InitializeComponent();
serialPort.PortName = "COM3";
serialPort.BaudRate = 9600;
serialPort.Open();
}
private void serialPort_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
serialDataIn = serialPort.ReadExisting();
this.Invoke(new EventHandler(saveData));
}
public void saveData(object sender, EventArgs e)
{
string trimmedMsg = serialDataIn;
richTextBox.Text += trimmedMsg;
}
private void richTextBox_TextChanged(object sender, EventArgs e)
{
richTextBox.SelectionStart = richTextBox.Text.Length;
richTextBox.ScrollToCaret();
}
private async void button_sendMsg_Click(object sender, EventArgs e)
{
button_sendMsg.Enabled = false;
await Task.Run(() => send(textBox_message.Text));
button_sendMsg.Enabled = true;
//WAIT FOR RESPONSE BEFORE ALLOWING THE USER TO SEND ANOTHER MESSAGE
}
private async void button_loopMsg_Click(object sender, EventArgs e)
{
button_loopMsg.Enabled = false;
for (int i = 0; i < 10; i++)
{
await Task.Run(() => send(textBox_message.Text));
//WAIT FOR RESPONSE BEFORE CONTINUING THE LOOP
}
button_loopMsg.Enabled = true;
}
private async Task send(String message)
{
serialDataIn = "";
serialPort.Write(message);
while (!serialDataIn.Contains("*"))
{
//PROCESS ANSWERS HERE
serialDataIn = serialPort.ReadExisting();
if (serialDataIn.Contains("*"))
{
this.Invoke(new EventHandler(saveData));
}
}
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
if (serialPort.IsOpen)
{
try
{
serialPort.Close();
}
catch (Exception error)
{
MessageBox.Show(error.Message);
}
}
}
}
}
我有异步方法发送数据,两个按钮也是异步的。当我按下它们时,我只是在等待任务完成,然后才允许另一个输入。我认为这也应该是其他项目的良好起点。 UI 保持响应,消息不会排队。 UI 上的 richTextBox 通过 Invoke 更新,因此消息一到达就显示。
这里是arduino的测试代码:
#define println Serial.println
char serialIn;
String appendSerialData;
void setup()
{
Serial.begin(9600);
}
void loop()
{
appendSerialData = "";
while (Serial.available() > 0)
{
serialIn = Serial.read();
appendSerialData += serialIn;
}
/* asterisk functions as message identifier */
delay(1000);
if (appendSerialData != "") println(appendSerialData + " *");
for (int i = 0; i < 5; i ++)
{
println(i);
}
delay(100);
}
如果我可以对此做出任何改进,我很高兴听到。