WPF应用程序中的串行端口 [英] Serial Port in WPF Application

查看:107
本文介绍了WPF应用程序中的串行端口的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在构建一个涉及通过串行端口发送数据的应用程序(使用WPF).当有大量数据要发送或接收时,我遇到了一个小(或大)问题.
数据以行形式发送/接收,每行之间的延迟很小.为了实现延迟,我使用了以下代码-(我发现AllowUIToUpdate例程已准备就绪,不确定是否有更好的方法).在发送/接收数据时,我使用此代码而不是正常的睡眠来更新文本块.

I am building an application (using WPF) that involves sending data via serial port. I have a small (or big) problem when there is a lot of data to send or receive.
The data is send/received in lines with a small delay between each line. For implementing the delay I used the following code - (the AllowUIToUpdate routine I found it ready made and am not sure if there is a better way). I use this code instead of a normal Sleep to update a textblock while sending/receiving the data.

private void waitms(int ms)
{
    bool done = false;
    int val1 = Environment.TickCount;
    while (!done)
    {
        if ((Environment.TickCount - val1) > ms) done = true;
        AllowUIToUpdate();
    }
}
void AllowUIToUpdate()
{
    DispatcherFrame frame = new DispatcherFrame();
    Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Render, new DispatcherOperationCallback(delegate(object parameter)
    {
        frame.Continue = false;
        return null;
    }), null);
    Dispatcher.PushFrame(frame);
}



无论如何,这对于几行数据来说都可以正常工作,但是当我有很多行数据要发送/接收时,系统开始变慢,这真的很糟糕.我已经在每行之间编码了500ms的延迟.前几行很好地跟随了这一行,但是随着行数的增加,延迟开始变得越来越大,最后,每行之间有多达5秒的延迟.

那么,是什么原因导致这种放缓?可以是上面的例程,也可以是串行通信本身吗?有什么想法吗?




Anyway this works fine for a few lines of data but when I have many lines of data to send / receive, the system starts to slow down really bad. I have coded a 500ms delay between each line. The first lines follow that pretty well but as the lines increase, the delay starts getting bigger and bigger and at the end I have up to 5seconds delay between each line.

So what is causing this slowdown? Can it be the routine above or the serial communication itself? Any ideas?


private void fprogram_Click(object sender, RoutedEventArgs e)
{
    DateTime now = DateTime.Now;
    int dataLength = 0;
    byte[] data;
    labelComms.Content = "Programming";
    if (serial.IsOpen)
    {
        for (int j = 0; j < _modnames.Count; j++)
        {
            for (int k = 0; k < 2; k++)
            {
                textBlock1.Inlines.Add("\n\n Programming Module " + _modnames[j] + k.ToString("D1") + "\n");
                waitms(1000);
                for (int i = 0; i < 21; i++)
                {
                    System.Threading.Thread.Sleep(100);
                    now = DateTime.Now;
                    textBlock1.Inlines.Add(now.ToString("\nhh:mm:ss - " ));
                    for (int m = 0; m < 5; m++)
                    {
                        textBlock1.Inlines.Add(_txbuffer[m].ToString("X2"));
                    }
                    scrollview1.ScrollToEnd();
                    for (int m = 0; m < 6; m++)
                    {
                        _txbuffer[(5+m)] = eprom[(m+(k*6)), i, j];                                  // fill TXbuffer
                        textBlock1.Inlines.Add(_txbuffer[(5+m)].ToString("X2"));          // write to Textblock
                    }
                    System.Threading.Thread.Sleep(100);                                             //wait 100ms
                    serial.DiscardInBuffer();                                                      // flush read buffer
                    serial.Write(_txbuffer, 0, 11);                                                // send TX buffer
                    //receive
                    bool timeOut = false;
                    int val1 = Environment.TickCount;
                    dataLength = 0;
                    while ((dataLength < 11) && (timeOut == false))     //wait untill RXbuffer has 11characters
                    {
                        dataLength = serial.BytesToRead;
                        if ((Environment.TickCount - val1) > 1000) timeOut = true;
                        AllowUIToUpdate();                         // Update UI - otherwise textblock will not be updated
                    }
                    System.Threading.Thread.Sleep(10);
                    if (timeOut == false)
                    {
                        dataLength = serial.BytesToRead;
                        data = new byte[dataLength];
                        int nbrDataRead = serial.Read(data, 0, dataLength);                         //read Data
                        textBlock1.Inlines.Add("  -  ");
                        foreach (byte chr in data)
                        {
                            textBlock1.Inlines.Add(chr.ToString("X2"));       // write to textblock
                        }
                        textBlock1.Inlines.Add(" .... ");
                        if ((data[0] == Comms.mod_initrx1) && (data[1] == Comms.mod_initrx2) && (data[2] == _txbuffer[2]) && (data[3] == _txbuffer[3]) && (data[5] == 0x88))
                        {
                            textBlock1.Inlines.Add("  -  OK");                      // receive data OK
                        }
                        else textBlock1.Inlines.Add("  -  NOT OK");                // receive data NOT OK
                    }
                    else
                    {
                        textBlock1.Inlines.Add("  === TIMEOUT \n");           //Timeout on waiting for receive
                        break;
                    }
                }
            }
        }
        textBlock1.Inlines.Add("\n\n  -  DONE   -\n\n");
    }
    else textBlock1.Inlines.Add("\n   NOT CONNECTED!!  \n ");
}



