Visual C ++:串行端口数据接收事件 - ReceivedByteThreshold问题

时间:2013-11-06 10:47:07

标签: visual-c++ visual-studio-2012 visual-studio-2013

我们正在使用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的。

我想知道是否有人遇到过同样的问题,或者对这个门槛问题有任何解决方案。

提前感谢您的帮助。

耀

1 个答案:

答案 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()调用中获取了这些延迟。