2013-02-27 6 views
5

Ich versuche, von mehreren seriellen Schnittstellen von Sensoren über Mikrocontroller zu lesen. Jeder serielle Port erhält mehr als 2000 Messungen (jede Messung ist 7 Bytes, alle in Hex). Und sie feuern gleichzeitig. Im Moment polle ich von 4 seriellen Ports. Außerdem übersetze ich jede Messung in String und hänge sie an einen Stringbuilder an. Wenn ich mit dem Empfang von Daten fertig bin, werden sie in eine Datei ausgegeben. Das Problem ist, dass der CPU-Verbrauch sehr hoch ist und zwischen 80% und 100% liegt.Serial Port Polling und Datenverarbeitung

Ich ging ein paar Artikel und legte Thread.Sleep (100) am Ende. Es reduziert die CPU-Zeit, wenn keine Daten kommen. Ich setze Thread.Sleep auch am Ende jeder Abfrage, wenn BytesToRead kleiner als 100 ist. Das hilft nur bis zu einem gewissen Grad.

Kann jemand vorschlagen, eine Lösung von der seriellen Schnittstelle abzufragen und Daten zu behandeln, die ich bekomme? Vielleicht hängt jedes Problem, wenn ich etwas bekomme, auf?

//I use separate threads for all sensors 
private void SensorThread(SerialPort mySerialPort, int bytesPerMeasurement, TextBox textBox,  StringBuilder data) 
    { 
     textBox.BeginInvoke(new MethodInvoker(delegate() { textBox.Text = ""; })); 

     int bytesRead; 
     int t; 
     Byte[] dataIn; 

     while (mySerialPort.IsOpen) 
     { 
      try 
      { 
       if (mySerialPort.BytesToRead != 0) 
       { 
        //trying to read a fix number of bytes 
        bytesRead = 0; 
        t = 0; 
        dataIn = new Byte[bytesPerMeasurement]; 
        t = mySerialPort.Read(dataIn, 0, bytesPerMeasurement); 
        bytesRead += t; 
        while (bytesRead != bytesPerMeasurement) 
        { 
         t = mySerialPort.Read(dataIn, bytesRead, bytesPerMeasurement - bytesRead); 
         bytesRead += t; 
        } 
        //convert them into hex string 
        StringBuilder s = new StringBuilder(); 
        foreach (Byte b in dataIn) { s.Append(b.ToString("X") + ","); } 
        var line = s.ToString(); 

              var lineString = string.Format("{0} ----   {2}", 
                 line, 
                mySerialPort.BytesToRead); 
        data.Append(lineString + "\r\n");//append a measurement to a huge Stringbuilder...Need a solution for this. 

        ////use delegate to change UI thread... 
        textBox.BeginInvoke(new MethodInvoker(delegate() { textBox.Text = line; })); 

        if (mySerialPort.BytesToRead <= 100) { Thread.Sleep(100); } 
       } 
      else{Thread.Sleep(100);} 

      } 
      catch (Exception ex) 
      { 
       //MessageBox.Show(ex.ToString()); 
      } 
     } 


    } 
+1

Verwenden Sie keine Schleife. Verwenden Sie bei empfangenen Ereignissen, wenn möglich, oder einem Multimedia-Timer, wenn nicht. –

+0

@HansPassant Ja. – Timtianyang

+0

@DannyVarod Was ist der Multimedia-Timer für in diesem Fall? – Timtianyang

Antwort

6

Dies ist keine gute Möglichkeit, es zu tun, es ist viel besser, auf dem DataReceived-Ereignis zu arbeiten.

grundsätzlich mit seriellen Schnittstellen gibt es einen 3-stufigen Prozess, der gut funktioniert.

  • die Daten von der seriellen Schnittstelle empfangen
  • Warten, bis Sie haben einen entsprechenden Datenblock
  • Interpretation der Daten

so etwas wie

class DataCollector 
{ 
    private readonly Action<List<byte>> _processMeasurement; 
    private readonly string _port; 
    private SerialPort _serialPort; 
    private const int SizeOfMeasurement = 4; 
    List<byte> Data = new List<byte>(); 

    public DataCollector(string port, Action<List<byte>> processMeasurement) 
    { 
     _processMeasurement = processMeasurement; 
     _serialPort = new SerialPort(port); 
     _serialPort.DataReceived +=SerialPortDataReceived; 
    } 

    private void SerialPortDataReceived(object sender, SerialDataReceivedEventArgs e) 
    { 
     while(_serialPort.BytesToRead > 0) 
     { 
      var count = _serialPort.BytesToRead; 
      var bytes = new byte[count]; 
      _serialPort.Read(bytes, 0, count); 
      AddBytes(bytes); 
     } 
    } 

