Finite-State-Maschinen sind vielleicht eines der grundlegendsten und am weitesten verbreiteten Konzepte in der Programmierung. Finite-State-Maschinen (KA) werden in vielen angewandten Nischen aktiv eingesetzt. Insbesondere in Nischen wie APCS und Telekommunikation, mit denen man sich befassen konnte, sind Raumfahrzeuge etwas seltener anzutreffen als bei jedem Schritt.
Daher werden wir in diesem Artikel versuchen, ĂŒber Raumfahrzeuge zu sprechen, hauptsĂ€chlich ĂŒber hierarchische endliche Zustandsmaschinen und ihre erweiterten FĂ€higkeiten. Und erzĂ€hlen Sie ein wenig ĂŒber die UnterstĂŒtzung von Raumfahrzeugen in
SObjectizer-5 , dem "Actor" -Framework fĂŒr C ++. Eine der
beiden wenigen, die offen, frei, plattformĂŒbergreifend und noch am Leben sind.
Selbst wenn Sie nicht an SObjectizer interessiert sind, aber noch nie von hierarchischen Finite-State-Maschinen gehört haben oder wie nĂŒtzlich solche erweiterten Funktionen eines Raumfahrzeugs wie Eingabe- / Ausgabehandler fĂŒr ZustĂ€nde oder Statusverlauf sind, sind Sie möglicherweise daran interessiert, unter die Katze und zu schauen Lesen Sie mindestens den ersten Teil des Artikels.
Allgemeine Worte zu endlichen Zustandsmaschinen
Wir werden nicht versuchen, ein vollstÀndiges Bildungsprogramm in dem Artikel zum Thema
Automaten und einer solchen Vielfalt wie
endlichen Zustandsmaschinen durchzufĂŒhren. Der Leser muss mindestens ein grundlegendes VerstĂ€ndnis fĂŒr diese Art von EntitĂ€ten haben.
Erweiterte Finite-State-Maschinen und ihre FĂ€higkeiten
Das Raumschiff verfĂŒgt ĂŒber mehrere "erweiterte" Funktionen, die die Benutzerfreundlichkeit des Raumfahrzeugs im Programm erheblich verbessern. Werfen wir einen kurzen Blick auf diese "erweiterten" Funktionen.
Haftungsausschluss: Wenn der Leser mit Zustandsdiagrammen von UML gut vertraut ist, findet er hier nichts Neues fĂŒr sich.
Hierarchische Zustandsmaschinen
Die vielleicht wichtigste und wertvollste Gelegenheit ist die Organisation einer Hierarchie / Verschachtelung von Staaten. Denn gerade die FĂ€higkeit, ZustĂ€nde ineinander zu versetzen, eliminiert die âExplosionâ der Anzahl der ĂbergĂ€nge von Zustand zu Zustand, wenn die KomplexitĂ€t des Raumfahrzeugs zunimmt.
Es ist schwieriger, dies in Worten zu erklĂ€ren, als es anhand eines Beispiels zu zeigen. Stellen wir uns daher vor, wir haben einen Infokiosk, auf dessen Bildschirm zuerst eine BegrĂŒĂungsnachricht angezeigt wird. Der Benutzer kann das Element "Dienste" auswĂ€hlen und in den Abschnitt zur Auswahl der Dienste gehen, die er benötigt. Oder er kann den Eintrag "Persönliches Konto" auswĂ€hlen und zum Abschnitt ĂŒber die Arbeit mit seinen persönlichen Daten und Diensten gehen. Oder er kann den Abschnitt Hilfe auswĂ€hlen. Bisher scheint alles einfach zu sein und kann durch das folgende Zustandsdiagramm (so vereinfacht wie möglich) dargestellt werden:

Versuchen wir jedoch sicherzustellen, dass der Benutzer durch Klicken auf die SchaltflĂ€che "Abbrechen" mit einer BegrĂŒĂungsnachricht von jedem Abschnitt zur Startseite zurĂŒckkehren kann:

Das Schema wird kompliziert, aber immer noch unter Kontrolle. Wir möchten jedoch daran erinnern, dass wir im Abschnitt "Dienste" möglicherweise mehrere weitere Unterabschnitte haben, z. B. "Beliebte Dienste", "Neue Dienste" und "VollstĂ€ndige Liste". Und von jedem dieser Abschnitte mĂŒssen Sie auch zur Startseite zurĂŒckkehren. Unser einfaches Raumschiff wird immer schwieriger:

Aber das ist noch lange nicht alles. Wir haben den "ZurĂŒck" -Button noch nicht berĂŒcksichtigt, mit dem wir zum vorherigen Abschnitt zurĂŒckkehren mĂŒssen. FĂŒgen wir eine Reaktion auf die SchaltflĂ€che "ZurĂŒck" hinzu und sehen, was wir erhalten:

Ja, jetzt sehen wir den Weg zu echtem SpaĂ. Aber wir haben noch nicht einmal die Unterabschnitte in den Abschnitten "Mein Konto" und "Hilfe" berĂŒcksichtigt ... Wenn wir anfangen, wird unser einfaches Raumschiff fast sofort zu etwas Unvorstellbarem.
Hier hilft uns die Verschachtelung von Staaten. Stellen wir uns vor, wir haben nur zwei ZustĂ€nde der obersten Ebene: WelcomeScreen und UserSelection. Alle unsere Abschnitte (dh "Dienste", "Mein Konto" und "Hilfe") werden im UserSelection-Status "verschachtelt". Sie können sagen, dass die Status ServicesScreen, ProfileScreen und HelpScreen untergeordnete Elemente der UserSelection sind. Und da sie Kinder sind, erben sie die Reaktion auf einige Signale aus ihrem elterlichen Zustand. Daher können wir die Antwort auf die SchaltflĂ€che Abbrechen in UserSelection definieren. Wir mĂŒssen diese Reaktion jedoch nicht in allen Nebengebieten bestimmen. Was macht unser Raumschiff prĂ€gnanter und verstĂ€ndlicher:

Hier können Sie feststellen, dass die Reaktion fĂŒr "Abbrechen" und "ZurĂŒck" wir in UserSelection definiert haben. Diese Reaktion auf die SchaltflĂ€che Abbrechen funktioniert ausnahmslos fĂŒr alle UserSelection-UnterzustĂ€nde (einschlieĂlich eines weiteren zusammengesetzten ServicesSelection-Unterzustands). Im Unterzustand ServicesSelection ist die Reaktion auf die SchaltflĂ€che ZurĂŒck bereits anders - die RĂŒckgabe erfolgt nicht in WelcomScreen, sondern in ServicesScreen.
Zertifizierungsstellen, die eine Hierarchie / Verschachtelung von ZustÀnden verwenden, werden als hierarchische endliche Zustandsmaschinen (ICA) bezeichnet.
Reaktion auf Ein- / Ausstieg in / aus dem Staat
Eine sehr nĂŒtzliche Funktion ist die Möglichkeit, eine Antwort auf das Eintreten in einen bestimmten Zustand sowie eine Reaktion auf das Verlassen eines Zustands zuzuweisen. Im obigen Beispiel mit einem Infokiosk kann ein Handler aufgehĂ€ngt werden, um in jeden der ZustĂ€nde einzutreten, wodurch der Inhalt des Infokioskbildschirms geĂ€ndert wird.
Das vorige Beispiel kann etwas erweitert werden. Angenommen, wir haben zwei UnterzustÀnde in WelcomScreen: BrightWelcomScreen, in dem der Bildschirm normal hervorgehoben wird, und DarkWelcomScreen, in dem die Bildschirmhelligkeit verringert wird. Wir können einen DarkWelcomScreen-Eingabehandler erstellen, der den Bildschirm dimmt. Und ein DarkWelcomScreen-Exit-Handler, der die normale Helligkeit wiederherstellt.

Automatische ZustandsÀnderung nach einer festgelegten Zeit
Manchmal kann es erforderlich sein, den Aufenthalt des Raumfahrzeugs in einem bestimmten Zustand zu begrenzen. Im obigen Beispiel können wir die Zeit, in der unser ICA im BrightWelcomScreen-Status bleibt, auf eine Minute beschrÀnken. Sobald die Minute abgelaufen ist, wechselt der ICA automatisch in den DarkWelcomScreen-Status.
Raumfahrzeuggeschichte
Ein weiteres sehr nĂŒtzliches Merkmal von ICA ist die Geschichte des Zustands des Raumfahrzeugs.
Stellen wir uns vor, wir haben eine Art abstrakte ICA dieser Art:

