C# 中的发出/响应串行端口处理
好吧,这就是问题(这与我之前的帖子有关)
我需要能够有一个串行通信的问题/响应系统,其工作原理如下:
问题:你好 回应:世界? 问题: 不,护士你好 回复:好吧,你一点也不有趣。
这意味着我说“你好”,远程单元预计会发回“世界?” 在某个时间范围内,如果没有,我应该有办法访问该缓冲区,所以这就是我的想法,请给我反馈
ReaderWriterLock'd 'readBuffer' 将写入流的问题方法 一个响应方法,它将监视 readBuffer 直到它包含我期望的内容或直到超时到期。
首先,stackoverflow 社区将如何设计这个类,其次,他们将如何编写 datarecieved 事件? 第三,他们如何使该代码更加健壮,以便该类的多个实例可以存在于并行线程中以进行同时通信?
Okay here's the problem (this is related to a previous post of mine)
I need to be able to have an issue/response system for serial comms that works something like this:
issue: hello
response: world?
issues: no, hello nurse
reponse: well you're no fun.
this would mean I say "hello" the remote unit is expected to send back "world?" within some timeframe, and if it doesn't i should have a way to access that buffer, so here's what i'm thinking, please give me feedback
a ReaderWriterLock'd 'readBuffer'
a Issue Method that will write to the stream
a Response Method that will watch the readBuffer until it contains what i'm expecting or until the timeout expires.
first, how would the stackoverflow community design this class, second, how would they write the datarecieved event? Third how would they make this code more robust so that multiple instances of the class can exist in parallel threads for simultaneous communications?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
这基本上是一个生产者-消费者问题,因此这应该是一般问题的基础设计。
以下是对此的一些想法:
a) FIFO 缓冲区(队列)
首先,您的类的每个实例都应该有一个线程安全队列(一个 FIFO 缓冲区)的实例。 一个线程将接收数据并填充它,而另一个线程将以线程安全的方式读取数据。 这仅意味着您必须在每个入队/出队操作上使用锁。
FIFO 队列使您能够同时处理工作线程中的数据,同时从通信线程填充数据。 如果您需要接收大量数据,您可以在工作线程中将一些数据出队,并在接收到所有数据之前对其进行解析。 否则,您需要等到收到所有数据才能立即解析所有数据。 在大多数情况下,在开始解析数据之前,您不知道应该获取多少数据。
b)等待数据的工作线程
我将创建一个工作线程,它将等待已收到新数据的信号。 您可以使用
ManualResetEvent.WaitOne(timeOut)
来设置超时,以防一段时间内没有任何反应。 收到数据后,您必须根据当前状态对其进行解析 - 因此这将是 状态机。c) 端口抽象
要处理不同类型的端口,您可以将串行端口包装在一个接口中,该接口至少可以有这些方法(我可能忘记了一些方法):
这将帮助您分离特定的端口来自状态机的通信代码。
注意:
(根据 Microsoft 的说法)不保证为每个接收到的字节引发 DataReceived 事件。 使用 BytesToRead 属性来确定缓冲区中还剩下多少数据可供读取。 因此,您可以创建自己的 IPort 实现,它将定期轮询 SerialPort 以确保您不会错过任何字节(有一个 SO 问题已经解决了这个问题)。
d) 接收数据
要接收数据,您必须为
IPort.DataReceived
事件(或SerialPort.DataReceived
,如果你没有包装它),并将接收到的数据排队到处理程序内的队列中。 在该处理程序中,您还可以设置提及ManualResetEvent
来通知工作线程已收到新数据。This is basically a producer-consumer problem, so that should be the basis for the general design.
Here are some thoughts on that:
a) FIFO buffer (Queue)
First of all, you should have an instance of a thread-safe Queue (a FIFO buffer) for each instance of your class. One thread would receive the data and fill it, while the other one would read the data in a thread-safe manner. This only means you would have to use a lock on each enqueue/dequeue operation.
FIFO Queue would enable you to simultaneously process the data in the worker thread, while filling it from the communication thread. If you need to receive lots of data, you could dequeue some data in the worker thread and parse it before all of it has been received. Otherwise you would need to wait until all data has been received to parse it all at once. In most cases, you don't know how much data you are supposed to get, until you start to parse it.
b) Worker thread waiting for data
I would create a worker thread which would wait for a signal that new data has been received. You could use
ManualResetEvent.WaitOne(timeOut)
to have a timeout in case nothing happens for a while. When the data is received, you would have to parse it, based on your current state -- so that would be an implementation of a state machine.c) Port abstraction
To handle different types of ports, you could wrap your serial port inside an interface, which could have at least these methods (I might have forgotten something):
This would help you separate the specific communication code from the state machine.
NOTE:
(according to Microsoft) The DataReceived event is not guaranteed to be raised for every byte received. Use the BytesToRead property to determine how much data is left to be read in the buffer. So you could create your own implementation of IPort which would poll SerialPort in regular intervals to ensure that you don't miss a byte (there is a question of SO which already addresses this).
d) Receiving data
To receive the data, you would have to attach a handler for the
IPort.DataReceived
event (orSerialPort.DataReceived
, if you're not wrapping it), and enqueue the received data to the Queue inside the handler. In that handler you would also set the mentionelManualResetEvent
to notify the worker thread that new data has been received.我在为格式化数据编写异步套接字侦听器时遇到了类似的设计问题。
我想出的 SerialPort / DataReceived 模型的翻译是这样的:
主类封装了问题/响应系统 - 它将包含根据输入生成响应的逻辑。 该类的每个实例都将绑定到一个可以在构造期间或之后设置的串行端口。 将有一个 StartCommunications 类型方法 - 它将 DataReceived 事件连接到类中的另一个方法。 此方法负责从端口抓取数据,并确定完整消息是否已到达。 如果是这样,它会引发自己的事件(在类上定义),该事件将有一个适当的方法连接到它。 您还可以让它调用类上的预定义方法,而不是引发事件 - 我定义了一个事件来提高灵活性。
该基本设计在生产系统中运行良好,并且可以比连接到它的其他系统处理更多的输入。
I had a similar design issue writing an asynchronous socket listener for formatted data.
A translation of what I came up with into the SerialPort / DataReceived model would be something like this:
The main class encapsulates the issue/response system - it will contain the logic for generating the response based on input. Each instance of the class will be bound to a single serial port that can be set during or after construction. There will be a StartCommunications type method - it will wire up the DataReceived event to another method in the class. This method is responsible for grabbing the data from the port, and determining if a full message has arrived. If so, it raises its own event (defined on the class), which will have an appropriate method wired to it. You could also have it call a predefined method on your class instead of raising an event - I defined an event to improve flexibility.
That basic design is working just fine in a production system, and can handle more input than the rest of the systems connected to it can.