Frage

Ich habe ein hässliches Stück Serial Port-Code, der sehr instabil ist.

void port_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
    Thread.Sleep(100);
    while (port.BytesToRead > 0)
    {
        var count = port.BytesToRead;

        byte[] buffer = new byte[count];

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

        if (DataEncapsulator != null)
            buffer = DataEncapsulator.UnWrap(buffer);


       var response = dataCollector.Collect(buffer);

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

       Thread.Sleep(100);
    }    
}

Wenn ich entfernen entweder Thread.Sleep (100) ruft der Code nicht mehr funktioniert.

Natürlich ist dies verlangsamt wirklich Dinge nach unten, und wenn viele Datenströme in, es nicht mehr funktioniert auch, wenn ich nicht den Schlaf noch größer zu machen. (Funktioniert nicht mehr, wie in reiner Deadlock)

Bitte beachten Sie die DataEncapsulator und Datacollector Komponenten zur Verfügung gestellt von MEF, aber ihre Leistung ist sehr gut.

Die Klasse hat eine List () Methode, die einen Hintergrundarbeiter beginnt Empfangen von Daten.

public void Listen(IDataCollector dataCollector)
{
    this.dataCollector = dataCollector;
    BackgroundWorker worker = new BackgroundWorker();

    worker.DoWork += new DoWorkEventHandler(worker_DoWork);
    worker.RunWorkerAsync();
}

void worker_DoWork(object sender, DoWorkEventArgs e)
{
    port = new SerialPort();

    //Event handlers
    port.ReceivedBytesThreshold = 15;
    port.DataReceived += new SerialDataReceivedEventHandler(port_DataReceived);

    ..... remainder of code ...

Vorschläge sind willkommen!

Update: * Nur eine kurze Notiz über das, was die IDataCollector Klassen tun. Es gibt keine Möglichkeit, wenn alle Bytes der Daten zu kennen, die gesendet wurde, in einem einzigen Lesevorgang gelesen. So wird jedes Mal Daten lesen ist zum DataColllector geleitet, wenn eine vollständige true zurückgibt und gültige Protokollnachricht empfangen wurde. In diesem Fall hier ist es einfach prüft, ob ein Sync-Byte, Länge, crc und Schwanz Byte. Die eigentliche Arbeit wird später von anderen Klassen durchgeführt. *

Update 2: Ich ersetzt nun den Code, wie vorgeschlagen, aber noch gibt es etwas falsch:

void port_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
        var count = port.BytesToRead;

        byte[] buffer = new byte[count];

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

        if (DataEncapsulator != null)
            buffer = DataEncapsulator.UnWrap(buffer);

        var response = dataCollector.Collect(buffer);

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

Sie sehen dies mit einer schnellen und stabilen Verbindung einwandfrei funktioniert. Aber OnDataReceived wird nicht jedes Mal, wenn Daten empfangen genannt. (Siehe die MSDN-Dokumentation für weitere Informationen). Also, wenn die Daten werden fragmentiert und Sie nur einmal gelesen, innerhalb der Ereignisdaten verloren.

Und jetzt erinnere ich mich, warum ich die Schleife in erster Linie hatte, weil es tatsächlich hat mehrfach zu lesen, wenn die Verbindung langsam oder instabil ist.

Natürlich kann ich nicht auf die while-Schleife Lösung gehen, so was kann ich tun?

War es hilfreich?

Lösung

Meine erste Sorge mit dem Original, während basierten Codefragment ist die ständige Zuweisung von Speicher für die Byte-Puffer. Putting eine „neue“ Erklärung hier speziell auf die .NET-Speicher-Manager gehen Speicher für den Puffer zu reservieren, während die in der letzten Iteration zugewiesenen Speicher zu nehmen und sie wieder in den nicht verwendeten Pool für eventuelle Garbage Collection zu senden. Das scheint wie eine Menge Arbeit in einer relativ engen Schleife zu tun.

Ich bin neugierig auf die Leistungsverbesserung Sie durch die Schaffung dieser Puffer zur Entwurfszeit mit einer angemessenen Größe gewinnen würde, sagen 8K, so dass Sie alle diese Speicherzuweisung und Aufhebung der Zuordnung und die Fragmentierung nicht haben. Würde das helfen?

private byte[] buffer = new byte[8192];

void port_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
    Thread.Sleep(100);
    while (port.BytesToRead > 0)
    {
        var count = port.BytesToRead;

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

        // ... more code   
    }
}

