In einer ereignisgesteuerten Architektur handelt jede Komponente nur, wenn ein Ereignis durch das System gesendet wird.
Stellen Sie sich ein hypothetisches Auto mit einem Bremspedal und einem Bremslicht vor.
- Der Bremslicht Windungen auf , wenn er ein brake_on Ereignis und ausgeschaltet , wenn er ein brake_off Ereignis.
- Das Bremspedal sendet ein brake_on- Ereignis, wenn es gedrückt wird, und ein brake_off- Ereignis, wenn es freigegeben wird.
Das ist alles in Ordnung und gut, bis Sie die Situation haben, in der das Auto mit bereits durchgetretenem Bremspedal eingeschaltet ist . Da die Bremsleuchte niemals ein brake_on- Ereignis empfangen hat, bleibt sie ausgeschaltet - eindeutig eine unerwünschte Situation. Wenn Sie das Bremslicht standardmäßig einschalten, wird die Situation nur umgekehrt.
Was könnte getan werden, um dieses Problem des Anfangszustands zu lösen?
EDIT: Vielen Dank für alle Antworten. Meine Frage war nicht über ein tatsächliches Auto. In Autos lösten sie dieses Problem, indem sie kontinuierlich den Status sendeten - daher gibt es in dieser Domäne kein Startproblem. In meiner Softwaredomäne würde diese Lösung viele unnötige CPU-Zyklen verwenden.
EDIT 2: Zusätzlich zur Antwort von @ gbjbaanb werde ich ein System verwenden, in dem:
- das hypothetische Bremspedal, nach der Initialisierung sendet ein Ereignis mit seinem Zustand, und
- Das hypothetische Bremslicht sendet nach der Initialisierung ein Ereignis, das ein Zustandsereignis vom Bremspedal anfordert.
Mit dieser Lösung gibt es keine Abhängigkeiten zwischen Komponenten, keine Rennbedingungen, keine veralteten Nachrichtenwarteschlangen und keine Hauptkomponenten.
quelle
initialize
) zu generieren, das die benötigten Sensordaten enthält.Antworten:
Es gibt viele Möglichkeiten, dies zu tun, aber ich bevorzuge es, ein nachrichtenbasiertes System so weit wie möglich zu entkoppeln. Dies bedeutet, dass das Gesamtsystem weder den Status einer Komponente noch den Status einer anderen Komponente lesen kann (da auf diese Weise Spaghettis von Abhängigkeiten vorliegen).
Während sich das laufende System um sich selbst kümmert, müssen wir jeder Komponente mitteilen, dass sie sich selbst starten soll, und wir haben so etwas bereits in der Komponentenregistrierung, dh das Kernsystem muss beim Start jeder Komponente mitteilen, dass es sich um eine Komponente handelt Jetzt registriert (oder fordert jede Komponente auf, ihre Details zurückzugeben, damit sie registriert werden kann). In dieser Phase kann die Komponente ihre Startaufgaben ausführen und Nachrichten wie im normalen Betrieb senden.
Das Bremspedal würde also beim Starten der Zündung eine Registrierungs- / Prüfmeldung von der Fahrzeugverwaltung erhalten und nicht nur eine Meldung "Ich bin hier und arbeite" zurücksenden, sondern auch seinen eigenen Zustand prüfen und die Meldung senden Meldungen für diesen Zustand (z. B. eine Meldung, dass das Pedal gedrückt wurde).
Das Problem wird dann zu einer Anlaufabhängigkeit, da das Bremslicht, wenn es noch nicht registriert ist, die Nachricht nicht empfängt. Dies lässt sich jedoch leicht beheben, indem alle diese Nachrichten in die Warteschlange gestellt werden, bis das Kernsystem seine Anlauf-, Registrierungs- und Prüfroutine abgeschlossen hat .
Der größte Vorteil ist, dass für die Initialisierung kein spezieller Code erforderlich ist, außer dass Sie bereits schreiben müssen (ok, wenn sich Ihre Nachricht für Bremspedalereignisse in einem Bremspedal-Handler befindet, müssen Sie dies auch in Ihrer Initialisierung aufrufen , aber das ist normalerweise kein Problem, es sei denn, Sie haben den Code stark an die Handlerlogik gebunden geschrieben und keine Interaktion zwischen Komponenten, außer denjenigen, die sie bereits wie gewohnt aneinander senden. Message-Passing-Architekturen sind deshalb sehr gut!
quelle
Sie können ein Initialisierungsereignis haben, das die Zustände beim Laden / Starten entsprechend festlegt. Dies kann für einfache Systeme oder Programme wünschenswert sein, die nicht mehrere Hardwareteile enthalten, jedoch für kompliziertere Systeme mit mehreren physischen Komponenten, da Sie das gleiche Risiko eingehen, als würden Sie überhaupt nicht initialisieren - wenn ein "Bremsen" -Ereignis in Ihrer Kommunikation übersehen wird oder verloren geht System (z. B. ein CAN-basiertes System) Sie können Ihr System versehentlich zurücksetzen, als ob Sie es mit gedrückter Bremse gestartet hätten. Je mehr Controller Sie haben, zum Beispiel mit einem Auto, desto wahrscheinlicher ist es, dass etwas übersehen wird.
Um dies zu berücksichtigen, können Sie die "Bremse ein" -Logik veranlassen, wiederholt "Bremse ein" -Ereignisse auszusenden. Vielleicht alle 1/100 Sekunde oder so. Ihr Code, der das Gehirn enthält, kann auf diese Ereignisse warten und "Bremse ein" auslösen, während er sie empfängt. Nachdem 1 / 10sec kein "brake on" Signal erhalten haben, wird ein internes "brake_off" Ereignis ausgelöst.
Unterschiedliche Ereignisse haben erheblich unterschiedliche zeitliche Anforderungen. In einem Auto muss das Bremslicht viel schneller sein als das Kontrolllicht (bei dem eine Verzögerung von mehreren Sekunden wahrscheinlich akzeptabel ist) oder andere weniger wichtige Systeme.
Die Komplexität Ihres physischen Systems bestimmt, welcher dieser Ansätze angemessener ist. Wenn es sich bei Ihrem Beispiel um ein Fahrzeug handelt, möchten Sie wahrscheinlich etwas Ähnliches.
In beiden Fällen möchten Sie sich bei einem physischen System NICHT darauf verlassen, dass ein einzelnes Ereignis korrekt empfangen / verarbeitet wird. Verbundene Mikrocontroller in einem vernetzten System haben aus diesem Grund häufig ein Timeout "Ich lebe".
quelle
In diesem Fall würde ich die Bremse nicht als einfaches Ein / Aus modellieren. Eher würde ich "Bremsdruck" Ereignisse senden. Zum Beispiel würde ein Druck von 0 aus anzeigen und ein Druck von 100 würde vollständig herabgesetzt werden. Das System (der Knoten) sendet bei Bedarf ständig (in einem bestimmten Intervall) Bremsdruckereignisse an die Steuerung (en).
Wenn das System gestartet wurde, begann es, Druckereignisse zu empfangen, bis es ausgeschaltet wurde.
quelle
Wenn Sie Zustandsinformationen nur über Ereignisse weitergeben können, sind Sie in Schwierigkeiten. Stattdessen müssen Sie in der Lage sein, beide:
Das Bremslicht kann als Beobachter des Bremspedals gesehen werden. Mit anderen Worten, das Bremspedal weiß nichts über die Bremsleuchte und kann ohne sie arbeiten. (Dies bedeutet, dass jede Vorstellung, dass das Bremspedal proaktiv ein Ereignis "Anfangszustand" an das Bremslicht sendet, falsch ist.)
Nach der Instantiierung des Systems registriert sich die Bremsleuchte beim Bremspedal, um Bremsbenachrichtigungen zu erhalten, und liest auch den aktuellen Zustand des Bremspedals und schaltet sich selbst ein oder aus.
Dann können die Bremsbenachrichtigungen auf drei Arten implementiert werden:
Ich bevorzuge den ersten Ansatz, was bedeutet, dass die Bremsleuchte nach Erhalt der Benachrichtigung einfach das tut, was sie bereits zu tun weiß: den aktuellen Zustand des Bremspedals ablesen und sich selbst ein- oder ausschalten.
quelle
In einem ereignisgesteuerten System (das ich derzeit verwende und liebe) finde ich es wichtig, die Dinge so entkoppelt wie möglich zu halten. Lassen Sie uns mit dieser Idee gleich loslegen.
Es ist wichtig, einen Standardzustand zu haben. Ihre Bremsleuchte würde den Standardzustand "Aus" und Ihr Bremspedal den Standardzustand "Auf" annehmen. Alle Änderungen danach wären ein Ereignis.
Nun zu Ihrer Frage. Stellen Sie sich vor, Ihr Bremspedal wird initialisiert und gedrückt, das Ereignis wird ausgelöst, aber es gibt noch keine Bremslichter, um das Ereignis zu empfangen. Ich habe festgestellt, dass es am einfachsten ist, die Erstellung der Objekte (wo die Ereignis-Listener initialisiert würden) als separaten Schritt zu trennen, bevor eine Logik initialisiert wird. Das verhindert die von Ihnen beschriebenen Rennbedingungen.
Ich finde es auch umständlich, zwei verschiedene Ereignisse für das zu verwenden, was eigentlich dasselbe ist .
brake_off
undbrake_on
könntee_brake
mit einem Parameter vereinfacht werdenbool on
. Sie können Ihre Veranstaltungen auf diese Weise vereinfachen, indem Sie unterstützende Daten hinzufügen.quelle
Was Sie brauchen, ist ein Sendeereignis und Nachrichteneingänge. Eine Sendung ist eine Nachricht, die an eine nicht festgelegte Anzahl von Zuhörern gesendet wird. Eine Komponente kann Broadcast-Ereignisse abonnieren, sodass sie nur Ereignisse empfängt, an denen sie interessiert ist. Dies bietet Entkopplung, da der Absender nicht wissen muss, wer die Empfänger sind. Die Abonnementtabelle muss während der Installation der Komponente statisch konfiguriert werden (statt beim Initialisieren von). Der Posteingang ist Teil des Nachrichtenrouters, der als Puffer für Nachrichten fungiert, wenn die Zielkomponente offline ist.
Die Verwendung von Rechnungen bringt ein Problem mit sich, nämlich die Größe des Posteingangs. Sie möchten nicht, dass das System eine wachsende Anzahl von Nachrichten für Komponenten speichern muss, die niemals mehr online sein werden. Dies ist insbesondere bei eingebetteten Systemen mit strengen Speicherbeschränkungen wichtig. Um die Größenbeschränkung des Posteingangs zu überwinden, müssen alle gesendeten Nachrichten einige Regeln befolgen. Die Regeln sind:
Der Broadcast-Name muss während der Installation der Komponente angegeben werden. Wenn eine Komponente eine zweite Sendung mit demselben Namen sendet, bevor der Empfänger die vorherige Sendung verarbeitet, überschreibt die neue Sendung die vorherige. Jetzt können Sie eine statische Größenbeschränkung für den Posteingang festlegen, die garantiert eine bestimmte Größe nicht überschreitet und anhand der Abonnementtabellen vorberechnet werden kann.
Schließlich benötigen Sie noch ein Broadcast-Archiv. Das Broadcast-Archiv ist eine Tabelle, die das letzte Ereignis aus jedem Broadcast-Namen enthält. Bei neuen Komponenten, die gerade installiert werden, wird der Posteingang mit Nachrichten aus dem Broadcast-Archiv vorab ausgefüllt. Wie der Nachrichteneingang kann auch das Broadcast-Archiv eine statische Größe haben.
Um mit Situationen fertig zu werden, in denen der Nachrichtenrouter selbst offline ist, benötigen Sie außerdem Nachrichtenausgänge. Der Nachrichtenausgang ist Teil der Komponente, in der ausgehende Nachrichten vorübergehend gespeichert sind.
quelle