好的,这是我用于发送/接收数据的功能.我试图在可能的地方将waitms更改为Sleep,但是如果更改它们,则所有文本块都不会更新.
因此,您能指出如何将串行通讯放在单独的线程中吗,因为我还是WPF的初学者.谢谢.



Ok here is the function I am using for sending /receiving the data. I tried to change waitms to Sleep where possible but if I change them all the the textblock will not be updated.
So can you point out how to put the serial comm in a separate thread as I am still a very beginner on WPF. Thanks.

推荐答案

从此代码中,尚不清楚串行通信中需要什么延迟以及为什么需要延迟,但是延迟的实现仍然是错误的.您使用旋转等待,无论如何都应避免.旋转等待会毫无用处地使用100%的CPU内核.

所有等待方法应基于线程方法. 1)要无条件等待一定时间,请使用System.Threading.Thread.Sleep. 2)如果您需要等到特定条件,请始终使用System.Threading.EventWaitHandle.WaitOne(是否超时).在这两种情况下,线程都被操作系统关闭,并且直到操作系统唤醒后才安排回执行.时间到期,或从另一个线程调用以下方法将其唤醒:Thread.AbortSystem.Threading.EventWaitHandle.Set(如果在同一线程上调用System.Threading.EventWaitHandle.WaitOne的线程处于等待状态,则使用第二个方法) EventWaitHandle的实例.

我什至不得不提到您的串行通信应该在单独的线程中完成,而不是在UI线程中完成?这绝对重要.

