2009-06-12 4 views
1

Nehmen wir an, ich möchte eine Funktion haben, die Daten aus dem SerialPort liest und ein Byte [] zurückgibt.Offensichtlich ist dies nicht die richtige Art zu lesen mit SerialPort

public byte[] RequestData(byte[] data) 
{ 
    //See code below 
} 

Etwas so einfach wie das wirklich nicht/ausführen funktioniert gut und ist nicht sehr zuverlässig:

byte[] response = new byte[port.ReadBufferSize]; 

port.Open();  
port.Write(data, 0, data.Length); 

Thread.Sleep(300); //Without this it doesn't even work at all 

Console.WriteLine("Bytes to read: {0}", port.BytesToRead); 

int count = port.Read(response, 0, port.ReadBufferSize); 

Console.WriteLine("Read {0} bytes", count); 

port.Close(); 
port.Dispose();  

return response.GetSubByteArray(0, count); 

Ich habe auch versucht die Thread.Sleep mit etwas zu ersetzen, wie:

while (port.BytesToRead < 14) 
{ 
    //Maybe Thread.Sleep(10) here? 
} 

Aber das verursacht Probleme. (PS: Ich weiß, ich brauche mindestens 14 Bytes)

Natürlich ist eine bessere Art und Weise (glaube ich), wie etwas zu haben wäre:

port.ReceivedBytesThreshold = 14; 
port.DataReceived += new SerialDataReceivedEventHandler(port_DataReceived); 
port.Open(); 

port.Write(data, 0, data.Length); 

Und dann einen Handler natürlich mit:

void port_DataReceived(object sender, SerialDataReceivedEventArgs e) 
{ 
    var port = (SerialPort)sender; 

    while (port.BytesToRead > 0) 
    { 
     //Read the data here 
    } 
} 

Aber dann kann ich die Daten nicht als Ergebnis der Funktion, die ich definieren wollte, zurückgeben? Der Clientcode, der dies verwendet, müsste ein Ereignis abonnieren, das durch diesen Code ausgelöst wird, , aber wie würde es dann wissen, dass die Antwort wirklich die Antwort auf die Anfrage ist, die es gerade gemacht hat.

(Mehrere Nachrichten können gesendet werden, und ich kann mir vorstellen, dass eine Nachricht länger auf der anderen Seite verarbeitet als die andere oder etwas). richtig

würde Jede beraten

UPDATE

Der folgende Code funktioniert viel besser, willkommen sein, aber wenn ich die Thread.Sleep() Aussagen entfernen stoppt es wieder zu arbeiten. Zum Beispiel zeigt das Überwachungstool für serielle Schnittstellen deutlich an, dass 17 Bytes in die serielle Leitung geschrieben wurden. Das erste Mal BytesToRead = 10 und das nächste Mal BytesToRead = 4, aber dann bleibt BytesToRead 0, also wohin sind die letzten 3 Bytes gegangen?

void port_DataReceived(object sender, SerialDataReceivedEventArgs e) 
{ 
    Thread.Sleep(100); 
    while (port.BytesToRead > 0) 
    { 
     Console.WriteLine("Bytes to read: {0}", port.BytesToRead); 
     var count = port.BytesToRead; 

     byte[] buffer = new byte[count]; 

     var read = port.Read(buffer, 0, count); 

     if (count != read) 
      Console.WriteLine("Count <> Read : {0} {1}", count, read); 

     var collectAction = new Action(() => 
     { 
      var response = dataCollector.Collect(buffer); 

      if (response != null) 
      { 
       this.OnDataReceived(response); 
      } 
     }); 

     collectAction.BeginInvoke(null, null); 
     Thread.Sleep(100); 
    }  
} 

Antwort

0

Problem gelöst:

void port_DataReceived(object sender, SerialDataReceivedEventArgs e) 
{ 
    var count = port.BytesToRead; 
    byte[] buffer = new byte[count]; 
    var read = port.Read(buffer, 0, count); 

    var response = dataCollector.Collect(buffer); 

    if (response != null) 
    { 
     this.OnDataReceived(response); 
    }    
} 

Es scheint das Problem nicht eigentlich dieser Code war aber der Code in der dataCollector.Collect() Methode.

+1

Dies ist immer noch sehr viel _nicht_ die Art, einen SerialPort zu lesen. –

+0

Dann was ist, weil es wirklich jetzt funktioniert – TimothyP

+0

Nur ein heads-up für jeden, der dies liest: Das DataReceived-Ereignis wird in einem anderen Thread ausgelöst als derjenige, der die SerialPort-Instanz erstellt hat. Siehe http://msdn.microsoft.com/en-us/library/system.io.ports.serialport.datareceived.aspx – rkagerer

3

Hier ist, wie ich es getan habe:

ich einen Wrapper für die Klasse, die die lebenswichtigen Daten für die Verbindung im Konstruktor akzeptiert und hat die grundlegende Einrichtung in diesem Konstruktor. Der Consumer der Klasse ruft eine Connect-Methode auf, die einen anderen Thread auslöst, um die Verbindung herzustellen (nicht blockierend).

Wenn die Verbindung hergestellt ist, wird ein StateEvent ausgelöst, das anzeigt, dass die Verbindung hergestellt wurde. Zu diesem Zeitpunkt wird eine Sendewarteschlange eingerichtet, ein Thread zum Arbeiten mit dieser Warteschlange wird ausgelöst und ein Lese-Thread wird ebenfalls eingerichtet. Der Lese-Thread liest 128 Zeichen von Daten aus dem SerialPort, wandelt diese in eine Zeichenfolge um und löst dann ein Ereignis aus, um die empfangenen Daten weiterzuleiten. Dies ist in einen while-Thread eingebettet, der so lange Schleife ausführt, wie die Verbindung beibehalten wird. Wenn der Verbraucher etwas senden möchte, werden die zu sendenden Daten einfach durch eine Send-Methode in die Warteschlange gestellt.

Soweit zu wissen, dass die Antwort als Reaktion auf etwas, das wirklich gesendet wurde, ist wirklich nicht die Aufgabe einer Verbindungsklasse. Durch Abstrahieren der Verbindung in etwas, das so einfach zu handhaben ist, kann der Benutzer der Klasse die Logik sauber beibehalten, um zu bestimmen, ob die Antwort so ist, wie sie erwartet wurde.

+0

Teilen Sie etwas Code? – TimothyP

+0

Leider ist der proprietäre Code zu eng mit dem Code verflochten, an dem Sie interessiert sind. – jasonh

+0

Kein Problem, thnx für die Hilfe in jedem Fall :) – TimothyP

0

Sind nicht serielle Anschlüsse Spaß. Mein einziger Gedanke ist, dass Ihr Fifo, vorausgesetzt, Ihr Gerät hat eins und es aktiviert ist, überrannt wird.