我需要事件,它会在收到完整行后调用我的函数,而不仅仅是一个字节。
.NET中的SerialPort对象有3个事件:DataReceived,ErrorReceived,PinChanged。
当我使用DataReceived时 - 事件在1个字节之后“触发”,或者在“ReceiveByteThreshold”属性中定义的“x”字节之后。线长可能会有所不同,所以我无法预测“x”。
有人能给我一个暗示吗?
我必须创建一些缓冲区,它将收集字节直到LF / CRLF,或者有更好的方法解决问题?
答案 0 :(得分:6)
你无法得到这个,唯一的选择是SerialPort.ReceivedBytesThreshold来延迟DataReceived事件处理程序调用,这对于可变长度响应是无用的。
解决方法非常简单,只需在DataReceived事件处理程序中调用ReadLine()即可。这将阻止工作线程,不会影响程序中发生的任何其他事情。在ReadLine()调用阻塞时触发其他事件没有危险,它在SerialPort类中是互锁的。如果通信不够可靠,请使用ReadTimeout属性,以便ReadLine()不会永久阻塞。将其设置为接收最长可能响应的预期延迟的十倍。
答案 1 :(得分:2)
你必须自己做。使用DataReceived并检查每个字节。收集缓冲区中的字节,直到获得换行符,然后在该点处将缓冲区作为一行处理。
答案 2 :(得分:0)
提示:
SerialPort
类具有属性NewLine
,用于设置用于解释对ReadLine方法的调用结束的值。
答案 3 :(得分:0)
这是我快速实现的,非阻塞的,相同的线程解决方案。它是一个非常基本的状态机,等待'\ r'和'\ n',然后发送所有缓冲的字符进行解析。您可以通过更改状态机本身将其更改为所需的任何换行符值。 在此方法中,您可以注册OnNewLineReceived事件并处理SerialStringMessgae事件处理程序中的数据。 没有尝试/捕获开销。没有死锁。
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace NonBlockingSerialPortReadLine
{
public partial class Form1 : Form
{
System.IO.Ports.SerialPort sp = new System.IO.Ports.SerialPort();
public event EventHandler OnNewLineReceived;
System.Windows.Forms.Timer NewDataTimer = new System.Windows.Forms.Timer();
int StateMachine = 0;
StringBuilder stringBuffer = new StringBuilder();
public Form1()
{
InitializeComponent();
InitTimer();
InitOnNewLineReceived();
}
private void InitTimer()
{
NewDataTimer.Interval = 50;
NewDataTimer.Tick += NewDataTimer_Tick;
}
private void InitOnNewLineReceived()
{
OnNewLineReceived += Form1_OnNewLineReceived;
}
void Form1_OnNewLineReceived(object sender, EventArgs e)
{
SerialStringMessgae STM = e as SerialStringMessgae;
string messgae = STM.message;
// PARSE YOU MESSAGE HERE - the debug line below is not mandatory
System.Diagnostics.Debug.WriteLine(messgae);
}
class SerialStringMessgae : EventArgs
{
public string message;
}
private void StartListeningButton_Click(object sender, EventArgs e)
{
StartListeningButton.Enabled = false;
sp = new System.IO.Ports.SerialPort("COM4",57600, System.IO.Ports.Parity.None, 8, System.IO.Ports.StopBits.One);
try
{
sp.Open();
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
return;
}
if (sp.IsOpen)
{
NewDataTimer.Enabled = true;
}
}
void NewDataTimer_Tick(object sender, EventArgs e)
{
string newData = sp.ReadExisting();
foreach (char c in newData)
{
switch (StateMachine)
{
case 0:
// waiting for '\r'
if (c == '\r')
{
StateMachine = 1;
}
else
{
stringBuffer.Append(c);
}
break;
case 1:
// waiting for '\n'
if (c == '\n')
{
if (OnNewLineReceived != null)
{
SerialStringMessgae STM = new SerialStringMessgae();
STM.message = stringBuffer.ToString();
OnNewLineReceived(this, STM);
}
}
// after parsing the message we reset the state machine
stringBuffer = new StringBuilder();
StateMachine = 0;
break;
}
}
}
}
}