Dies kann unser ICA von TopLevelState1 zu TopLevelState2 und umgekehrt gehen. In TopLevelState1 gibt es jedoch mehrere verschachtelte ZustÀnde. Wenn der ICA einfach von TopLevelState2 zu TopLevelState1 wechselt, werden sofort zwei Status aktiviert: TopLevelState1 und NestedState1. NestedState1 ist aktiviert, da es sich um den anfÀnglichen Unterzustand des Status TopLevelState1 handelt.
Stellen Sie sich nun vor, dass unser ICA seinen Status weiter von NestedState1 in NestedState2 geĂ€ndert hat. In NestedState2 wurde der SubState InternalState1 aktiviert (da dies der ursprĂŒngliche Unterzustand fĂŒr NestedState2 ist). Und von InternalState1 gingen wir zu InternalState2. Somit sind gleichzeitig die folgenden ZustĂ€nde aktiv: TopLevelState1, NestedState2 und InternalState2. Und hier gehen wir zu TopLevelState2 (d. H. Wir haben TopLevelState1 im Allgemeinen verlassen).
Aktiv wird zu TopLevelState2. Danach wollen wir zu TopLevelState1 zurĂŒckkehren. Es befindet sich in TopLevelState1 und nicht in einem bestimmten Unterzustand in TopLevelState1.
Also, von TopLevelState2 gehen wir zu TopLevelState1 und wo kommen wir hin?
Wenn TopLevelState1 keinen Verlauf hat, kommen wir zu TopLevelState1 und NestedState1 (da NestedState1 der anfĂ€ngliche Unterzustand fĂŒr TopLevelState1 ist). Das heiĂt, Die ganze Geschichte ĂŒber die ĂbergĂ€nge in TopLevelState1, die vor dem Verlassen von TopLevelState2 stattfanden, ging vollstĂ€ndig verloren.
Wenn TopLevelState1 eine sogenannte hat flacher Verlauf, dann kommen wir bei der RĂŒckkehr von TopLevelState2 zu TopLevelState1 zu NestedState2 und InternalState1. Wir gelangen in NestedState2, weil es im Statusverlauf von TopLevelState1 aufgezeichnet ist. Und wir kommen zu InternalState1, weil es der Start fĂŒr NestedState2 ist. Es stellt sich heraus, dass in der oberflĂ€chlichen Historie fĂŒr TopLevelState1 Informationen nur ĂŒber die UnterzustĂ€nde der allerersten Ebene gespeichert werden. Die Geschichte der eingebetteten ZustĂ€nde in diesen UnterzustĂ€nden bleibt nicht erhalten.
Wenn TopLevelState1 jedoch eine lange Geschichte hat, gelangen wir bei der RĂŒckkehr von TopLevelState2 zu TopLevelState1 in NestedState2 und InternalState2. Denn in einer tiefen Geschichte werden vollstĂ€ndige Informationen ĂŒber aktive UnterzustĂ€nde gespeichert, unabhĂ€ngig von ihrer Tiefe.
Orthogonale ZustÀnde
Bisher haben wir ICA untersucht, bei der nur einer der UnterzustÀnde innerhalb des Staates aktiv sein konnte. Manchmal kann es jedoch Situationen geben, in denen in einem bestimmten ICA-Zustand mehrere gleichzeitig aktive UnterzustÀnde vorhanden sein sollten. Solche UnterzustÀnde werden orthogonale ZustÀnde genannt.
Ein klassisches Beispiel, das orthogonale ZustÀnde demonstriert, ist die bekannte Computertastatur und ihre Modi NumLock, CapsLock und ScrollLock. Wir können sagen, dass die Arbeit mit NumLock / CapsLock / ScrollLock durch orthogonale UnterzustÀnde im aktiven Zustand beschrieben wird:

