【问题标题】:serialport responding to EventHandler, but not ReadExisting or ReadLine?串行端口响应 EventHandler,但不响应 ReadExisting 或 ReadLine?
【发布时间】:2011-09-27 16:35:09
【问题描述】:

我有一个从 C# 中的串行端口读取的程序。我需要快速写入一个端口,从中读取,然后关闭它。我不能让它打开。我知道串口读写速度很慢,我尝试将 ReadTimeout 和 WriteTimeout 属性设置为高,并添加了一个线程。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 秒。如果我在没有任何调试中断的情况下运行它,它只会在几分之一秒内完成它

11 年 10 月 25 日更新

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

    }

【问题讨论】:

  • 使用 ReadExisting 时会发生什么? TimeoutException 上升了?
  • 实际上什么也没发生。当我在那里放一个断点时,它不会读取任何值。但它适用于事件处理程序
  • 实际上当我在 messagereceived 方法处设置断点时。因为除非收到响应,否则它不会调用该方法
  • 你能解释一下为什么你不能让串口“打开”吗?此代码必须在您的应用程序中涵盖哪些功能要求?

标签: c# serial-port


【解决方案1】:

您应该能够同时执行这些操作(假设没问题)。然后,您将在引发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();
        };     

【讨论】:

  • 但是如果我遍历一个根本没有响应的端口,那么它根本不会被关闭,这就是问题所在。我需要某种方法来在发生 ReadTimeout 时关闭端口,如果敲门时没有人在门口
  • 我在上面都试过了。如果我分配一个新值,例如 localserialport,它就不起作用。我尝试了另一个(我认为这是最好的路线),但是一旦我尝试写信到港口,它就说它已经关闭了。我在方法的开头定义它,并在我调用 testSerial.Open 时添加 timer.enabled。我把它们放在正确的地方了吗?
  • @darthwillard:你必须发布你的最新实现以获得关于放置的建议。
  • @darthwillard:你永远不会重新打开串口也不会启用定时器......
  • 我应该把 timer.enable = true 放在哪里?
【解决方案2】:

检查来自 Microsoft 的此信息:

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

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

【讨论】:

  • 除了 serialport.read 的 msdn 定义之外,我找不到使用将字符作为字符串返回的示例。某处有例子吗?另外,serialport.read 在返回该方法之前是否等待缓冲区填满?因为我只在每个端口上寻找 1 或 2 个字符
  • 没关系,我让它工作了。仍然无法按照我想要的方式为我工作。不过谢谢。
  • 当然。要从字节数组创建字符串,只需使用 System.Text.ASCII.GetString(bytearray)
【解决方案3】:

请看一下这个(我还在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();
        }
    }
}

【讨论】:

    【解决方案4】:

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

    【讨论】:

      【解决方案5】:

      您不能使用 Thread.Sleep。它阻止从设备读取。您需要生成一个新线程。

      【讨论】:

        【解决方案6】:

        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();
        

        【讨论】:

          【解决方案7】:

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

          【讨论】:

            猜你喜欢
            • 2021-04-19
            • 1970-01-01
            • 2012-01-23
            • 2014-01-06
            • 2018-06-14
            • 1970-01-01
            • 2018-12-01
            • 2012-07-31
            • 2015-12-10
            相关资源
            最近更新 更多