Hallo, ich versuche mich gerade daran eine Tool für die Kommunikation mit dem seriellen Endgerät. Auch um zu lernen und für spätere Anwendungen die ggf. mit CAN oder ähnlichem arbeiten realisieren zu können. Ich empfange die Daten von dem UART über einen Interrupt, lese sie in einen Messagebuffer und werte hier das LEN-Byte aus um zu wissen wann ich einen ganzen Frame empfangen habe. Am Ende prüfe ich darin auch noch das CRC. Das funktioniert alles asynchron, Byteweise. Über einen beim ersten Byte gestarteten Timer handle ich dabei den Timeout. Der wird bei jeder Iteration zurückgesetzt. Kommt längere Zeit (hab jetzt mal 100ms genommen) nichts, wird er ausgelöst. Wir ein Frame vollständig empfangen wird der Timer angehalten. Habe ich ein Frame empfangen schiebe ich es, so wie es ist, in eine Mailbox, welche ich mit einem globalen Array realisiert habe. Immer schön mit Push oben drupp. Etwas ähnliches mache ich zum senden. Also fertigen Frame in die Out-Mailbox. Ein Timer prüft diese regelmäßig (alle 10ms) auf Inhalt ab. Findet er was darin, sendet er die Bytes in einem Schwung los. Währenddessen halte ich den Sendetimer an um Doppelauslösungen zu verhindern. Wie aber bediene ich die beiden Mailboxen nun im Sinne einer Challenge/Response Kommunikation? Ich muss ja einen bestimmten Ablauf durchziehen, starte mit dem senden oder empfangen einer Botschaft und dann geht es immer hin und her. Auch hier braucht man ja einen Timeout wenn der Partner nicht antwortet. Ich muss ja in meiner Sequenz immer was senden, dann warten bis der Empfänger antwortet, die Antwort auswerten und ggf. davon abhängig eine neue Frage zusammenbauen oder auch abbrechen. Dabei wäre es wohl kein guter Plan in der Sequenz blockierende waits einzubauen. Zumal unabhängig von meiner Anfrage auch Daten spontan vom Empfänger gesendet werden können. Sende- und Empfangsroutine müssen also in der Lage sein unabhängig voneinander zu arbeiten, auch was die Sitzungen angeht. D.H. wenn ich eine Kommunikation starte und auf den Empfang einer Nachricht warte, kann es sein das der Empfänger selbst eine Anfrage an mich stellt und diese darf dann natürlich nicht als Antwort auf meine Frage gewertet werden. Die Grafik zeigt nur ein sehr einfaches Beispiel. Andere Frames enthalten 12 oder mehr Datenbytes. Wie geht man da vor? Lädt man sich die zu sendenden Pakete in ein Array? Oder arbeitet man da eher mit abstrakten Symbolen? Mir fehlt da grad jede Idee... (Programmiersprache ist Javascript, sollte aber fürs Konzept keine Rolle spielen?) Vielen Dank!
:
Bearbeitet durch User
Olli Z. schrieb: > Wie geht man da vor? Typischerweise überlegt man zuerst mal, welche Randbedingungen erfüllt werden sollen. Ein Minimalist fragt sich, was das System mindestens leisten muß (wieviel Teilnehmer, wieviel Messages je Sekunde). Ein Maximalist fragt sich zuerst, was das System in Zukunft leisten soll. Erst danach überlegt man, wie man das wohl realisiert. Gruß Klaus (der soundsovielte)
Max M. schrieb: >> Programmiersprache ist Javascript > > Auf einem Mikrcontroller? Gibts sogar für dem RP Pico
:
Bearbeitet durch User
Wenn einem nichts besseres einfaellt, schreibt ein, zwei, ... N Statemachines, und das Problem ist geloest.
Mir geht es um den theoretischen Ansatz, aber wenn es hilft können wir das gern am praktischen Beispiel machen. ------------------------------------------------ Das serielle Bauteile über das ich hier spreche ist ein Funkempfänger. Dieser nutzt eine 1-Draht Schnittstelle welche am normalerweise daran angeschlossenen Steuergerät mittels eines LIN-Transceivers angeschlossen ist. Es handelt sich beim Übertragungsprotokoll (Layer 1) aber nicht um LIN, sondern um einfachen UART mit 9600 Baud 8E1. Ich möchte mit diesem Empfänger arbeiten und daher die Funktion des Steuergeräts emulieren. Zunächst an einem PC mit ScriptCommunicator als Framework. Im Original hängen mehrere Teilnehmer am gleichen 1-Draht Bus, es gibt aber keine direkte Adressierung. Vielmehr reagieren bestimmte Teilnehmer nur auf bestimmte Kommandos. Die Datagramm-Frequenz habe ich noch nicht gemessen, ist aber überschaubar da anders als bei CAN nur ereignisbehaftet gesendet wird. Der Empfänger kann auf bestimmte Sender "programmiert" werden. Jeder Sender hat eine 4 Byte ID. Sendet ein solches drahtlosgerät, sendet der Empfänger selbst, spontan (also eben nicht wie bei I2C) eine Botschaft mit dem Inhalt. Die Datagramme haben immer diesen Aufbau:
1 | DIR LEN CMD [DATA..] CRC |
DATA ist optional und in meinen Messungen bis zu 15 Bytes lang. Anders als in "klassischen" UART Anwendungen gibt es keinen Handshake und auch kein EOL Zeichen. Man muss also am Datenstrom erkennen wie viele Bytes zu einem Frame gehören. Das LEN Byte gibt dabei die Anzahl der nach ihm folgenden Bytes an. Das CMD Byte gibt Aufschluss darüber ob ein Datagramm per Funk empfangen wurde, oder man was vom Empfängerbaustein will (z.B. eine ID programmieren). CRC ist einfach ein XOR aller Bytes des Frame. ------------------------------------------------ Als Schnittstelle habe ich einen FTDI-USB UART von dem RX und TX über einen LIN-Transceiver am 1-Draht Bus des Empfängers hängt. Aufgrund des 1-Draht Busses ist obligatorisch das die Busteilnehmer die selbst ausgesandten Daten auch wieder empfangen. Da es keine Bus-Arbitierung, Master/Slave oder ähnliches gibt, ist es wichtig das der Sender prüft ob gerade Daten auf dem Bus geschickt werden oder nicht um eine laufende Kommunikation nicht zu stören. TX und RX müssen sich also intern gegenseitig verriegeln damit sich Sendungen von A->B nicht mit dem eigenen Echo von B->A vermischen, etc. ------------------------------------------------ Das Protokoll ist mir nicht 100% bekannt, man erkennt aber einen paar Flows. Zum einen wird ein vom Steuergerät gesendetes Kommando vom Empfänger quittiert indem er es zurücksendet und nach dem CMD Byte ein ACK-Byte (0x01) oder NAK-Byte (0x00) voranstellt. Darüber hinaus gibt es Sequenzen die mehrere Datenpakete beinhalten, wie z.B. hier am Beispiel der Sender-ID Programmierung: - Steuergerät sendet "Wakeup" (wird nicht quittiert) - Steuergerät sendet "Start der Programmierung" (wird quittiert) - Steuergerät sendet "Sender-ID 1" (wird quittiert) - Steuergerät sendet weitere Sender-IDs... - Steuergerät sendet eine Ende-Sequenz (wird quittiert) ------------------------------------------------
Olli Z. schrieb: > Die Datagramm-Frequenz habe ich noch nicht gemessen, ist aber > überschaubar da anders als bei CAN nur ereignisbehaftet gesendet wird. "Überschaubar" ist ein Gummibegriff und CAN wurde extra für prioritätsgesteuerte ereignisbasierte Peer-to-Peer-Kommunikation erfunden (im Gegensatz zu zyklischen Protokollen wie ASI oder Interbus und Token-basierenden Round-Robin-Sytemen wie ArcNet). Gruß Klaus (der soundsovielte)
Versuche nicht, eine Eier-legende Wollmilchsau zu schaffen. Programmiere nur das, was du wirklich brauchst. Nichts verschwindet schneller wieder in der Versenkung, als Software.
Mein Ansatz bislang war folgender: Empfang der Daten über ISR -------------------------- Die vom UART empfangenen Bytes lösen einen Interrupt aus. Die ISR schiebt die Daten einfach in einen RX-Buffer damit nichts verloren geht. Im Javascript löse ich das mit einem Array auf das die Bytes gepushed werden:
1 | var rxBuffer = []; |
2 | |
3 | function rxISR() |
4 | { |
5 | rxBuffer.push(serialPort.readAll()); |
6 | } |
Auswertung der Daten über Protokoll-Dekoder ------------------------------------------- Seine Aufgabe ist es in den Empfangenen Daten einen Frame zu entdecken und als Frames in die Mailbox zu werfen. Aufgerufen wird der Dekoder über einen Single-Shot Timer. Nach Beendigung seiner Funktion aktiviert er diesen wieder. Die Auslösezeit kann abhängig vom Zustand der Routine sein: ist grad nichts im Buffer wartet er länger.
1 | const FRAME_LEN_MIN = 2; |
2 | const FRAME_LEN_MAX = 0x1F; |
3 | |
4 | var rxDeframerMode = 1; |
5 | var rxDeframerLen = 0; |
6 | var rxDeframerFrame = []; |
7 | |
8 | function rxDeframer() |
9 | { |
10 | var dirBytes = [ 0x12, 0x21 ]; |
11 | var nextPoll = 100; |
12 | |
13 | // find next DIR (start of frame) |
14 | if (rxDeframerMode == 1 && rxBuffer.length > 0) |
15 | { |
16 | while (var dir = rxBuffer.shift()) |
17 | { |
18 | if (dirBytes.indexOf(dir) > -1) |
19 | { |
20 | rxDeframerFrame = [ dir ]; |
21 | rxDeframerMode = 2; |
22 | rxDeframerLen = 0; |
23 | break; |
24 | } |
25 | } |
26 | } |
27 | |
28 | // read LEN byte |
29 | if (rxDeframerMode == 2 && rxBuffer.length > 0) |
30 | { |
31 | // LEN sanity check |
32 | if (rxBuffer[0] < FRAME_LEN_MIN || rxBuffer[0] > FRAME_LEN_MAX) |
33 | { |
34 | rxDeframerMode = 1; |
35 | } |
36 | else |
37 | { |
38 | rxDeframerLen = rxBuffer.shift(); |
39 | rxDeframerFrame.push(rxDeframerLen); |
40 | rxDeframerMode = 3; |
41 | } |
42 | } |
43 | |
44 | // read DATA bytes (wait until all needed bytes in buffer) |
45 | if (rxDeframerMode == 3 && rxBuffer.length >= rxDeframerLen) |
46 | { |
47 | // verify CRC |
48 | var crcRead = rxBuffer[rxDeframerLen-1]; |
49 | var crcCalc = calcChecksum(rxDeframerFrame.concat(rxBuffer.slice(0, rxDeframerLen-1))); |
50 | if (crcCalc == crcRead) |
51 | { |
52 | // valid frame found |
53 | rxDeframerFrame.push(rxBuffer.splice(0, rxDeframerLen)); |
54 | rxMailbox.push(rxDeframerFrame); |
55 | rxDeframerMode = 1; |
56 | } |
57 | else |
58 | { |
59 | // invalid frame, restart tokenizer |
60 | rxDeframerMode = 1; |
61 | } |
62 | } |
63 | |
64 | // if unhandled bytes left in buffer, do short-wait |
65 | if (rxBuffer.length > 0) { |
66 | nextPoll = 10; |
67 | } |
68 | |
69 | rxDeframerTimer.start(nextPoll); // restart for next poll |
70 | } |
Ein Frame beginnt immer mit einem DIR-Byte. Der Einfachheit halber nehmen wir an das dieses nur 0x12 oder 0x21 sein kann. Ein solches Byte kann aber auch aus einem anderen Fragment einer Übertragung aufgeschnappt worden sein. Wenn also am Anfang des Buffers kein DIR-Byte steht, müssen die Bytes verworfen werden bis ein solches gefunden wurde. Wurde ein Byte als DIR-Byte qualifiziert wird als nächstes ein LEN-Byte erwartet. Das kann man nun auf Plausibilität prüfen (mind. 2 max. X Zeichen) und ggf. wieder von vorn anfangen wenn es nicht passt. Sobald die angegebene LEN Anzahl Bytes im rxBuffer liegen, überprüfe ich das CRC. Stimmt das nicht, ist entweder die Übertragung gestört gewesen oder das erste und zweite Byte waren Daten und kein DIR LEN. In diesem Fall verwerfe ich DIR und LEN und starte neu. Habe ich ein gültiges Frame, lege ich das in die Mailbox.
Olli Z. schrieb: > Im Original hängen mehrere Teilnehmer am gleichen 1-Draht Bus, es gibt > aber keine direkte Adressierung. > Der Empfänger kann auf bestimmte Sender "programmiert" werden. Jeder > Sender hat eine 4 Byte ID. Ich empfinde Deine Beschreibung als in sich widersprüchlich. Manchmal ist eine Zeichnung (selbst von Hand) aussgekräftiger als viele Wörter. Gruß Klaus (der soundsovielte)
Klaus S. schrieb: > Olli Z. schrieb: >> Im Original hängen mehrere Teilnehmer am gleichen 1-Draht Bus, es gibt >> aber keine direkte Adressierung. > >> Der Empfänger kann auf bestimmte Sender "programmiert" werden. Jeder >> Sender hat eine 4 Byte ID. > > Ich empfinde Deine Beschreibung als in sich widersprüchlich. Manchmal > ist eine Zeichnung (selbst von Hand) aussgekräftiger als viele Wörter. Hm, habe ich doch angefügt... Aber die o.g. ID ist Teil der Nutzdaten und hat nichts mit Empfängerbaustein und Steuergerät zu tun, sondern ist eine Kennung der Funksender welche vom Empfängerbaustein empfangen werden.
:
Bearbeitet durch User
Steve van de Grens schrieb: > Versuche nicht, eine Eier-legende Wollmilchsau zu schaffen. Programmiere > nur das, was du wirklich brauchst. Nichts verschwindet schneller wieder > in der Versenkung, als Software. Tue ich nicht, maximal erfinde ich das Rad neu ;-) Aber ich will das für mich verstehen, nicht unbedingt um ein laufendes Programm zu haben, sondern wie man sowas lösen kann. Ich denke das der modulare Ansatz sogar sehr hilfreich ist, denn es kümmern sich unabhängige Module (threads) um verschiedene Dinge. Sprich, in der Hauptroutine werde ich nur mit den Mailboxen arbeiten. Ich schiebe eine Nachricht in die txMailbox rein und erwarte innerhalb einer gewissen Zeit eine Antwort in der rxMailbox. Dabei brauche ich mich nicht drum zu kümmern das diese Nachricht störungsfrei auf den Bus geht und wie die Antwort herein kommt, das machen alles die darunter liegenden Schichten, sicher und performant.
Olli Z. schrieb: > Aber ich will das für mich verstehen, nicht unbedingt um ein > laufendes Programm zu haben, sondern wie man sowas lösen kann. Dann überlege dir, wie dein Konzept mit Daten funktioniert, die nicht ins RAM passen und wo die finale Länge erst während der Übertragung ermittelt wird. Das ist ja (je nach Anwendung) auch ein durchaus realistisches Szenario.
Olli Z. schrieb: > Ich denke das der modulare Ansatz sogar sehr hilfreich ist, denn es > kümmern sich unabhängige Module (threads) um verschiedene Dinge. Sprich, > in der Hauptroutine werde ich nur mit den Mailboxen arbeiten. Ich > schiebe eine Nachricht in die txMailbox rein und erwarte innerhalb einer > gewissen Zeit eine Antwort in der rxMailbox. Dabei brauche ich mich > nicht drum zu kümmern das diese Nachricht störungsfrei auf den Bus geht > und wie die Antwort herein kommt, das machen alles die darunter > liegenden Schichten, sicher und performant. Ja, genau so. Und jetzt fehlt nur noch ein zweites Programm, das die Datagramme aus der Mailbox holt, sie interpretiert und die angeforderten Aktionen ausführt. Motopick hat bereits geschrieben, wie es am bequemsten geht. Was daran hast Du nicht verstanden? Gruß Klaus (der soundsovielte)
Ich finde Protokolle mit Timeout immer gruselig. Will man solche Protokolle über andere Schnittstellen tunneln, geht dabei die Zeitbeziehung verloren. Ich lese einfach alle Bytes in einen FIFO, egal wie lange es dauert. Es kostet ja keine CPU-Zeit. Sobald ein Byte im FIFO ist, wird es in den Parsepuffer geschoben. Dieser prüft auf das Endezeichen und erst dann parst er das ganze Paket. Bei einem Überlauf wird der Puffer nicht weitergezählt, d.h. das neue Byte überschreibt das letzte. Kann das Endezeichen auch im Datenfeld auftreten, wird dort ein Escapezeichen eingefügt. Damit weiß der Parser, daß es nicht das Ende ist. Damit ich mich beim Debuggen nicht mit Binärdaten abquälen muß, übertrage ich bei neuen Geräten die Daten nur noch als Klartext.
Olli Z. schrieb: > - Steuergerät sendet "Wakeup" (wird nicht quittiert) Das würde ich nochmal überdenken. Wenn er das schlecht verstanden hat (Crc Fehler) wacht er nicht auf. Wenn alle jederzeit drauf los plappern dürfen sollten sie immer zuhören. Auswerten ob sie angesprochen wurden oder die Daten für sie interessant sind. Wenn eine Frage/Anweisung an einen anderen gerichtet ist, dessen Antwort abwarten um dann sofort die eigene Frage/Anweisung zu senden. Der Master hat dann die Antwort auf seine Frage und gleich die Frage eines anderen um dann darauf zu reagieren. Wenn viele Slaves regelmäßig eigenständig Plappern wollen brauchts noch eine weitere Regel z.B. er darf nur dann seine Frage anhängen wenn sein Vorgänger (der mit der nächst niedrigeren ID) geantwortet hat. Oder (wie im einem Wechselrichterprotokoll) nach einer zufälligen Zeit loslegen. Spricht dann schon jemand wird eben gewachtet auf die nächste Frage. Eine weitere zufällige Zeit nachdem der ausgeredet hat würde auch gehen. Hier fragt nur einer wenn der Master schläft (5 Minuten nichts mehr gefragt hat.) Anderenfalls braucht er auch nicht fragen, da die gewünschten Daten in den Antworten zum Master vorhanden sind.
Peter D. schrieb: > Ich finde Protokolle mit Timeout immer gruselig. Will man solche Das soll ja nur der Betriebssicherheit dienen, sodass Störungen etc. nicht zu einem Deadlock führen. > Ich lese einfach alle Bytes in einen FIFO, egal wie lange es dauert. Es Ja, genauso mache ich es auch. > Sobald ein Byte im FIFO ist, wird es in den Parsepuffer geschoben. > Dieser prüft auf das Endezeichen und erst dann parst er das ganze Paket. Auch das ist mein Ansatz. So eine Art Tokenizer für Frames. Die Herausforderung ist das Ende-Zeichen. Das ergibt sich bei mir nur durch die Kombination LEN + gültige CRC. Es könnte nämlich immer sein das ich etwas gelesen habe was ich für ein DIR und LEN halte, es aber nur Daten waren. Daher muss ich warten bis ich genug Bytes im Puffer habe um das zu entscheiden. > Bei einem Überlauf wird der Puffer nicht weitergezählt, d.h. das neue Was wäre bei mir ein Überlauf? Hm.. also ich empfange und empfange und mein Tokenizer erkennt keine gültigen Frames. Da habe ich den Ansatz gewählt das ich bei einem Musterfehler (DIR und LEN scheinten zu stimmen, aber die CRC passt dann nicht) so viele Bytes vom Anfang des Puffers wegwerfe bis ich wieder eine gutaussehende DIR/LEN Kombination finde. Danach beginnt das Spiel von neuem. So kann mir der Puffer eigentlich nicht überlaufen. > Byte überschreibt das letzte. Kann das Endezeichen auch im Datenfeld > auftreten, wird dort ein Escapezeichen eingefügt. Damit weiß der Parser, > daß es nicht das Ende ist. Das muss er aber wissen das dies Daten sind. Klingt ein wenig nach Henne/Ei Problem für mich. > Damit ich mich beim Debuggen nicht mit Binärdaten abquälen muß, > übertrage ich bei neuen Geräten die Daten nur noch als Klartext. Ich sehe das Konzept, aber dieses Protokoll ist nicht von mir, daher habe ich diesbezüglich keine Optionen.
Achim H. schrieb: > Olli Z. schrieb: >> - Steuergerät sendet "Wakeup" (wird nicht quittiert) > Das würde ich nochmal überdenken. Wenn er das schlecht verstanden hat > (Crc Fehler) wacht er nicht auf. Genau, sowas kann passieren. Ich habe das sogar bei meinen Sniffs zwischen Original-Steuergerät und Empfänger beobachtet beim Bootstrap. Da sendet das Steuergerät mehrere Befehle ohne Antwort vom Empfänger. Dann hält es kurz inne und beginnt wieder mit einem Wakeup. Beim zweiten Mal klappte es dann immer. Klemme ich den Empfänger ab, macht das Steuergerät das bis zum Sanktnimmerleinstag. Daran finde ich jetzt nichts falsches. Und genau für sowas brauche ich Timeout-Timer, oder es muss ins Protokoll der Kommunikation eingebaut werden. Quasi 3x denselben Befehl senden und wenn dann keine Antwort kommt => Neustart. > Wenn alle jederzeit drauf los plappern dürfen sollten sie immer zuhören. Ganz so schlimm ist es in der Praxis wohl nicht. Es scheint so zu sein das wenn ich als Steuergerät dieses Wakeup sende, hält der Empfänger erstmal den Mund, die anderen auch. Damit habe ich etwas Zeit weitere Daten zu senden, selbst wenn der Empfänger ein gültiges Datagramm von einem Funksender erhält welches er auf den Bus legen könnte. Der Rest Deiner Antwort leuchtet mir ein. Ich habe zunächst vor nur mit einem Empfänger zu arbeiten, das wird die Sache enorm vereinfachen. Ggf. nehme ich für einen weiteren Empfänger einfach eine weitere Schnittstelle. Ich glaube das ich mich ein Stück darauf verlassen kann das nach einem Frame vom Steuergerät an den Empfänger, dieser nur auf diese Nachricht antwortet. Da Sende-/Empfangs-Frames keine Session-ID enthalten wäre es sonst auch unmöglich zu wissen ob die Antwort die gerade kommt zum letzten Kommando oder einem davor gehören würde.
Nochmal zum Ablauf. Die Daten werden praktisch immer asynchron und non-blocking im Hintergrund empfangen, geparsed und in einer rxMailbox gepuffert. Ebenso Sendedaten. Den Code dazu habe ich noch etwas verfeinert und er scheint so auch gut zu laufen. Ich bediene mit meiner Sequenz also nur die Mailboxen. Die Sequenz enthält ja erstmal nur die gewünschte Ausführung, müsste aber auch auf Probleme eingehen können. Also doch eine State-Machine? Wenn ich als Steuergerät sende, wird mir der Empfänger nicht mit Funkdatagrammen dazwischenfunken. Diese Annahme KANN falsch sein, das muss ich noch näher untersuchen. Wenn es falsch ist, dann müsste ich ja die rxMailbox nach einer zum zuletzt gesendeten Befehl passenden Antwort durchsuchen. Genau hier drehe ich mich grad im Kreis. Ist es eine Routine die die rxMailbox Frame für Frame abarbeitet und daraus weitere Aktionen auslöst, oder ist es meine Sequenz die sendet und auf die Antwort wartet? Bei letzterem bräuchte man keine Statemachine, hätte aber im Code Warteschleifen auf Ereignisse. Diese ließen sich aber sehr einfach mit einem Timeout ummanteln. Mache ich das Frame-Gesteuert dann muss der rxMailbox-Handler wissen was er mit einer Nachricht zu tun hat. Wie programmiert man sowas?
:
Bearbeitet durch User
Olli Z. schrieb: > Klemme ich den Empfänger ab, macht das Steuergerät das bis > zum Sanktnimmerleinstag. Das sollte er natürlich nicht. Wenn einer mehrfach nicht antwortet hat er z.B. einfach keinen Strom oder einen Schaden? Das muß der Master wissen um dann eine entsprechende Meldung ab zu geben. Er hat ja auch noch andere Jobs die nicht liegen bleiben sollten/dürfen. > wäre es sonst auch unmöglich zu wissen ob die Antwort die gerade kommt > zum letzten Kommando oder einem davor gehören würde Ist das denn wichtig? Oder welche Routine gefragt hat? Jede Antwort enthält Daten die irgendwo benötigt werden. Diese einfach ablegen und die nächste bearbeiten. Spätestens wenn alle Boxen leer sind fällt auf das die Antwort fehlt. Dann nochmal fragen, evt Fehlerzähler erhöhen und wenn nichts kommt den Slave als "Problemkind" melden. > Wenn ich als Steuergerät sende, wird mir der Empfänger nicht mit > Funkdatagrammen dazwischenfunken. Dazwischenfunken sollte natürlich niemand, da können Regeln festgelegt werden (Busüberwachung / Timeouts). Andererseits solange der Bus das Überlebt gibts nur defekte Daten. Die sorgen nur für eine Verzögerung ohne weitere Probleme. (Solange nicht immer alle gleichzeitig Quatschen ;-) > dann muss der rxMailbox-Handler wissen was > er mit einer Nachricht zu tun hat Er kann unterscheiden ob es die eigene Frage war = Verwerfen, evt "Hardware OK " Flag setzen. Dann ob es eine fremde Frage war - auch verwerfen, evt ein "Slave xy lebt" Flag setzen und "Antwortwartezeit" resetten. Oder ungefragte Antworten - verwerfen, ggf die enthaltenen Daten einsortieren zum Anzeigen, auswerten, loggen falls sie für ihn interessant sind. Und Antworten auf eigene Fragen haben ja einen Zweck/eine Routine die die Daten braucht. Einsortieren und "Antwort da" Flag setzen. Oder gleich die passende Routine auslösen?
Beim einlesen in die Erstellung von State-Machines mit JavaScript habe ich diesen netten Dienst hier entdeckt: https://musing-rosalind-2ce8e7.netlify.app/ Leider kann man sein Werk darin nicht laden und sichern, daher hier mal den Code den ich erstellt habe:
1 | { |
2 | "initial": "idle", |
3 | "states": { |
4 | "idle": { |
5 | "on": { |
6 | "btnProgKeysClicked": { |
7 | "startProgKeySession": {} |
8 | } |
9 | } |
10 | }, |
11 | "startProgKeySession": { |
12 | "on": { |
13 | "cmdProgKeyAccepted": { |
14 | "progKeyId1": {} |
15 | }, |
16 | "cmdProgKeyDenied": { |
17 | "abort": {} |
18 | }, |
19 | "cmdProgKeyTimeout": { |
20 | "cmdProgKeyTimeout": {} |
21 | } |
22 | } |
23 | }, |
24 | "cmdProgKeyTimeout": { |
25 | "on": { |
26 | "retryWaitElapsed": { |
27 | "startProgKeySession": {} |
28 | } |
29 | } |
30 | }, |
31 | "progKeyId1": { |
32 | "on": { |
33 | "cmdProgKeyId1Accepted": { |
34 | "progKeyId2": {} |
35 | }, |
36 | "cmdProgKeyId1Denied": { |
37 | "abort": {} |
38 | }, |
39 | "cmdProgKeyId1Timeout": { |
40 | "abort": {} |
41 | } |
42 | } |
43 | }, |
44 | "progKeyId2": { |
45 | "on": { |
46 | "cmdProgKeyId2Accepted": { |
47 | "progKeyDone": {} |
48 | }, |
49 | "cmdProgKeyId2Denied": { |
50 | "abort": {} |
51 | }, |
52 | "cmdProgKeyId2Timeout": { |
53 | "abort": {} |
54 | } |
55 | } |
56 | }, |
57 | "progKeyDone": { |
58 | "type": "final" |
59 | }, |
60 | "abort": { |
61 | "type": "final" |
62 | } |
63 | } |
64 | } |
So könnte das doch schon hinkommen?
Olli Z. schrieb: > Das muss er aber wissen das dies Daten sind. Klingt ein wenig nach > Henne/Ei Problem für mich. Nö, das ist sehr einfach zu parsen. Wird ESC erkannt, dann wird ein Flag gesetzt "das nächste Byte ist immer ein Datenbyte und wird nicht als EOT gewertet". Man weiß also spätestens nach 2 Bytes, ob ein Paket zu ende ist oder nicht. Olli Z. schrieb: > Es könnte nämlich immer sein das ich > etwas gelesen habe was ich für ein DIR und LEN halte Eben deshalb habe ich lieber Gewißheit, als nur etwas vermuten zu müssen. Olli Z. schrieb: > Was wäre bei mir ein Überlauf? Die Überlaufabsicherung ist nur fehlertolerantes Programmieren. Der Sendepointer könnte z.B. in den Wald zeigen und irgendwelche Daten senden, die das Protokoll verletzen. Oder es ist eine Funkstrecke dazwischen, die einige Bytes verstümmelt. Dann soll der Empfänger nicht auch noch abstürzen. Oder man hat versehentlich einen zu kleinen Puffer gewählt oder nachträglich das Protokoll geändert, ohne den Puffer anzupassen. Ich nehme daher für den Überlauftest immer sizeof() und keine magische Nummer. Dann macht der Compiler den Test immer richtig.
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.