Laden...

serielle Schnittstelle einlesen - unterschiedliche Anzahl von Zeichen

Erstellt von MailoKooiker vor 9 Jahren Letzter Beitrag vor 9 Jahren 4.349 Views
M
MailoKooiker Themenstarter:in
8 Beiträge seit 2014
vor 9 Jahren
serielle Schnittstelle einlesen - unterschiedliche Anzahl von Zeichen

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

S
248 Beiträge seit 2008
vor 9 Jahren

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

T
708 Beiträge seit 2008
vor 9 Jahren

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).

M
MailoKooiker Themenstarter:in
8 Beiträge seit 2014
vor 9 Jahren

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)

185 Beiträge seit 2005
vor 9 Jahren

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.

M
MailoKooiker Themenstarter:in
8 Beiträge seit 2014
vor 9 Jahren

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.

16.806 Beiträge seit 2008
vor 9 Jahren

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.

185 Beiträge seit 2005
vor 9 Jahren

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.

M
MailoKooiker Themenstarter:in
8 Beiträge seit 2014
vor 9 Jahren

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)

185 Beiträge seit 2005
vor 9 Jahren

Vielleicht solltest du uns mal etwas von dem relevanten Code zeigen.

M
MailoKooiker Themenstarter:in
8 Beiträge seit 2014
vor 9 Jahren

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]);
4.931 Beiträge seit 2008
vor 9 Jahren

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.

M
MailoKooiker Themenstarter:in
8 Beiträge seit 2014
vor 9 Jahren

Das war der Gedankengang der gefehlt hat - Besten Dank - es läuft

4.221 Beiträge seit 2005
vor 9 Jahren

@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...

M
MailoKooiker Themenstarter:in
8 Beiträge seit 2014
vor 9 Jahren

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;
                }
       
            }

         }