窗口中断串行端口后台工作程序并导致读取超时发生

时间:2012-07-17 15:02:24

标签: c# wpf multithreading progress-bar backgroundworker

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

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

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

从以下地方调用函数:

        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;
    }

1 个答案:

答案 0 :(得分:0)

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

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

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

我还会避免手动调度回设备,例如deviceReading和deviceDisconnect。这就是进展事件的目的。

我之所以避免这种情况,是因为您要回发UI线程进行这些操作 - 这意味着您的后台线程被阻止,直到UI可以调用这些操作。如果UI正忙于重新绘制屏幕或处理菜单选项,则无法处理此操作。这会暂停您的后台工作,直到UI线程可以处理它,从而超时串行通信。