Guten Morgen,
ich habe ein Problem beim Auslesen der seriellen Schnittstelle. Es sollen Meldungen eingelesen werden, die eine feste Anzahl an Zeichen besitzen. Das funktioniert auch soweit. Jetzt möchte ich gern den Fehlerfall (weniger Zeichen) abfangen. Genau da liegt das Problem. Mit den Methoden ReadExisting und Readline kann ich ohne ein "Thread.Sleep"(das möchte ich gern vermeiden) nicht alle 132 Zeichen einlesen. Ich das Programm scheint den InBuffer schneller auszulesen als dieser liefern kann.
ReadChar und ReadByte hängen aber wenn ich nicht 132 Zeichen liefer.
zB for (int i =0; i<132; i++) Empfangsdaten += serialPort.ReadByte();
Diese Zeile kann ich auch nicht mit try catch abfangen.
Kennt jemand eine Lösung für dieses Problem? Habe schon viel gesucht - ohne brauchbares Ergebnis.
Besten Dank
Hallo MailoKooiker,
erzeuge ein Byte-Array mit der genwünschten Größe und fülle dieses mit Hilfe der Stream.Read Methode in einer while-Schleife so lange, bis dieses komplett gefüllt ist (die Methode muss nicht die angeforderte Anzahl liefern) oder Read den Wert 0 zurück gibt (dein Fehlerfall).
Dabei musst du offset und count nach jedem Durchlauf anpassen.
Grüße
spooky
Hallo zusammen,
weshalb den Zeitpunkt des Auslesens mit dem Sleep künstlich verzögern, wenn man auch ein Event verwenden kann?
Das Event
DataReceived
wird aufgerufen, sobald Daten im ComPort angekommen sind (i.d.R. mit einem CarriageReturn und/oder Linefeed abgeschlossen wurden).
Darin dann per ReadExisting() alles auslesen was bisher angefallen ist und mit der von Dir vordefinierten Zeichenlänge vergleichen.
Normalerweise kann man durch die korrekte Konfiguration den Com-Port so definieren, dass das Event exakt mit Deiner Datenmenge aufgerufen wird. Stichwort Zeilenendzeichen (NewLine).
Der erste Vorschlag sieht viel versprechend aus. Funktioniert einmal 😉 muss die Offset und Count Einstellung mal genau durchdenken. Ich werte mit dem DataReceived aus!- nur kann ich ohne den Timeout nich alle Zeichen des Eingangspuffers auslesen. Das Programm läuft weiter ohne alles abgeholt zu haben. Ich vermute das die Daten langsamer ankommen als diese gelesen werden. Die Meldung ist definiert und die Steuerzeichen kommen zu Begin /r/n ...... deswegen kann ich das Zeilenende nicht als Trigger nutzen (sonst muss ich ja bis zur nächsten Meldung warten)
Du kannst nie sicher sein, dass alle bytes auf einmal eingelesen werden.
Ich mache das generell so, das ich auf jedes Zeichen reagiere, und die Daten dann einlese.
Da die Steuerzeichen bei dir am Anfang kommen, kannst du damit ja den Start bestimmen, und die folgenden bytes bis zu der benötigten Länge anhängen.
Gutem Morgen noch einmal,
bei der Stream Methode scheint das selbe Problem aufzutreten wie zuvor. Ohne eine künstliche Pause holt er nur 6 bis 9 Zeichen ab und arbeitet weiter. 😦
@MartinH das Problem ist, dass ich den Fehlerfall, wenn zu wenige Zeichen ankommen, abfangen möchte. Somit kann ich nicht auf die benötigte LAnge warten.
Das ist halt eine Hop oder Top Entscheidung.
Technisch gesehen kannst Du halt nie wissen, wann der letzte Message-Part kommt.
Du musst Dir quasi selbst ein Timeout bauen und dann auch damit leben, dass Du vielleicht die ein oder andere Message damit nicht fangen wirst.
- performance is a feature -
Microsoft MVP - @Website - @AzureStuttgart - github.com/BenjaminAbt - Sustainable Code
Ohne eine künstliche Pause holt er nur 6 bis 9 Zeichen ab und arbeitet weiter. 😦
Dann hast du hier aber ein Design-Problem.
Nach der Längenprüfung schiebst du den Datensatz in eine Queue, die du dann abarbeitest.
JA genau. Ich will das ja vermeiden. Genau darum geht es.
ABER Wenn ich auf das Event reagiere holt der bei READ immer nur 6-9 Zeichen ab. (ohne Sleep)
Vielleicht solltest du uns mal etwas von dem relevanten Code zeigen.
private void EventDatenEmpfangen(object sender, SerialDataReceivedEventArgs e)
{
SerielleSchnittstelle = (SerialPort)sender;
byte[] Datenarray = new byte[132];
int numBytesToRead = Einstellungen.Zeichen_je_Meldung;
int numBytesRead =0;
while (numBytesToRead>0)
{
Thread.Sleep(250);
try
{
int n = SerielleSchnittstelle.Read(Datenarray, numBytesRead, 132);
if (n == 0) break;
numBytesRead += n;
numBytesToRead -= n;
}
catch
{
MessageBox.Show("zu kurz");
break;
}
}
numBytesRead = 0;
numBytesToRead = 132;
for (int i = 0; i < 132; i++) Empfangsdaten += Convert.ToChar(Datenarray[i]);
Hallo MailoKooiker,
deine DataReceived-Ereignismethode ist ja auch nicht richtig. Du darfst nur soviel lesen, wie in der SerialPort.BytesToRead-Eigenschaft drinsteht bzw. mittels ReadExisting().
Wie oft das DataReceived-Ereignis ausgelöst wird, kann und sollte dabei völlig egal sein (und wenn nur jeweils 1 Zeichen übertragen wird).
Du mußt dir halt selber einen Puffer (als Klassenmember!) erstellen und dort dann die Daten ablegen. Und wenn du dann genug Daten empfangen hast (entsprechend deinem Protokoll) dann kannst du die Daten weiterverarbeiten.
@MailoKooiker
Poste doch bitte noch den nun angepassten Code... vielleicht wird es dir mal jemand verdanken der das selbe oder ein ähnliches Problem hat
Gruss
Programmierhans
Früher war ich unentschlossen, heute bin ich mir da nicht mehr so sicher...
Ja klar.
Wobei die Sache noch nicht rund ist Die Abholung funktioniert seht gut.
MEin Problem liegt jetzt in der Auswertung.
Bei Fehler, dass zu wenig Zeichen gesendet werden, wird die folgende richtige Meldung auch gelöscht. Das ist auch nicht schlimm aber ich brauche wieder einen sleep(wenn auch kürzer) damit ich die Bedingung BytesToRead==0 nutzen kann.
private void EventDatenEmpfangen(object sender, SerialDataReceivedEventArgs e)
{
SerielleSchnittstelle = (SerialPort)sender;
int numBytesToRead = SerielleSchnittstelle.BytesToRead;
byte[] Datenarray = new byte[numBytesToRead];
int numBytesRead = 0;
while (numBytesToRead>0)
{
int n = SerielleSchnittstelle.Read(Datenarray, numBytesRead, numBytesToRead);
if (n == 0) break;
numBytesRead += n;
numBytesToRead -= n;
}
for (int i = 0; i < numBytesRead; i++) Empfangsdaten += Convert.ToChar(Datenarray[i]);
Thread.Sleep(100);
if (Empfangsdaten.Length >= 136 && SerielleSchnittstelle.BytesToRead==0)
{
if (Empfangsdaten.Length == Convert.ToInt16(Einstellungen.Zeichen_je_Meldung)&&Empfangsdaten.Substring(0,4)=="/n/r")
{
Empfangsdaten = Empfangsdaten.Remove(0, 4);
this.BeginInvoke(new EventHandler(Verweis)); //Verweis auf den Hauptthread
}
else
{
Empfangsdaten = string.Empty;
}
}
}
Warum fragst du überhaupt auf "BytesToRead==0" ab?