尝试关闭串口或exít应用程序时出现异常

时间:2014-08-09 16:29:59

标签: c# multithreading serial-communication xbee real-time-data

我有一个应用程序通过串口读取XBee帧并在richtextbox中显示帧。它还在图表中显示2个模拟通道(温度读数)并实时更新。

我遇到的问题是应用程序读取串口的速度比字节进入的速度快,所以我添加了这一行:

while (comPort.BytesToRead < (inLength + 4)) Thread.Sleep(10);

解决了这个问题,但现在我无法关闭串口或退出应用程序而不收到消息:&#34;未处理的类型&#39; System.InvalidOperationException&#39;发生在System.dll中的附加信息:端口已关闭。&#34;

我怀疑这是一个多线程问题,但我该如何解决?我在一篇类似的帖子中看到,使用BeginInvoke而不是Invoke可以解决问题,但我已经在使用它了。

以下是代码:

namespace SerialTest
{
public partial class frmMain : Form
{
    delegate void SetTextCallback(string text);
    delegate void SetChartCallback(double a, double b);

    string inRawFrame = String.Empty;
    double temp1 = 0;
    double temp2 = 0;

    public frmMain()
    {
        InitializeComponent();
    }

    private void btnGetSerialPorts_Click(object sender, EventArgs e)
    {
        if(btnGetSerialPorts.Text == "Open")
        {
            btnGetSerialPorts.Text = "Close";
            comPort.PortName = Convert.ToString(cboPorts.Text);
            comPort.BaudRate = Convert.ToInt32(cboBaudRate.Text);
            comPort.ReadTimeout = 4000;
            comPort.WriteTimeout = 6000;
            if (!comPort.IsOpen)
            {
                try
                {
                    comPort.Open();
                    cboPorts.Enabled = false;
                    cboBaudRate.Enabled = false;
                }
                catch(UnauthorizedAccessException ex)
                {
                    MessageBox.Show(ex.Message);
                }
            }
        }
        else if (btnGetSerialPorts.Text == "Close")
        {
            btnGetSerialPorts.Text = "Open";
            comPort.Close();
            cboPorts.Enabled = true;
            cboBaudRate.Enabled = true;
        }
    }

    private void Form1_Load(object sender, EventArgs e)
    {
        string[] arrayComPortsNames = null;
        int index = 0;
        string comPortName = null;

        arrayComPortsNames = SerialPort.GetPortNames();
        Array.Sort(arrayComPortsNames);

        while (!((arrayComPortsNames[index] == comPortName) || (index == arrayComPortsNames.GetUpperBound(0))))
        {
            cboPorts.Items.Add(arrayComPortsNames[index]);
            index++;
        }
        comPortName = arrayComPortsNames[0];
        cboPorts.Text = comPortName;

        cboBaudRate.Items.Add(9600);
        cboBaudRate.Items.Add(14400);
        cboBaudRate.Items.Add(19200);
        cboBaudRate.Items.Add(38400);
        cboBaudRate.Items.Add(57600);
        cboBaudRate.Items.Add(115200);
        cboBaudRate.Items.ToString();
        cboBaudRate.Text = cboBaudRate.Items[5].ToString();
    }

    private void comPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
    {
        SerialPort sp = (SerialPort)sender;
        byte[] incByte = new byte[3];
        int length = 0;

        if(comPort.BytesToRead > 3)
        {
            comPort.Read(incByte, 0, 3);
            if (incByte[0] == 0x7E)
            {
                length = (incByte[1] << 8) + incByte[2];
                byte[] buffer = new byte[length+4];
                buffer[0] = incByte[0];
                buffer[1] = incByte[1];
                buffer[2] = incByte[2];

                ReadFrame(buffer, length, DateTime.Now);
                temp1 = ReadTemp(buffer, 1);
                temp2 = ReadTemp(buffer, 2);
                DisplayFrame();
                UpdateChart();
            }
        }
    }

