2013-04-15 8 views
5

Ciao e grazie per aver considerato questa query: Invio un comando da un PC su una porta seriale virtuale a un sistema incorporato che echos indietro il comando quando il sistema incorporato ha completato il comando.C# attendere un eco sulla porta seriale, controllarlo e attendere un attimo sull'attesa

Posso inviare il comando e vedere l'eco, quando il comando è completato dal sistema incorporato, ma sto riscontrando problemi nel trovare un modo adeguato per attendere o ritardare il programma fino a quando non viene ricevuto il comando echo, in modo da può procedere e inviare il comando successivo. Suppongo che sia un tipo di controllo di flusso "di alto livello" che sto cercando di implementare. Il codice è in C#. Mi piacerebbe aspettare l'eco e avere un timeout anche nel caso in cui la comunicazione si interrompa tra il PC e il sistema embedded, in modo che il programma non si blocchi. Tutti i bambini Wizz là fuori che possono suggerire un modo pulito per fare questo? Non sono un grande programmatore C#, sto solo imparando.

Questa è la funzione di ricezione che ho:

private void port_DataReceived(object sender, SerialDataReceivedEventArgs e) 
    { 
     // If the com port has been closed, do nothing 
     if (!comport.IsOpen) return; 

     // This method will be called when there is data waiting in the port's buffer 

     // Determain which mode (string or binary) the user is in 
     if (CurrentDataMode == DataMode.Text) 
     { 
      // Read all the data waiting in the buffer 
      string data = comport.ReadExisting(); 

      // Display the text to the user in the terminal 
      Log(LogMsgType.Incoming, data); 
     } 
     else 
     { 
      // Obtain the number of bytes waiting in the port's buffer 
      int bytes = comport.BytesToRead; 

      // Create a byte array buffer to hold the incoming data 
      byte[] buffer = new byte[bytes]; 

      // Read the data from the port and store it in our buffer 
      comport.Read(buffer, 0, bytes); 

      // Show the user the incoming data in hex format 
      Log(LogMsgType.Incoming, ByteArrayToHexString(buffer)); 
      } 
    } 

Questo è un esempio di una chiamata a un comando transmitt:

 text = "AR" + 50 + "\r";   //transmit a command to move 
    this.comport.Write(text); 

Attualmente sto usando un tempo di ritardo [Thread. Sleep (TIMEGAP)] e supponendo che il messaggio sia eseguito e che l'eco ritorni dal sistema embedded va bene MA non lo controllo e attendo anche molto tempo per assicurarmi che sia completato:

 text = "AR" + 50 + "\r";   //transmit a command to move 
    this.comport.Write(text); 
    Thread.Sleep(TIMEGAP);    //Timegap = 10000ms 

Vorrei davvero sostituire la chiamata di ritardo [Thread.Sleep (TIMEGAP)] con una funzione/metodo che monitora la risposta sulla porta seriale, controlla che sia uguale a quella inviata e quindi consente al codice del programma di passare al comando successivo, E se l'eco corretto [AR50 \ r nell'esempio precedente] non viene ricevuto, ad esempio per 5 secondi, il programma segnala un errore.

Qualche suggerimento?

Grazie!

+0

L'evento DataReceived non si attiverà per una linea completa, così si dovrebbe concatenare i dati ricevuti in un buffer globale. –

+0

Dopo aver iniziato a considerare l'utilizzo di Thread.Sleep(), non ci sono buoni motivi per utilizzare l'evento DataReceived.Basta usare SerialPort.Read() per ottenere una risposta. Avere a che fare con i lunghi ritardi che questo può causare è di solito il prossimo problema. Rock e un posto difficile. –

risposta

6

Il modo più semplice è non utilizzare l'evento DataReceived, ma impostando uno ReadTimeout e utilizzando il metodo Read.

E dal momento che si tratta di ASCII, è necessario controllare il metodo ReadLine.

Entrambi genereranno un TimeoutException se ReadTimeout è trascorso senza dati in entrata.

Se, tuttavia, il sistema incorporato può inviare messaggi indesiderati, è necessario un altro approccio. Quindi è possibile inserire l'eco che si prevede in una variabile di stringa globale e impostare l'evento di ricezione su ManualResetEvent quando l'eco è stato ricevuto. Quindi puoi aspettare lo ManualResetEvent con un timeout. Ciò implicherà anche la sincronizzazione dei thread utilizzando l'istruzione lock.

Se GC non è un problema, probabilmente iniziare con qualcosa di simile:

using System.Text; 
using System.IO.Ports; 
using System.Threading; 

namespace ConsoleApplication2 
{ 
    class Program 
    { 
     static string serialBuffer = ""; 
     static string expectedEcho = null; 
     static object expectedEchoLock = new object(); 
     static ManualResetEvent expectedEchoReceived = new ManualResetEvent(false); 
     static SerialPort port = new SerialPort("COM1", 19200, Parity.None, 8, StopBits.One); 

     static void Main(string[] args) 
     { 
      port.DataReceived += new SerialDataReceivedEventHandler(port_DataReceived); 
     } 

     static void port_DataReceived(object sender, SerialDataReceivedEventArgs e) 
     { 
      while (port.BytesToRead > 0) 
      { 
       byte[] buffer = new byte[port.BytesToRead]; 
       int bytesRead = port.Read(buffer, 0, buffer.Length); 
       if (bytesRead <= 0) return; 
       serialBuffer += Encoding.UTF8.GetString(buffer, 0, bytesRead); 
       string[] lines = serialBuffer.Split('\r', '\n'); 
       // Don't process the last part, because it's not terminated yet 
       for (int i = 0; i < (lines.Length - 1); i++) 
       { 
        if (lines[i].Length > 0) 
         ProcessLine(lines[i]); 
       } 
       serialBuffer = lines[lines.Length - 1]; // keep last part 
      } 
     } 

     static void ProcessLine(string line) 
     { 
      bool unsolicitedMessageReceived = false; 
      lock (expectedEchoLock) 
      { 
       if (line == expectedEcho) 
       { 
        expectedEchoReceived.Set(); 
       } 
       else 
       { 
        unsolicitedMessageReceived = true; 
       } 
      } 
      if (unsolicitedMessageReceived) 
      { 
       // Process unsolicited/unexpected messages 
      } 
     } 

     /// <summary> 
     /// Send a command and wait for echo 
     /// </summary> 
     /// <param name="command">The command to send</param> 
     /// <returns>True when echo has been received, false on timeout.</returns> 
     static bool SendCommand(string command) 
     { 
      lock (expectedEchoLock) 
      { 
       expectedEchoReceived.Reset(); 
       expectedEcho = command; 
      } 
      port.Write(command); 
      return expectedEchoReceived.WaitOne(5000); // timeout after 5 seconds 
     } 
    } 
} 
+0

Nel codice di trasmissione penso che voglia impostare WriteTimeout. E suppongo quindi che comport.Write() sia una chiamata bloccante e che sotto la cappa comunichi fondamentalmente il trasmettitore di successo o lanci un'eccezione di timeout. – Richthofen

+0

Scrittura verrà bloccata solo se si utilizza il controllo del flusso come l'handshaking RTS/CTS. –

+0

Grazie a Wouter, Richthofen, ci provo. Sembra essere "ciò che il dottore ha ordinato", penso. – user2281033

Problemi correlati