State Machine mit While-Schleifen.bitte um Kommentare

Gast #5639771
Lesenswert?

Hallo,

mir kam gerde folgende Idee zur Umsetzung einer State Machine:
1
int main(void)
2

3
int Status = STATUS_A;
4

5
{
6

7
while (Status = STATUS_A)
8
{
9

10
if (irgendwas)
11
{
12
eine_funktion();
13
}
14
else
15
{
16
Status = STATUS_B;
17
}
18

19
}
20

21
while (Status = STATUS_B)
22
{
23

24
if (irgendwas_anderes)
25
{
26
eine_andere_funktion();
27
}
28
else
29
{
30
Status =  STATUS_A
31
}
32

33
}

Die Idee dahinter ist, die übliche Hauptschleife in mehrere Schleifen zu 
unterteilen, die je nach Zustand des Systems unterschiedlich auf 
Eingaben reagieren. Z.B. könnte STATUS_A das Hochfahren einer Mascheine 
sein, und STATUS_B dann der laufende Betrieb. Und einmal muss man z.B. 
auf die Temperatur einer Heizung warten (A), später wird stattdessen 
eine Fehlermeldung ausgegeben(B).

Was meint ihr? Taugt das Konzept was?

Gruß,
Dieter
Gast #5639778
Lesenswert?

Dr. Sommer schrieb:
> Wenn ständig die Schleifen laufen, hast du immer 100% CPU-Last und eine
> hohe Leistungsaufnahme.

Häää....

Die üblichen µC laufen immer mit 100% Last, mit voller 
Leistungsaufnahme.
(wenn sie nicht gerade schlafen)



> Was meint ihr? Taugt das Konzept was?
Wenn es deine konkreten Anforderungen erfüllt, dann gut.

Aber universell ist das nicht.
Denn normalerweise tummeln sich etliche StateMachines auf einem µC zu 
einer Zeit. Diese würden sich bei dir gegenseitig blockieren.
Moderator (Firma: Titel) Persönliche Seite #5639799
Lesenswert?

Dieter schrieb:
> Was meint ihr? Taugt das Konzept was?
Nein.
Die Hauptschleife wird immer komplett und möglichst schnell durchlaufen. 
Und dann abhängig von irgendwelchen Zuständen irgendwelcher Automaten 
(und "Unterautomaten") irgendwelche Aktionen ausgelöst, von denen aber 
keine den Ablauf irgendwie blockiert.

Dieter schrieb:
> Und einmal muss man z.B. auf die Temperatur einer Heizung warten (A),
> später wird stattdessen eine Fehlermeldung ausgegeben(B).
1
int Status = STATUS_A;
2

3
int main(void)
4
{
5
   while (1) {
6
      if (Status == STATUS_A) {
7
         machwas();
8
      }
9

10
      if (Status == STATUS_B) {
11
         machwasanderes();
12
      }
13

14
      if (Status == FEHLER) {
15
         maschine_anhalten();
16
         fehlerlampe_anschalten();
17
         wunden_lecken();
18
      }
19

20
      if (irgendwas) {
21
         if (machirgendwas() == OK)  Status = STATUS_B;
22
         else                        Status = FEHLER;
23
      }
24

25
      if (Sonst_ein_Fehler_aufgetreten()) {
26
         Status = FEHLER;
27
      }
28
      
29
      if (Zeichen_auf_serieller_Schnitte_empfangen()) {
30
         Zeichen_auslesen();
31
      }
32
    
33
      if (sonstwas()) {
34
         if (tu_sonstwas() == NOK) Status = FEHLER;
35
      }
36
      :
37
      :
38
   }
39
}
#5639801
Lesenswert?

Dieter schrieb:

> mir kam gerde folgende Idee zur Umsetzung einer State Machine:

Nach einer Sauftour?

> Die Idee dahinter ist, die übliche Hauptschleife in mehrere Schleifen zu
> unterteilen, die je nach Zustand des Systems unterschiedlich auf
> Eingaben reagieren.

Das ist dir aber maximal mißlungen.

> Z.B. könnte STATUS_A das Hochfahren einer Mascheine
> sein, und STATUS_B dann der laufende Betrieb.

Das machen die klassischen Konzepte DEUTLICH besser und übersichtlicher.

> Und einmal muss man z.B.
> auf die Temperatur einer Heizung warten (A), später wird stattdessen
> eine Fehlermeldung ausgegeben(B).
>
> Was meint ihr? Taugt das Konzept was?

Nö.

Denn der Witz einer Statemachine, besonders in Software ist es ja 
gerade, daß sie nur wenige Aufgaben, die im aktuellen Zustand zu prüfen 
und erledigen sind, prüft und erledigt und dann schnell wieder den 
Programmfluß für andwere Dinge, meist andere State machines freigibt. 
Deine hat "lebenslänglich", wenn sich der aktuelle Zustand nicht ändert, 
alos das totale Gegenteil! Optimal, um eine CPU zu 100% mit Nichtstun 
auszulasten ;-) Außerdem ist deine Schreibweise auch nicht 
übersichtlicher.
Und warum sollte man die jeweiligen Funktionen mehrfach ausführen. Das 
ist im allgemeinen nicht so.

Dein Ziel

"Die Idee dahinter ist, die übliche Hauptschleife in mehrere Schleifen 
zu
unterteilen, die je nach Zustand des Systems unterschiedlich auf
Eingaben reagieren. "

Erreicht man mit mehreren, klassischen State machines deutlich besser.
Denn die können und dürfen miteinander kommunizieren. Sei es, daß die 
Zustände der State machines global sichtbar sind oder daß sie spezielle 
Kommunikationsvariablen nutzen.
Gast #5639848
Lesenswert?

Dieter schrieb:
> Was meint ihr?

Ich meine, du solltest bsser mit Events arbeiten.

also
1
immerzu:
2
if (EventAvailable())
3
{ event = GetEvent();
4
  switch (event)
5
  { case dieser: Erledige_dieses...; break;
6
    case jener: Erledige_jenes....; break;
7
    ...
8
und so weiter.

Sinn der Übung ist es, eben auf alles reagieren zu können, was denn da 
so an behandlungsbedürftigen Ereignissen herein kommen mag. Aber dazu 
darf man nirgendwo blockierend programmieren.

Es ginge in main sogar noch kürzer und damit sogar projektunabhängiger:
1
immerzu:
2
  if (EventAvailable()) 
3
  DispatchEvent(GetEvent());
4
goto immerzu;
Das wäre wohl die kürzeste main für sowas.

W.S.
Gast #5639861
Lesenswert?

Man kann ja mal etwas ausprobieren. Die anderen "Lehrbuchmethoden" hat 
sich auch irgendwann mal Irgendjemand ausgedacht -die sind nicht von 
einem Berg herab verkündet worden.

Ich habe hier auch schon die lustigsten Einfälle mit einem 
Experimentier-Bord auf Atmega32-Basis ausprobiert, das immer ma Rechner 
hängt. Viele davon nutze ich weiter, nachdem ich sie anständig 
dokumentiert habe. Aber: NIE würde ich auf die Idee kommen, die hier zu 
diskutieren. Dafür sind mir hier zu viele Lautsprecher und 
Riesenschnauzer unterwegs.
Gast #5639871
Lesenswert?

Arduino Fanboy D. schrieb:
> Häää....
>
> Die üblichen µC laufen immer mit 100% Last, mit voller
> Leistungsaufnahme.
> (wenn sie nicht gerade schlafen)

Ganz genau; wenn man eine Nicht-Blockierende Statemachine hat, kann man 
zwischendurch den Prozessor in den Sleep-Modus versetzen und bei 
Ereignissen per Interrupt aufwecken.
Gast #5639885
Lesenswert?

@TO

Bei Deiner Statemachine kommst Du von Zustand A nach Zustand B, aber nie 
wieder zurück, weil die Main-Funktion dann zu Ende ist. Um das zu 
erreichen, musst du das Ganze in eine While(1)-Schleife verpacken und 
bist dann doch wieder bei einer Hauptschleife.
Gast #5639889
Lesenswert?

I am the Wunderer schrieb:
> Man kann ja mal etwas ausprobieren. Die anderen "Lehrbuchmethoden" hat
> sich auch irgendwann mal Irgendjemand ausgedacht -die sind nicht von
> einem Berg herab verkündet worden.
> [...]

Sicher. Man hat auch mal eine "Eingebung".
Aber es ist sinnlos , eine Eingebung oder einen Einfall einfach so in 
den Raum zu stellen, ohne vorher selbst darüber nachzudenken, ob der 
überhaupt zu irgend etwas taugt.

In diesem Fall ist der Einfall in einer Hinsicht trivial (nämlich durch 
die Lehrbuchmethoden schon abgedeckt) und in anderer Hinsicht 
unzweckmässig, weil er unnötig die CPU beschäftigt. Und das ist nicht 
etwa nur durch eine verwickelte Kette von Schlussfolgerungen zu 
erschliessen, bei der man sich als Fortgeschrittener mal irren kann, 
sondern zu einem gewissen Grad offensichtlich.

Die Lehrbuchmethoden sind entweder zur Erreichung eines bestimmten 
Zweckes erdacht worden, oder - darüber kann man nur spekulieren -, man 
hat einen Einfall im nachhinein als zweckmässig erkannt.

Aus diesen Gründen ist es absurd, ein Forum dazu zu verwenden, Einfälle 
ungefiltert, unreflektiert beurteilen zu lassen. Falls das Mode wird, 
würde das Forum von allen möglichen halbgebildeten Einfällen überflutet 
werden, die gar keinen Sinn haben, die aber nur mit Grundlagenwissen zu 
beurteilen sind, das dem TO einfach nur fehlt.

Die Frage nach dem möglichen Vorteil dieser Methode ist also in 
zweierlei Hinsicht sinnvoll: 1. Weil er schlicht nicht angegeben worden 
ist und 2. weil er den TO möglicherweise dazu bringt, mal über seinen 
Einfall nachzudenken und sich evtl. fehlendes Wissen anzueignen.
#5639893
Lesenswert?

spess53 schrieb:
> Hi
>
>>Dafür sind mir hier zu viele Lautsprecher und
>>Riesenschnauzer unterwegs.
>
> Ist halt nichts für Zwergpinscher.
>
> MfG Spess

Ich muss doch noch Popcorn machen. Bier darf ich nicht ... obwohl, ist 
das nicht sogar gut bei Grippe.

Die State Machine ist immer eines der best diskutierten Themen hier.
Wird nur von C++ geschlagen.
#5639899
Lesenswert?

Lothar M. schrieb:
> Dieter schrieb:
>> Und einmal muss man z.B. auf die Temperatur einer Heizung warten (A),

Die State Machine warte niemals auf irgend etwas, sondern reagiert auf 
Bedingungen und Veränderungen.
Und (!) die schläft auch nicht. Jedenfalls nicht, wenn sie eine 
relevante State Machine ist.

Wie Lothar schon richtig schreibt, sie muss so schnell wie möglich die 
Hauptschleife durchlaufen.
Gast #5639911
Lesenswert?

F. F. schrieb:
> Schlaf

Ich behaupte:
Jedes funktionierende Programm auf einem µC, ist, oder implementiert, 
eine oder mehrere, Zustandsautomaten.
Damit dürfte ich ca. 99,9% aller Programme erwischt haben.


Und das hat nichts damit zu tun, ob der jeweilige Programmierer, 
beabsichtigt hat, es zu implementieren, oder gar schon jemals von dem 
Pattern gehört hat.
#5639922
Lesenswert?

Arduino Fanboy D. schrieb:
> Ich behaupte:

Stimme ich dir voll zu.

In gewisser Weise ist die Betrachtung auch schon immer meine gewesen.
Die Diskussion ist hier immer zu Abstrakt, sie bezieht sich nur auf den 
Code.
Was soll denn die State Machine machen?
Sie soll reale Zustände überwachen und dann meist an Übergängen in einen 
anderen Zustand ein Reaktion auslösen. Selbst die Fehlermeldung ist 
solche. Wenn z.B. der Temperatursensor nach 1000 Abfragen nicht einmal 
seinen Wert geändert hat.

Aber noch mal, sie schläft nie! Natürlich kann der µC schlafen und auf 
einen Trigger warten, doch während sie läuft, muss sie das und möglichst 
ohne Unterbrechungen.
Gast #5639924
Lesenswert?

Einige Anmerkungen dazu:

- Ja, da muss nochmal die Hauptschleife drum herum. Mein Fehler.
- Es müssen in jedem Zustand zyklisch Messwerte erfasst und Ports 
ausgelesen werden. Daher die while-Schleifen und die mehrfachgen 
Funktionsaufrufe.

Vielleicht macht das den Hintergrund zur Idee etwas klarer.

Gruß,
Dieter.
Gast #5639951
Lesenswert?

Dr. Sommer schrieb:
> Wenn ständig die Schleifen laufen, hast du immer 100% CPU-Last und eine
> hohe Leistungsaufnahme. Außerdem muss da "==" in die Schleifenbedingung.

Ok, wenn die Schleifen jetzt nicht immer laufen, wie frage ich dann 
Messwerte vom AD-Wandler ab, überwache Ports. Ich kann ja nicht allem 
einen Interrupt zuordnen, zumindesst nicht auf dem AVR.
#5639954
Lesenswert?

W.S. schrieb:
> alsoimmerzu:
> if (EventAvailable())
> { event = GetEvent();
>   switch (event)
>   { case dieser: Erledige_dieses...; break;
>     case jener: Erledige_jenes....; break;
>     ...
> und so weiter.

Wobei sich die Dringlichkeit der Ausführung immer noch stellt. Beim ABS 
sollte das Rad sofort wieder frei gegeben werden, wenn es blockiert.
Bei deiner Heizung? Wenn die Temperatur deiner Heizung um 0,3 Grad 
fällt, musst du dann in der State Machine nachregeln, oder kannst du das 
dann vielleicht nicht besser außerhalb machen?
Gast #5639957
Lesenswert?

Dieter schrieb:
> Ok, wenn die Schleifen jetzt nicht immer laufen,

Nee...
Eine Endlosschleife, ist schon ok.
Und von mir aus auch, bei einem Multitaskingsystem, für jeder Task eine 
eigene Endlosschleife.


---
Es gibt sicherlich dutzende Möglichkeiten, endliche Automaten zu bauen. 
Aber Schleifen, welche andere Dinge blockieren, sind mit Vorsicht zu 
genießen.
Mal eben 3 Dinge in eine Schleife tun, kein Problem.
Aber im Kopf einer While() Schleife auf eine Zustandsänderung warten 
blockiert alles andere im System.
#5639983
Lesenswert?

Arduino Fanboy D. schrieb:
> Ich behaupte:
> Jedes funktionierende Programm auf einem µC, ist, oder implementiert,
> eine oder mehrere, Zustandsautomaten.
> Damit dürfte ich ca. 99,9% aller Programme erwischt haben.
>
> Und das hat nichts damit zu tun, ob der jeweilige Programmierer,
> beabsichtigt hat, es zu implementieren, oder gar schon jemals von dem
> Pattern gehört hat.

Diese Aussage ist richtig aber nutzlos. Denn man kann problemlos jeden 
wirren Mist incl. Goto zusammenschustern. Und auch dieser Quark wird 
sich deterministisch verhalten, läuft ja auf einer deterministischen 
Hardware.

ABER!!!

Das ist weder sonderlich lesbar, wartbar, erweiterbar, testbar noch 
nachvollziehbar. Und ob der Code WIRKLICH das macht, was er machen SOLL, 
ist auch noch so eine Frage.

Ergo. Mit den bekannten, bewährten Konzepten fährt man zu 90% nicht nur 
sehr gut sondern auch deutlich besser.
Gast #5639997
Lesenswert?

Falk B. schrieb:
> Denn man kann problemlos jeden
> wirren Mist incl. Goto zusammenschustern.

Weiß gar nicht, was du gengen Goto hast...

Denn gerade beim Bau von endlichen Automaten ist es doch eine der vielen 
Optionen. Vielleicht nicht die schönste... aber immerhin, eine mögliche.

Zumindest eher, als eine blockierende while() Schleife, von der man 
nicht weiß, ob, oder wann sie endet.
#5640010
Lesenswert?

Arduino Fanboy D. schrieb:
> Falk B. schrieb:
>> Denn man kann problemlos jeden
>> wirren Mist incl. Goto zusammenschustern.
>
> Weiß gar nicht, was du gengen Goto hast...

Goto braucht man in der Praxis sehr selten und sollte nur von Leuten 
genutzt werden, die wissen was sie WIRKLICH tun und wenn es WIRKLICH 
nicht sinnvoll anders geht.

> Denn gerade beim Bau von endlichen Automaten ist es doch eine der vielen
> Optionen. Vielleicht nicht die schönste... aber immerhin, eine mögliche.

Eine Suppe mit der Gabel essen ist auch eine Möglichkeit.
Vielleicht nicht die schönste... aber immerhin, eine mögliche.

> Zumindest eher, als eine blockierende while() Schleife, von der man
> nicht weiß, ob, oder wann sie endet.

Darum ging es gar nicht.
#5640039
Lesenswert?

Die Statemachines müssen so geschrieben sein, daß sie zum Main zurück 
kehren, solange es nichts zu tun gibt.
Dann können mehrere Statemachines parallel ausgeführt werden.
Jede Statemachine sollte so klein wie möglich sein. Teilaufgaben macht 
man dann in einer separaten Statemachine.
Anbei mal ein Beispiel. Den Statemachines und States gibt man natürlich 
sinnvolle Namen.
Angehängte Dateien:
Gast #5640073
Lesenswert?

Dieter schrieb:
> Dr. Sommer schrieb:
>> Wenn ständig die Schleifen laufen, hast du immer 100% CPU-Last und eine
>> hohe Leistungsaufnahme. Außerdem muss da "==" in die Schleifenbedingung.
>
> Ok, wenn die Schleifen jetzt nicht immer laufen, wie frage ich dann
> Messwerte vom AD-Wandler ab, überwache Ports. Ich kann ja nicht allem
> einen Interrupt zuordnen, zumindesst nicht auf dem AVR.

Du kannst den Portpins und dem AD-Wandler Interrupts zuordnen. Es gibt 
relativ viel, dem Du Interrupts zuordnen kannst.

Ganz abstrakt gehen auch die Timer-Interrupts um evtl. andere Ereignisse 
zu erkennen.

Es ist an sich schon richtig, dass ein uC nicht grundsätzlich direkt auf 
alle denkbaren asynchron stattfindenden Ereignisse reagieren kann.

Konkret aber sind dafür, - ich meine in den allermeisten Fällen -, 
gewisse Zeitvorgaben oder kausale Zusammenhänge vorhanden. Aus denen 
kann man ein entsprechendes Eingangssignal für die State-Machine 
generieren.

Ich will nicht strikt behaupten, dass das immer und immer ohne 
Kopfstände möglich ist, allerdings stelle ich doch mal in den Raum, dass 
es eher unwahrscheinlich ist, dass das nicht geht.

Es mag aber sein, dass Du einen konkreten Punkt nennen kannst. Dann tue 
das bitte.

---

Ich will einräumen, dass die Frage für den imaginären Fall eines 
Ereignisses auf den der uC nicht mit einem Interrupt reagieren kann, 
generell interessant ist.
Dennoch meine ich, es ist keine gute Idee, sich darauf zu verlassen, 
dass solch ein Ereignis genau dann noch erkannt werden kann, wenn der 
uC im Maximaltempo die Kriterien dafür auswertet.

In einem bestimmten Sinn ist das "unkontrolliertes" Verhalten. Ein wenig 
so, als wenn Du, falls Du gerade nichts anderes zu tun hast, immer 
wieder die Kühlschranktür aufmachst um zu sehen ob Du noch was einkaufen 
musst.
In vielen Fällen würdest Du die Tür aufmachen, nachsehen ob was fehlt, 
die Tür wieder zumachen und sie sofort wieder aufmachen und nachsehen.
Du verschwendest Deine Zeit damit immer wieder das selbe festzustellen.

Bei einem uC liegt die Sache etwas anders, denn da kann schon mal was 
ohne Dein Wissen aus dem Kühlschrank verschwinden.

ABER: im allgemeinen gilt, dass Ereignisse mit einer gewissen 
beschränkten Häufigkeit pro Zeiteinheit geschehen. Und das ist für einen 
uC, der ja auch in der Zeit agiert, das wesentliche Kriterium.

Falls also das Ereignis mit einer so hohen Rate geschieht, dass der uC 
gerade noch mit Deiner Methode mitkommt, so ist er eher 
unterdimensioniert, als das die Methode richtig ist.

"Sauber" und korrekt ist vielmehr, dass unter allen (notwendigen) 
Umständen, egal welche Abfolge von Zuständen durchlaufen wird, der uC 
dennoch rechtzeitig einen Punkt im Programm erreicht, an dem er das 
Kriterium auswerten kann. Aber nicht so schnell wie möglich.

Der Unterschied ist tatsächlich zum Teil philosophischer Natur als 
allein ein zwingend Technischer.
Das liegt daran, dass man in der Programmierung von gewissen Abfolgen 
und Häufigkeiten ausgeht, diese kalkuliert und ein Programm schreibt, 
dass diesen Abfolgen "aktiv" folgt.
Hingegen schreibt man ein Programm nicht so, dass es so schnell wie 
möglich auf alle "Eventualitäten" passiv reagieren kann. Das würde ja 
nur dann notwendig sein, falls man sich über gewisse Umstände nicht 
vollständig im klaren ist. So einen Zustand vermeidet man aber, wie der 
sprichwörtliche Teufel das Weihwasser.
Man bevorzugt und setzt viel Energie darein, eben genau und im Detail, 
zeitlich und logisch den Gesamtzusammenhang aller Umstände zu kennen.

Du wirst evtl. einwenden, dass Du ja nicht ausdrücklich darauf abzielst, 
auf irgendwelche Eventualitäten zu reagieren, die Du nicht einkalkuliert 
hast. Du magst einwenden, dass dies eben nur die textuell "einfachste" 
Methode ist, irgendwie so schnell wie möglich auf den "Rest" der 
Ereignisse zu reagieren.
Es ist aber egal, ob Du einfach nur in Kauf nimmst, dass Du keine 
Kontrolle hast oder ob Du absichtlich darauf verzichtest.
Der springende Punkt ist, dass das Du die Kontrolle aufgibst.

Ich hoffe das hilft Dir etwas weiter.
Gast #5640082
Lesenswert?

Manche Leute müsste man mal zur Windows 3.1 zurück schicken, damit sie 
lernen, mehrere Aufgaben quasi-parallel abzuarbeiten. Und zwar ohne ein 
Betriebssystem, dass die Threads automatisch verwaltet und ohne 
Interrupts.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren