提供了异步串口通信 [英] Providing Asynchronous Serial Port Communication

查看:123
本文介绍了提供了异步串口通信的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

目前我们的应用程序通过串行端口连接到一个Arduino。我们的回报发送一些ASCII格式的命令,并得到相同。要做到这一点,我们有命令队列,专门编写这些命令的端口线程,并致力于读取线程和处理所有收到的回复。类本身是负责调度的答复,这是给它太多的责任(应该只是负责港口业务,而不是业务逻辑)。

Currently our application connects to an Arduino over a serial port. We send some ASCII-formatted commands, and get the same in return. To do this, we have a queue of commands, a thread dedicated to writing those commands to the port, and a thread dedicated to reading and handling all incoming replies. The class itself is responsible for dispatching the replies, which is giving it way too much responsibility (should just be responsible for port operations, not business logic).

我们宁愿做在一个异步的方式。任何系统可以发送一个回调函数和超时的命令。如果串口得到一个正确的答复,它会调用回调函数。否则,超时,也许调用第二个回调(或可能以成功了吗?标记一个回调)。

We would rather do this in an async manner. Anything in the system can send a command with a callback function and a timeout. If the serial port gets a correct reply, it calls the callback function. Otherwise, it times out and maybe calls a second callback (or possibly a single callback with a succeeded? flag).

不过,我们永远只能消耗异步方法(尤其是在网络操作)​​,不写这样的系统。任何人都可以给我们介绍一下如何进行一些指点?

However, we've only ever consumed async methods (particularly in web operations), not written such a system. Can anyone give us some pointers about how to proceed?

我们目前的计划是将存储这些命令的队列。在任何答复,如果相关联的命令被发现(通过比较ASCII值)被出列并执行回调。计时器将定期检查超时,出队,并执行适当的回调。这似乎是一个简单的解决方案,但code的量来支持这个正在大幅度增加,我们希望确保有没有这方面的任何更好的内置解决方案和最佳实践。

Our current plan is to store a queue of these commands. Upon any reply, if an associated command is found (by comparing ASCII values) it is dequeued and the callback is executed. A timer will periodically check for timeouts, dequeue, and execute the appropriate callback. It seems like a straightforward solution, but the amount of code to support this is increasing substantially and we wanted to ensure there weren't any better built-in solutions or best practices for this.

修改:为了进一步澄清,这个特殊类是单(或好或坏),并有许多运行的其他线程可以访问它。例如,一个线程可能想要请求的传感器值,而另一个线程可以被控制的电机。这些命令及其关联的答复不以线性方式发生;定时可以颠倒。因此,传统的生产者 - 消费者模式是不够的;这更是一个调度员。

Edit: To clarify further, this particular class is a singleton (for better or worse), and there are many other threads running that could access it. For example, one thread may want to request a sensor value, while another thread could be controlling a motor. These commands and their associated replies do not happen in a linear fashion; the timing may be reversed. Thus, a traditional producer-consumer model is not enough; this is more of a dispatcher.

例如,我们称之为单例类的Arduino 线程A 运行,并想送* 03,所以它调用命令 Arduino.Instance.SendCommand(* 03)。同时,线程B 发送一个命令* 88,这两者在近实时被发送。一段时间后的的Arduino SerialPort.Read()线程拿起了的答复* 88 ,然后的答复* 03 (即以相反的顺序它们发送)。我们如何让双方线程A 线程B 正确阻止等待具体的答复进来吗?我们假设,我们将使用的AutoResetEvent 每个线程内,与异步回调,让我们。设置

For example, let's call this singleton class Arduino. Thread A is running, and wants to send a command "*03", so it calls Arduino.Instance.SendCommand("*03"). Meanwhile, Thread B sends a command "*88", both of which get sent in near-realtime. Sometime later the Arduino's SerialPort.Read() thread picks up a reply for *88 and then a reply for *03 (i.e. in the opposite order they were sent). How do we allow both Thread A and Thread B to block correctly waiting on the specific reply to come in? We're assuming we will use AutoResetEvent inside each thread, with an async callback to let us .Set it.

推荐答案

如果表现你以后和异步在其最好的水平,我建议寻找到完成端口。这就是最终的下面,隐藏在Windows内核,它的真棒。当我用他们,我用C ++,竟然发现因为它的内核错误,我仅限于语言。

If performance is what you're after, and async at its finest level, I suggest looking into Completion Ports. This is what is ultimately underneath, hidden in the Windows Kernel, and it's awesome. When I used them, I used C++, even found a Kernel bug because of it, but I was limited to the language.

我见过上$ C $的CProject这篇文章这可能是值得探讨的,看看在那里你可以采取进一步的你的想法和/或使用code,它的存在。

I've seen this article on CodeProject which might be worth exploring to see where you can take your idea further and/or use the code that's there.

完成端口的本质是对回调的工作。即,在一般情况下,你放在队列中的请求,并且当一些土地有,则请求被读取和指定的回调被读出。事实上,这是一个队列,但就像我说的,在最低(管理)级(获得几乎金属之前)。

The nature of Completion ports is to work on callbacks. That is, in general, you "put" a request in the queue, and when something lands there, the request is read and the callback specified is read. It is in fact, a queue, but like I said, at the lowest (manageable) level (before getting almost on metal).

编辑:我已经写了一个排序FTP服务器/客户端测试,使用完成端口效用,所以基本过程是相同的 - 阅读和命令的写作中的 queuable 的时尚。希望它帮助。

I've written a sort of a FTP server/client testing utility with Completion ports, so the base process is the same - reading and writing of commands in a queuable fashion. Hope it helps.

编辑#2:确定,这里就是我会做,根据您的反馈和意见。我将有一个传出队列 ConcurrentQueue<消息> 。您可以通过出队每个消息发送消息一个单独的线程。的注意,如果你想成为一个更安全的,我建议偷看的消息,发送,然后出队就的反正Message类可以是内部的,而且是这个样子:

EDIT #2: Ok, here's what I would do, based on your feedback and comments. I would have an "outgoing queue", ConcurrentQueue<Message>. You can have a separate thread for sending messages by dequeueing each message. Note, if you want it a bit more "safe", I suggest peeking at the message, sending it, then dequeuing it. Anyway, the Message class can be internal, and look something like this:

private class Message {
    public string Command { get; set; }
    ... additonal properties, like timeouts, etc. ...
}

在单例类(我称之为 CommunicationService ),我也有一个 ConcurrentBag&lt;作用&LT;响应&GT;&GT; 。这就是现在的乐趣开始:O)。当一个单独的关注想做一件事,它registeres本身,例如,如果你有一个 TemepratureMeter 我会让它做这样的事情:

In the singleton class (I'll call it CommunicationService), I'd also have a ConcurrentBag<Action<Response>>. This is now where the fun starts :o). When a separate concern wants to do something, it registeres itself, for example, if you have a TemepratureMeter I would have it do something like this:

public class TemperatureMeter {
   private AutoResetEvent _signal = new AutoResetEvent(false);

   public TemperatureMeter {
     CommunicationService.AddHandler(HandlePotentialTemperatureResponse);
   }

   public bool HandlePotentialTemperatureResponse(Response response) {
     // if response is what I'm looking for
     _signal.Set();

     // store the result in a queue or something =)
   }

   public decimal ReadTemperature() {
     CommunicationService.SendCommand(Commands.ReadTemperature);
     _signal.WaitOne(Commands.ReadTemperature.TimeOut); // or smth like this

     return /* dequeued value from the handle potential temperature response */;
   }

}

而现在,在你CommunicationService,当您收到任何答复,则干脆来个

And now, in your CommunicationService, when you receive a response, you simply to a

foreach(var action in this._callbacks) {
   action(rcvResponse);
}

瞧,关注点分离。它是否回答你的问题的更好吗?

Voila, separation of concerns. Does it answer your question any better?

另一个可能的策略是,以夫妻的消息和回调,但有回调是 Func键&LT;响应,布尔&GT; 如果结果从返回的调度线程检查该函数功能是真实的,那么这个回调的处理

Another possible tactic would be, to couple message and callback, but having the Callback be a Func<Response, bool> and the dispatcher thread checks if the result returned from the Func is true, then this callback is disposed.

这篇关于提供了异步串口通信的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