serialport响应EventHandler,但不是ReadExisting或ReadLine?

时间:2011-09-27 16:35:09

标签: c# serial-port

我有一个程序正在读取c#中的串口。我需要快速写入端口,从中读取,然后关闭它。我不能把它打开。我知道串口缓慢读写,我试图将ReadTimeout和WriteTimeout属性设置为高,并添加了一个thread.Sleep来尝试拖动设备的读写时间。这里有一些代码:

我写入端口的方法:

    private void CheckPorts(string testMessage)
    {

        foreach (string s in SerialPort.GetPortNames())
        {
            portNumber = Int32.Parse(s.Remove(0, 3));
            testSerial = new SerialPort(s, baudRate, Parity.None, 8, StopBits.One);
            if (testSerial.IsOpen)
            {
                testSerial.Close();
            }
            testSerial.ReadTimeout = 2000;
            testSerial.WriteTimeout = 1000;
            testSerial.Open();
            if (testSerial.IsOpen)
            {
                string received;
                testSerial.DiscardInBuffer();
                try
                {
                    //testSerial.DataReceived += new SerialDataReceivedEventHandler(testSerialPort_DataReceived);

                    testSerial.Write(testMessage);
                    System.Threading.Thread.Sleep(2000);

                    received = testSerial.ReadExisting();  //EITHER I USE THIS OR EVENT HANDLER, NOT BOTH
                }
                catch (TimeoutException e)
                {
                    testSerial.Close();
                    continue;
                }

               if (received.Length > 0)
                {
                    MessageReceived(received);
                }
                testSerial.Close();
            }
       } 
 }



 private void testSerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
    {
        string received = testSerial.ReadExisting();
        int y = received.IndexOf("\r");
        while (y == -1)
        {
            received = received + testSerial.ReadExisting();
            y = received.IndexOf("\r");
        }

        if (testSerial.IsOpen)
        {
            testSerial.Close();
        }

    }

我想知道,如果我必须使用数据处理程序,我如何保持串口打开足够长的时间来读取它,但是在下一个端口需要打开之前关闭串口?

看,第一个方法被调用几次,它遍历一个foreach循环,在几个端口上尝试消息,然后尝试读取响应。所以,在某些时候我必须关闭端口,否则下次它通过它,它不能正常工作,因为端口仍然是打开的

这是我的更新代码(仍未正常工作):

 private void CheckPorts(string testMessage, int baudRate)
    {

        foreach (string s in SerialPort.GetPortNames())
        {
            var interval = 3000; // ms 
            var timer = new System.Timers.Timer(interval);
            timer.Elapsed += (o, e) =>
            {
                timer.Enabled = false;

                if (testSerial.IsOpen)
                    testSerial.Close();  // may not be necessary with Dispose? 

                testSerial.Dispose();
                timer.Dispose();
            };

            portNumber = Int32.Parse(s.Remove(0, 3));
            testSerial = new SerialPort(s, baudRate, Parity.None, 8, StopBits.One);
            testSerial.ReadTimeout = 2000;
            testSerial.WriteTimeout = 2000;
            if (testSerial.IsOpen)
            {
                testSerial.Close();
            }

            testSerial.Open();
            timer.Enabled = true; 

            if (testSerial.IsOpen)
            {
                string received;
                //testSerial.DiscardInBuffer();
                //autoEvent = new AutoResetEvent(false);
                try
                {
                   // testSerial.DataReceived += new SerialDataReceivedEventHandler(testSerialPort_DataReceived);

                  // autoEvent.Reset();
                    lblPortNum.Content = s;
                    lblPortNum.Refresh();

                    testSerial.Write(testMessage);
                    //System.Threading.Thread.Sleep(2000);

                    //testSerial.NewLine = "\r\n";
                    byte[] rBuff = new byte[2];
                    int rCnt = testSerial.Read(rBuff, 0, 2);
                    System.Text.Encoding enc = System.Text.Encoding.ASCII;
                    received = enc.GetString(rBuff);



                     //received = testSerial.ReadLine();
                }
                catch (TimeoutException e)
                {
                    testSerial.Close();
                    continue;
                }

               if (received.Length > 0)
               {
                    MessageReceived(received, Int16.Parse(s.Remove(0, 3)));
                }
                /*
                if (autoEvent.WaitOne(2000))
                {
                    // the port responded 
                   // testSerial.Close();
                    autoEvent.Dispose();
                    lblPortNum.Content = "HEY I RESPONDED";
                }
                else
                {
                    testSerial.Close();
                    autoEvent.Dispose();
                    continue;
                    // port did not respond within 2 seconds 
                }*/
              //testSerial.Close();
            }
        } 
     }

再次更新(仍然无法正常工作)

private void CheckPorts(string testMessage, int baudRate)
    {

        foreach (string s in SerialPort.GetPortNames())
        {
            portNumber = Int32.Parse(s.Remove(0, 3));

            // MUST BE LOCAL 
            var serialOneOfMany = new SerialPort(s, baudRate, Parity.None, 8, StopBits.One);
            serialOneOfMany.ReadTimeout = 2000;
            serialOneOfMany.WriteTimeout = 2000;
            if (serialOneOfMany.IsOpen)
            {
                serialOneOfMany.Close();
            }

            // timer must be defined _after_ serialOneOfMany 
            var interval = 3000; // ms  
            var timer = new System.Timers.Timer(interval);
            timer.Elapsed += (o, e) =>
            {
                timer.Enabled = false;

                if (serialOneOfMany.IsOpen)
                    serialOneOfMany.Close();  // may not be necessary with Dispose?  

                serialOneOfMany.Dispose();
                timer.Dispose();
            };

            if (serialOneOfMany.IsOpen)
            {
                string received;

                try
                {
                    lblPortNum.Content = s;
                    lblPortNum.Refresh();

                    serialOneOfMany.Write(testMessage);
                    byte[] rBuff = new byte[2];
                    int rCnt = serialOneOfMany.Read(rBuff, 0, 2);
                    System.Text.Encoding enc = System.Text.Encoding.ASCII;
                    received = enc.GetString(rBuff);

                }
                catch (TimeoutException e)
                {
                    serialOneOfMany.Close();
                    continue;
                }

                if (received.Length > 0)
                {
                    CheckIfTheMessageMatches(received, Int16.Parse(s.Remove(0, 3)));
                }

            }
        } 

    }

所以有了这个更新,它只是吹过代码,我可以逐行逐步执行代码,但它根本不会停止3秒。如果我在没有任何调试中断的情况下运行它,它只需要经过它一小段时间

更新10-25-11

 private void CheckPorts(string testMessage, int baudRate)
    {
        foreach (string s in SerialPort.GetPortNames())
        {
            string received = "";
            testSerial = new SerialPort(s,baudRate, Parity.None, 8, StopBits.One);

            lblStatus.Content = "Scanning...";
            lblStatus.Refresh();

            if (testSerial.IsOpen)
            {
                testSerial.Close();
            }
            else
            {
                testSerial.Open();
            }

            if (testSerial.IsOpen)
            {
                try
                {
                    testSerial.NewLine = "\r";
                    lblPortNum.Content = s;
                    lblPortNum.Refresh();
                    testSerial.WriteTimeout= 500;
                    testSerial.ReadTimeout = 1000;
                    testSerial.WriteLine(testMessage);

                    System.Threading.Thread.Sleep(500);

                    /*THIS DOESN'T WORK
                    byte[] buffer = new byte[testSerial.BytesToRead];
                    int rCnt = testSerial.Read(buffer, 0, buffer.Length);
                    received = enc.GetString(buffer);*/

                    //received = Convert.ToString(testSerial.BaseStream.Read(buffer, 0, (int)buffer.Length));


                    received =  testSerial.ReadLine();


                   int y = received.IndexOf("\r");
                   while (y == -1)
                   {
                       received = received + testSerial.ReadExisting();
                       y = received.Length;
                   }

                   if (lblInfo.Dispatcher.Thread == Thread.CurrentThread)
                   {
                       CheckIfTheMessageMatches(received, s);
                       received = received + lblInfo.Content;
                       lblInfo.Content = received;
                   }
                   else
                   {
                       lblInfo.Dispatcher.Invoke(DispatcherPriority.Normal, new ThreadCheck(threadCheck), received);
                   }
                   if (testSerial.IsOpen)
                   {
                       testSerial.Close();
                   }

                    /*I USE THIS WITH THE sPort.Read() METHOD
                    while (rCnt > 0)
                    {
                        if (lblInfo.Dispatcher.Thread == Thread.CurrentThread)
                        {
                            CheckIfTheMessageMatches(received, s);
                            rCnt = 0;
                            received = received + lblInfo.Content;
                            lblInfo.Content = received;                                
                        }

                        else
                        {
                            lblInfo.Dispatcher.Invoke(DispatcherPriority.Normal, new ThreadCheck(threadCheck), received);
                        }
                    }
                     */

                   if (testSerial.IsOpen)
                   {
                       testSerial.Close();
                   }

                }
                catch (TimeoutException e)
                {
                    testSerial.Close();
                    continue;
                }
                received = null;
            }
        } 

        lblStatus.Content = "Finished Scanning.";
        lblPortNum.Content = "";
    }

