window中断串口后台worker导致读取超时 [英] Window interrupts serial port background worker and causes read timeout to occur

查看:64
本文介绍了window中断串口后台worker导致读取超时的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在与设备连接,并使用 c# 从设备发送和接收大约 2000 个字节.我发现如果我单击连接按钮并让它运行,则该过程在 80% 的时间内完成,在 20% 的时间内发生读取超时.这本身就是一个问题.

I am interfacing with a device and sending and receiving around 2000 bytes from the device using c#. I find that if I click my connect button and just let it run, the process completes 80% of the time, and 20% of the time a read time out occurs. This in and of itself is a problem.

此外,如果我在读取数据时移动窗口,则会发生读取超时.我认为这可能是一个优先级问题,所以我尝试弄乱后台工作线程和接口线程的优先级,但没有运气.

Additionally, if I move my windows around at all while it is reading data a read timeout occurs. I thought it may have been a priority issue, so I tried messing with the priority of the background worker and interface threads, but no luck.

有没有人遇到过这种情况?和建议表示赞赏.以下是相关代码,如果您认为需要更多代码,请告诉我,我也会发布.提前致谢.

Has anyone had this happen to them before? And advice is appreciated. Below is what code is relevant, if you think you need more code let me know and I will post it as well. Thanks in advance.

函数从哪里调用:

        private void connectButton_Click(object sender, RoutedEventArgs e) //Connect button clicked
    {
        if (!Global.isConnected)
        {
            agentradioread.connect((string)portsOpen.SelectedValue, this);
            form.deviceConnecting(this);
        }
        else
        {
            agentradioread.disconnect();
            form.deviceDisconnect(this);
        }
    }

    private void loadSettings_Click(object sender, RoutedEventArgs e) //Loads settings
    {
        if (Global.isConnected)
        {
            agentradioread.readValues(this);
        }
    }

    private void sendSettings_Click(object sender, RoutedEventArgs e) //Sends settings
    {
        if (Global.isConnected)
        {
            agentradioread.readTime();
            string minutes = (Create.sysval[10].ToString().Length == 1) ? "0" + Create.sysval[10].ToString() : Create.sysval[10].ToString();
            string date = Create.sysval[11].ToString() + "/" + Create.sysval[12].ToString() + "/20" + Create.sysval[13].ToString();
            string time = Create.sysval[9].ToString() + ":" + minutes;
            MessageBoxResult result = MessageBox.Show("The current device date and time is: " + date + " " + time + "\r\nIs this time correct?", "Date & Time Verification", MessageBoxButton.YesNo, MessageBoxImage.Question);

            if (result == MessageBoxResult.Yes) //Time is correct
            {
                Create.sysval[9] = 255;
            }
            else //Time is incorrect
            {
                dateTimeChange dateTime = new dateTimeChange();
                dateTime.ShowDialog();
            }

            agentradioread.sendValues(this);
        }
    }

调用的函数:

public void connect(string selectedPort, agentRadio agentR) //Connects device and reads values
{
        agentSerial = new SerialPort(selectedPort, 9600, Parity.None, 8, StopBits.One);

        connectWorker = new BackgroundWorker();
        connectWorker.WorkerReportsProgress = true;
        connectWorker.DoWork += new DoWorkEventHandler(connectWorker_DoWork);
        connectWorker.ProgressChanged += new ProgressChangedEventHandler(connectWorker_ProgressChanged);

        connectWorker.RunWorkerAsync(agentR);
    }

    public void readTime() //Reads time into sysval locations
    {
        agentSerial.DiscardInBuffer();
        agentSerial.DiscardOutBuffer();
        agentSerial.Write(Global.TIME, 0, Global.TIME.Length); //begin send values
        loadTime();
    }

    public void readValues(agentRadio agentR) //Read values
    {
        readWorker = new BackgroundWorker();
        readWorker.WorkerReportsProgress = true;
        readWorker.DoWork += new DoWorkEventHandler(readWorker_DoWork);
        readWorker.ProgressChanged += new ProgressChangedEventHandler(readWorker_ProgressChanged);

        agentSerial.DiscardInBuffer();
        agentSerial.DiscardOutBuffer();
        readWorker.RunWorkerAsync(agentR);
    }

    public void sendValues(agentRadio agentR) //Send values
    {
        sendWorker = new BackgroundWorker();
        sendWorker.WorkerReportsProgress = true;
        sendWorker.DoWork += new DoWorkEventHandler(sendWorker_DoWork);
        sendWorker.ProgressChanged += new ProgressChangedEventHandler(sendWorker_ProgressChanged);

        agentSerial.DiscardInBuffer();
        agentSerial.DiscardOutBuffer();
        sendWorker.RunWorkerAsync(agentR);
    }

    public void restoreDevice(agentRadio agentR) //Restore device to defaults
    {
        agentSerial.DiscardInBuffer();
        agentSerial.DiscardOutBuffer();
        agentSerial.Write(Global.RESTORE, 0, Global.RESTORE.Length); //device restore command
        MessageBox.Show(agentSerial.ReadByte().ToString());
        try
        {
            Global.restoring = true;
            readValues(agentR);
        }
        catch
        {
            MessageBox.Show("Your device was restored, but a read time out occured. You need to go to the connect tab and reconnect to the device, and your settings will update.", "Device Restore Notice", MessageBoxButton.OK, MessageBoxImage.Information);
        }
    }

    public void disconnect() //Disconnects device
    {
        if (Global.isConnected == true)
        {
            try
            {
                agentSerial.DiscardInBuffer();
                agentSerial.DiscardOutBuffer();
                agentSerial.Write(Global.EXIT_PROGRAM, 0, Global.EXIT_PROGRAM.Length);
            }
            catch
            {
                //leave option to reConnect
            }
            agentSerial.Close();
            Global.isConnected = false;
        }
    }

    /*Methods that cannot be called from elsewhere in the application*/

    private void connectWorker_ProgressChanged(object sender, ProgressChangedEventArgs e) //Updates progressbar while device is connecting
    {
        ProgressBar progressBar = e.UserState as ProgressBar;

        App.Current.Dispatcher.Invoke(
            System.Windows.Threading.DispatcherPriority.Send,
                new Action(
                    delegate()
                    { progressBar.Value = e.ProgressPercentage; }));
    }

    private void connectWorker_DoWork(object sender, DoWorkEventArgs e) //Connects and read values
    {
        startRead((agentRadio)e.Argument);
    }

    private void readWorker_ProgressChanged(object sender, ProgressChangedEventArgs e) //Alerts user when values are loaded
    {
        ProgressBar progressBar = e.UserState as ProgressBar;

        App.Current.Dispatcher.Invoke(
            System.Windows.Threading.DispatcherPriority.Send,
                new Action(
                    delegate()
                    { progressBar.Value = e.ProgressPercentage; }));
    }

    private void readWorker_DoWork(object sender, DoWorkEventArgs e) //Reads values
    {
        agentRadio agentR = (agentRadio)e.Argument;

        App.Current.Dispatcher.Invoke(
            System.Windows.Threading.DispatcherPriority.Send,
                new Action(
                    delegate()
                    { form.deviceReading(agentR); }));

        agentSerial.Write(Global.READ_VALUES, 0, Global.READ_VALUES.Length); //begin read values

        byte key = (byte)agentSerial.ReadByte();

        if (Global.START_COMMAND == key) //Verify continue key
        {
            object progressBarObject = new object();
            progressBarObject = agentR.connectProgress;

            for (int i = 1; i < 2247; i++) //Loop through values, calling read function and updating progress bar
            {
                int progress = (int)(((float)i / 2246.0) * 100);
                readWorker.ReportProgress(progress, progressBarObject);

                try
                {
                    readData(i, agentR);
                    agentSerial.Write(Global.GO_AHEAD, 0, Global.GO_AHEAD.Length);

                    agentSerial.DiscardInBuffer();
                    agentSerial.DiscardOutBuffer();
                }
                catch
                {
                    break;
                }
            }
        }
        else //Key failed and displays error
        {
            App.Current.Dispatcher.Invoke(
                System.Windows.Threading.DispatcherPriority.Send,
                    new Action(
                        delegate()
                        { form.deviceDisconnect(agentR); }));
            MessageBox.Show("Connection Failed, Invalid Key");
        }
    }

    private void sendWorker_ProgressChanged(object sender, ProgressChangedEventArgs e) //Alerts user when values are sent
    {
        ProgressBar progressBar = e.UserState as ProgressBar;

        App.Current.Dispatcher.Invoke(
            System.Windows.Threading.DispatcherPriority.Send,
                new Action(
                    delegate()
                    { progressBar.Value = e.ProgressPercentage; }));
    }

    private void sendWorker_DoWork(object sender, DoWorkEventArgs e) //Sends values
    {
        agentRadio agentR = (agentRadio)e.Argument;

        App.Current.Dispatcher.Invoke(
            System.Windows.Threading.DispatcherPriority.Send,
                new Action(
                    delegate()
                    { form.deviceSending(agentR); }));

        agentSerial.Write(Global.SEND_VALUES, 0, Global.SEND_VALUES.Length); //begin send values

        byte key = (byte)agentSerial.ReadByte();

        if (Global.START_COMMAND == key) //Verify continue key
        {
            object progressBarObject = new object();
            progressBarObject = agentR.connectProgress;

            for (int i = 1; i < 659; i++) //Loops and sends data to device
            {
                int progress = (int)(((float)i / 658.0) * 100);
                sendWorker.ReportProgress(progress, progressBarObject);

                try
                {
                    byte result = sendData(i, agentR);

                    if (result != Global.GO_AHEAD[0])
                    {
                        MessageBox.Show("Fatal Error");
                        break;
                    }

                    agentSerial.DiscardInBuffer();
                    agentSerial.DiscardOutBuffer();
                }
                catch
                {
                    break;
                }
            }
        }
        else //Key failed and displays error
        {
            App.Current.Dispatcher.Invoke(
                System.Windows.Threading.DispatcherPriority.Send,
                    new Action(
                        delegate()
                        { form.deviceDisconnect(agentR); }));
            MessageBox.Show("Connection Failed, Invalid Key");
        }
    }

    private void loadTime() //Loads device time into sysval locations
    {
        byte key = (byte)agentSerial.ReadByte();

        if (Global.START_COMMAND == key) //Verify continue key
        {
            for (int i = 0; i < 5; i++)
            {
                try
                {
                    byte result = (byte)agentSerial.ReadByte();
                    Create.sysval[9 + i] = result;
                }
                catch
                {
                    Create.sysval[9 + i] = 0;
                }
            }
            agentSerial.ReadByte();
        }

        agentSerial.DiscardInBuffer();
        agentSerial.DiscardOutBuffer();
    }

    private string sendInitial() //Sends a ? and returns 7 bytes of data
    {
        string handshake = "";
        agentSerial.Write("?");

        for (int i = 0; i < 7; i++)
        {
            try
            {
                handshake += agentSerial.ReadByte().ToString();
            }
            catch
            {
                break;
            }
        }

        return handshake;
    }

    private void startRead(agentRadio agentR) //Initializes data send and receive
    {
        System.Threading.Thread.CurrentThread.Priority = System.Threading.ThreadPriority.Highest;
        App.Current.Dispatcher.Invoke(
            System.Windows.Threading.DispatcherPriority.Send,
                new Action(
                    delegate()
                    { agentR.connectProgress.IsIndeterminate = true; }));

        agentSerial.ReadTimeout = 5000;
        agentSerial.Open();

        agentSerial.DiscardInBuffer();
        agentSerial.DiscardOutBuffer();

        string handshake = sendInitial();

        if (handshake == Global.AGENT_RADIO) //Result matches agent radio version
        {
            agentSerial.Write(Global.READ_VALUES, 0, Global.READ_VALUES.Length); //begin read values

            byte key = (byte)agentSerial.ReadByte();

            if (Global.START_COMMAND == key) //Verify continue key
            {
                App.Current.Dispatcher.Invoke(
                System.Windows.Threading.DispatcherPriority.Send,
                    new Action(
                        delegate()
                        { agentR.connectProgress.IsIndeterminate = false; }));

                object progressBarObject = new object();
                progressBarObject = agentR.connectProgress;

                for (int i = 1; i < 2247; i++) //Loop through values, calling read function and updating progress bar
                {
                    int progress = (int)(((float)i / 2246.0) * 100);
                    connectWorker.ReportProgress(progress, progressBarObject);

                    try
                    {
                        readData(i, agentR);
                        agentSerial.Write(Global.GO_AHEAD, 0, Global.GO_AHEAD.Length);

                        agentSerial.DiscardInBuffer();
                        agentSerial.DiscardOutBuffer();
                    }
                    catch
                    {
                        break;
                    }
                }
            }
            else //Key failed and displays error
            {
                agentSerial.Close();
                App.Current.Dispatcher.Invoke(
                System.Windows.Threading.DispatcherPriority.Send,
                    new Action(
                        delegate()
                        { form.deviceDisconnect(agentR); }));
                MessageBox.Show("Connection Failed, Invalid Key");
            }
        }
        else //Result did not match an agent radio version
        {
            agentSerial.Close(); //Closes port

            if (attempt < 3) //Attempt to connect to device 5 times
            {
                attempt++;
                startRead(agentR);
            }
            else //Displays error to user if connection failed
            {
                App.Current.Dispatcher.Invoke(
                System.Windows.Threading.DispatcherPriority.Send,
                    new Action(
                        delegate()
                        { form.deviceDisconnect(agentR); }));
                MessageBox.Show("Connection Failed, Not Agent Radio Version");
            }
        }
    }

    private void readData(int iteration, agentRadio agentR) //Reads data for current iteration
    {
        byte value = 0; //Current value

        try
        {
            value = (byte)agentSerial.ReadByte(); //Reads byte
        }
        catch
        {
            agentSerial.Close();
            App.Current.Dispatcher.Invoke(
                System.Windows.Threading.DispatcherPriority.Send,
                    new Action(
                        delegate()
                        { form.deviceDisconnect(agentR); }));
            MessageBox.Show("Connection Failed, Read Timeout"); //Displays message if read timeout occured
        }

        if (iteration > 0 && iteration < 385) //read schedule
        {
            double pos = (iteration - 1) / 48;

            int i = (int)Math.Floor(pos);
            int j = (iteration - 1) - (i * 48);

            Create.schedule[i, j] = value;
        }

        if (iteration > 384 && iteration < 1285) //read alarm history
        {
            double pos = (iteration - 385) / 9;

            int i = (int)Math.Floor(pos);
            int j = (iteration - 385) - (i * 9);

            Create.alarms[i, j] = value;
        }

        if (iteration > 1284 && iteration < 1345) //read error log
        {
            double pos = (iteration - 1285) / 6;

            int i = (int)Math.Floor(pos);
            int j = (iteration - 1285) - (i * 6);

            Create.errors[i, j] = value;
        }

        if (iteration > 1344 && iteration < 1945) //read voltage history
        {
            double pos = (iteration - 1345) / 6;

            int i = (int)Math.Floor(pos);
            int j = (iteration - 1345) - (i * 6);

            Create.voltage[i, j] = value;
        }

        if (iteration > 1944 && iteration < 1973) //read holidays
        {
            Create.holidays[iteration - 1945] = value;
        }

        if (iteration > 1972 && iteration < 2168) //read message sequences
        {
            double pos = (iteration - 1973) / 15;

            int i = (int)Math.Floor(pos);
            int j = (iteration - 1973) - (i * 15);

            Create.messages[i, j] = value;
        }

        if (iteration > 2167 && iteration < 2196) //read message info
        {
            Create.recordings[iteration - 2168] = value;
        }

        if (iteration > 2195 && iteration < 2246) //read sysval
        {
            Create.sysval[iteration - 2196] = value;
        }

        if (iteration == 2246 && value == Global.FINISH_COMMAND)
        {
            if (Global.restoring)
            {
                App.Current.Dispatcher.Invoke(
                System.Windows.Threading.DispatcherPriority.Send,
                    new Action(
                        delegate()
                        { form.deviceConnect(agentR); }));
                MessageBox.Show("Your device has been restored, and all settings from the device have been received.", "Device Restore Succesful", MessageBoxButton.OK, MessageBoxImage.Information);
            }
            else
            {
                App.Current.Dispatcher.Invoke(
                System.Windows.Threading.DispatcherPriority.Send,
                    new Action(
                        delegate()
                        { form.deviceConnect(agentR); }));
                Global.isConnected = true;
                MessageBox.Show("Your device is connected, and all settings from the device have been received.", "Data Transfer Succesful", MessageBoxButton.OK, MessageBoxImage.Information);
            }
        }
    }

    private byte sendData(int iteration, agentRadio agentR) //Sends data for current iteration
    {
        byte value = 0; //For return later

        if (iteration > 0 && iteration < 51) //sysval
        {
            byte[] toWrite = new byte[1];
            toWrite[0] = Create.sysval[iteration - 1];
            agentSerial.Write(toWrite, 0, 1);

            value = (byte)agentSerial.ReadByte();
        }
        if (iteration > 50 && iteration < 79) //holiday
        {
            byte[] toWrite = new byte[1];
            toWrite[0] = Create.holidays[iteration - 51];
            agentSerial.Write(toWrite, 0, 1);

            value = (byte)agentSerial.ReadByte();
        }
        if (iteration > 78 && iteration < 463) //schedule
        {
            double pos = (iteration - 79) / 48;

            int i = (int)Math.Floor(pos);
            int j = (iteration - 79) - (i * 48);

            byte[] toWrite = new byte[1];
            toWrite[0] = Create.schedule[i, j];
            agentSerial.Write(toWrite, 0, 1);

            value = (byte)agentSerial.ReadByte();
        }
        if (iteration > 462 && iteration < 658) //message sequence
        {
            double pos = (iteration - 463) / 15;

            int i = (int)Math.Floor(pos);
            int j = (iteration - 463) - (i * 15);

            byte[] toWrite = new byte[1];
            toWrite[0] = Create.messages[i, j];
            agentSerial.Write(toWrite, 0, 1);

            value = (byte)agentSerial.ReadByte();
        }

        if (iteration == 658 && ((byte)agentSerial.ReadByte() == Global.FINISH_COMMAND)) //Last iteration should get finish command
        {
            App.Current.Dispatcher.Invoke(
                System.Windows.Threading.DispatcherPriority.Send,
                    new Action(
                        delegate()
                        { form.deviceConnect(agentR); }));
            MessageBox.Show("Your settings have been transfered succesfully to the device.", "Successful Transfer", MessageBoxButton.OK, MessageBoxImage.Information);

            value = 17; //Simulates GO_AHEAD character for last iteration of loop
        }

        return value; //Return character off port, should be GO_AHEAD
    }
}

表单类:

public string[] populateAvailablePorts() //Returns string array of open ports
    {
        String[] portsAvailable = SerialPort.GetPortNames();
        return portsAvailable;
    }

    public void deviceConnect(agentRadio agentR) //Adjusts display for when device is disconnected
    {
        agentR.portsOpen.IsEnabled = false;
        agentR.connectButton.Content = "Disconnect";
        agentR.connectButton.IsEnabled = true;
        agentR.connectProgress.Value = 100;
        agentR.loadSettingsConnect.Content = "Load Settings";
        agentR.sendSettingsConnect.Content = "Send Settings";
        agentR.loadSettingsConnect.IsEnabled = true;
        agentR.sendSettingsConnect.IsEnabled = true;
        agentR.DeviceRestore.IsEnabled = true;

        agentR.VoltageStack.Children.Clear();
        agentR.ErrorStack.Children.Clear();
        agentR.Events.Children.Clear();

        agentR.DeviceRestore.IsEnabled = true;

        display.setDisplay(agentR);
    }

    public void deviceConnecting(agentRadio agentR) //Adjusts display for when device is disconnected
    {
        agentR.portsOpen.IsEnabled = false;
        agentR.connectButton.Content = "Connecting...";
        agentR.connectButton.IsEnabled = false;
        agentR.loadSettingsConnect.IsEnabled = false;
        agentR.sendSettingsConnect.IsEnabled = false;
        agentR.DeviceRestore.IsEnabled = false;
    }

    public void deviceReading(agentRadio agentR) //Adjusts display for when device is disconnected
    {
        agentR.connectButton.IsEnabled = false;
        agentR.loadSettingsConnect.Content = "Reading Data...";
        agentR.loadSettingsConnect.IsEnabled = false;
        agentR.sendSettingsConnect.IsEnabled = false;
        agentR.DeviceRestore.IsEnabled = false;
    }

    public void deviceSending(agentRadio agentR) //Adjusts display for when device is disconnected
    {
        agentR.connectButton.IsEnabled = false;
        agentR.sendSettingsConnect.Content = "Sending Data...";
        agentR.loadSettingsConnect.IsEnabled = false;
        agentR.sendSettingsConnect.IsEnabled = false;
        agentR.DeviceRestore.IsEnabled = false;
    }

    public void deviceDisconnect(agentRadio agentR) //Adjusts display for when device is diconnected
    {
        agentR.portsOpen.IsEnabled = true;
        agentR.connectButton.IsEnabled = true;
        agentR.connectButton.Content = "Connect";
        agentR.loadSettingsConnect.Content = "Load Settings";
        agentR.sendSettingsConnect.Content = "Send Settings";
        agentR.connectProgress.Value = 0;
        agentR.connectProgress.IsIndeterminate = false;
        agentR.loadSettingsConnect.IsEnabled = false;
        agentR.sendSettingsConnect.IsEnabled = false;

        agentR.DeviceRestore.IsEnabled = false;
    }