Meine andere Sorge mit bei jeder Iteration der Schleife dieses Puffers Neuzuteilung ist, dass die Neuverteilung nicht notwendig sein kann, wenn der Puffer bereits groß genug ist. Betrachten Sie das folgende:

  • Schleifeniterationslatenzzeit 1: 100 Bytes empfangen; zuteilen Puffer von 100 Byte
  • Schleifeniterationslatenzzeit 2: 75 Bytes empfangen; zuteilen Puffer von 75 Bytes

In diesem Szenario müssen Sie nicht wirklich auf den Puffer neu zuzuweisen, da der Puffer von 100 Byte in Schleife belegt Iteration 1 ist mehr als genug, um die 75 Bytes in Schleifeniterationslatenzzeit 2. Dort erhielt zu handhaben ist nicht erforderlich, die 100-Byte-Puffer und erstellen Sie einen 75-Byte-Puffer zu zerstören. (Dies ist strittig, natürlich, wenn Sie nur statisch die Puffer erstellen und aus der Schleife zusammen bewegen.)

Bei einer anderen Tangente, könnte ich vorschlagen, dass die DataReceived Schleife Sorge sich nur mit dem Empfang der Daten. Ich bin nicht sicher, was die MEF-Komponenten tun, aber ich fragen, ob ihre Arbeit in der Datenempfangsschleife durchgeführt werden muss. Ist es möglich, dass die empfangenen Daten auf eine Art Warteschlange und die MEF-Komponenten können sie dort gestellt werden abholen? Ich bin daran interessiert, die DataReceived Schleife so zügig wie möglich zu halten. Vielleicht können die empfangenen Daten in einer Warteschlange gestellt werden, so dass es gleich wieder gehen kann Empfang mehr Daten zu arbeiten. Sie können einen anderen Thread einrichten, vielleicht, um Daten zu sehen in der Warteschlange ankommen und haben die MEF-Komponenten, die Daten von dort abholen und ihre Arbeit von dort aus. Das kann mehr Codierung sein, aber es kann die Datenempfangsschleife sein so reagieren wie möglich helfen.

Andere Tipps

Und es kann so einfach sein ...

Entweder verwenden Sie DataReceived Handler aber ohne eine Schleife und sicher ohne Sleep (), lesen Sie, welche Daten bereit und schieben Sie es irgendwo (auf eine Queue oder Memorystream)

oder

Starten Sie einen Thread (BgWorker) und machen Sie einen (Blockierung) serialPort1.Read (...), und wieder drücken oder die Daten zusammenstellen Sie erhalten.

Edit:

Von dem, was Sie auf dem Laufenden Ich würde sagen: Drop die Eventhandler und Lesen nur die Bytes innerhalb DoWork (). Das hat den Vorteil, Sie können angeben, wie viele Daten Sie wollen, so lange, wie es ist (viel) kleiner ist als die ReadBufferSize.

EDIT2, in Bezug auf Update2:

Sie werden nach wie vor von innerhalb eines BgWorker mit einer while-Schleife viel besser sein, nicht zu der Veranstaltung mit. Der einfache Weg:

byte[] buffer = new byte[128];  // 128 = (average) size of a record
while(port.IsOpen && ! worker.CancelationPending)
{
   int count = port.Read(buffer, 0, 128);
   // proccess count bytes

}

Nun, vielleicht Ihre Aufzeichnungen sind mit variabler Größe und Sie nicht wollen nicht warten, bis die nächsten 126 Bytes kommen ein zu vervollständigen. Sie können diese Melodie durch die Puffergröße zu reduzieren oder einen Readtimeout gesetzt. Um sehr feinkörnig Sie port.ReadByte verwenden könnten (). Da die von der Readbuffer liest dann ist es nicht wirklich langsamer.

Wenn Sie die Daten in eine Datei und die serielle Schnittstelle stoppt jeder so oft ist dies ein einfacher Weg, es zu tun, schreiben wollen. Wenn möglich, machen Sie Ihren Puffer groß genug, um alle Bytes zu halten, die Sie planen, eine einzelne Datei setzen in. Dann schreiben Sie den Code in Ihre DataReceived Ereignishandler wie unten gezeigt. Dann, wenn Sie eine Gelegenheit bekommen, den gesamten Puffer in eine Datei schreiben, wie unten gezeigt, dass. Wenn Sie von Ihrem Puffer lesen muss, während die serielle Schnittstelle wird dann auf Ihren Puffer zu lesen versuchen, ein gepufferte Stream-Objekt mit Deadlocks und Race Conditions zu vermeiden.

private byte[] buffer = new byte[8192]; 
var index = 0;
void port_DataReceived(object sender, SerialDataReceivedEventArgs e)
{    
    index += port.Read(buffer, index, port.BytesToRead);
} 

void WriteDataToFile()
{
    binaryWriter.Write(buffer, 0, index); 
    index = 0;
}
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top