简体   繁体   English

SerialPort.BaseStream.BeginRead 的正确用法是什么?

[英]What is the proper use of SerialPort.BaseStream.BeginRead?

I am having issues with a program that is getting unreliable data from a serial port, and I believe https://www.sparxeng.com/blog/software/must-use.net-system-io-ports-serialport#comments我在使用从串口获取不可靠数据的程序时遇到问题,我相信https://www.sparxeng.com/blog/software/must-use.net-system-io-ports-serialport#comments

This article has some answers - the bytestoread value seems erratic, the callback isn't always called when data is there and all of the default serial port functions are unreliable, including the datareceived event.这篇文章有一些答案——bytestoread 值似乎不稳定,当数据存在时并不总是调用回调,并且所有默认串行端口函数都不可靠,包括 datareceived 事件。 Exactly what I am experiencing in this program.正是我在这个项目中所经历的。 In fact, all over the microsoft documentation for serial ports there are disclaimers and notes of failed functionality.事实上,在整个串行端口的微软文档中,都有免责声明和功能失败的注释。 What it doesn't have is solutions to these problems when they pop up in your applications.它没有的是当这些问题出现在您的应用程序中时的解决方案。

However, when I attempt to use the provided solution in the article it seems to output the same first byte over and over forever: "pppppppppppppppppppppppppppp" I can confirm the first byte received is actually "p", but why it is not removing that byte after reading it and proceeding, or receiving any bytes after, I do not know.但是,当我尝试使用文章中提供的解决方案时,似乎 output 永远都是相同的第一个字节:“pppppppppppppppppppppppppppp” 我可以确认收到的第一个字节实际上是“p”,但为什么它没有删除该字节在阅读并继续,或收到任何字节之后,我不知道。 As far as I can tell it is the exact same solution proposed in the article.据我所知,它与文章中提出的解决方案完全相同。 Here is the code:这是代码:

private void Form1_Shown(object sender, EventArgs e)
    {
        try
        {
            sp.Open();
            SPDataHelper();
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.Message);
        }
    }


private void SPDataHelper()
    {
        byte[] buffer = new byte[8000];
        Action kickoffRead = null;

        kickoffRead = delegate
        {
            sp.BaseStream.BeginRead(buffer, 0, buffer.Length, delegate (IAsyncResult ar)
            {
                try
                {
                    int actualLength = sp.BaseStream.EndRead(ar);
                    byte[] received = new byte[actualLength];
                    Buffer.BlockCopy(buffer, 0, received, 0, actualLength);
                    sp_DataReceived(System.Text.Encoding.UTF8.GetString(received));//not called by the actual serialport anymore
                }
                catch (IOException exc)
                {
                    WriteUUTWindow("Exception: "+exc.ToString());
                }
                kickoffRead();
            }, null);
        };
        kickoffRead();
    }

When I put a breakpoint on the "actuallength" after its assignment it shows 1, suggesting it never reads after the first byte.当我在分配后在“actuallength”上放置断点时,它显示 1,表明它从不在第一个字节之后读取。 The breakpoint for both that assignment and the callback are never reached again afterwards, but the application continues to spam "p".之后再也不会到达该分配和回调的断点,但应用程序继续发送垃圾邮件“p”。 Any idea what's going on here?知道这里发生了什么吗?

If you're interested in using BeginRead , try the following - it's been tested with a barcode scanner:如果您对使用BeginRead感兴趣,请尝试以下操作 - 它已经过条形码扫描仪测试:

Create a class (name: HelperSerialPort.cs)创建一个class(名称:HelperSerialPort.cs)

Option 1 :选项 1

HelperSerialPort.cs HelperSerialPort.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.IO.Ports;
using System.Diagnostics;

namespace SerialPortTest
{
    public enum PortBaudRate : int
    {
        Baud1200 = 1200,
        Baud2400 = 2400,
        Baud4800 = 4800,
        Baud9600 = 9600,
        Baud14400 = 14400,
        Baud19200 = 19200,
        Baud28800 = 28800,
        Baud38400 = 38400,
        Baud56000 = 56000,
        Baud57600 = 57600,
        Baud76800 = 76800,
        Baud115200 = 115200,
        Baud128000 = 128000,
        Baud230400 = 230400,
        Baud250000 = 250000,
        Baud256000 = 256000
    };

    public class HelperSerialPort
    {
        public SerialPort Port { get; private set; } = null;

        private string _dataReceived = string.Empty;

        public HelperSerialPort(string portName, PortBaudRate baudRate = PortBaudRate.Baud9600)
        {
            Initialize(portName, baudRate);
        }

        private void Initialize(string portName, PortBaudRate baudRate = PortBaudRate.Baud9600)
        {
            //create new instance
            this.Port = new SerialPort();

            //set properties
            Port.BaudRate = (int)baudRate;
            Port.DataBits = 8;
            Port.Parity = Parity.None; //use 'None' when DataBits = 8; if DataBits = 7, use 'Even' or 'Odd'
            Port.DtrEnable = true; //enable Data Terminal Ready
            Port.Handshake = Handshake.None;
            Port.PortName = portName;
            Port.ReadTimeout = 200; //used when using ReadLine
            Port.RtsEnable = true; //enable Request to send
            Port.StopBits = StopBits.One;
            Port.WriteTimeout = 50;

            //open
            Port.Open();

            Listen();
        }

        private void Listen()
        {
            byte[] buffer = new byte[65536];

            IAsyncResult result = Port.BaseStream.BeginRead(buffer, 0, buffer.Length, delegate (IAsyncResult ar)
            {
                try
                {
                    if (Port.IsOpen)
                    {
                        int bytesRead = Port.BaseStream.EndRead(ar);

                        byte[] received = new byte[bytesRead];
                        Buffer.BlockCopy(buffer, 0, received, 0, bytesRead);

                        _dataReceived = System.Text.Encoding.UTF8.GetString(received);
                        Debug.WriteLine("Info: " + DateTime.Now.ToString("HH:mm:ss:fff") + " - _dataReceived: " + _dataReceived);

                        Listen();
                    }
                    
                }
                catch (IOException ex)
                {
                    Debug.WriteLine("Error (Listen) - " + ex.Message);
                }
            }, null);
        }

        public void Dispose()
        {
            if (Port != null)
            {
                Port.Close();
                Port.Dispose();

                Port = null;
            }
        }
    }
}

Update更新

Here's another version which seems to work as well-it uses a slightly modified version of your code.这是另一个似乎也能正常工作的版本——它使用了对您的代码稍作修改的版本。

Option 2 :选项 2

HelperSerialPort.cs HelperSerialPort.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.IO.Ports;
using System.Diagnostics;

namespace SerialPortTest
{
    public enum PortBaudRate : int
    {
        Baud1200 = 1200,
        Baud2400 = 2400,
        Baud4800 = 4800,
        Baud9600 = 9600,
        Baud14400 = 14400,
        Baud19200 = 19200,
        Baud28800 = 28800,
        Baud38400 = 38400,
        Baud56000 = 56000,
        Baud57600 = 57600,
        Baud76800 = 76800,
        Baud115200 = 115200,
        Baud128000 = 128000,
        Baud230400 = 230400,
        Baud250000 = 250000,
        Baud256000 = 256000
    };


    public class HelperSerialPort
    {
        public SerialPort Port { get; private set; } = null;

        private string _dataReceived = string.Empty;

        public HelperSerialPort(string portName, PortBaudRate baudRate = PortBaudRate.Baud9600)
        {
            Initialize(portName, baudRate);
        }

        private void Initialize(string portName, PortBaudRate baudRate = PortBaudRate.Baud9600)
        {
            //create new instance
            this.Port = new SerialPort();

            //set properties
            Port.BaudRate = (int)baudRate;
            Port.DataBits = 8;
            Port.Parity = Parity.None; //use 'None' when DataBits = 8; if DataBits = 7, use 'Even' or 'Odd'
            Port.DtrEnable = true; //enable Data Terminal Ready
            Port.Handshake = Handshake.None;
            Port.PortName = portName;
            Port.ReadTimeout = 200; //used when using ReadLine
            Port.RtsEnable = true; //enable Request to send
            Port.StopBits = StopBits.One;
            Port.WriteTimeout = 50;

            //open
            Port.Open();

            SPDataHelper();
        }


        private void SPDataHelper()
        {
            byte[] buffer = new byte[65536];
            Action kickoffRead = null;

            kickoffRead = delegate
            {
                IAsyncResult result = Port.BaseStream.BeginRead(buffer, 0, buffer.Length, delegate (IAsyncResult ar)
                {
                    try
                    {
                        if (Port.IsOpen)
                        {
                            int bytesRead = Port.BaseStream.EndRead(ar);

                            byte[] received = new byte[bytesRead];
                            Buffer.BlockCopy(buffer, 0, received, 0, bytesRead);

                            _dataReceived = System.Text.Encoding.UTF8.GetString(received);
                            Debug.WriteLine("Info: " + DateTime.Now.ToString("HH:mm:ss:fff") + " - _dataReceived: " + _dataReceived);

                            kickoffRead();
                        }

                    }
                    catch (IOException ex)
                    {
                        Debug.WriteLine("Error (SPDataHelper) - " + ex.Message);
                    }
                }, null);
            };

            kickoffRead();
        }

        public void Dispose()
        {
            if (Port != null)
            {
                Port.Close();
                Port.Dispose();

                Port = null;
            }
        }
    }
}

Option 3选项 3

Here's an option that uses SerialPort DataReceived .这是一个使用 SerialPort DataReceived的选项。

HelperSerialPort.cs : HelperSerialPort.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.IO.Ports;
using System.Diagnostics;

namespace SerialPortTest
{
    public enum PortBaudRate : int
    {
        Baud1200 = 1200,
        Baud2400 = 2400,
        Baud4800 = 4800,
        Baud9600 = 9600,
        Baud14400 = 14400,
        Baud19200 = 19200,
        Baud28800 = 28800,
        Baud38400 = 38400,
        Baud56000 = 56000,
        Baud57600 = 57600,
        Baud76800 = 76800,
        Baud115200 = 115200,
        Baud128000 = 128000,
        Baud230400 = 230400,
        Baud250000 = 250000,
        Baud256000 = 256000
    };

    public class HelperSerialPort : IDisposable 
    {
        public SerialPort Port { get; private set; } = null;

        private string _dataReceived = string.Empty;

        public HelperSerialPort(string portName, PortBaudRate baudRate = PortBaudRate.Baud9600)
        {
            Initialize(portName, baudRate);  
        }

        private void Initialize(string portName, PortBaudRate baudRate = PortBaudRate.Baud9600)
        {
            //create new instance
            this.Port = new SerialPort();

            //set properties
            Port.BaudRate = (int)baudRate;
            Port.DataBits = 8;
            Port.Parity = Parity.None; //use 'None' when DataBits = 8; if DataBits = 7, use 'Even' or 'Odd'
            Port.DtrEnable = true; //enable Data Terminal Ready
            Port.Handshake = Handshake.None;
            Port.PortName = portName;
            Port.ReadTimeout = 200; //used when using ReadLine
            Port.RtsEnable = true; //enable Request to send
            Port.StopBits = StopBits.One;
            Port.WriteTimeout = 50;

            //subscribe to events
            Port.DataReceived += Port_DataReceived;
            Port.ErrorReceived += Port_ErrorReceived;

            //open
            Port.Open();

        }

        private void Port_ErrorReceived(object sender, SerialErrorReceivedEventArgs e)
        {
            Debug.WriteLine("Error: (sp_ErrorReceived) - " + e.EventType);
        }

        private void Port_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            _dataReceived = Port.ReadExisting();
            Debug.WriteLine("Info: " + DateTime.Now.ToString("HH:mm:ss:fff") + " - _dataReceived: " + _dataReceived);
        }

        public void Dispose()
        {
            if (Port != null)
            {
                //unsubscribe from events
                Port.DataReceived -= Port_DataReceived;
                Port.ErrorReceived -= Port_ErrorReceived;

                Port.Close();
                Port.Dispose();

                Port = null;
            }
        }
    }
}

Usage :用法

HelperSerialPort helper = new HelperSerialPort("COM1");

Note : Ensure Dispose is called when you're finished with the SerialPort.注意:确保在使用完 SerialPort 后调用Dispose

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM