Wenn das Projekt "Theater" ist, verwenden Sie Schauspieler ...

In diesem Artikel wird über die Erfahrungen mit der Verwendung des Schauspieleransatzes in einem interessanten Projekt eines automatisierten Steuerungssystems für ein Theater berichtet. Dies ist genau der Eindruck der Nutzung, nichts weiter.


Vor kurzem konnte ich an einer sehr interessanten Aufgabe teilnehmen - der Modernisierung, aber tatsächlich - der Entwicklung eines neuen automatisierten Steuerungssystems zum Heben von Gestellen für eines der Theater.


Ein modernes Theater (wenn es groß ist) ist eine ziemlich komplexe Organisation. Daran sind viele Menschen, Geräte und verschiedene Systeme beteiligt. Eines dieser Systeme ist das Steuerungssystem zum „Anheben und Absenken“ der Szenerie auf der Bühne. Moderne Aufführungen und immer mehr Opern und Ballette werden von Jahr zu Jahr mit technischen Mitteln gesättigt. Es verwendet viele komplexe Landschaften und deren Bewegung während der Aktion. Die Szenerie wird aktiv in Regieplänen verwendet, um die Bedeutung des Geschehens zu erweitern und sogar „Ihre eigene Nebenrolle zu spielen“). Im Allgemeinen war es sehr interessant, das Backstage-Leben des Theaters kennenzulernen und herauszufinden, was dort während der Aufführungen passiert. Schließlich sehen gewöhnliche Zuschauer nur, was auf der Bühne passiert.


Aber dieser Artikel ist immer noch technisch und ich wollte die Erfahrungen mit der Verwendung des Akteuransatzes zur Implementierung des Managements teilen. Und teilen Sie auch die Erfahrung mit einem der wenigen C ++ - Actor -Frameworks - Sobjectizer .


Warum genau er? Wir haben ihn schon lange beobachtet. Es gibt Artikel über ein Habr, es hat eine ausgezeichnete detaillierte Dokumentation mit Beispielen. Das Projekt ist ziemlich ausgereift. Ein kurzer Blick auf die Beispiele zeigte, dass die Entwickler mit „vertrauten“ Konzepten (Zustände, Zeitgeber, Ereignisse) arbeiten, d. H. Es wurden keine großen Probleme mit dem Verstehen und Beherrschen für die Verwendung in unserem Projekt erwartet. Und ja, wichtig ist, dass die Entwickler angemessen und freundlich sind und mit Rat und Tat zur Seite stehen (auf Russisch) . Also beschlossen wir es zu versuchen ...


Was machen wir


Wie ist unser „Kontrollobjekt“? Das System der Shtanketovy-Hebebühnen - dies sind 62 Shankets (Metallrohre) über die gesamte Breite der Bühne, die über dieser Szene hängen, ungefähr alle 30 - 40 cm vom Rand der Bühne in der Tiefe. Die Schenkel selbst sind an Seilen aufgehängt und können bis zur Bühne auf- oder absteigen (vertikale Bewegung). In jeder Aufführung (oder Oper oder Ballett) wird ein Teil der Strophen zur Dekoration verwendet. Die Szenerie wird an sie gehängt und während der Aktion verschoben (sofern das Skript dies erfordert). Die Bewegung selbst wird auf Befehl der Bediener (sie haben spezielle Bedienfelder) mit dem System „Motor - Kabel - Gegengewicht“ ausgeführt (ungefähr so ​​wie Aufzüge in Häusern). Die Motoren befinden sich an den Bühnenrändern (auf mehreren Ebenen), so dass sie für den Betrachter nicht sichtbar sind. Alle Motoren sind in 8 Gruppen unterteilt und jede Gruppe verfügt über drei Frequenzumrichter (IF). In jeder Gruppe können drei Motoren gleichzeitig aktiviert werden, die jeweils an einen eigenen Umrichter angeschlossen sind. Insgesamt haben wir ein System von 62 Motoren und 24 Umrichtern, die wir steuern müssen.


Unsere Aufgabe war es, eine Bedienoberfläche für die Verwaltung dieser Wirtschaft zu entwickeln und Verwaltungsalgorithmen zu implementieren. Das System enthält drei Kontrollposten. Zwei Kontrollpfosten befinden sich direkt über der Bühne, und ein Pfosten befindet sich im Maschinenraum (wo sich die Schaltschränke befinden) und dient zur Überwachung der Arbeit eines diensthabenden Elektrikers. In den Schaltschränken befinden sich Steuerungen, die Befehle ausführen, die PWM steuern, die Motoren mit Strom versorgen und die Position der Schäfte verfolgen. Auf den oberen beiden Fernbedienungen befinden sich Monitore, eine Systemeinheit, in der sich Steueralgorithmen und Trackball als „Maus“ drehen. Zwischen den Bedienfeldern wird ein Ethernet-Netzwerk verwendet. Jeder Schaltschrank verfügt über einen RS485-Kanal (d. H. 8 Kanäle) von jedem der beiden Bedienfelder. Die Verwaltung kann gleichzeitig von beiden Fernbedienungen (die sich über der Bühne befinden) durchgeführt werden. Gleichzeitig wird jedoch nur eine der Fernbedienungen (vom Bediener als Hauptbetreiber bezeichnet) mit den Schränken ausgetauscht. Die zweite Konsole wird derzeit als Backup betrachtet und der Austausch ist deaktiviert.


Und hier die Schauspieler


Aus Sicht der Algorithmen baut das gesamte System auf Ereignissen auf. Dies sind entweder einige Änderungen an den Sensoren oder die Aktionen des Bedieners oder der Beginn einer bestimmten Zeit (Timer). Und solche Algorithmen sind sehr gut im System der Akteure platziert, die eingehende Ereignisse verarbeiten, eine Art Antwort bilden und dies alles abhängig von ihrem Zustand. Im Sobjectizer sind alle diese Mechanismen sofort einsatzbereit. Die Hauptprinzipien, auf denen ein solches System basiert, können zugeschrieben werden: Die Interaktion zwischen Akteuren erfolgt durch Nachrichten, Akteure können Zustände haben und sich zwischen ihnen bewegen, in jedem Zustand verarbeitet der Akteur nur die Nachrichten, die ihn im Moment interessieren. Interessanterweise unterscheidet sich die Arbeit mit Schauspielern in einem Sobjectizer konzeptionell von der Arbeit mit Workflows. Das heißt, Sie können die Akteure beschreiben, die Sie benötigen, ihre Logik erkennen, ihre Interaktion durch Nachrichten realisieren. Lösen Sie dann aber separat das Problem der Zuweisung von Threads (Ressourcen) für ihre Arbeit. Dies wird durch die sogenannten "Dispatcher" sichergestellt, die für eine bestimmte Politik der Arbeit mit Threads verantwortlich sind. Beispielsweise gibt es einen Dispatcher, der jedem Akteur einen separaten Thread zuweist, mit dem gearbeitet werden soll. Es gibt einen Dispatcher, der einen Pool von Threads bereitstellt (d. H. Es können mehr Akteure als Threads vorhanden sein), mit der Möglichkeit, die maximale Anzahl von Threads festzulegen. Es gibt einen Dispatcher, der einen Thread für alle zuweist. Das Vorhandensein von Dispatchern bietet einen sehr flexiblen Mechanismus zum Einrichten eines Akteursystems, das Ihren Anforderungen entspricht. Sie können Gruppen von Akteuren kombinieren, um mit einem der Dispatcher zu arbeiten, während Sie einen Dispatcher-Typ in einen anderen ändern. Dies ändert im Wesentlichen eine Codezeile. Laut den Autoren des Frameworks ist es auch nicht schwierig, einen eigenen Dispatcher zu schreiben. Dies wurde in unserem Projekt nicht benötigt, da sich alles, was wir brauchten, bereits im Sobjectizer befand.


Ein weiteres interessantes Merkmal ist das Vorhandensein des Konzepts der „Zusammenarbeit“ von Akteuren. Kooperation ist eine Gruppe von Akteuren, die entweder alle existieren oder alle zerstört (oder nicht gestartet) werden können, wenn mindestens ein Akteur in der Kooperation nicht in der Lage war, die Arbeit aufzunehmen oder abzuschließen. Ich habe keine Angst davor, eine solche Analogie zu geben ( obwohl es aus einer anderen "Oper" stammt ) dass das Konzept der "Kooperation" wie das Konzept der "Herde" in den jetzt modischen Kubernetes ist, es scheint nur im Sobjectizer, es ist früher entstanden ...


Zum Zeitpunkt der Erstellung ist jeder Akteur in die Zusammenarbeit einbezogen (die Zusammenarbeit kann aus einem Akteur bestehen), wird an den einen oder anderen Disponenten gebunden und beginnt zu arbeiten. Gleichzeitig können Akteure (und Kooperationen) (leicht) in großer Zahl dynamisch erstellt werden, und wie die Entwickler versprechen, ist dies nicht teuer. Alle Akteure tauschen sich über " Mailboxen " (mbox) aus. Dies ist auch ein interessantes und starkes Konzept im Sobjectizer. Es bietet einen sehr flexiblen Mechanismus für die Verarbeitung eingehender Nachrichten. Erstens kann sich mehr als ein Empfänger hinter einer Box verstecken. Es ist wirklich sehr praktisch. Beispielsweise wird eine Box erstellt, in der Ereignisse von externen Sensoren empfangen werden und jeder Akteur Ereignisse abonniert, die für ihn von Interesse sind. Dies bietet einen Betriebsstil "Veröffentlichen / Abonnieren". Zweitens haben die Entwickler die Möglichkeit geboten, relativ einfach eine eigene Implementierung von Postfächern zu erstellen, mit denen eingehende Nachrichten vorverarbeitet werden können (z. B. sie irgendwie filtern oder auf besondere Weise an die Verbraucher verteilen können). Darüber hinaus verfügt jeder Akteur über ein eigenes Postfach und kann beispielsweise sogar einen „Link“ in Nachrichten an andere Akteure senden, damit diese eine Art Benachrichtigung als Antwort senden können.


In unserem Projekt wurden alle Steuerobjekte in 8 Gruppen (entsprechend der Anzahl der Schaltschränke) unterteilt, von denen jede drei Arbeiter hatte, um die Unabhängigkeit der Motorgruppen untereinander sowie den „asynchronen“ Betrieb der Motoren innerhalb der Gruppe sicherzustellen Durchfluss (da nicht mehr als drei Motoren gleichzeitig in einer Gruppe betrieben werden können).
Es sollte auch gesagt werden, dass der Sobjectizer (in der aktuellen Version 5.5) keine Interprozess- und Netzwerkinteraktionsmechanismen enthält und diesen Teil den Entwicklern überlässt. Die Autoren haben dies ganz bewusst getan, damit das Framework „einfacher“ ist. Darüber hinaus existierten die Mechanismen der Netzwerkinteraktion „einmal“ in früheren Versionen, wurden jedoch ausgeschlossen. Dies verursacht jedoch keine Unannehmlichkeiten, da die Netzwerkinteraktion in der Tat sehr stark von den zu lösenden Aufgaben, den verwendeten Austauschprotokollen usw. abhängt. Hier kann eine universelle Implementierung nicht in allen Fällen optimal sein.


In unserem Fall haben wir für die Netzwerk- und Interprozesskommunikation eine unserer langjährigen Entwicklungen verwendet - die libuniset2- Bibliothek. Infolgedessen sieht die Architektur unseres Systems ungefähr so ​​aus:


  • libuniset bietet Netzwerk- und Interprozesskommunikation (basierend auf Sensoren)
  • Sobjectizer bietet die Schaffung eines Systems von Akteuren, die miteinander (im selben Adressraum) interagieren und Steueralgorithmen implementieren.