更新的代码 这里有一些新的代码,仍然没有工作,dataeventhandler甚至没有调用过一次。我知道它正在收到消息,因为我有另一个与串行设备配合使用的程序

private void CheckPorts(string testMessage, int baudRate)
    {
        foreach (string s in SerialPort.GetPortNames())
        {
            var serialOneOfMany = new SerialPort(s, baudRate, Parity.None, 8, StopBits.One);
            serialOneOfMany.ReadTimeout = 700;
            serialOneOfMany.WriteTimeout = 100;

            var interval = 500; // ms
            var timer = new System.Timers.Timer(interval);
            timer.Elapsed += (o, e) =>
            {
                timer.Enabled = false;

                if (serialOneOfMany.IsOpen)
                    serialOneOfMany.Close();  // may not be necessary with Dispose?

                serialOneOfMany.Dispose();
                timer.Dispose();
            };
            timer.Enabled = true;

            lblStatus.Content = "Scanning...";
            lblStatus.Refresh();

            if (serialOneOfMany.IsOpen)
            {
                serialOneOfMany.Close();
            }
            else
            {
                serialOneOfMany.Open();
            }

            if (serialOneOfMany.IsOpen)
            {
                string received;

                try
                {
                    lblPortNum.Content = s;
                    lblPortNum.Refresh();

                    serialOneOfMany.WriteLine(testMessage);
                    System.Threading.Thread.Sleep(400);
                    serialOneOfMany.DataReceived += new SerialDataReceivedEventHandler(testSerialPort_DataReceived);

                }
                catch (TimeoutException e)
                {
                    serialOneOfMany.Close();
                    continue;
                }
            }
        } 

        lblStatus.Content = "Finished Scanning.";
        lblPortNum.Content = "";
    }

    private void testSerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
    {
        SerialPort receivingSerial = sender as SerialPort;
        string received = receivingSerial.ReadExisting();
        int y = received.IndexOf("\r");
        while (y == -1)
        {
            received = received + receivingSerial.ReadExisting();
            y = received.IndexOf("\r");
        }

        if (lblInfo.Dispatcher.Thread == Thread.CurrentThread)
        {
            string name = receivingSerial.PortName;
            received = received + lblInfo.Content;
            lblInfo.Content = received;
            CheckIfTheMessageMatches(received, name);
        }
        else
        {
            lblInfo.Dispatcher.Invoke(DispatcherPriority.Normal, new ThreadCheck(threadCheck), received);
        } 
        if (receivingSerial.IsOpen)
        {
            receivingSerial.Close();
        }

    }

7 个答案:

答案 0 :(得分:4)

你应该能够同时做这些(假设没问题)。然后,当引发DataReceived事件(删除无关代码)时,您将关闭它们。只是不要关闭CheckPorts中的端口。

private void testSerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
    SerialPort localSerialPort = sender as SerialPort;

    ... // use localSerialPort instead of global/class variable

    if (localSerialPort.IsOpen)
    {
        localSerialPort.Close();
    }
}

编辑:回应评论。

您可以随时添加计时器。如果你把它放在foreach循环中,你将获得一个定时器,用于在3秒后处理其给定串行端口的每个串行端口。在这里,重要的是在foreach循环中声明计时器。

var interval = 3000; // ms
var timer = new System.Timers.Timer(interval);
timer.Elapsed += (o,e) => 
    {
        timer.Enabled = false;

        if (testSerial.IsOpen)
            testSerial.Close();  // may not be necessary with Dispose?

        testSerial.Dispose();
        timer.Dispose();
    }

timer.Enabled = true;

编辑:代码已更新,因此我将更新

范围对于我提供的代码非常重要。你应该摆脱非本地testSerial或在这里使用完全不同的名称。

        portNumber = Int32.Parse(s.Remove(0, 3));

        // MUST BE LOCAL
        var serialOneOfMany = new SerialPort(s, baudRate, Parity.None, 8, StopBits.One);
        serialOneOfMany.ReadTimeout = 2000;
        serialOneOfMany.WriteTimeout = 2000;
        if (serialOneOfMany.IsOpen)
        {
            serialOneOfMany.Close();
        }

        // timer must be defined _after_ serialOneOfMany
        var interval = 3000; // ms 
        var timer = new System.Timers.Timer(interval);
        timer.Elapsed += (o, e) =>
        {
            timer.Enabled = false;

            if (serialOneOfMany.IsOpen)
                serialOneOfMany.Close();  // may not be necessary with Dispose? 

            serialOneOfMany.Dispose();
            timer.Dispose();
        };     

答案 1 :(得分:1)

从Microsoft查看此信息:

  

此方法以字符串形式返回SerialPort对象的流和内部缓冲区的内容。此方法不使用超时。请注意,此方法可以在内部缓冲区中留下尾随前导字节,这会使BytesToRead值大于零。

为什么不使用通常的Read方法SerialPort.Read (Byte[], Int32, Int32)

答案 2 :(得分:1)

请看一下(我还在answer中使用了darthwillard提出的串口相关问题)。所有端口一个接一个地打开,DataReceived事件被绑定(您需要做的就是测试传入消息),但不需要等待。计时器事件处理程序可以关闭所有端口或保留您想要使用的端口等。我希望它有所帮助!

private List<SerialPort> openPorts = new List<SerialPort>();

private void button3_Click(object sender, EventArgs e)
{
    int baudRate = 9600;
    string testMessage = "test";
    txtPortName.Text = "Testing all serial ports";
    foreach (string s in SerialPort.GetPortNames())
    {
        SerialPort newPort = new SerialPort(s, baudRate, Parity.None, 8, StopBits.One);
        if (!newPort.IsOpen)
        {
            try
            {
                newPort.Open();
            }
            catch { }
        }
        if (newPort.IsOpen)
        {
            openPorts.Add(newPort);
            newPort.DataReceived += new SerialDataReceivedEventHandler(serialOneOfMany_DataReceived);
            newPort.Write(testMessage);
        }
        else
        {
            newPort.Dispose();
        }
    }
    txtPortName.Text = "Waiting for response";
    tmrPortTest.Enabled = true;
}

private void serialOneOfMany_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
    txtPortName.Text = ((SerialPort)sender).PortName;
}

private void tmrPortTest_Tick(object sender, EventArgs e)
{
    tmrPortTest.Enabled = false;
    foreach (SerialPort port in openPorts)
    {
        if (port.PortName != txtPortName.Text)
        {
            port.Close();
            port.Dispose();
        }
    }
}

答案 3 :(得分:0)

在写入端口之前尝试设置事件处理程序,然后查看它是否没有捕获到您的断点。

答案 4 :(得分:0)

您无法使用Thread.Sleep。它阻止从设备读取。你需要产生一个新的线程。

答案 5 :(得分:0)

BackgroundWorker你最好。例如:

        BackgroundWorker worker=new BackgroundWorker();
        worker.DoWork += (s, dwe) =>
                             {
        // do your serial IO here
        worker.RunWorkerCompleted += (s, rwe) =>
        {
            // check for rwe.Error and respond
        };
        worker.RunWorkerAsync();

答案 6 :(得分:-1)

以公共形式打开端口1 就在InitializeComponent()之后/之下; myport.open 并关闭收到的数据。 成功了!