    private void AddBytes(byte[] bytes) 
    { 
     Data.AddRange(bytes); 
     while(Data.Count > SizeOfMeasurement)    
     { 
      var measurementData = Data.GetRange(0, SizeOfMeasurement); 
      Data.RemoveRange(0, SizeOfMeasurement); 
      if (_processMeasurement != null) _processMeasurement(measurementData); 
     } 

    } 
} 

Hinweis: Fügen Bytes sammelt Daten, bis Sie genug haben, um als eine Messung zu zählen, oder wenn Sie einen Datenburst erhalten, teilt es auf o separate Messungen .... so können Sie 1 Byte einmal, 2 die nächste, und 1 mehr die nächste, und es wird dann nehmen, dass eine Messung zu machen. Die meiste Zeit, wenn Ihr Mikro es in einem Burst sendet, wird es kommen als eine, aber manchmal wird es in 2.

gespalten erhalten dann irgendwo kann man

var collector = new DataCollector("COM1", ProcessMeasurement); 

und

private void ProcessMeasurement(List<byte> bytes) 
      { 
       // this will get called for every measurement, so then 
       // put stuff into a text box.... or do whatever 
      } 
tun
+0

Interpretiere ich Daten und aktualisiere UI in einem anderen Thread? – Timtianyang

+0

nein, Sie müssen nicht, Sie könnten alles auf dem Rückruf tun. –

+0

Denken Sie daran, Ihr Computer kann viel mehr Daten verarbeiten, als über eine serielle Schnittstelle mit voller Geschwindigkeit übertragen werden kann! –

1

ich denke, was Sie sollten sich tun, einen Event-Handler hinzugefügt eingehende Daten zu verarbeiten:

mySerialPort.DataReceived += new SerialDataReceivedEventHandler(mySerialPort_DataReceived); 

Diese elimina Sie müssen für jeden seriellen Port, den Sie hören, einen separaten Thread ausführen. Außerdem wird jeder DataReceived-Handler genau dann aufgerufen, wenn Daten verfügbar sind und er wird nur so viel CPU-Zeit verbrauchen, wie für die Verarbeitung der Daten erforderlich ist, und dann an die Anwendung/das Betriebssystem geliefert.

Wenn das CPU-Auslastungsproblem nicht gelöst wird, bedeutet dies, dass Sie zu viel verarbeiten. Aber wenn Sie nicht einige sehr schnelle serielle Ports haben, kann ich mir nicht vorstellen, dass der Code, den Sie dort haben, ein Problem darstellen wird.

+0

Ich werde den Event-Handler ausprobieren. Aber ich weiß, dass die seriellen Anschlüsse Daten kontinuierlich abfeuern (von Mikrocontrollern), ist das nicht eine bessere Lösung dafür? – Timtianyang

+0

Nicht wirklich, nein. Polling ist nur dann eine gute Idee, wenn die Datenquelle nicht die Aufmerksamkeit Ihres Threads stehlen kann oder wenn der Overhead des Ereignisbehandlungsmechanismus beträchtlich ist. In Ihrem Fall ist der Overhead angesichts der Geschwindigkeit der seriellen Schnittstelle und der Geschwindigkeit Ihrer CPU gering. – ReturningTarzan

2

Betrachten Sie zunächst einmal Using Stopwatches and Timers in .NET. Sie können damit jedes Leistungsproblem aufschlüsseln und genau sagen, welcher Teil Ihres Codes das Problem verursacht.

Verwenden Sie SerialPort.DataReceived Event, um den Datenempfangsprozess auszulösen.

Getrennter Empfangsprozess und Datenmanipulationsprozess. Speichern Sie Ihre Daten zuerst und verarbeiten Sie sie dann.

Bearbeiten Sie die Benutzeroberfläche nicht aus der Leseschleife.

+0

Meinen Sie, das Empfangsereignis so kurz wie möglich zu machen und dann einen anderen Thread zu verwenden, um Daten zu interpretieren und die Benutzeroberfläche zu aktualisieren? – Timtianyang

+1

@Ttimtianyang: Es geht um [Trennung von Bedenken] (http://en.wikipedia.org/wiki/Separation_of_concerns) und [Modulare Programmierung] (http://en.wikipedia.org/wiki/Modular_programming). Keith Nicholas Code ist ein gutes Beispiel dafür, wie Sie Ihren Code strukturieren können. – rumburak