现在,由于某种原因,您似乎需要等待直到用新数据完全更新UI.首先,再想一想,您真的需要吗?如果您坚持这一点(是的,从几个方面来说这可能是一个好的解决方案),那么可以实现起来更简单一些.您可以使用Dispatcher.Invoke代替Dispatcher.BeginInvoke.第一种方法将调用Invoke的线程延迟到委​​托实际调用的结束,而BeginInvoke将在委托实例和用于调用的数据放置到UI线程的调用队列后立即返回. br/>
有关调用的更多详细信息,另请参见我过去的回答:
Control.Invoke()与Control.BeginInvoke() [ ^ ]
Treeview Scanner和MD5的问题 [如何将ref参数传递给线程 [ ^ ].
其他选项包括使用线程池(可以创建类似的包装器)或BackgroundWorker(我宁愿建议用于临时任务).
组织同步通信的一种好方法是使用通用阻止队列.请参阅我的提示/技巧文章和替代"答案(不是真正的替代选择,它只是使用v.4.0中提供的类似类的建议):
用于线程通信和线程间调用的简单阻塞队列 [ ^ ].

对于WPF,重要的是要知道您不能从非UI线程调用任何UI元素,因此您需要使用线程间调用.对于WPF,应使用Dispatcher.InvokeDispatcher.BeginInvoke.有关其工作原理和示例的说明,请参见我的其他答案:
Control.Invoke()与Control.BeginInvoke() [ ^ ]
Treeview Scanner和MD5的问题 [
From this code, it is not clear what delay in serial communication is required and why, but the implementation of the delay is wrong anyway. You use spin wait which should be avoided by any means. Spin wait use 100% of the CPU core without any purposes.

All wait methods should be based on thread methods. 1) To wait unconditionally for certain amount of time, use System.Threading.Thread.Sleep. 2) If you need to wait until certain condition, always use System.Threading.EventWaitHandle.WaitOne (with timeout or not). In both cases, the thread is switched off by OS and never scheduled back to execution until waken up by OS. It will be waken up by expiration of time, or the following methods called from another thread: Thread.Abort or System.Threading.EventWaitHandle.Set (second one to be used if a thread is in the wait state at the call to System.Threading.EventWaitHandle.WaitOne on the same instance of EventWaitHandle.

Do I even have to mention that your serial communication should be done in a separate thread, not UI thread? This is absolutely important.

Now, it looks like by some reason you need to wait until the UI is fully updated with new data. First, think again, do you really need it? If you insist on that (yes, it can be a good solution by several reasongs), it can be achieved a bit simpler. You can use Dispatcher.Invoke instead of Dispatcher.BeginInvoke. First method delays the thread calling Invoke be the end of the actual call of the delegate, while BeginInvoke will return as soon as delegate instance and the data used for the call are placed to the invocation queue of the UI thread.

For more detail on invocation, see also my past Answers:
Control.Invoke() vs. Control.BeginInvoke()[^]
Problem with Treeview Scanner And MD5[^]


You need certainly to put port communications in a separate thread, good point.
The main design idea is: as you do the communications during all the life time of your application, it can be created from the very beginning and kept running. It can be synchronized using thread synchronization primitives.
Best way to create such a thread is to use my thread wrapper:
How to pass ref parameter to the thread[^].
Other options include using a thread pool (you can create a similar wrapper) or BackgroundWorker (which I would rather recommend for temporary tasks.
A good way to organize synchronized communication is using a generic blocking queue. See my Tips/Tricks article and the "alternative" answer (not really alternative, it''s just the advice to use the similar class available in v.4.0):
Simple Blocking Queue for Thread Communication and Inter-thread Invocation[^].

At to WPF, it is important to know that you cannot call any UI elements from non-UI thread, so you need to use inter-thread invocation. For WPF, you should use Dispatcher.Invoke or Dispatcher.BeginInvoke. See my other Answers for the explanation of how it works and the samples:
Control.Invoke() vs. Control.BeginInvoke()[^]
Problem with Treeview Scanner And MD5[^].

—SA


看看 ^ ]-第一个修订版提供了更多代码.

正如SAKryukov指出的那样,利用多线程可能是一个好主意. .Net提供 ThreadPool.QueueUserWorkItem [
Take a look at revision 1 of Barcode scanning[^] - as there is a lot more code provided with the first revision.

As SAKryukov points out, it''s probably a good idea to leverage multi-threading. .Net provides ThreadPool.QueueUserWorkItem[^] as a simple mechanism for implementing functionality using worker threads.

Regards
Espen Harlinn


进一步测试之后,我设法找到了我在原始问题中提到的性能问题.
这与 TextBlock 有关.我用 TextBox 替换了它,现在即使没有线程,我的应用程序也可以正常工作.当然,我将研究线程并尝试在我的应用程序中实现它们,但与此同时它正在按预期工作.

因此,对于可能遇到相同计时问题的其他人,只需使用 TextBox AppendText()而不是 TextBlock.Inlines.Add(). 后者适用于几行,但是当您有多行时,它会变得非常慢.那导致了我所有的计时问题.
After testing further I managed to locate the performance problem I mentioned in the original question.
It has all to do with the TextBlock. I replaced it with a TextBox and now my application works just fine even without Threads. Ofcourse I will be studying Threads and try to implement them in my application but in the meantime it is working as intended.

So for others who might experience the same timing problem, just use a TextBox and AppendText() instead of TextBlock.Inlines.Add(). The latter works fine for few lines but becomes very very slow when you have many lines. That was causing all my timing problems.


这篇关于WPF应用程序中的串行端口的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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