Hallo, ich arbeite seit einiger Zeit mit C# an einer Aufgabe zur Datenerfassung mehrerer serieller Schnittstellen. Das Einlesen einer einzigen seriellen Schnittstelle funktioniert sehr gut. Hierzu nutze ich den DataReceveived-Handler und es werden nacheinander 14 Bytes eingelesen, gespeichert, verarbeitet und auf einem Label angezeigt. Nun zu meinem Problem: Leider muss ich nicht nur einen seriellen Port auslesen, sondern fünf. Das Programm funktioniert weiterhin, allerdings brauchst das Programm bis zu 4 Sekunden, um das Label eines seriellen Ports zu aktualisieren. Das ist für meine Anwendung viel zu langsam. Hier beispielhaft der Code für die Datenerfassung von nur zwei Ports: Das Programm schaltet hierbei den seriellen Port 1 an und die Verarbeitung wird in den Unterprogrammen durchlaufen. Ist das geschehen, wird Port 1 geschlossen und Port 2 geöffnet. Ist die Verarbeitung dort abgeschlossen, wird Port 2 geschlossen und Port 1 wieder geöffnet. // DATA RECEIVED1 ------------------------------------------------------------------------ --- private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e) { try { if (erstesByte == 1) { do { ByteBack = serialPort1.ReadByte(); } while (ByteBack > 32); this.Invoke(new EventHandler(Fkt_CreateArray)); i++; for (i = 1; i < 14; i++) { ByteBack = serialPort1.ReadByte(); this.Invoke(new EventHandler(Fkt_CreateArray)); } this.Invoke(new EventHandler(Fkt_auswertung)); erstesByte = 0; } i = 0; serialPort1.Close(); serialPort2.Open(); } catch (System.TimeoutException) { MessageBox.Show("Hallo?"); } } // DATA RECEIVED 2 ------------------------------------------------------------------------ --- private void serialPort2_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e) { try { if (erstesByte == 1) { do { ByteBack = serialPort2.ReadByte(); } while (ByteBack > 32); this.Invoke(new EventHandler(Fkt_CreateArray)); i++; for (i = 1; i < 14; i++) { ByteBack = serialPort2.ReadByte(); this.Invoke(new EventHandler(Fkt_CreateArray)); } this.Invoke(new EventHandler(Fkt_auswertung)); erstesByte = 0; } i = 0; serialPort2.Close(); serialPort1.Open(); } catch (System.TimeoutException) { MessageBox.Show("Hallo?"); } } ------------------------------------------------------------------------ Kann sich jemand vorstellen, warum das Programm so langsam ist? Ich komme absolut nicht weiter. Liegt es vielleicht an dem DataReceived-Handler? Ich wäre sehr dankbar, wenn mir jemand unter die Arme greifen könnte! Gruß steka
Steffen Kauth schrieb: > Leider muss ich nicht nur einen seriellen Port auslesen, sondern fünf. > Das Programm funktioniert weiterhin, allerdings brauchst das Programm > bis zu 4 Sekunden, um das Label eines seriellen Ports zu aktualisieren. > Das ist für meine Anwendung viel zu langsam. ist ja klar, dein Programmstuktur ist nicht brauchbar. In einem DataReceivedEventArgs sollte man genau so viel Byte einlesen wie gerade im Buffer sind, nicht mehr und nicht weniger. Dieser code ist also dort fehl am Platz: for (i = 1; i < 14; i++) { ByteBack = serialPort2.ReadByte(); Man könnte auch für jeden Port einen Thread Starten und einfach mit Read einlesen.
Danke für deine schnelle Antwort! Von meiner Sendeeinheit werden kontinuierlich Daten gesendet. Muss ich also den Zählerstand der Buffer einlesen und ab 14 Bytes meine Unterprogramme aufrufen? Wie kann ich in diesem Fall auf die einzelnen Bytes zugreifen? Wenn ich die Bytes einlese, wird der Buffer dann wieder freigegeben? Threading ist ein ganz neues Thema für mich. Was kann ich damit erreichen?
Steffen Kauth schrieb: > Von meiner Sendeeinheit werden kontinuierlich Daten gesendet. Muss ich > also den Zählerstand der Buffer einlesen und ab 14 Bytes meine > Unterprogramme aufrufen? Wie kann ich in diesem Fall auf die einzelnen > Bytes zugreifen? Wenn ich die Bytes einlese, wird der Buffer dann wieder > freigegeben? ja, genauso. dafür kann man einen liste nehmen: list<Byte> buffer = new list<Byte>(); im Event machst du dann int anzahl = Port.BytesToRead(); while ( anzahl ) { buffer.Add( Port.ReadByte() ); anzahl--; } if (buffer.length >= 14) { //jetzt kannst du auf die Byte mit buffer[0..13] zugreifen //jetzt den buffer wieder leer machen buffer.Clear(); } (code hat bestimmt viele Fehler, ist nur schnell getippt) Wenn man es mit Thread macht, braucht man diese Events nicht. Das ist wie als ob dein Programm dann viele dinge gleichzeitig mache kann. Aber dabei muss man einiges beachten.
Ich werde es ausprobieren und berichten. Meinst du, dass das Programm dadurch schneller wird oder liegt das Problem vielleicht an anderer Stelle?
Steffen trinkt schrieb: > Meinst du, dass das Programm dadurch schneller wird oder liegt das > Problem vielleicht an anderer Stelle? das Programm ist nicht langsam, es blockiert nur. Das liegt daran das du erst alle Daten einsammelst und in dieser zeit kann nichts anders Passieren. Ja ich denke das es nach dem umbau flüssiger läuft
Es funktioniert jetzt auch mit deiner Vorgehensweise, allerdings hat sich das Programmablauf nicht "beschleunigt". Trotzdem danke! Bei Google habe ich etwas gefunden, was sich "DoEvents()" nennt. Könnte man dadurch eine Verbesserung erreichen?
Steffen trinkt schrieb: > Bei Google habe ich etwas gefunden, was sich "DoEvents()" nennt. Könnte > man dadurch eine Verbesserung erreichen? nein. Die Frage ist was langsam ist. Also wo hängt denn das Programm fest? Zeige noch mal den akuellen quellcode.
Hallo Steffen, Warum machst Du die Ports jeweils wieder zu? Wenn ein Port zu ist, dann empfängst du auf dem auch nichts. --jmp
Ich schließe die Ports, da sie alle auf die gleiche Verarbeitungsfunktion zugreifen (Fkt_auswertung). Damit die Funktion nicht gleichzeitig durchlaufen wird und es zu Fehlern kommt, möchte ich die Ports nacheinander "durchschalten". Vielleicht sollte ich einfach eine seperate Verarbeitungsfunktion für jeden einzelnen Port anlegen. Nicht sehr elegant, aber vielleicht funktioniert es.
Steffen trinkt schrieb: > Vielleicht sollte ich einfach eine seperate Verarbeitungsfunktion für > jeden einzelnen Port anlegen. Nicht sehr elegant, aber vielleicht > funktioniert es. warum sollte eine Funktion nicht für viele Ports funktionieren? Zeig uns doch mal den Quellcode, nur so können wie eventuell helfen. Keine kann wissen was du alles geändert hast.
Hier noch einmal der Ablauf für 2 serielle Ports mit Auswertung der Zahlenwerte, welche von Sendeeinheit kommen: // DATA RECEIVED1 ------------------------------------------------------------------------ --- private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e) { int anzahl = serialPort1.BytesToRead; while (anzahl != 0) { buffer.Add(serialPort1.ReadByte()); anzahl--; } if (buffer.Count >= 28) { // Suche erstes Byte in Buffer --> Wert muss kleiner 32 sein! do { if (buffer[lauf] <= 32) { erstesByteGefunden = true; } else { lauf++; } } while (erstesByteGefunden == false); for (i = 0; i < 14; i++) { ByteBack = buffer[lauf]; this.Invoke(new EventHandler(Fkt_CreateArray)); lauf++; } this.Invoke(new EventHandler(Fkt_auswertung)); lauf = 0; i = 0; erstesByteGefunden = false; buffer.Clear(); serialPort1.Close(); serialPort2.Open(); } } // DATA RECEIVED 2 ------------------------------------------------------------------------ --- private void serialPort2_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e) { { int anzahl = serialPort2.BytesToRead; while (anzahl != 0) { buffer.Add(serialPort2.ReadByte()); anzahl--; } if (buffer.Count >= 28) { do { if (buffer[lauf] <= 32) { erstesByteGefunden = true; } else { lauf++; } } while (erstesByteGefunden == false); for (i = 0; i < 14; i++) { ByteBack = buffer[lauf]; this.Invoke(new EventHandler(Fkt_CreateArray)); lauf++; } this.Invoke(new EventHandler(Fkt_auswertung)); lauf = 0; i = 0; erstesByteGefunden = false; buffer.Clear(); serialPort2.Close(); serialPort1.Open(); } } // ERZEUGUNG ARRAY - SCHREIBE BYTES IN ARRAY ------------------------------------------------------------------------ --------------- private void Fkt_CreateArray(object serialPort1_DataReceived, EventArgs e) { array1[i] = ByteBack; } // AUSWERTUNG DER EINZELNEN ARRAYWERTE ------------------------------------------------------------------------ -------------------- private void Fkt_auswertung(object s, EventArgs e) { // AUSWERTUNG ZAHL A switch (((array1[1] & 0x07) * 16) + (array1[2] & 0x0f)) { case 125: ZahlA = 0; break; case 5: ZahlA = 1; break; case 91: ZahlA = 2; break; case 31: ZahlA = 3; break; case 39: ZahlA = 4; break; case 62: ZahlA = 5; break; case 126: ZahlA = 6; break; case 21: ZahlA = 7; break; case 127: ZahlA = 8; break; case 63: ZahlA = 9; break; default: label_Messwert_1.Text = "WAIT"; break; } // AUSWERTUNG ZAHL B switch (((array1[3] & 0x07)) * 16 + (array1[4] & 0x0F)) { case 125: ZahlB = 0; break; case 5: ZahlB = 1; break; case 91: ZahlB = 2; break; case 31: ZahlB = 3; break; case 39: ZahlB = 4; break; case 62: ZahlB = 5; break; case 126: ZahlB = 6; break; case 21: ZahlB = 7; break; case 127: ZahlB = 8; break; case 63: ZahlB = 9; break; default: label_Messwert_1.Text = "WAIT"; break; } // AUSWERTUNG ZAHL C switch (((array1[5] & 0x07) * 16) + (array1[6] & 0x0F)) { case 125: ZahlC = 0; break; case 5: ZahlC = 1; break; case 91: ZahlC = 2; break; case 31: ZahlC = 3; break; case 39: ZahlC = 4; break; case 62: ZahlC = 5; break; case 126: ZahlC = 6; break; case 21: ZahlC = 7; break; case 127: ZahlC = 8; break; case 63: ZahlC = 9; break; default: label_Messwert_1.Text = "WAIT"; break; } // AUSWERTUNG ZAHL D switch (((array1[7] & 0x07) * 16) + (array1[8] & 0x0F)) { case 125: ZahlD = 0; break; case 5: ZahlD = 1; break; case 91: ZahlD = 2; break; case 31: ZahlD = 3; break; case 39: ZahlD = 4; break; case 62: ZahlD = 5; break; case 126: ZahlD = 6; break; case 21: ZahlD = 7; break; case 127: ZahlD = 8; break; case 63: ZahlD = 9; break; default: label_Messwert_1.Text = "WAIT"; break; } Fkt_createAnzeige(); } ***************************************************************** Ungefährer Ablauf ist also: 1. Öffne Port1 --> Schreibe Werte in Array1 2. Auswertung Array1 --> Fkt_createAnzeige gibt nur noch Werte auf jeweiliges Label aus 3. Schließe Port 1 --> Öffne Port 2 4. das gleiche nochmal mit Port2 Das große Problem der Vergangenheit: Wenn Port1 und Port2 aktiv sind wird Array1 teilweise mit Werten von POrt1 beschrieben und teilweise mit Werten von Port2! Was haltet ihr von dem Ansatz alle Ports zu öffnen, dafür allerdings mehrere Verarbeitungsfunktionen mit unterschiedlichen Variablen zu schreiben? Z.B.: Fkt_createArray_Port1 und Fkt_auswertung_Port1, Fkt_createArray_Port2 und Fkt_auswertung_Port2
naja, der code ist noch recht umständlich.
1 | for (i = 0; i < 14; i++) |
2 | {
|
3 | ByteBack = buffer[lauf]; |
4 | this.Invoke(new EventHandler(Fkt_CreateArray)); |
5 | |
6 | lauf++; |
7 | }
|
warum übergibt so jedes Byte einzeln über eine globale Variabel über eine Invoke Funktion? fülle doch gleich das Array selber ab. lauf wird auch erstmals verwendet ohne das man sieht woher es kommt. Muss lauf denn global sein? der Funktion Fkt_auswertung kannst du doch einfach das Array mit den Daten übergeben. Fkt_auswertung( array1 ); oder halt Fkt_auswertung( array2 ); du hast mir zu viele Globale Variablen ( ZahlA, ZahlB ... ) du sollte die Variablen in einer Stukt oder Classen zusammenfassen. Dann kann man das Programm auf beliebig viele Ports gleichzeitig laufen lassen, ohne das man den code x mal schreiben muss. DataReceived kann man auch für viele Ports gleichzeitig nutzen, dafür muss man dann den Sender Parameter auswerten. Also mache eine neue Klasse wo folgenden drin ist ComPort BufferArray der Rowdaten DatenArray mit den nutzdaten ZahlA Zahlb ... damit kann man dann schon mal besser arbeiten und hat auch noch einen überblick.
Steffen trinkt schrieb: > Bei Google habe ich etwas gefunden, was sich "DoEvents()" nennt. Könnte > man dadurch eine Verbesserung erreichen? "Keeping your UI Responsive and the Dangers of Application.DoEvents": http://blogs.msdn.com/b/jfoscoding/archive/2005/08/06/448560.aspx
Vielen Dank super Tipps! Ich werde es heute umsetzen und erneut testen, ob es läuft!
Ich glaube, ich habe jetzt alles unter Kontrolle. Vielen Dank für eure Hilfe!!
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.