推荐答案

不要在 DoWork 处理程序中使用 ProgressBar 对象.ProgressChanged 处理程序 (readWorker_ProgressChanged) 应该获得它自己报告进度所需的一切.

Don't use the ProgressBar object in your DoWork handler. The ProgressChanged handler (readWorker_ProgressChanged) should get everything it needs to report progress itself.

另外,你为什么要调度IsIndeterminate的设置?

Also, why are you dispatching the setting of IsIndeterminate?

还有.ProgressChanged 处理程序将始终在 UI 线程上调用(除非您自己直接调用它,否则无需在 readWorker_ProgressChanged 中调度任何内容.

Also. the ProgressChanged handler will always be called on the UI thread (unless you invoke it directly yourself, there's no need to dispatch anything in readWorker_ProgressChanged.

我也会避免手动分派回表单以处理诸如 deviceReading 和 deviceDisconnect 之类的事情.这就是进度事件的用途.

I would also avoid manually dispatching back to the form for things like deviceReading and deviceDisconnect. That's what the progress event is for.

我会避免这种情况的原因是因为您将这些操作发送回 UI 线程——这意味着您的后台线程被阻塞,直到 UI 可以调用这些操作.如果 UI 忙于重绘屏幕或处理菜单选择,则无法处理此操作.这会暂停您的后台工作,直到 UI 线程可以处理它,从而使串行通信超时.

The reason I would avoid this is because you are sending back to the UI thread for these operations--this means your background thread is blocked until the UI can invoke these actions. If the UI is busy redrawing the screen or handling menu selections, it can't deal with this action. This halts your background work until the UI thread can handle it, thus timing out the serial communications.

这篇关于window中断串口后台worker导致读取超时的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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