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、一个用于输入消息的文本框和两个以两种不同方式发送消息的按钮。

第一种情况,用户手动发送消息很简单。只需停用该按钮,这样您就无法向其发送垃圾邮件。在第二种情况下,我试图在一个循环中发送多条消息,这似乎比我最初想象的要复杂得多。

我试着概述我的需求:

在我的另一个问题的已批准解决方案中使用 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);

}

如果我可以对此做出任何改进,我很高兴听到。