编辑:
我正在开发一个从串口接收数据的程序,数据将以" \ r \ n"结尾。我正在使用Data_Received
事件从串口接收数据。
下面是我的程序,我尽量避免使用Thread.Sleep并使用线程等待数据完全接收,但步骤总是跳转到return "Error"
步骤。
我想知道如何让程序在跳转到return "Error"
步之前等待数据完全接收?
如果不使用线程,如果有人有更好的方法,那就没关系。
using System;
using System.Windows.Forms;
using System.IO.Ports;
namespace SerialPortExample
{
public partial class Form1 : Form
{
string port1ReceivedMessage = string.Empty;
internal delegate void SerialDataReceivedEventHandlerDelegate(
object sender, SerialDataReceivedEventArgs e);
SerialPort comPort = new SerialPort();
string InputData = string.Empty;
delegate void SetTextCallBack(string text);
public Form1()
{
InitializeComponent();
comPort.DataReceived += new SerialDataReceivedEventHandler(port_DataReceived);
}
~Form1()
{
comPort.Close();
}
public string SendDataAndReadData()
{
port1ReceivedMessage = string.Empty;
string sendData = "?";
Send(sendData); // To send data
Thread t = new Thread(ThreadProc);
t.Name = "Thread_1";
t.Start();
//Thread.Sleep(1000);
if(!string.IsNullOrEmpty(port1ReceivedMessage) && port1ReceivedMessage.Contains("\n"))
{
event_1.Set();
return port1ReceivedMessage;
}
return "Error";
}
private void ThreadProc()
{
event_1.WaitOne();
}
private void port_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
InputData = comPort.ReadExisting();
if(InputData != string.Empty)
{
try
{
this.BeginInvoke(new SetTextCallBack(SetText), new object[] { InputData });
}
catch (TimeoutException timeoutEx)
{
//Skip exception handling codes
}
catch (Exception ex)
{
//Skip exception handling codes
}
}
}
private void SetText(string text)
{
port1ReceivedMessage += text;
}
private void portOpenButton_Click(object sender, EventArgs e)
{
comport = new SerialPort("COM4", 9600, Parity.None, 8, StopBits.One);
comport.ReadTimeout = 2000;
if (!comport.IsOpen)
{
comport.Open();
}
}
private void sendButton_Click(object sender, EventArgs e)
{
SendDataAndReadData();
}
private void Send(string text)
{
// Skip some text formatting codes
comPort.Write(text);
}
}
答案 0 :(得分:1)
DataRecieved
事件引发了一些randomely。当它发射时你不会总是得到整个弦。值得庆幸的是,一旦掌握了整个内容,就会知道您的消息是什么样的。你需要建立你的字符串,直到你看到一个/r/n
,然后知道你拥有整个字符串后你需要做什么。
private StringBuilder sb;
private SerialPort sp;
public void init_state_machine()
{
sp = new SerialPort("COM4", 9600, Parity.None, 8, StopBits.One);
sp.DataReceived += new SerialDataReceivedEventHandler(sp_DataReceived);
sb = new StringBuilder();
sb.Clear();
}
private void sp_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
string currentLine = "";
string Data = sp.ReadExisting();
Data.Replace("\n", ""); //remove new lines
foreach (char c in Data)
{
if (c == '\r')
{
currentLine = sb.ToString();
sb.Clear();
Console.WriteLine(currentLine);
}
else
{
sb.Append(c);
}
}
}
至于发送,您只需进行发送,当设备响应时,您的DataRecieved
事件将会触发。 DataRecieved
事件已经在它自己的线程中,因此您不需要处理您尝试执行的所有线程化事件。
答案 1 :(得分:0)
请阅读:
If you must use .NET System.IO.Ports.SerialPort。
这是Ben Voigt在sparxeng.com上撰写的2014年博客文章。
摘录:
说得客气一点,System.IO.Ports.SerialPort是由远远超出其核心竞争力的计算机科学家设计的。他们既不了解串行通信的特性,也不了解常见的用例,它表明了这一点。它也不能在运输之前的任何现实世界场景中进行测试,而不会发现存在记录的界面和未记录的行为的缺陷,并使用System.IO.Ports.SerialPort(以下称为IOPSP)进行可靠的通信是一场真正的噩梦。
和
最糟糕的违规System.IO.Ports.SerialPort成员,不仅不应该被使用,而且是深层代码味道的迹象,需要重新架构所有IOPSP使用:
- DataReceived事件(100%冗余,也完全不可靠)
- BytesToRead属性(完全不可靠)
- Read,ReadExisting,ReadLine方法(处理错误完全错误,并且是同步的)
- PinChanged事件(按顺序发送 尊重你可能想知道的每一件有趣的事情。)
和
并且没有人使用的一个成员因为MSDN没有给出任何例子,但对你的理智绝对是必不可少的:
- BaseStream属性
尽管Ben Voigt负责,我们使用BytesToRead
属性和ReadExisting()
Thread.Sleep()
。我们避免使用DataReceived()
和ReadLine()
。这是我们的解决方法,我们也不知道BaseStream属性。
对于新项目,我建议使用BaseStream
属性。