Ich möchte Sie daran erinnern, dass wir 62 Motoren haben. Jeder Motor kann an den Umrichter angeschlossen werden, dem entsprechenden Ständer kann die Koordinate zugewiesen werden, zu der Sie ankommen müssen, und die Geschwindigkeit, mit der Sie sich bewegen müssen. Darüber hinaus hat der Motor die folgenden Bedingungen:


  • bereit zu gehen
  • verbunden
  • Laufen (Spinnen)
  • Unfall
  • Verbindung (Übergangszustand)
  • Herunterfahren (Übergangszustand)

Infolgedessen wird jede „Engine“ im System durch einen Akteur dargestellt, der die Logik von Übergängen zwischen Zuständen implementiert, Ereignisse von Sensoren verarbeitet und Steuerbefehle ausgibt. In sobjectizer können Schauspieler einfach erstellt werden. Erben Sie einfach Ihre Klasse von der Basisklasse so_5 :: agent_t. Gleichzeitig muss der Konstruktor den sogenannten :: so_5 :: context_t-Kontext als erstes Argument verwenden, die verbleibenden Argumente werden durch die Notwendigkeit des Entwicklers bestimmt.


class Drive_A: public so_5::agent_t { public: Drive_A( context_t ctx, ... ); ... } 

Weil Dieser Artikel ist nicht lehrreich, daher werde ich hier nicht die detaillierten Texte der Beschreibungen von Klassen oder Methoden bereitstellen. Der Artikel wollte nur zeigen, wie einfach es ist (in wenigen Zeilen), mit Hilfe von sobjectizer all dies zu implementieren. Ich möchte Sie daran erinnern, dass das Projekt eine ausgezeichnete detaillierte Dokumentation mit einer Reihe verschiedener Beispiele enthält.


Und wie sind die „Zustände“ dieser Akteure? Worüber sprichst du?


Die Verwendung von Zuständen und Übergängen zwischen ihnen für ACS ist im Allgemeinen ein natives Thema. Dieses „Konzept“ passt sehr gut zum Event-Handling. In sobjectizer wird dieses Konzept auf API-Ebene unterstützt. In einer Schauspielerklasse lassen sich Zustände ziemlich leicht deklarieren


 class Drive_A final: public so_5::agent_t { public: Drive_A( context_t ctx, ... ); virtual ~Drive_A(); //  state_t st_base {this}; state_t st_disabled{ initial_substate_of{st_base}, "disabled" }; state_t st_preinit{ substate_of{st_base}, "preinit" }; state_t st_off{ substate_of{st_base}, "off" }; state_t st_connecting{ substate_of{st_base}, "connecting" }; state_t st_disconnecting{ substate_of{st_base}, "disconnecting" }; state_t st_connected{ substate_of{st_base}, "connected" }; ... } 

und außerdem bestimmt der Entwickler für jeden Zustand die erforderlichen Handler. Oft sind einige Aktionen erforderlich, wenn Sie in einen Status eintreten oder ihn verlassen. Dies ist auch im Sobjectizer vorgesehen. Ebenso einfach definieren Sie Ihre Handler für diese Ereignisse („Statuseintrag“, „Statusausgang“). Es wird davon ausgegangen, dass die Entwickler in der Vergangenheit über umfangreiche Erfahrungen mit ACS-shny verfügen ...


Ereignishandler


Event-Handler: Hier wird die Logik Ihrer Anwendung implementiert. Wie oben erwähnt, wird ein Abonnement für ein bestimmtes Postfach und für einen bestimmten Status des Akteurs abgeschlossen. Wenn ein Akteur keine explizit im Code deklarierten Zustände hat, befindet er sich implizit im Sonderzustand "default_state". In verschiedenen Zuständen können Sie verschiedene Handler für dieselben Ereignisse definieren. Wenn Sie den Handler eines Ereignisses in diesem Postfach nicht angegeben haben, wird es einfach ignoriert (d. H. Es ist für den Akteur einfach nicht vorhanden).


Die Syntax zum Definieren von Handlern ist sehr einfach. Es reicht aus, Ihre Funktion anzuzeigen. Es sind keine Typen oder Vorlagenargumente erforderlich. Alles wird automatisch aus der Funktionsdefinition abgeleitet. Zum Beispiel:


 so_subscribe(drv->so_mbox()) .in(st_base) .event( &Drive_A::on_get_info ) .event( &Drive_A::on_control ) .event( &Drive_A::off_control ); 

Hier ist ein Beispiel für das Abonnieren von Ereignissen in einem bestimmten Feld für den Status st_base. Interessanterweise ist in diesem Beispiel st_base der Basiszustand für andere Zustände, und dementsprechend gilt dieses Abonnement für alle Zustände, die von st_base "geerbt" werden. Mit diesem Ansatz können Sie "Kopieren und Einfügen" entfernen, um dieselben Handler für verschiedene Status zu ermitteln. Gleichzeitig können Sie in einem bestimmten Status entweder den angegebenen Handler überschreiben oder ihn "deaktivieren" (unterdrücken).


Es gibt eine andere Möglichkeit, Handler zu definieren. Dies ist eine direkte Definition von Lambda-Funktionen. Dies ist eine sehr bequeme Methode, da Handler häufig kurze Funktionen in einigen Aktionen sind, etwas an jemanden senden oder den Status wechseln.


 so_subscribe(drv->so_mbox()) .in(st_disconnecting) .event([this](const msg_disconnected_t& m) { ... st_off.activate(); }) .event([this]( const msg_failure_t& m ) { ... st_protection.activate(); }); 

Diese Syntax erscheint zunächst kompliziert. Aber in nur wenigen Tagen aktiver Entwicklung gewöhnt man sich daran und fängt sogar an, es zu mögen. Weil die gesamte Logik der Arbeit des Schauspielers in dem einen oder anderen Zustand in einen ziemlich kurzen Code passen kann und alles vor Ihren Augen liegt. In dem gezeigten Beispiel tritt beispielsweise im getrennten Zustand (st_disconnecting) entweder der Übergang in den getrennten Zustand (st_off.) Oder der Schutzzustand (st_protection) auf, wenn eine Meldung über einen Fehler auftritt. Ein solcher Code ist ziemlich einfach zu lesen.


Übrigens gibt es für einfache Fälle, in denen ein Ereignis nur in einen bestimmten Zustand versetzt werden muss, eine noch kürzere Syntax:


 auto mbox = drv->so_mbox(); st_off .just_switch_to<msg_connected_t>(mbox, st_connected) .just_switch_to<msg_failure_t>(mbox, st_protection) .just_switch_to<msg_on_limit_t>(mbox, st_protection) .just_switch_to<msg_on_t>(mbox, st_on); 

Management


Wie funktioniert das Management all dieser Wirtschaft? Wie oben erwähnt, sind zwei Fernbedienungen zur direkten Steuerung der Bewegung der Shtankets vorgesehen. Auf jeder Fernbedienung befinden sich ein Monitor, ein Manipulator (Trackball) und eine Kurzwahl (zusätzlich zu dem in der Fernbedienung versteckten „Computer“, auf dem sich alles dreht, und jede Menge Konverter aller Art). Das System verfügt über mehrere Modi zur Steuerung der Bewegung der Shtankets. Manueller und "Skriptmodus". Über den "Szenariomodus" wird weiter diskutiert, und nun ein wenig über den "manuellen Modus". In diesem Modus wählt der Bediener den gewünschten Schaft aus, bereitet ihn für die Bewegung vor (verbindet den Motor mit dem Umrichter), setzt die Markierung (Zielposition) für den Schaft und sobald die Geschwindigkeit größer als Null ist, beginnen sich die Schäfte zu bewegen. Zum Einstellen der Geschwindigkeit wird ein spezieller physikalischer Einsteller in Form eines „Potentiometers mit einem Knopf“ verwendet, aber es gibt auch einen „Bildschirmeinsteller“ für die Geschwindigkeit. Je mehr "gedreht", desto lauter geht schneller. Die Höchstgeschwindigkeit ist auf 1,5 m / s begrenzt. Geschwindigkeitsknopf - einer für alle. Das heißt, Im manuellen Modus bewegen sich alle vom Bediener angeschlossenen Schäfte mit der gleichen eingestellten Geschwindigkeit. Obwohl sie sich in verschiedene Richtungen bewegen können (hängt davon ab, wohin der Bediener sie geleitet hat). Natürlich ist es für eine Person schwierig, mehr als zwei oder drei Shtankets gleichzeitig im Auge zu behalten, daher bewegen sie sich im manuellen Modus normalerweise nicht viel. Von zwei Stationen aus können Bediener gleichzeitig jeden ihrer Shtankets verwalten. Zusätzlich hat jede Konsole (Bediener) einen eigenen Geschwindigkeitsregler.


Aus Sicht der Implementierung enthält der manuelle Modus keine spezielle Logik. Der Befehl zum Verbinden der Engine kommt von der grafischen Oberfläche und wird in eine Nachricht an den entsprechenden Akteur umgewandelt, der daran arbeitet. Durchlaufen der Zustände "Aus" -> "Verbinden" -> "Verbunden". Gleiches gilt für das Einstellen der Position für die Bewegung des Stunkets und das Einstellen der Geschwindigkeit. Alle diese Ereignisse erreichen den Schauspieler in Form von Nachrichten, auf die er reagiert. Es sei denn, es kann festgestellt werden, dass die grafische Oberfläche und der Steuerungsprozess selbst unterschiedliche Prozesse sind und zwischen ihnen eine "Interprozess" -Interaktion durch die "Sensoren" unter Verwendung von libuniset2 besteht .


Skriptausführungsmodus (wieder diese Akteure?)


Tatsächlich wird der manuelle Steuermodus hauptsächlich nur zum Abhängen während der Proben oder in einfachen Fällen verwendet. Der Hauptmodus, in dem das Steuerelement ausgeführt wird, ist der "Skriptausführungsmodus" oder kurz "Skriptmodus". In diesem Modus bewegt sich jeder Shtank mit den im Skript angegebenen Parametern (Geschwindigkeit und Zielmarke) an seinen Punkt. Für den Bediener besteht die Steuerung in diesem Modus aus zwei einfachen Befehlen:


  • Machen Sie sich bereit (die richtige Gruppe von Motoren ist angeschlossen)
  • Lass uns gehen (die Gruppe bewegt sich zu den jeweils festgelegten Zielpositionen).

Das gesamte Szenario ist in sogenannte „Agenden“ unterteilt. Eine Agenda ist eine Bewegung einer Shtanket-Gruppe. Das heißt, Jede Agenda enthält eine Gruppe von Shtankets mit der Zielgeschwindigkeit und der Marke, zu der Sie kommen müssen. Tatsächlich ist das Drehbuch in Handlungen unterteilt, Handlungen sind in Gemälde unterteilt, Gemälde sind in Vorladungen unterteilt und Vorladungen bestehen bereits aus „Zielen“ für bestimmte Schtankets. Aus Sicht des Managements ist diese Aufteilung jedoch nicht wichtig, weil Auf der Tagesordnung stehen am Ende bestimmte Parameter der Bewegung.


Um dieses Regime umzusetzen, wurde das Akteursystem so gut wie möglich wieder eingeführt. Es wurde ein „Script Player“ entwickelt, der eine Gruppe spezieller Schauspieler erstellt und diese startet. Wir haben zwei Arten von Akteuren entwickelt: Schauspieler-Schauspieler, die Aufgaben für einen bestimmten Shtanket ausführen sollen, und einen Schauspieler-Koordinator, der Aufgaben zwischen den Darstellern verteilt. Darüber hinaus werden nach Bedarf darstellende Schauspieler geschaffen, wenn zum Zeitpunkt des nächsten Teams nicht frei ist. Der Koordinator ist für die Erstellung und Pflege des Pools der darstellenden Akteure verantwortlich. Infolgedessen sieht das Management ungefähr so ​​aus:


  • Anweisung lädt das Skript
  • "Kippt" es auf die gewünschte Agenda (normalerweise nur in einer Reihe).
  • Drücken Sie im richtigen Moment die Schaltfläche "Vorbereiten", mit der ein Befehl (eine Nachricht) für jedes in der aktuellen Agenda enthaltene Formlet mit Bewegungsparametern an den koordinierenden Akteur gesendet wird.
  • Der Schauspieler-Koordinator schaut sich seinen Pool an frei auftretenden Schauspielern an, nimmt einen freien (wenn er keinen neuen erstellt) und gibt ihm eine Aufgabe (Anzahl der Shankets und Bewegungsparameter).
  • Jeder Schauspieler-Schauspieler, der die Aufgabe erhalten hat, beginnt, den Befehl „Bereit machen“ zu erfüllen. Das heißt, Es verbindet den Motor und wechselt in den Standby-Modus des Befehls „go“.
  • Wenn die Zeit gekommen ist, gibt der Bediener den Befehl "Los geht's".
  • Das Team "go" kommt zum Koordinator. Er sendet es an alle seine derzeit aktiven Darsteller und sie beginnen mit der „Hinrichtung“.

Es ist erwähnenswert, dass auf der Tagesordnung zusätzliche Parameter stehen. Starten Sie beispielsweise die Bewegung mit einer Verzögerung von N Sekunden oder starten Sie die Bewegung erst nach einem separaten speziellen Bedienerbefehl. Daher ist die Liste der Zustände für jeden darstellenden Akteur ziemlich umfangreich: "Bereit zum Ausführen des nächsten Befehls", "Bereit zum Bewegen", "Verzögerte Bewegung", "Warten auf den Befehl des Bedieners", "Bewegung", "Ausführung abgeschlossen", "Fehlfunktion". .


Nachdem der Shanket die angegebene Marke erfolgreich (oder nicht) erreicht hat, benachrichtigt der Darsteller den Koordinator über die abgeschlossene Aufgabe. Der Koordinator gibt entweder den Befehl zum Ausschalten dieser Engine (wenn sie nicht mehr an der aktuellen Agenda teilnimmt) oder gibt neue Bewegungsparameter aus. Der Darsteller erhielt seinerseits einen Befehl zum Ausschalten der Engine, zum Ausschalten und zum Warten auf neue Befehle oder zum Ausführen eines neuen Befehls.


Aufgrund der Tatsache, dass der Sobjectizer über eine gut durchdachte und bequeme API für die Arbeit mit Status verfügt, ist der Implementierungscode recht präzise. Zum Beispiel wird die Verzögerung der Bewegung in einer Zeile beschrieben:


 st_delay.time_limit( std::chrono::milliseconds{target->delay()}, st_moving ); st_delay.activate(); ... 

Die Funktion time_limit legt ein Zeitlimit fest, wie viel in einem bestimmten Zustand ausgegeben werden kann und welcher Zustand nach einer bestimmten Zeit übergeben werden soll (st_moving).


Schutzakteure


Natürlich können während des Betriebs Fehlfunktionen auftreten. Das System muss diese Situationen bewältigen. Auch hier gab es einen Platz für den Einsatz von Schauspielern. Betrachten Sie mehrere dieser Schutzmaßnahmen:


  • Überstromschutz
  • Schutz vor Messfehlern
  • Schutz gegen Bewegung in die entgegengesetzte Richtung (und dies kann sein, wenn etwas mit dem Sensor oder Messgerät nicht stimmt)
  • Schutz vor Bewegung ohne Befehl
  • Kontrolle über die Ausführung des Teams (Kontrolle darüber, dass sich der Shtanket in Bewegung gesetzt hat)

Sie können sehen, dass alle diese Schutzmaßnahmen vom Standpunkt der Implementierung unabhängig (autark) sind und "parallel" funktionieren sollten. Das heißt, Jede Bedingung kann funktionieren. Gleichzeitig hat die Logik zum Überprüfen der Auslösebedingungen für jeden der Schutzfunktionen ihre eigene, manchmal ist eine Verzögerung (Zeitgeber) zum Auslösen erforderlich, manchmal ist eine vorläufige Verarbeitung mehrerer vorheriger Messungen erforderlich usw. Daher erwies sich die Implementierung jeder Art von Schutz als separater kleiner Akteur als sehr praktisch. Alle diese Akteure werden zusätzlich (in Zusammenarbeit) zu dem Hauptakteur gestartet, der die Steuerlogik implementiert. Dieser Ansatz macht es einfach, zusätzliche Arten von Verteidigungen hinzuzufügen, indem einfach ein weiterer Akteur zur Gruppe hinzugefügt wird. Gleichzeitig bleibt die Implementierung eines solchen Akteurs ziemlich einfach und verständlich, weil Es implementiert nur eine Funktion.


Schutzakteure haben auch mehrere Staaten. Grundsätzlich schalten sie sich nur dann ein (gehen in den Zustand „Ein“), wenn der Motor angeschlossen ist oder sich der Schaft bewegt. Wenn die Schutzbedingungen ausgelöst werden, veröffentlichen sie eine Benachrichtigung über den Schutz (mit einem Sicherheitscode und einigen Details für die Protokollierung). Der Hauptakteur reagiert bereits auf diese Benachrichtigung, die bei Bedarf die Engine ausschaltet und in den Schutzmodus wechselt.


Als Fazit ..


... natürlich ist dieser Artikel keine "Entdeckung". Der Akteuransatz wird seit langem in vielen Systemen erfolgreich eingesetzt. Für mich war es jedoch die erste Erfahrung, in einem relativ kleinen Projekt bewusst den Akteuransatz zum Erstellen von Steuerungssystemalgorithmen zu verwenden. Und die Erfahrung war ziemlich erfolgreich. Ich hoffe, ich konnte zeigen, dass die Schauspieler den Steuerungsalgorithmen sehr gut überlagert sind, sie haben buchstäblich überall einen Platz gefunden.


Aus den Erfahrungen früherer Projekte ging hervor, dass wir auf die eine oder andere Weise „so etwas“ implementiert haben (Status, Messaging, Flusskontrolle usw.), aber dies war kein einheitlicher Ansatz. Mit dem Sobjectizer haben wir ein übersichtliches, leichtes Entwicklungswerkzeug erhalten, das eine Menge Probleme löst. Es ist nicht mehr erforderlich (explizit), Synchronisationstools (Mutexe usw.) zu verwenden, es gibt keine explizite Arbeit mit Streams, keine Realisierungen der Zustandsmaschine. All dies ist im Framework, logisch miteinander verbunden und darüber hinaus als praktische API dargestellt, ohne die Kontrolle über die Details zu verlieren. Die Erfahrung war also interessant. Für diejenigen, die immer noch Zweifel haben, empfehle ich, auf den Schauspieleransatz und insbesondere auf das Sobjectizer- Framework zu achten . Er hinterlässt positive Emotionen.


Und der Schauspieleransatz funktioniert wirklich! Besonders im Theater.

Source: https://habr.com/ru/post/de438196/


All Articles