2017-07-31 11 views
0

こんにちは私はC#でASCII文字列を受け取るプログラムを作成しました。文字列はXMLマークアップです。C#serial port時にはデータがありません

私からデータを受信するコンピュータが制御することはできません、それはすべての10分、私が収集して格納し、このデータを持っていますが、それ

。コンソールアプリについてのCOMポートにデータを送信し、応答を受け付けません常に動作しない私は時間のデータの約50%がパケットやバイトのように失われていると言うだろうし、XML文字列はXmlDocumentに読み込まれません

私はこれをより安定させるために約1週間これは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

ええ、私はdidntそれはそれがファイルを保存していたのでそれに影響を与えると思ったが、それを再オープンする必要がなくなった必要がなくなった – Anthony

+2

あなたのコールバックの時間の量を最小限に抑えたいあなたはデータを失うでしょう。 _circular buffer_または類似のテクニックを使用して、バッファを別の_thread_に移動することをお勧めします。 – MickyD

答えて

1

これは私の実装です。 あなたは

まず一般的なアイデアを取得する必要があります - そして、GetByteFromDeviceメソッドを呼び出します別のクラスを実装し、接続

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

からデータを取得します。不幸なことに、このコードはあまりにもあなたのために紛らわしいので、ここに掲示されたboに特有です。

ロジックを分離し、リアルタイムですべてのバイトをプッシュし、別のスレッド(ループなど)で前に読み込んだバイトを取得して解析します。

また、xml検出に使用している方法は、私の意見では最適ではありません。ストリームの開始シーケンスと終了シーケンスの検出を実装しました。あなたがコードを見たいと思ったら教えてください

+0

ありがとうございました。現在のプログラムでこのコードの一部を使用して成功しませんでした – Anthony

+0

私はこの回答を受け入れましたこのコードはいくつかの変更の後に動作しています。 – Anthony

+1

を参照するとよいxml検出のコードがある場合は、そのようなコードはありませんが、私はそれをどうやってやるか教えてくれます。キューにあるすべての文字を確認して、xmlの開始を監視し、開始タグと終了タグをトレースすることができます。このような質問をhttps://stackoverflow.com/questions/891223/better-way-to-detect-xmlで探してください。 –

関連する問題