C#串口有时会丢失数据

时间:2017-07-31 03:59:45

标签: c#

您好我在C#中创建了一个程序来接收一个ASCII字符串,该字符串是一个XML标记。

我从i接收数据的计算机无法控制,并且不接受每隔10分钟在COM端口发送数据的响应

控制台应用程序我收集并存储了这些数据,但它并不总是有效我会说大约有50%的时间数据丢失,就像数据包或字节丢失而XML字符串不会读入XmlDocument

我已经尝试了大约一个星期来使这个更稳定,但这是我第一次参加C#并希望有一些帮助可以改善这一点。

CODE

  class SerialPortProgram : IDisposable
  {
    // Create the serial port with basic settings
    private SerialPort port = new SerialPort("COM1",
       115200, Parity.None, 8, StopBits.One);

    string sBuffer = null;
    string filePath1 = @"C:\Data\data1.xml";
    string filePath2 = @"C:\Data\data2.xml";

    [STAThread]
    static void Main(string[] args)
    {
        // Instatiate this class
        new SerialPortProgram();

    }

    private SerialPortProgram() 
    {
        Console.WriteLine("Started Data Monitoring:");

        //Attach a method to be called when there
        //is data waiting in the port's buffer
        port.ReadBufferSize = 20971520;
        port.ReceivedBytesThreshold = 1;
        port.DataReceived += new SerialDataReceivedEventHandler(Port_DataReceived);

        //Begin communications
        port.Open();

        //Enter an application loop to keep this thread alive
        Application.Run();

    }

    private void Port_DataReceived(object sender, SerialDataReceivedEventArgs e)
    {
        //Show all the incoming data in the port's buffer
        SerialPort sp = (SerialPort)sender;
        sBuffer += sp.ReadExisting();

        if (sBuffer.Length > 26000) // check the file size
        {
            if (sBuffer.Substring(sBuffer.Length - 6) == "</xml>") // check for end of file
            {
                Console.WriteLine("Found: Processing...");
                //Thread.Sleep(1000);
                ProcessXML();
                sBuffer = null;
                Console.WriteLine("Done!");
                DateTime now = DateTime.Now;
                Console.WriteLine(now);
                Console.WriteLine("Monitoring...");
            }
            else
            {
                Console.WriteLine("Still Receiving Data: " + sBuffer.Length);
            }
        }
        else
        {
            Console.WriteLine("Receiving Data: " + sBuffer.Length);
        }
    }
 private void ProcessXML()
    {
        XmlDocument xmlDoc = new XmlDocument();
        try
        {
            xmlDoc.LoadXml("<wrapper>" + sBuffer + "</wrapper>");
            int index = 0;
            XmlNodeList xnl = xmlDoc.SelectNodes("wrapper/xml");
            foreach (XmlNode node in xnl)
            {
                // Console.WriteLine(index.ToString());
                if (index == 0)// xml file 1
                {
                    using (XmlReader r = new XmlNodeReader(node))
                    {
                        DataSet ds = new DataSet();
                        ds.ReadXml(r);
                        ds.WriteXml(filePath1);
                        Console.WriteLine("NEW Data1");
                        ds.Dispose();
                        var db = new Database();
                        db.SaveMetersToDatabase(ds);
                    }
                }
                else if (index == 1)// xml file 2
                {
                    using (XmlReader r1 = new XmlNodeReader(node))
                    {
                        DataSet dst = new DataSet();
                        dst.ReadXml(r1);
                        dst.WriteXml(filePath2);
                        Console.WriteLine("NEW Data2");
                        dst.Dispose();
                    }
                }

                index++;
            }
        }
        catch
        {
            Console.WriteLine("Error: in data");
            try
            {
                string now = DateTime.Now.ToString("yyyyMMddHHmmss");
                System.IO.File.WriteAllText(@"C:\Data\log" + now + ".xml", "<wrapper>" + sBuffer + "</wrapper>");
            }
            catch
            {
                Console.WriteLine("Failed to write to log");
            }
        }
    }

    protected virtual void Dispose(bool disposing)
    {
        if (disposing && port != null)
        {
            port.Dispose();
            port = null;
        }
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

  }
}

更新代码:

  private void Port_DataReceived(object sender, SerialDataReceivedEventArgs e)
    {
        //Show all the incoming data in the port's buffer
        SerialPort sp = (SerialPort)sender;
        sBuffer += sp.ReadExisting();

        new Thread(() =>
        {
            Thread.CurrentThread.IsBackground = true;
            if (sBuffer.Length > 25000) // check the file size
            {
                if (sBuffer.Substring(sBuffer.Length - 6) == "</xml>") // check for end of file
                {
                    Console.WriteLine("Found: Processing...");
                    Task.Run(() =>
                    {
                        ProcessXML();
                        sBuffer = null;
                        Console.WriteLine("Done!");
                        DateTime now = DateTime.Now;
                        Console.WriteLine(now);
                        Console.WriteLine("Monitoring...");
                    });
                }
                else
                {
                    Console.WriteLine("Still Receiving Data: " + sBuffer.Length);
                }
            }
            else
            {
                Console.WriteLine("Receiving Data: " + sBuffer.Length);
            }
        }).Start();
    }

更新

仍然有这个问题有可能发送计算机有时不发送所有数据或有丢包我尝试过所有我尝试过这个新代码使用串口BaseStream BeginRead

  private SerialPortProgram() 
    {
        Console.WriteLine("Started Data Monitoring:");

        //Attach a method to be called when there
        try
        {
            Port.BaudRate = 115200;
            Port.DataBits = 8;
            Port.Parity = Parity.None;
            Port.StopBits = StopBits.One;
            Port.Handshake = Handshake.None;
            Port.DtrEnable = true;
            Port.NewLine = Environment.NewLine;
            Port.ReceivedBytesThreshold = 2048;
            Port.Open();

            byte[] buffer = new byte[35000];
            Action StartRead = null;
            StartRead = () => {
                Port.BaseStream.BeginRead(buffer, 0, buffer.Length, async (IAsyncResult ar) =>
                {
                    try
                    {
                        int actualLength = Port.BaseStream.EndRead(ar);
                        byte[] received = new byte[actualLength];
                        Buffer.BlockCopy(buffer, 0, received, 0, actualLength);
                        await Task.Run(() =>
                        {
                            sBuffer += Encoding.ASCII.GetString(received);
                            CheckBuffer();
                        });
                    }
                    catch (Exception exc)
                    {
                        Console.WriteLine(exc);
                    }
                    StartRead();
                }, null);
            };
            StartRead();

        }
        catch (Exception ex)
        {
            Console.WriteLine("Error accessing port." + ex);
            Port.Dispose();
            Application.Exit();
        }

        //Enter an application loop to keep this thread alive
        Application.Run();
    }

    private void CheckBuffer()
    {
        if (sBuffer != null && sBuffer.Length > 26000) // check the file size
        {
            if (sBuffer.Substring(sBuffer.Length - 6) == "</xml>") // check for end of file
            {
                new Thread(async () =>
               {
                   Console.WriteLine("Found: Processing...");

                   await Task.Run(() => ProcessXML());

                   sBuffer = null;
                   Console.WriteLine("Done!");
                   DateTime now = DateTime.Now;
                   Console.WriteLine(now);
                   Console.WriteLine("Monitoring...");
               }).Start();
            }
            else
            {
                Console.WriteLine("Still Receiving Data: " + sBuffer.Length);
            }
        }
        else if (sBuffer != null && sBuffer.Length > 0)
        {
            Console.WriteLine("Receiving Data: " + sBuffer.Length);
        }

    }

1 个答案:

答案 0 :(得分:1)

这是我的实施。 你应该得到一般的想法

首先 - 从连接中获取数据

internal class SerialListener : Listener
{
    private SerialPort sp;
    private ConnectionInfo _connection;
    private Timer _listenerTimer;
    private bool should_exit = false;
    private bool busy = false;
    ConcurrentQueue<byte> fifo_peekonly = null;
    BlockingCollection<byte> fifo_queue = null;

    public SerialListener(ConnectionInfo connection)
        : base(connection)
    {
        _connection = connection;
        InitSerialConnection();
    }

    private void InitSerialConnection()
    {
        sp = new SerialPort(_connection.ifname_ip);
        sp.BaudRate = _connection.baudrate_port;
        sp.Parity = _connection.parity;
        sp.DataBits = _connection.charactersize;
        sp.StopBits = _connection.stopbits;
        sp.Handshake = _connection.flowcontrol;
        sp.DtrEnable = true;
        sp.ReadTimeout = 100;
        sp.Open();
        fifo_peekonly = new ConcurrentQueue<byte>();
        fifo_queue = new BlockingCollection<byte>(fifo_peekonly);
        sp.DataReceived += (sender, e) => 
        { 
            byte[] buffer = new byte[sp.BytesToRead];
            if (!sp.IsOpen)
            {
                throw new System.InvalidOperationException("Serial port is closed.");
            }
            sp.Read(buffer,0,sp.BytesToRead);
            foreach (var b in buffer)
                fifo_queue.Add(b);
        };
    }

    public override byte GetByteFromDevice()
    {
        byte b;
        b = fifo_queue.Take();
        return b;
    }

    public override byte PeekByteFromDevice ()
    {
        byte b;
        bool peeked = false;
        do {
            peeked = fifo_peekonly.TryPeek(out b);
            if (!peeked)
                Thread.Sleep(100);
        } while (!peeked);
        return b;
    }

    public override void Close()
    {
        base.Close();
        sp.DiscardInBuffer();
        sp.DiscardOutBuffer();
        Thread.Sleep(3000);
        sp.Close();
    }
}

然后实现另一个将调用GetByteFromDevice方法的类。不幸的是,代码太具体了,不能在这里发布,因为它只会让你感到困惑。

所以 - 分离逻辑,并实时推送所有字节,并在一个单独的线程(某些循环,定时器等)中获取先前读取的字节,然后分析它们。

在我看来,用于xml检测的方法也不是最佳选择。我已经实现了检测流中的开始和结束序列。如果您想查看代码,请告诉我