2017-07-31 83 views
0

嗨,我在C#中创建了一个程序来接收一串ASCII字符串是一个XML标记。C#串口有时缺少数据

我接收到的数据,从我在无法控制和不接受的响应它发送数据的COM端口上大约每10分钟

控制台应用程序我有收集和存储这些数据,但它的计算机并不总是工作我想说的时间数据的约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(); 
    } 

UPDATE

仍然有这个问题会不会是可能的,发送的计算机有时不发送的所有数据或者没有包损失我已经尝试了一切,我尝试了下面使用串行端口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); 
     } 

    } 
+0

什么是睡觉?这只是要求丢失数据 –

+0

是的好吧我没想到它会影响它在那里,因为它是保存文件重新打开它,但不再需要已经删除 – Anthony

+2

通过通信,你想最大限度地减少回调时间你会失去数据。我建议你使用_circular buffer_或类似的技术,并将你的缓冲区处理移动到另一个_thread_ – MickyD

回答

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检测的方法在我看来并不是最优的。我已经实现了检测流中的开始和结束序列。让我知道,如果你想看看代码

+0

感谢您的帮助,但我是在我当前的程序中没有成功使用这些代码 – Anthony

+0

我已经接受了这个答案我现在已经得到了一些修改后的代码,它已经解决了我的问题。如果你有用于xml检测的代码,很好看 – Anthony

+1

我没有这样的代码,但我可以告诉你我该怎么做。您拥有队列中的所有字符,您可以查看队列以监视xml start,然后跟踪开始vs结束标记。寻找类似这样的问题https://stackoverflow.com/questions/891223/better-way-to-detect-xml。 –