简体   繁体   English

C#:读取串口 - BytesToRead

[英]C#: reading serial port - BytesToRead

I am modifying a C# based UI that interfaces to a small PIC microcontroller tester device. 我正在修改一个基于C#的UI,它与一个小型PIC单片机测试设备接口。

The UI consists of a couple buttons that initiates a test by sending a "command" to the microcontroller via a serial port connection. UI由几个按钮组成,通过串行端口连接向微控制器发送“命令”来启动测试。 Every 250 milliseconds, the UI polls the serial interface looking for a brief message comprised of test results from the PIC. 每250毫秒,UI轮询串行接口,查找由PIC的测试结果组成的简短消息。 The message is displayed in a text box. 消息显示在文本框中。

The code I inherited is as follows: 我继承的代码如下:

try
{
    btr = serialPort1.BytesToRead;
    if (btr > 0)
        Thread.Sleep(300);
    btr = serialPort1.BytesToRead;
    if (btr > 0)
    {
        Thread.Sleep(300);
        btr = serialPort1.BytesToRead;

        numbytes = serialPort1.Read(stuffchar, 0, btr);
        for (x = 0; x < (numbytes); x++)
        {
            cc = (stuffchar[x]);
            stuff += Convert.ToString(Convert.ToChar((stuffchar[x])));
        }

What would be the rationale for the first several lines consisting of three calls to BytesToRead and two 300 millisecond sleep calls before finally reading the serial port? 在最终读取串口之前,前三行包括三次调用BytesToRead和两次300毫秒睡眠调用的理由是什么? Unless I am interpreting the code incorrectly, any successful read from the serial port will take more than 600 milliseconds, which seems peculiar to me. 除非我不正确地解释代码,否则从串口读取任何成功的内容都需要600毫秒以上,这对我来说似乎很奇怪。

It is a dreadful hack around the behavior of SerialPort.Read(). 这是一个关于SerialPort.Read()行为的可怕黑客。 Which returns only the number of bytes actually received. 它仅返回实际接收的字节数。 Usually just 1 or 2, serial ports are slow and modern PCs are very fast. 通常只有1或2个,串口很慢,现代PC速度很快。 So by calling Thread.Sleep(), the code is delaying the UI thread long enough to get the Read() call to return more bytes. 因此,通过调用Thread.Sleep(),代码将UI线程延迟足够长的时间以使Read()调用返回更多字节。 Hopefully all of them, whatever the protocol looks like. 希望所有这些,无论协议是什么样的。 Usually works, not always. 通常有效,但并非总是如此 And in the posted code it didn't work and the programmer just arbitrarily delayed twice as long. 在发布的代码中它没有用,程序员只是随意延迟了两倍。 Ugh. 啊。

The great misery of course is that the UI thread is pretty catatonic when it is forced to sleep. 当然,最大的痛苦是UI线程在被迫睡眠时非常紧张。 Pretty noticeable, it gets very slow to paint and to respond to user input. 非常引人注目,绘画和响应用户输入变得非常慢。

This needs to be repaired by first paying attention to the protocol. 这需要首先注意协议来修复。 The PIC needs to either send a fixed number of bytes in its response, so you can simply count them off, or give the PC a way to detect that the full response is received. PIC需要在其响应中发送固定数量的字节,因此您可以简单地将它们计数,或者让PC以某种方式检测是否已收到完整响应。 Usually done by sending a unique byte as the last byte of a response (SerialPort.NewLine) or by including the length of the response as a byte value at the start of the message. 通常通过发送唯一字节作为响应的最后一个字节(SerialPort.NewLine)或通过将响应的长度包含在消息开头的字节值来完成。 Specific advice is hard to give, you didn't describe the protocol at all. 具体的建议很难给出,你根本没有描述协议。

You can keep the hacky code and move it into a worker thread so it won't affect the UI so badly. 您可以保留hacky代码并将其移动到工作线程中,这样就不会对UI造成太大影响。 You get one for free from the SerialPort.DataReceived event. 您可以从SerialPort.DataReceived事件中免费获得一个。 But that tend to produce two problems instead of solving the core issue. 但这往往会产生两个问题,而不是解决核心问题。

If that code initially was in a loop it might have been a way to wait for the PIC to collect data. 如果该代码最初处于循环中,则可能是等待PIC收集数据的一种方法。

If you have real hardware to test on I would sugest you remove both Sleeps. 如果你有真正的硬件要测试我会消化你删除两个睡眠。

@TomWr you are right, from what I'm reading this is the case. @TomWr你是对的,从我正在读的是这种情况。
Your snippet below with my comments: 您的评论下面的代码段:

try
{
    // Let's check how many bytes are available on the Serial Port
    btr = serialPort1.BytesToRead;

    // Something? Alright then, let's wait 300 ms.
    if (btr > 0)
        Thread.Sleep(300);

    // Let's check again that there are some bytes available the Serial Port
    btr = serialPort1.BytesToRead;

    // ... and if so wait (maybe again) for 300 ms 
    // Please note that, at that point can be all cumulated about 600ms 
    // (if we actually already waited previously)
    if (btr > 0)
    {
        Thread.Sleep(300);
        btr = serialPort1.BytesToRead;

        numbytes = serialPort1.Read(stuffchar, 0, btr);
        for (x = 0; x < (numbytes); x++)
        {
            // Seems like a useless overhead could directly use
            // an Encoding and ReadExisting method() of the SerialPort.
            cc = (stuffchar[x]);
            stuff += Convert.ToString(Convert.ToChar((stuffchar[x])));
        }

My guess is the same it as been already mentioned above by idstam, basically probably to check whether is data sent by your device and fetch them 我的猜测与idstam上面已经提到的相同,基本上可能是为了检查你的设备是否发送数据并获取它们

You can easily refactor this code with the appropriate SerialPort methods cause there are actually much better and concise ways to do check whether there are data available or not on the Serial Port. 您可以使用适当的SerialPort方法轻松地重构此代码,因为实际上有更好,更简洁的方法来检查串行端口上是否有可用数据。

Instead of "I'm checking how many bytes are on the port, if there is something then I wait 300 ms and later same thing again." 而不是“我正在检查端口上有多少字节,如果有什么东西,那么我等待300毫秒,然后再重复同样的事情。” that is miserably ending up with 那是悲惨的结局
"So yep 2 times 300 ms = 600ms, or just once (depending on whether there was a first time", or maybe nothing at all (depending on the device you are communicating to through this UI which can be really slacky since the Thread.Sleep is blocking the UI...). " “所以是2次300毫秒= 600毫秒,或者只是一次(取决于是否有第一次”,或者根本没有任何东西)(取决于你通过这个UI进行通信的设备,自从线程以来可能真的很松懈。睡眠阻碍了用户界面......)。“

First let's considering for a while that you are trying to keep as much of the same codebase, why not just wait for 600ms? 首先让我们考虑一下你试图保持相同的代码库,为什么不等待600ms呢?

Or why not just using the ReadTimeout property and catching the timeout exception, not that clean but at least better in terms of readability and plus you are getting your String directly instead of using some Convert.ToChar() calls... 或者为什么不只是使用ReadTimeout属性并捕获超时异常,不是那么干净但至少在可读性方面更好,而且你直接得到你的String而不是使用一些Convert.ToChar()调用......

I sense that the code has been ported from C or C++ (or at least the rationale behind) from someone who rather has mostly an embedded software background. 我感觉代码已经从C或C ++移植(或者至少是背后的理由)来自一个主要是嵌入式软件背景的人。

Anyway, back to the number of available bytes checks, I mean unless the Serial Port data are flushed in another Thread / BackgroundWorker / Task handler, I don't see any reason of checking it twice especially in the way it is coded. 无论如何,回到可用字节数的检查,我的意思是除非在另一个Thread / BackgroundWorker / Task处理程序中刷新串行端口数据,否则我没有看到任何检查它的原因,特别是它的编码方式。
To make it faster? 为了加快速度? Not really, cause there is an additional delay if there are actually data on the Serial Port. 不是真的,因为如果串行端口上确实存在数据,则会有额外的延迟。 It does not make that much sense to me. 它对我来说没有多大意义。

Another way to make your snippet slightly better is to poll using the ReadExisting(). 使你的代码片段更好的另一种方法是使用ReadExisting()进行轮询。

Otherwise you can also consider asynchronous methods using the SerialPort BaseStream. 否则,您还可以考虑使用SerialPort BaseStream的异步方法。

All in all it's pretty hard to say without access the rest of your codebase, aka, the context. 总而言之,如果不访问代码库的其余部分(即上下文),很难说。

If you had more information about what are the objectives / protocol it could give some hints about what to do. 如果您有关于目标/协议的更多信息,它可以给出一些关于如何做的提示。 Otherwise, I just can say this seems to be poorly coded, once again, out of context. 否则,我可以说这似乎编码很差,再次脱离了上下文。

I double and even triple back what Hans mentioned about the UI responsiveness in the sense that really I hope your snippet is running in a thread which is not the UI one (although you mentioned in your post that the UI is polling I still hope the snippet is for another worker). 我将Hans提到的关于UI响应的内容翻了三倍甚至三倍,我希望你的代码片段在一个不是UI的线程中运行(虽然你在帖子中提到UI是轮询我仍然希望片段是另一个工人)。

If that's really the UI thread then it will be blocked every time there is a Thread.Sleep call which makes the UI not really responsive to the user interactions and may give some feeling of frustration to your end-user. 如果那真的是UI线程,那么每当有一个Thread.Sleep调用时它就会被阻塞,这使得UI不能真正响应用户交互,并且可能给最终用户带来一些挫败感。

Might also worth to subscribe to the DataReceived event and perform what you want/need with the handler (eg using a buffer and comparing value, etc.). 可能也值得订阅DataReceived事件并使用处理程序执行您想要/需要的事情(例如使用缓冲区和比较值等)。

Please note that mono is still not implementing the trigger of this event but if you are running against a plain MS .NET implementation this is perfectly fine without the hassles of the multi-threading. 请注意,mono仍未实现此事件的触发器,但如果您针对普通的MS .NET实现运行,则无需多线程的麻烦就完全没问题。

In short: 简而言之:

  • Check which thread(s) is(are) taking care of your snippet and mind about the UI responsiveness 检查哪些线程正在处理您的代码片段并关注UI响应性
    • If it is the UI, then use another thread through Thread, BackgroundWorker (Threadpool) or Task. 如果是UI,则通过Thread,BackgroundWorker(Threadpool)或Task使用另一个线程。
    • Stream Asynchronous Methods in order to avoid the hassles of the of UI thread synchronization 流异步方法以避免UI线程同步的麻烦
  • Try to see whether the objectives really deserve a double 300 ms Thread Sleep method call 尝试看看目标是否真的值得双300毫秒线程睡眠方法调用
  • You can directly fetch the String instead of gathering if latter checks are using so to perform operations rather than gathering the Bytes by yourself (if the chosen encoding can fulfill your needs). 您可以直接获取String而不是收集,如果后面的检查使用,则执行操作而不是自己收集字节(如果所选的编码可以满足您的需要)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM