Ankündigung

Einklappen
Keine Ankündigung bisher.

Zustandsautomat / State Machine

Einklappen
Dieses Thema ist geschlossen.
X
X
 
  • Filter
  • Zeit
  • Anzeigen
Alles löschen
neue Beiträge

    [wiregate] Zustandsautomat / State Machine

    Neben den (Zeitschalt-)Uhr Plugins brauche ich eigentlich etwas leicht anderes:

    Einen Zustandsautomat / State Machine.

    Wäre es nicht sinnvoll, wenn wir dafür ein passendes Framework hätten?
    D.h. eine fertige Stuktur, so dass man nur noch jeweils eine Funktion angeben muss, die angibt, ob in einen anderen Zustand gewechselt werden soll, so wie eine Funktion die beim Zustandswechsel aufgerufen wird.

    So sollte sich hoffentlich ganz sauber und übersichtlich eine Rollladensteuerung realisieren lassen, die zu gewissen Uhrzeiten (je nach Wochenende oder Arbeitstag) fährt, aber nicht über/unter einer gewissen Helligkeitsschwelle. Und nicht nochmal wenn man per Hand übersteuert hat (da der Zustand ja schon gewechselt wurde)...

    Ich habe zwar schon ein paar Plugins, die in diese Richtung gehen, aber mangels Framework werden die immer schnell unübersichtlich
    Gibt's hier schon andere Implementierungen?
    TS2, B.IQ, DALI, WireGate für 1wire so wie Server für Logik und als KNX Visu die CometVisu auf HomeCockpit Minor. - Bitte keine PNs, Fragen gehören in das Forum, damit jeder was von den Antworten hat!

    #2
    Dafür: Anwendungsfall hier Multitaster.
    Mehrmals auf den Taster gedrückt sollte verschiedene GAs triggern...
    Also nicht gemorst, sonder eine nach der anderen.
    Derzeit zwischen Kistenauspacken und Garten anlegen.
    Baublog im Profil.

    Kommentar


      #3
      So, hab jetzt mal eine Demo erstellt.

      Die macht im Grunde nichts anderes als zwischen den Zuständen "Tag", "Abend" und "Nacht" zu wechseln, jedes mal wenn sie aufgerufen wird (mit Zyklus auf 60s passiert das ja ausreichend oft...).

      Das spannendere ist aber eher, wie der Zustandsautomat sich aufbaut, d.h. die Aufrufe addState() und addStateTransition().

      Im SVN ist's noch nicht eingecheckt, da ich hier gerne klären würde, wie wir am besten die Arbeits-Logik von der State-Machine-Logik trennen können und letztere in eine Bibliothek auslagern können.

      Code:
      ###############################################################################
      # Plugin: State Machine
      # V0.1 2011-11-01
      # Copyright: Christian Mayer (mail at ChristianMayer.de)
      # License: GPL (v3)
      #
      # Plugin for demonstrating a state machine
      #
      # NOTE: $plugin_info{ $plugname . '_state' } is reserved for the functionality
      ###############################################################################
      
      ###############################################################################
      # Preparation, do not change this block:
      my %StateTransitionFunctions = (); # Empty hash to be filled later
      my %StateEntryFunctions      = (); # Empty hash to be filled later
      my $ret_val = '';
      ###############################################################################
      
      ###############################################################################
      # Configuration:
      #
      my $reset = 0;      # set to 1 to reset plugin, otherwise leave at 0
      my $show_debug = 1; # switches debug information that will be shown in the log
      
      # Set the update cycle (not required for the state machine)
      $plugin_info{ $plugname . '_cycle' } = 60;
      
      #
      # Set the state to start from at first run and after reset
      # NOTE: when the state gets reset, the StateEntryFunction will NOT be called.
      #       If that is desired, you should add a dummy init state that will
      #       transition to the firt "real" state
      #
      my $initState = 'Tag';
      
      #
      # For each state known to the system you need an addState() call.
      #
      addState( 'Tag', sub {
        $ret_val .= 'EnterTag';
      });
      
      addState( 'Abend', sub {
        $ret_val .= 'EnterAbend (war: ' . $plugin_info{ $plugname . '_state' } . ')';
      });
      
      addState( 'Nacht', sub {
        $ret_val .= 'EnterNacht';
      });
      
      #
      # Define all known state transitions
      #
      addStateTransition( 'Tag', 'Abend', sub {
        $ret_val .= '[Tag->Abend:0]';
        return 0;
      });
      
      addStateTransition( 'Tag', 'Abend', sub {
        $ret_val .= '[Tag->Abend:1]';
        return 1;
      });
      
      addStateTransition( 'Abend', 'Nacht', sub {
        $ret_val .= '[Abend->Nacht]';
        return 1;
      });
      
      addStateTransition( 'Nacht', 'Tag', sub {
        $ret_val .= '[Nacht->Tag]';
        return 1;
      });
      
      #
      ###############################################################################
      
      ###############################################################################
      # Do NOT change anything below!
      ###############################################################################
      
      ###############################################################################
      # The logic for the state machine: Public functions
      
      # Call this function to add a new state.
      # The first parameter is the name of the new state
      # The second parameter is a function that gets called when this state is
      #     changed to
      #
      # NOTE: The function might look at $plugin_info{ $plugname . '_state' } to
      #       figure out the old state if necessary
      sub addState
      {
        my $stateName          = shift;
        my $stateEntryFunction = shift;
        $StateEntryFunctions{ $stateName } = $stateEntryFunction;
      }
      
      # Call this function to add a new state transition.
      # The first parameter is the state where this change originates from
      # The second parameter ist the state where this change leads to
      # The third parameter is a function reference.
      #   This function will be called when the plugin runs and is in the according
      #   state.
      # NOTE: the functions are called in a non deterministic order and the the first
      #       that returns true will define the next state.
      #       That implies that if more than one function would return true, the next
      #       state is chosen non deterministicly!
      sub addStateTransition
      {
        my $originalState = shift;
        my $nextState     = shift;
        my $func          = shift;
        
        push( @{ $StateTransitionFunctions{ $originalState } }, [ $nextState, $func ] );
      }
      
      ###############################################################################
      # The logic for the state machine: initialisation
      if( $reset or not exists $plugin_info{ $plugname . '_state' } )
      {
        $plugin_info{ $plugname . '_state' } = $initState;
      }
        
      ###############################################################################
      # The logic for the state machine: the state machine itself
      my $currentState = $plugin_info{ $plugname . '_state' };
      $ret_val .= "Current state: '$currentState'; ";
      foreach my $ref ( @{ $StateTransitionFunctions{ $currentState } } )
      {
        my $nextState = $ref->[0];
        my $func      = $ref->[1];
        if( &$func )
        {
          $ret_val .= "New state: '$nextState'; ";
          &{ $StateEntryFunctions{ $nextState } };
          $plugin_info{ $plugname . '_state' } = $nextState;
          last;
        }
      }
      
      if( $show_debug ) { return $ret_val; }
      return;
      
      #############################################################################
      # Version history:
      # ================
      #
      # 0.1:
      # ------
      # * initial release
      #
      #############################################################################
      # ToDo:
      # =====
      # * Throw error when addStateTransition() uses unknown states
      # * Move logic to external library
      #############################################################################
      TS2, B.IQ, DALI, WireGate für 1wire so wie Server für Logik und als KNX Visu die CometVisu auf HomeCockpit Minor. - Bitte keine PNs, Fragen gehören in das Forum, damit jeder was von den Antworten hat!

      Kommentar


        #4
        Hi Chris,

        hast Du hier schon irgendwie weitergemacht? Es scheint ja nicht so ein grosses Feedback zu geben, ich wäre aber an einem Zustandsautomaten auch interessiert. Ich mülle mir bei den Plugins meine plugin_info immer mehr zu mit allen möglichen Zustandswerten aller möglichen GA's... Deine Idee ist da wesentlich sauberer und überlichtlicher!

        Offensichtlich übersteigen Deine Perl-Kenntnisse meine bei weitem, da ich Dein Coding semantisch verstanden habe, aber syntaktisch nie hinbekommen hätte. Ist auf jeden Fall ne saubere Grundlage.

        Meine Frage: Wie macht man das mit externen Libs? Ich wollte schon immer einen Pool von externen (Hilfs-)Routinen haben, die man immer wieder braucht, hab aber keine Ahnung, wie das bei Plugins anzustellen wäre...

        Bin für jede Idee dankbar,
        Gruß, Waldemar
        OpenKNX www.openknx.de

        Kommentar


          #5
          Zitat von mumpf Beitrag anzeigen
          Meine Frage: Wie macht man das mit externen Libs? Ich wollte schon immer einen Pool von externen (Hilfs-)Routinen haben, die man immer wieder braucht, hab aber keine Ahnung, wie das bei Plugins anzustellen wäre...
          Hier ist eine Lösung noch offen - habe ich aber bei Makki schon angefordert

          So lange ist die StateMachine halt per Copy&Paste zu befüllen...
          TS2, B.IQ, DALI, WireGate für 1wire so wie Server für Logik und als KNX Visu die CometVisu auf HomeCockpit Minor. - Bitte keine PNs, Fragen gehören in das Forum, damit jeder was von den Antworten hat!

          Kommentar


            #6
            Zitat von Chris M. Beitrag anzeigen
            So lange ist die StateMachine halt per Copy&Paste zu befüllen...
            Das habe ich befürchtet... Ich habe schon zig gleiche subs in ähnlichen Plugins - ist der Horror, wenn man dann irgendein Feature dazubasteln will!!!

            Sei es drum, werde Deine StateMachine kommenden Mittwoch gleich ausprobieren, wenn ich wieder zu Hause bin.

            Und falls es was neues zu externen Libs gibt, lass es mich bitte wissen.

            Gruß, Waldemar
            OpenKNX www.openknx.de

            Kommentar


              #7
              Hi Chris,

              ich habe jetzt mal mit Deinem code gespielt - ist echt klasse! Trotzdem ist mir gleich bei meinem ersten Zustandsautomaten aufgefallen, dass mir 2 Sachen fehlen:
              1. Transiente States (ich hoffe das heisst so) - also States, die gleich in den nächsten wechseln, falls es eine mögliche Transition gibt.
              2. Zeitabhängige States oder noch besser zeitabhängige Transitionen - also so was wie "wechsel nach 10 Min. in den nächsten State".

              Für den 1. Fall hab ich ne kleine Änderung in Dein Coding eingebaut (incl. Zyklus-Kontrolle, um Endlosschleifen zu vermeiden), das teste ich noch ein bisschen. Falls es was ist, stelle ich es gerne zur Verfügung.

              Der 2. Fall ist mit der derzeitigen plugin-Infrastruktur irgendwie nicht hinzubekommen (genauer gesagt, ich bekomm es nicht hin). Mein Problem ist, dass ich nicht vernünftig unterschiedliche Zeiten für die Transitionen hinbekomme (also nicht korrekt den Aufrufzyklus des Plugins setzen kann).

              Ich könnte mir vorstellen, dass Du auch vor solchen Problemen gestanden hast. Hast Du vielleicht ne Lösung oder Ideen diesbezüglich - ich wäre auch hier für jeden Tip dankbar!

              Gruß, Waldemar
              OpenKNX www.openknx.de

              Kommentar


                #8
                Der Wunsch mit den Libs ist angekommen und auch angenommen, das wird aber definitiv ein paar warme Winternächte brauchen

                Primär, da ich mir aufgrund so mancher Erfahrung keine neuen Leichen bauen will, ohne es in allen Lebenslagen ausgetestet zu haben.. In schnell ginge das einfach, nur..
                Die Plugins sollen "echt" Multithreaded werden - aber abwärtskompatibel bleiben - und trotzdem eigentlich schon wenigstens noch die nächste halbe Dekade so überleben sollen.. Schmaler Grat..

                Für #2 sehe ich in der derzeitigen Struktur allerdings ganz wenig Chance, es funktioniert de-fakto mit Perl nichtmal das POSIX-Signalling auch nur ansatzweise (was für so sachen wie "alarms" oder eben Zeitbasiert doch recht essentiell wäre..);
                wenn man da ernsthaft gute Vorschläge hat, bitte, gerne, aber dann gehts eher hier - in C - weiter..
                (die Plugins bleiben natürlich wie sie sind in Perl, nur für den geneigten Mitleser: Perl ist eigentlich in C geschrieben und da funktionieren solche Sachen wie Threads und Signale halt, also muss "Papa nur auf die kleinen Buben aufpassen", alles halb so wild, nur ein bisschen blutig wenn man auf einmal wieder etwas rechtzeitig auf den Stack pushen muss und dabei auch noch wissen was das bedeutet..)

                Makki
                EIB/KNX & WireGate & HS3, Russound,mpd,vdr,DM8000, DALI, DMX
                -> Bitte KEINE PNs!

                Kommentar

                Lädt...
                X