我们正在使用Visual Studio 2013 .NET Framework 4.5中的串行端口字节处理。我们遇到了改变传入数据包大小的问题。当我们尝试将ReceivedByteThreshold调整为相应的数据包大小时,当阈值设置为低于传入数据包大小时,数据Received事件被触发多次,并且当阈值大于数据包大小时不会激活。我们尝试了几种不同的方法来解决这个问题,但它们似乎都没有完美地运作。
private: System::Void serialPort1_DataReceived(System::Object^ sender, System::IO::Ports::SerialDataReceivedEventArgs^ e)
{
array<uint8_t>^ CS = gcnew array<uint8_t>(2);
//Check InvokeRequired to prevent cross threading problem
if (textBox3->InvokeRequired) {
//Invoke delegate
textBox3->Invoke(
gcnew System::IO::Ports::SerialDataReceivedEventHandler(this, &MyForm::serialPort1_DataReceived),
gcnew array<System::Object^> { sender, e }
);
}
else
{
//ASCII -> HEX Conversion
int numbytes = serialPort1->BytesToRead;
array<Byte>^ encodedBytes = gcnew array<Byte>(500);
serialPort1->Read(encodedBytes, 0, numbytes);
}
}
上面的代码仅在阈值设置为确切的数据包大小时才有效。 由于我们对传入数据长度有一些了解,因此我们使用按钮单击事件来根据传出缓冲区更改阈值,但数据仍未正确读取。 Readexisting()工作正常,但是当我们扫描任何8位ASCII字符时,它显示为“?”对于前0xFF的。
我想知道是否有人遇到过同样的问题,或者对这个门槛问题有任何解决方案。
提前感谢您的帮助。
耀
答案 0 :(得分:1)
是的,依赖ReceivedThreshold生成非常脆弱的代码。有两种强大的故障模式,显而易见的一种是在接收到上一个命令的响应之前调用SerialPort.Write()的代码中设置属性。需要联锁才能防止这种情况发生,你没有任何联锁。调试时工作正常,而不是全速运行代码。
非显而易见的失败模式是DataReceived事件处理程序中的e.EventType属性。您必须检查它以验证是否为SerialData.Chars调用了事件处理程序。当您传输二进制数据时,它也会触发SerialData.Eof,您必须忽略它。
Interlocking至少需要一个AutoResetEvent,在事件处理程序中调用它的Set()方法,你必须在调用Write()的代码中调用它的WaitOne()方法,这样它才能在收到响应之前继续进行。您现在遇到另一个令人讨厌的问题,您的Invoke()调用将会死锁。您需要通过调用BeginInvoke()来修复它,并且只在调用Read()之后才这样做。您还必须传递数组的副本,以便在DataReceived继续触发时无法更改它。
这一切都很难做到。核心问题是,您的接收代码本身无法确定何时收到完整的响应。这需要一个协议,响应字节中有足够的信息,以便读者知道响应何时完成。您可能完全取消DataReceived事件并让调用Write()的代码也立即调用Read()来获取响应。这会导致延迟,但您已经从所需的WaitOne()调用中获取了这些延迟。