    private void ReadFrame(byte[] inBuffer, int inLength, DateTime time)
    {
        while (comPort.BytesToRead < (inLength + 4)) Thread.Sleep(10);
        comPort.Read(inBuffer, 3, (inBuffer.Length - 3));

        inRawFrame = time + " " + BitConverter.ToString(inBuffer).Replace("-", " ");
    }

    private void DisplayFrame()
    {

        if (rtbIncomingData.InvokeRequired)
        {
            rtbIncomingData.BeginInvoke(new SetTextCallback(SetText), new object[] { inRawFrame });
        }
        else
        {
            SetText(inRawFrame);
        }
    }

    private void SetText(string text)
    {
        this.rtbIncomingData.AppendText(text + Environment.NewLine);
    }

    private double ReadTemp(byte[] data, int channel)
    {
        if(data[3] == 0x92)
        {
            if(channel == 1)
            {
                return ((((data[19] << 8) + data[20]) * 1.2 / 1023) - 0.5) * 100.0;
            }
            else
            {
                return ((((data[21] << 8) + data[22]) * 1.2 / 1023) - 0.5) * 100.0;
            }
        }
        else
            return 100;
    }

    private void UpdateChart()
    {
        if (chart1.InvokeRequired)
            chart1.BeginInvoke(new SetChartCallback(SetChart), new object[] { temp1, temp2 });
        else
            SetChart(temp1, temp2);
    }

    private void SetChart(double val1, double val2)
    {
        chart1.ChartAreas["ChartArea1"].AxisX.LabelStyle.Format = "HH:mm:ss";
        chart1.ChartAreas["ChartArea1"].AxisX.MajorGrid.LineColor = Color.LightGray;
        chart1.ChartAreas["ChartArea1"].AxisY.MajorGrid.LineColor = Color.LightGray;
        chart1.ChartAreas["ChartArea1"].AxisX.LabelStyle.Font = new Font("Consolas", 8);
        chart1.ChartAreas["ChartArea1"].AxisY.LabelStyle.Font = new Font("Consolas", 8);
        chart1.ChartAreas["ChartArea1"].AxisY.Maximum = 30;
        chart1.ChartAreas["ChartArea1"].AxisY.Minimum = 10;
        chart1.ChartAreas["ChartArea1"].AxisY.Interval = 1;
        chart1.Series[0].Name = "Temp 1";
        chart1.Series[1].Name = "Temp 2";
        chart1.Series[0].ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.Line;
        chart1.Series[0].MarkerStyle = System.Windows.Forms.DataVisualization.Charting.MarkerStyle.Diamond;
        chart1.Series[0].XValueType = System.Windows.Forms.DataVisualization.Charting.ChartValueType.DateTime;
        chart1.Series[1].ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.Line;
        chart1.Series[1].MarkerStyle = System.Windows.Forms.DataVisualization.Charting.MarkerStyle.Diamond;
        chart1.Series[1].XValueType = System.Windows.Forms.DataVisualization.Charting.ChartValueType.DateTime;
        chart1.Series[0].Points.AddXY(DateTime.Now, val1);
        chart1.Series[1].Points.AddXY(DateTime.Now, val2);
    }
}
}

1 个答案:

答案 0 :(得分:0)

如果在ReadFrame()仍在等待字节时关闭串口,则最终会在已关闭的串行端口上检查BytesToRead。也许试试这个版本:

private void ReadFrame(byte[] inBuffer, int inLength, DateTime time)
{
    while (comPort.IsOpen)
    {
        if (comPort.BytestoRead >= inLength + 4)
        {
            comPort.Read(inBuffer, 3, (inBuffer.Length - 3));
            inRawFrame = time + " " + BitConverter.ToString(inBuffer).Replace("-", " ");
            return;
        }
        Thread.Sleep(10);
    }
}

如果串口在等待帧时关闭,它将退出,只检查BytesToRead端口是否仍然打开。