不断从带有后台线程的串口读取

时间:2010-11-12 21:32:51

标签: .net python ironpython serial-port

由于串口通信是异步的,我很早就想到了涉及与RS 232设备通信的项目,我必须让后台线程不断读取接收数据的端口。现在,我正在使用IronPython(.NET 4.0),因此我可以访问.NET内置的光滑SerialPort类。这让我可以编写如下代码:

self.port = System.IO.Ports.SerialPort('COM1', 9600, System.IO.Ports.Parity.None, 8, System.IO.Ports.StopBits.One)
self.port.Open()
reading = self.port.ReadExisting() #grabs all bytes currently sitting in the input buffer

足够简单。但正如我所提到的,我希望在新数据到达时不断检查此端口。理想情况下,只要数据等待,我就会让操作系统告诉我。 Whaddaya知道,我的祈祷已经回答,提供了DataReceived事件!

self.port.DataReceived += self.OnDataReceived

def OnDataReceived(self, sender, event):
    reading = self.port.ReadExisting()
    ...

太糟糕了,这是毫无价值的,because this event isn't guaranteed to be raised

  

不保证每收到一个字节都会引发DataReceived事件。

那么回到编写一个监听器线程。我用BackgroundWorker快速完成了这项工作,只是一遍又一遍地调用port.ReadExisting()。它在进入时读取字节,当它看到一行结束(\r\n)时,它将读取的内容放入linebuffer。然后我的程序的其他部分查看linebuffer以查看是否有任何完整的行等待使用。

现在,这显然是一个经典的生产者 - 消费者问题。制作人是BackgroundWorker,将完整的行放入linebuffer。消费者是一些代码,它们尽可能快地消耗linebuffer中的那些行。

但是,消费者效率低下。现在他经常检查linebuffer,每次都发现它是空的,感到失望;虽然每隔一段时间就会发现一条线在里面等待。优化这种方法的最佳方法是什么,以便消费者只有在有线路可用时醒来?这样,消费者不会不停地访问linebuffer,这可能会引入一些并发问题。

另外,如果有一个更简单/更好的方式从串口不断阅读,我愿意接受建议!

4 个答案:

答案 0 :(得分:3)

我不明白为什么你不能使用DataReceived事件。正如文档所述

  

DataReceived事件不是   保证为每个字节提高   接收。使用BytesToRead属性   确定剩下多少数据   在缓冲区中读取。

所有这一切都表明,您无法保证为每个单独的数据字节获取单独的事件。您可能需要使用BytesToRead属性检查并查看端口上是否有多个字节可用,并读取相应的字节数。

答案 1 :(得分:2)

没有必要使用旋转线程来轮询串行端口。

我建议使用SerialPort.BaseStream.BeginRead(...)方法。它比尝试在SerialPort类中使用事件要好得多。对BeginRead的调用立即返回并注册一个读取完成后调用的异步回调。在回调方法中,您调用EndRead并返回读入提供的缓冲区的字节数。 BaseStream(SerialStream)继承自Stream并遵循.Net Streams的一般模式,这非常有用。

但重要的是要记住,它是一个调用回调的.Net线程,因此您需要快速处理数据,或者将任何繁重的工作转移到您自己的线程之一。我强烈建议阅读以下链接,特别是备注部分。

http://msdn.microsoft.com/en-us/library/system.io.stream.beginread.aspx

答案 2 :(得分:1)

为什么在LineBuffer

中放置完整一行时,您的制作人不会举起活动

另外,rom阅读文档,我相信它只是说读取100个字节!= 100个事件引发 - 它并不是说不能依赖这些事件。

答案 3 :(得分:1)

这是一些VB代码,表达了我对如何做到这一点的看法:

Dim rcvQ As New Queue(Of Byte()) 'a queue of buffers
Dim rcvQLock As New Object

Private Sub SerialPort1_DataReceived(ByVal sender As System.Object, _
                                     ByVal e As System.IO.Ports.SerialDataReceivedEventArgs) _
                                 Handles SerialPort1.DataReceived

    'an approach
    Do While SerialPort1.IsOpen AndAlso SerialPort1.BytesToRead <> 0
        'what if the number of bytes available changes at ANY point?
        Dim bytsToRead As Integer = SerialPort1.BytesToRead
        Dim buf(bytsToRead - 1) As Byte

        bytsToRead = SerialPort1.Read(buf, 0, bytsToRead)

        'place the buffer in a queue that can be processed somewhere else
        Threading.Monitor.Enter(rcvQLock)
        rcvQ.Enqueue(buf)
        Threading.Monitor.Exit(rcvQLock)

    Loop

End Sub

对于它的价值,与此非常相似的代码已经处理了接近1Mbps的串行端口数据。