Alles, was Sie ĂŒber endliche Zustandsmaschinen wissen wollten, aber ...
Im Allgemeinen gibt es einen grundlegenden Artikel ĂŒber formale Notation fĂŒr Zustandsdiagramme von David Harel: Zustandsdiagramme
: Ein visueller Formalismus fĂŒr komplexe Systeme (1987) .
Dort werden am Beispiel der Steuerung einer gewöhnlichen elektronischen Uhr verschiedene Situationen untersucht, die beim Arbeiten mit Finite-State-Maschinen auftreten können. Wenn jemand es nicht gelesen hat, kann ich es nur empfehlen. GrundsĂ€tzlich ging alles, was Harel beschrieb, in die UML-Notation. Wenn Sie jedoch die Beschreibung von Zustandsdiagrammen aus der UML lesen, verstehen Sie nicht immer, was, warum und wann Sie sie benötigen. In dem Artikel von Harel geht die PrĂ€sentation jedoch von einfachen zu komplexeren Situationen ĂŒber. Und Sie sind sich der Macht bewusst, die endliche Zustandsmaschinen in sich selbst verbergen.
Finite-State-Maschinen in SObjectizer
Weiter werden wir ĂŒber SObjectizer und seine Besonderheiten sprechen. Wenn Sie die folgenden Beispiele nicht ganz verstehen, ist es möglicherweise sinnvoll, mehr ĂŒber SObjectizer zu erfahren. Zum Beispiel aus unserem
Ăbersichtsartikel ĂŒber SObjecizer und mehreren nachfolgenden Artikeln, in denen Leser SObjectizer kennenlernen und von einfach zu komplex wechseln (
erster Artikel,
zweiter und
dritter ).
Agenten in SObjectizer sind Zustandsautomaten
Agenten in SObjectizer waren von Anfang an Zustandsautomaten mit expliziten ZustĂ€nden. Selbst wenn der Entwickler des Agenten keinen seiner eigenen Status in seiner Agentenklasse beschrieben hat, hatte der Agent immer noch einen Standardstatus, der standardmĂ€Ăig verwendet wurde. Wenn ein Entwickler beispielsweise einen so trivialen Agenten erstellt hat:
class simple_demo final : public so_5::agent_t { public:
dann kann er nicht einmal vermuten, dass in Wirklichkeit alle Abonnements, die er gemacht hat, fĂŒr den Standardzustand gemacht wurden. Wenn der Entwickler dem Agenten jedoch seine eigenen Status hinzufĂŒgt, mĂŒssen Sie bereits darĂŒber nachdenken, den Agenten ordnungsgemÀà im richtigen Status zu signieren. Hier beispielsweise eine einfache (und wie ĂŒblich) falsche Modifikation des oben gezeigten Agenten:
class simple_demo final : public so_5::agent_t {
Wir setzen zwei verschiedene Handler fĂŒr das how_are_you-Signal, jeder fĂŒr seinen eigenen Status.
Und der Fehler bei dieser Modifikation des Agenten "simple_demo" besteht darin, dass der Agent in st_free oder st_busy ĂŒberhaupt nicht auf das Beenden reagiert, weil Wir haben das Beendigungsabonnement im Standardzustand belassen, aber die entsprechenden Abonnements fĂŒr st_free und st_busy nicht erstellt. Eine einfache und offensichtliche Möglichkeit, dieses Problem zu beheben, besteht darin, st_free und st_busy die entsprechenden Abonnements hinzuzufĂŒgen:
simple_demo(context_t ctx) : so_5::agent_t{std::move(ctx)} {
Richtig, diese Methode riecht nach Kopieren und EinfĂŒgen, was nicht gut ist. Sie können das Kopieren und EinfĂŒgen entfernen, indem Sie einen gemeinsamen ĂŒbergeordneten Status fĂŒr st_free und st_busy eingeben:
class simple_demo final : public so_5::agent_t {
Aus GrĂŒnden der Gerechtigkeit sollte hinzugefĂŒgt werden, dass Agenten in SObjectizer zunĂ€chst nur einfache Zustandsmaschinen sein konnten. Die UnterstĂŒtzung fĂŒr hierarchische Raumfahrzeuge erschien vor relativ kurzer Zeit im Januar 2016.
Warum sind SObjectizer-Agenten Finite-State-Maschinen?
Diese Frage hat eine sehr einfache Antwort: So kam es
, dass die Wurzeln von SObjectizer aus der Welt der Prozessleitsysteme stammen und dort sehr oft Finite-State-Maschinen verwendet werden. Daher hielten wir es fĂŒr notwendig, dass die Agenten in SObjectizer auch Zustandsmaschinen sind. Dies ist sehr praktisch, wenn in der Anwendung, fĂŒr die SObjectizer angewendet werden soll, Zertifizierungsstellen verwendet werden. Und der Standardzustand, den alle Agenten haben, erlaubt es uns, nicht an Raumfahrzeuge zu denken, wenn die Verwendung von Raumfahrzeugen nicht erforderlich ist.
Wenn Sie sich das Actors-Modell selbst und die Prinzipien ansehen, auf denen dieses Modell basiert, gilt im Prinzip Folgendes:
- Ein Schauspieler ist eine Einheit mit Verhalten.
- Akteure reagieren auf eingehende Nachrichten;
- Nach Erhalt der Nachricht kann der Schauspieler:
- eine bestimmte Anzahl von Nachrichten an andere Akteure senden;
- eine Reihe neuer Akteure schaffen;
- Definieren Sie ein neues Verhalten fĂŒr die Verarbeitung nachfolgender Nachrichten.
Man kann eine starke Ăhnlichkeit zwischen einfachen Raumfahrzeugen und Schauspielern finden. Man könnte sogar sagen, dass Schauspieler einfache endliche Zustandsmaschinen sind.
Welche Funktionen von Advanced State Machines unterstĂŒtzt SObjectizer?
Von den oben genannten Funktionen fortschrittlicher Finite-State-Maschinen unterstĂŒtzt SObjectizer alles auĂer orthogonalen ZustĂ€nden. Andere Extras wie verschachtelte Status, Eingabe- / Ausgabehandler, EinschrĂ€nkungen fĂŒr die im Status verbrachte Zeit und der Verlauf fĂŒr die Status werden unterstĂŒtzt.
Mit der UnterstĂŒtzung orthogonaler ZustĂ€nde ist das erste Mal nicht zusammengewachsen. Einerseits sollte die interne Architektur von SObjectizer nicht mehrere unabhĂ€ngige und gleichzeitig aktive ZustĂ€nde des Agenten unterstĂŒtzen. Andererseits gibt es ideologische Fragen, wie sich ein Agent mit orthogonalen ZustĂ€nden verhalten sollte. Das Gewirr dieser Fragen erwies sich als zu kompliziert, und der nĂŒtzliche Auspuff war zu klein, um dieses Problem zu lösen. Ja, und in unserer Praxis gab es noch keine Situationen, in denen orthogonale ZustĂ€nde erforderlich wĂ€ren, aber es wĂ€re beispielsweise unmöglich, die Arbeit auf mehrere Agenten aufzuteilen, die an einen gemeinsamen Arbeitskontext gebunden sind.
Wenn jedoch jemand eine Funktion wie orthogonale ZustĂ€nde benötigt und Sie Beispiele aus der Praxis fĂŒr Aufgaben haben, bei denen dies erforderlich ist, lassen Sie uns sprechen. Wenn wir konkrete Beispiele vor Augen haben, können wir diese Funktion möglicherweise zu SObjectizer hinzufĂŒgen.
Wie die UnterstĂŒtzung fĂŒr erweiterte Funktionen von ICA im Code aussieht
In diesem Teil der Geschichte werden wir versuchen, die SObjectizer-5-API schnell zu ĂŒberprĂŒfen, um mit ICA zu arbeiten. Ohne tief in die Details zu gehen, nur damit der Leser eine Vorstellung davon hat, was ist und wie es aussieht. Weitere Informationen finden Sie auf Wunsch
in der offiziellen Dokumentation .
Verschachtelte Staaten
Um einen verschachtelten Status zu deklarieren, mĂŒssen Sie den Ausdruck initial_substate_of oder substate_of an den Konstruktor des entsprechenden state_t-Objekts ĂŒbergeben:
class demo : public so_5::agent_t { state_t st_parent{this};
Wenn der Zustand S mehrere UnterzustĂ€nde C1, C2, ..., Cn hat, sollte einer von ihnen (und nur einer) als initial_substate_of markiert werden. VerstöĂe gegen diese Regel werden zur Laufzeit diagnostiziert.
Die maximale Verschachtelungstiefe in SObjectizer-5 ist begrenzt. In Version 5.5 sind dies 16 Ebenen. VerstöĂe gegen diese Regel werden zur Laufzeit diagnostiziert.
Der wichtigste Trick bei verschachtelten ZustÀnden besteht darin, dass bei Aktivierung eines Zustands mit verschachtelten ZustÀnden mehrere ZustÀnde gleichzeitig aktiviert werden. Angenommen, es gibt einen Zustand A mit den UnterzustÀnden B und C, und in Unterzustand B gibt es die UnterzustÀnde D und E:

Wenn Zustand A aktiviert ist, werden tatsÀchlich sofort drei ZustÀnde aktiviert: A, AB und ABD
Die Tatsache, dass mehrere Staaten gleichzeitig aktiv sein können, hat die schwerwiegendste Auswirkung auf zwei Archivierungssachen. Erstens, um nach einem Handler fĂŒr die nĂ€chste eingehende Nachricht zu suchen. In dem gerade gezeigten Beispiel wird der Nachrichtenhandler zuerst im ABD-Zustand durchsucht. Wenn dort kein geeigneter Handler vorhanden ist, wird die Suche in seinem ĂŒbergeordneten Zustand fortgesetzt, d. H. in AB Und bei Bedarf bereits verletzt, wird die Suche in Zustand A fortgesetzt.
Zweitens beeinflusst das Vorhandensein mehrerer aktiver ZustĂ€nde die Reihenfolge des Aufrufs von Eingabe- / Ausgabehandlern fĂŒr ZustĂ€nde. Dies wird jedoch weiter unten erörtert.
Status-E / A-Handler
FĂŒr einen Status können Status-Handler fĂŒr den Statusein- und -ausgang angegeben werden. Dies erfolgt mit den Methoden state_t :: on_enter und state_t :: on_exit. In der Regel werden diese Methoden in der Methode so_define_agent () aufgerufen (oder direkt im Agentenkonstruktor, wenn der Agent trivial ist und keine Vererbung von ihm bereitgestellt wird).
class demo : public so_5::agent_t { state_t st_free{this}; state_t st_busy{this}; ... void so_define_agent() override {
Der wahrscheinlich schwierigste Moment mit on_enter / on_exit-Handlern besteht darin, sie fĂŒr verschachtelte ZustĂ€nde zu verwenden. Kehren wir zum Beispiel mit den ZustĂ€nden A, B, C, D und E zurĂŒck.

Angenommen, jeder Status verfĂŒgt ĂŒber einen Handler on_enter und on_exit.
Lassen Sie A. zum aktuellen Status des Agenten werden. ZustÀnde A, AB und ABD werden aktiviert WÀhrend der ZustandsÀnderung eines Agenten werden A.on_enter, ABon_enter und ABDon_enter aufgerufen. Und in dieser Reihenfolge.
Angenommen, es gibt einen Ăbergang zu ABE. ABDon_exit und ABEon_enter werden aufgerufen.
Wenn wir dann den Agenten in den AC-Status versetzen, wird ABEon_exit, ABon_exit, ACon_enter aufgerufen.
Wenn sich der Agent im AC-Status abmeldet, werden unmittelbar nach Abschluss der Methode so_evt_finish () die Handler ACon_exit und A.on_exit aufgerufen.
Fristen
Das Zeitlimit fĂŒr den Agenten, um in einem bestimmten Status zu bleiben, wird mithilfe der Methode state_t :: time_limit festgelegt. Wie bei on_enter / on_exit werden die Methoden time_limit normalerweise aufgerufen, wenn der Agent fĂŒr die Arbeit im SObjectizer konfiguriert ist:
class led_indicator : public so_5::agent_t { state_t inactive{this}; state_t active{this}; ... void so_define_agent() override {
Wenn das Zeitlimit fĂŒr den Status festgelegt ist, beginnt SObjectizer, sobald der Agent in diesen Status eintritt, die im Status verbrachte Zeit zu zĂ€hlen. Wenn der Agent den Status verlĂ€sst und dann wieder in diesen Status zurĂŒckkehrt, beginnt der Countdown erneut.
Wenn fĂŒr eingebettete ZustĂ€nde Zeitlimits festgelegt sind, mĂŒssen Sie vorsichtig sein, weil neugierige Tricks sind möglich:
class demo : public so_5::agent_t {
Angenommen, ein Agent tritt in den Zustand A ein. Die ZustĂ€nde A und C sind sowohl fĂŒr A als auch fĂŒr C aktiviert. Zuvor wird es fĂŒr Status C beendet und der Agent wechselt zu Status D. Dies startet den Countdown fĂŒr den Aufenthalt in Status D. Der Countdown fĂŒr den Aufenthalt in A wird jedoch fortgesetzt! Da der Agent wĂ€hrend des Ăbergangs von C nach D weiterhin in Zustand A blieb. Und fĂŒnf Sekunden nach dem erzwungenen Ăbergang von C nach D geht der Agent in Zustand B ĂŒber.
Geschichte fĂŒr das GlĂŒck
StandardmĂ€Ăig haben Agentenstatus keinen Verlauf. Um das Speichern des Verlaufs fĂŒr einen Status zu aktivieren, ĂŒbergeben Sie die Konstante flat_history (der Status hat einen flachen Verlauf) oder deep_history (der Status hat einen tiefen Verlauf) an den Konstruktor state_t. Zum Beispiel:
class demo : public so_5::agent_t { state_t A{this, shallow_history}; state_t B{this, deep_history}; ... };
Die Geschichte fĂŒr Staaten ist ein schwieriges Thema, insbesondere wenn eine anstĂ€ndige Verschachtelungstiefe von Staaten verwendet wird und die UnterzustĂ€nde ihre eigene Geschichte haben. AusfĂŒhrlichere Informationen zu diesem Thema finden Sie
in der Dokumentation . Nun, um uns zu fragen, ob Sie es nicht selbst herausfinden können;)
just_switch_to, transfer_to_state, unterdrĂŒcken
Die Klasse state_t verfĂŒgt ĂŒber eine Reihe der am hĂ€ufigsten verwendeten Methoden, die oben bereits gezeigt wurden: event () zum Abonnieren von Ereignissen fĂŒr eine Nachricht, on_enter () und on_exit () zum Festlegen von Eingabe- / Ausgabehandlern, time_limit () zum Festlegen eines Grenzwerts fĂŒr die in einem Status verbrachte Zeit.
Neben diesen Methoden sind bei der Arbeit mit ICA die folgenden Methoden der Klasse state_t sehr nĂŒtzlich:
Methode just_switch_to (), die fĂŒr den Fall entwickelt wurde, dass die einzige Reaktion auf eine eingehende Nachricht darin besteht, den Agenten in einen neuen Zustand zu versetzen. Sie können schreiben:
some_state.just_switch_to<some_msg>(another_state);
statt:
some_state.event([this](mhood_t<some_msg>) { this >>= another_state; });
Die Methode transfer_to_state () ist sehr nĂŒtzlich, wenn eine Nachricht M in zwei oder mehr ZustĂ€nden S1, S2, ..., Sn auf dieselbe Weise verarbeitet wird. Wenn wir uns jedoch in den ZustĂ€nden S2, ..., Sn befinden, mĂŒssen wir zuerst zu S1 zurĂŒckkehren und erst dann die Verarbeitung M durchfĂŒhren.
Wenn dies schwierig klingt, wird diese Situation in einem Codebeispiel möglicherweise besser verstanden:
class demo : public so_5::agent_t { state_t S1{this}, S2{this}, ..., Sn{this}; ... void actual_M_handler(mhood_t<M> cmd) {...} ... void so_define_agent() override { S1.event(&demo::actual_M_handler); ...
Verwenden Sie transfer_to_state, anstatt sehr Ă€hnliche Ereignishandler fĂŒr S2, ..., Sn zu definieren:
class demo : public so_5::agent_t { state_t S1{this}, S2{this}, ..., Sn{this}; ... void actual_M_handler(mhood_t<M> cmd) {...} ... void so_define_agent() override { S1.event(&demo::actual_M_handler); ...
Die Methode replace () unterdrĂŒckt eine Ereignishandlersuche nach dem aktuellen Unterzustand und allen ĂŒbergeordneten UnterzustĂ€nden. Angenommen, wir haben einen ĂŒbergeordneten Status A, in dem std :: abort () fĂŒr Nachricht M aufgerufen wird. Und es gibt einen untergeordneten Zustand von B, in dem M sicher ignoriert werden kann. Wir mĂŒssen die Reaktion auf M in Unterzustand B bestimmen, denn wenn wir dies nicht tun, wird der Handler fĂŒr B in A gefunden. Daher mĂŒssen wir etwas schreiben wie:
void so_define_agent() override { A.event([](mhood_t<M>) { std::abort(); }); ... B.event([](mhood_t<M>) {});
Mit der Methode suppress () können Sie diese Situation expliziter und grafischer in Code schreiben:
void so_define_agent() override { A.event([](mhood_t<M>) { std::abort(); }); ... B.suppress<M>();
Sehr einfaches Beispiel
Zu den Standardbeispielen von SObjectizer v.5.5 gehört ein einfaches Beispiel,
blinking_led , das den Betrieb einer blinkenden LED-Anzeige simuliert. Das Agentenstatusdiagramm aus diesem Beispiel lautet wie folgt:

Und hier ist der vollstÀndige Agentencode aus diesem Beispiel:
class blinking_led final : public so_5::agent_t { state_t off{ this }, blinking{ this }, blink_on{ initial_substate_of{ blinking } }, blink_off{ substate_of{ blinking } }; public : struct turn_on_off : public so_5::signal_t {}; blinking_led( context_t ctx ) : so_5::agent_t{ ctx } { this >>= off; off.just_switch_to< turn_on_off >( blinking ); blinking.just_switch_to< turn_on_off >( off ); blink_on .on_enter( []{ std::cout << "ON" << std::endl; } ) .on_exit( []{ std::cout << "off" << std::endl; } ) .time_limit( std::chrono::milliseconds{1250}, blink_off ); blink_off .time_limit( std::chrono::milliseconds{750}, blink_on ); } };
Hier wird die gesamte eigentliche Arbeit in den E / A-Handlern fĂŒr den Unterzustand blink_on ausgefĂŒhrt. AuĂerdem sind die Aufenthaltsdauer in den UnterzustĂ€nden blink_on und blink_off begrenzt.
Kein sehr einfaches Beispiel
Zu den Standardbeispielen von SObjectizer v.5.5 gehört auch ein viel komplexeres Beispiel,
intercom_statechart , das das Verhalten der TĂŒrsprechanlage nachahmt. Das Zustandsdiagramm des Hauptagenten in diesem Beispiel sieht ungefĂ€hr so ââaus:

Alles ist so hart, weil diese Nachahmung nicht nur das Anrufen einer Wohnung nach Nummer unterstĂŒtzt, sondern auch Dinge wie einen eindeutigen Geheimcode fĂŒr jede Wohnung sowie einen speziellen Servicecode. Mit diesen Codes können Sie das TĂŒrschloss öffnen, ohne irgendwo zu wĂ€hlen.
In diesem Beispiel gibt es noch interessante Dinge. Es ist jedoch zu groĂ, um im Detail beschrieben zu werden (selbst ein separater Artikel reicht möglicherweise nicht aus). Wenn Sie also daran interessiert sind, wie wirklich komplexe ICAs in SObjectizer aussehen, können Sie dies in diesem Beispiel sehen. Und wenn etwas nicht klar ist, können Sie uns eine Frage stellen. Zum Beispiel in den Kommentaren zu diesem Artikel.
Ist es möglich, die UnterstĂŒtzung fĂŒr in SObjectizer-5 integrierte Raumfahrzeuge nicht zu verwenden?
Daher bietet SObjectizer-5 eine integrierte UnterstĂŒtzung fĂŒr ICA mit einer Vielzahl von unterstĂŒtzten Funktionen. Diese UnterstĂŒtzung wird natĂŒrlich gemacht, um sie zu nutzen. Insbesondere die Debugging-Mechanismen von SObjectizer, wie die
Ablaufverfolgung der NachrichtenĂŒbermittlung , kennen den Status des Agenten und zeigen den aktuellen Status in ihren jeweiligen Debug-Nachrichten an.
Wenn der Entwickler jedoch aus irgendeinem Grund die integrierten SObjectizer-5-Tools nicht verwenden möchte, kann er dies möglicherweise nicht tun.
Sie können beispielsweise die Verwendung von SObjectizer state_t und Ă€hnlichen Programmen ablehnen, da state_t ein ziemlich schweres Objekt mit einem inneren std :: string, einigen std :: -Funktionen und mehreren ZĂ€hlern wie std :: size_t ist. fĂŒnf Zeiger auf verschiedene Objekte und eine andere Kleinigkeit. Zusammen ergibt dies unter 64-Bit-Linux und GCC-5.5 beispielsweise 160 Bytes pro state_t (abgesehen von dem, was im dynamischen Speicher zugewiesen werden kann).
Wenn Sie beispielsweise eine Million Agenten in der Anwendung benötigen, von denen jeder 10 Status hat, ist der Overhead von SObjectizer state_t möglicherweise nicht akzeptabel. In diesem Fall können Sie einen anderen Mechanismus fĂŒr die Arbeit mit Zustandsautomaten verwenden und die Nachrichtenverarbeitung manuell an diesen Mechanismus delegieren. So etwas wie:
class external_fsm_demo : public so_5::agent_t { some_fsm_type my_fsm_; ... void so_define_agent() override { so_subscribe_self() .event([this](mhood_t<msg_one> cmd) { my_fsm_.handle(*cmd); }) .event([this](mhood_t<msg_two> cmd) { my_fsm_.handle(*cmd); }) .event([this](mhood_t<msg_three> cmd) { my_fsm_.handle(*cmd); }); ... } ... };
In diesem Fall zahlen Sie fĂŒr die Effizienz, indem Sie den manuellen Arbeitsaufwand und den Mangel an Hilfe durch die Debugging-Mechanismen von SObjectizer erhöhen. Aber hier ist es Sache des Entwicklers, zu entscheiden.
Fazit
Der Artikel erwies sich als umfangreich, viel mehr als ursprĂŒnglich geplant. Vielen Dank an alle, die diesen Ort gelesen haben. Wenn einer der Leser es fĂŒr möglich hĂ€lt, Ihr Feedback in den Kommentaren zum Artikel zu hinterlassen, ist es groĂartig.
Wenn etwas unklar bleibt, dann stellen Sie Fragen, wir werden gerne antworten.
Bei dieser Gelegenheit möchte ich auch diejenigen, die sich fĂŒr SObjectizer interessieren, darauf aufmerksam machen, dass die Arbeit an der nĂ€chsten Version von SObjectizer im Rahmen von Zweig 5.5 begonnen hat. Kurz darĂŒber, was fĂŒr die Implementierung in 5.5.23 in Betracht gezogen wird, wird hier beschrieben . AusfĂŒhrlicher, aber auf Englisch, hier . Sie können Ihre Meinung zu den fĂŒr die Implementierung vorgeschlagenen Funktionen abgeben oder etwas anderes anbieten. Das heiĂt,
Es gibt eine echte Chance, die Entwicklung von SObjectizer zu beeinflussen. DarĂŒber hinaus kann es nach der Veröffentlichung von Version 5.5.23 zu einer Unterbrechung der Arbeit am SObjectizer kommen, und die nĂ€chste Möglichkeit, etwas NĂŒtzliches in den SObjectizer 2018 aufzunehmen, ist möglicherweise nicht möglich.