Forum: PC-Programmierung State Machine in Ansi C


von Progger (Gast)


Lesenswert?

Hi,

ich möchte für ein Softwareprojekt eine State Machine in Ansi C 
programmieren. Wie realisiert man dies in der Programmiersprache Ansi C?
Ich suche dazu ein kleines anschauliches Beispiel. Kann mir jemand sagen 
wo ich dazu Unterlagen im Netz finden kann?

von StinkyWinky (Gast)


Lesenswert?

Hier in den Foren gibt es etliche Beiträge.
Im Web kenne ich den:
http://www.embedded.com/columns/technicalinsights/10700829?_requestid=42049

von Progger (Gast)


Lesenswert?

Danke für den Link. Hast du eine deutsche Quelle dazu?

von Andreas W. (Gast)


Lesenswert?


von Progger (Gast)


Lesenswert?

Hmmm... aus diesem Beitrag kann ich nichts entnehmen, was ich für mich 
brauchen könnte. Hier wird der Funktionszeiger erwähnt. Kann mir 
trotzdem nicht vorstellen wie das funktionieren soll. Hat dazu jemand 
ein kleines Beispiel mit Funktionszeiger?

von Karl H. (kbuchegg)


Lesenswert?

Progger schrieb:
> Hmmm... aus diesem Beitrag kann ich nichts entnehmen, was ich für mich
> brauchen könnte. Hier wird der Funktionszeiger erwähnt. Kann mir
> trotzdem nicht vorstellen wie das funktionieren soll. Hat dazu jemand
> ein kleines Beispiel mit Funktionszeiger?

Für deine erste Statemachine würde ich dir keine Funktionszeiger 
empfehlen, solange du noch danach fragen musst. Du hast sonst zuviele 
Baustellen.

Nimm die klassische Form:

* Es gibt States
* Jeder State hat eine Nummer (die sofort mit einem #define einen
  symbolischen Namen bekommt)
* Der momentan aktive State ist in einer (globalen) Variablen
  gespeichert
* Wird aufgrund irgendwelcher Bedingungen der State gewechselt, dann
  ist das einfach eine Zuweisung an diese Variable
* Die Statemachine wird periodisch aufgerufen und macht ihr Ding
  je nachdem in welchem State (=Zustand) sie ist
* Die eigentliche Statemaschine ist ein
1
    switch( state ) {
2
3
      case ZUSTAND_1:
4
         // mach irgendwas
5
         break;
6
7
      case ZUSTAND_2:
8
         // mach was anderes
9
         break;
10
11
      case ZUSTAND_3:
12
         // und jetzt ganz was anderes
13
14
         // der nächste Zustand ist ZUSTAND_2
15
         state = ZUSTAND_2;
16
         break;
17
18
      case ZUSTAND_4:
19
         // je nachdem
20
         if( .... )
21
           state = ZUSTAND_1;
22
         else
23
           state = ZUSTAND_2;
24
         brea;
25
26
       ....
27
28
       default:
29
         // ungültiger Zustand!
30
         // Sollte eigentlich nie auftreten
31
    }

Deine erste Statemachine wird noch nicht allzuviele Zustände haben, 
sodass das organisatorisch noch gut zu handhaben ist.
Die Hauptarbeit beim Bau einer Statemachine ist ja auch nicht die 
Programmierung, sondern das Identifizieren welche States es geben soll 
und wie und warum die Statemachine von einem Zustand in einen anderen 
Zustand überwechselt und welche Aktionen dabei ausgeführt werden sollen.

von Klaus W. (mfgkw)


Lesenswert?

zu Funktionszeigern:
das nächste C-Buch?

von Klaus W. (mfgkw)


Lesenswert?

Der Vollständigkeit halber eine Endlosschleife mit Funktionszeigern:
1
#include <stdio.h>
2
#include <string.h>
3
#include <stdlib.h>
4
5
6
7
typedef enum
8
{
9
  eMotivation_heiss,
10
  eMotivation_gehtso,
11
  eMotivation_lau,
12
  eMotivation_unterirdisch,
13
  eMotivation_LAST
14
} eMotivation_t;
15
16
17
// manuelle Variante:
18
19
void automat_manuell()
20
{
21
  // Zustandsvariable:
22
  eMotivation_t    MotivationDesFragestellersAnhandSeinerFrage = eMotivation_gehtso;
23
24
  while( 69>42 )
25
  {
26
    switch( MotivationDesFragestellersAnhandSeinerFrage )
27
    {
28
      case eMotivation_heiss:
29
        printf( "machEineTolleAntwort...\n" );
30
        break;
31
32
      case eMotivation_gehtso:
33
        printf( "machEineSchnelleAntwort\n" );
34
        break;
35
36
      case eMotivation_lau:
37
        printf( "machEinePampigeAntwort\n" );
38
        break;
39
40
      case eMotivation_unterirdisch:
41
        printf( "gehe Bier holen\n" );
42
        break;
43
44
      default :
45
        fprintf( stderr, "oioioi, das geht ja gar nicht\n" );
46
        abort();
47
        break;
48
    }
49
  }
50
}
51
52
53
// Variante mit Sprungtabellen (Funktionszeiger):
54
55
// zuerst die Reaktion auf die je einen Zustand jeweils als eine Funktion:
56
void machEineTolleAntwort()
57
{
58
  printf( "machEineTolleAntwort...\n" );
59
}
60
61
void machEineSchnelleAntwort()
62
{
63
  printf( "machEineSchnelleAntwort\n" );
64
}
65
66
void machEinePampigeAntwort()
67
{
68
  printf( "machEinePampigeAntwort\n" );
69
}
70
71
void saufDirEinenAn()
72
{
73
  printf( "gehe Bier holen\n" );
74
}
75
76
77
void automat_ganztoll()
78
{
79
  // Tabelle mit Zeigern auf Funktionen, die jeweils etwas tun:
80
  void  (*sprungtabelle[eMotivation_LAST])(void) =
81
    {
82
      [eMotivation_heiss] = machEineTolleAntwort,
83
      [eMotivation_gehtso] = machEineSchnelleAntwort,
84
      [eMotivation_lau] = machEinePampigeAntwort,
85
      [eMotivation_unterirdisch] = saufDirEinenAn,
86
    };
87
88
  // Zustandsvariable:
89
  eMotivation_t    MotivationDesFragestellersAnhandSeinerFrage = eMotivation_gehtso;
90
91
  while( "Merkel ist doof" )
92
  {
93
    if( MotivationDesFragestellersAnhandSeinerFrage<eMotivation_LAST )
94
    {
95
      // je nach Zustand die richtige Funktion aufrufen:
96
      sprungtabelle[MotivationDesFragestellersAnhandSeinerFrage]();
97
    }
98
    else
99
    {
100
      // sollte nie vorkommen:
101
      fprintf( stderr, "oioioi, das geht ja gar nicht\n" );
102
      abort();
103
    }
104
  }
105
106
}
107
108
109
int main()
110
{
111
  automat_manuell();
112
  //automat_ganztoll();
113
  return 0;
114
}

von Karl H. (kbuchegg)


Lesenswert?

LOL
1
  while( "Merkel ist doof" )

You made my day!

von Andreas W. (Gast)


Lesenswert?

Hast du dir mal Wikipedia durchgelesen?
http://de.wikipedia.org/wiki/Endlicher_Automat

ist ganz gut beschrieben.

Ganz einfach ist das Beispiel mit der Tür.
Nach tür-öffnen kommt tür-schließen.
tür-schließen bei Tür-zu ist ja irgendwie sinnlos.

von G. O. (aminox86)


Lesenswert?

Hier noch eine weitere Methode einer (stark 
vereinfachten)tabellengesteuerten Zustandsmaschine. Die Grundidee stammt 
aus einem Artikel, der ca 1990 im C-Users Journal veröffentlicht wurde.
Das Bemerkenswerte hier ist, dass die Wahrheitstabelle, wie man sie etwa 
auf dem Papier erstellen und optimieren würde, direkt die Maschinentafel 
übernommen wird.
Auf des Grund des strukturierten Datentyps sind beliebige Erweiterungen, 
Verschachtelungen, Abwandlungen usw. der Zustandsmaschine möglich.

Im vorliegenden Programm wird Tastenfolge 'quertz' oder 'querty' 
abgefragt.

1
#include <stdio.h>
2
#include <conio.h>
3
4
#define FALSE   0
5
#define TRUE    1
6
#define TASTE  unsigned char
7
8
typedef struct { int okzustand,                    /*nächster gut-zustand*/
9
                     nokzustand;                   /*nächster schlecht-zustand*/
10
                void (*okfun)(TASTE t),            /*gut-funktion*/
11
                     (*nokfun)(TASTE t);           /*fehler-funktion*/
12
                int (*test_fun)(TASTE t, char c);  /*test-funktion*/
13
                char test;}FSM;                    /*testwert*/
14
15
int compare(TASTE t, char c);
16
void print_ok(TASTE t), print_nok(TASTE t);
17
void print_d(TASTE t), print_e(TASTE t);
18
19
FSM maschine[]={
20
/*0*/  {1, 0, print_ok, print_nok, compare, 'q'},
21
/*1*/  {2, 1, print_ok, print_nok, compare, 'u'},
22
/*2*/  {3, 2, print_ok, print_nok, compare, 'e'},
23
/*3*/  {4, 3, print_ok, print_nok, compare, 'r'},
24
/*4*/  {5, 4, print_ok, print_nok, compare, 't'},
25
/*5*/  {0, 6, print_d, print_nok, compare, 'z'},
26
/*6*/  {0, 6, print_e, print_nok, compare, 'y'},};
27
28
TASTE get_taste(void);
29
30
void main(void)
31
{  int zustand=0;
32
  TASTE key;
33
34
  clrscr();
35
  puts("programm mit taste 'x' beenden");
36
  do
37
  {  key=get_taste();
38
    if((*maschine[zustand].test_fun)(key, maschine[zustand].test))
39
    {   (*maschine[zustand].okfun)(key);
40
      zustand=maschine[zustand].okzustand;}
41
    else
42
    {   (*maschine[zustand].nokfun)(key);
43
      zustand=maschine[zustand].nokzustand;}}
44
  while(key!='x');
45
}
46
47
int compare(TASTE t, char test )
48
{
49
  return(t==test?TRUE:FALSE);
50
}
51
52
void print_ok(TASTE key)
53
{
54
  printf("'%c' %s", key, "erwartete taste betätigt\n");
55
}
56
57
void print_nok(TASTE key)
58
{
59
  printf("'%c' %s", key, "diese taste wurde nicht gesucht\n");
60
}
61
62
void print_d(TASTE key)
63
{
64
  printf("'%c' %s", key, "deutsche tastenfolge\n");
65
}
66
67
void print_e(TASTE key)
68
{
69
  printf("'%c' %s", key, "englische tastenfolge\n");
70
}
71
72
TASTE get_taste(void)
73
{  TASTE key;
74
75
  while(!kbhit());
76
  return(key=getch());
77
}
mfg

von Progger (Gast)


Lesenswert?

Ich hab nun mal den Code vom obigen Beitrag "Datum: 25.06.2009 15:14 " 
in ein neus Visual Studio 6.0 C++ Projekt eingefügt. Der Compiler spuckt 
mir immer folgende Fehlermeldungen aus:
--------------------Konfiguration: FSM - Win32 Debug--------------------
Kompilierung läuft...
fsm.cpp
Linker-Vorgang läuft...
fsm.obj : error LNK2001: Nichtaufgeloestes externes Symbol "void __cdecl 
print_e(unsigned char)" (?print_e@@YAXE@Z)
fsm.obj : error LNK2001: Nichtaufgeloestes externes Symbol "void __cdecl 
print_d(unsigned char)" (?print_d@@YAXE@Z)
fsm.obj : error LNK2001: Nichtaufgeloestes externes Symbol "int __cdecl 
compare(unsigned char,char)" (?compare@@YAHED@Z)
fsm.obj : error LNK2001: Nichtaufgeloestes externes Symbol "void __cdecl 
print_nok(unsigned char)" (?print_nok@@YAXE@Z)
fsm.obj : error LNK2001: Nichtaufgeloestes externes Symbol "void __cdecl 
print_ok(unsigned char)" (?print_ok@@YAXE@Z)
Debug/FSM.exe : fatal error LNK1120: 5 unaufgeloeste externe Verweise

Hier ist mein C-Code:
1
#include "fsm.h"
2
#include <stdio.h>
3
#include <conio.h>
4
#include <windows.h>
5
6
#define FALSE   0
7
#define TRUE    1
8
#define TASTE  unsigned char
9
10
11
typedef struct 
12
{ 
13
int okzustand;        /*nächster gut-zustand*/
14
int nokzustand;        /*nächster schlecht-zustand*/
15
void (*okfun)(TASTE t);      /*gut-funktion*/
16
void (*nokfun)(TASTE t);    /*fehler-funktion*/
17
int (*test_fun)(TASTE t, char c);  /*test-funktion*/
18
char test;
19
}FSM;          /*testwert*/
20
21
22
int compare(TASTE t, char c);
23
void print_ok(TASTE t), print_nok(TASTE t);
24
void print_d(TASTE t), print_e(TASTE t);
25
26
FSM maschine[]={
27
/*0*/  {1, 0, print_ok, print_nok, compare, 'q'},
28
/*1*/  {2, 1, print_ok, print_nok, compare, 'u'},
29
/*2*/  {3, 2, print_ok, print_nok, compare, 'e'},
30
/*3*/  {4, 3, print_ok, print_nok, compare, 'r'},
31
/*4*/  {5, 4, print_ok, print_nok, compare, 't'},
32
/*5*/  {0, 6, print_d, print_nok, compare, 'z'},
33
/*6*/  {0, 6, print_e, print_nok, compare, 'y'}};
34
35
36
TASTE get_taste(void);
37
38
39
int main(void)
40
{
41
42
43
  return 0;
44
}

von Progger (Gast)


Lesenswert?

Für den Einstieg bräuchte ich ein lauffähiges Beispielprogramm in AnsiC.
Kennt jemand da eine gute Quelle?

von Daniel (root) (Gast)


Lesenswert?

Für den Einstieg wurden genüg Beispiele gezeigt.
Du brauchst keinen Quellcode zum Copy/Reinpasten ;)
Abtippen ist angesagt. Glaub mir :)

Was FSM angeht, so gibt es viele Möglichkeiten denkbar.

a) Eine uC timer interrupt routine, die regelmässig aufwacht,
ihre inputs (Pins zB) checkt, dann neuen Zustand berechnet und
neue Ausgaben (Pins zB beschreibt). => Input kommt aus Jetzt!
=> reaktives System

b) Compiler kann als FSM betrachtet werden. Tokens, also Input,
wird Zeichen oder Tokenweise aus einer bereits stehenden Quelle(Datei)
geholt. => Input kommt Stückweise, aber an sich aus einem Guss.

c) wenn Software auf mehrere Threads verteilt ist, so ist die
Situation ähnlich Punkt a). Inputs stammen aber aus anderen Thread.
Der Thread mit FSM kann zb blockierd warten. => siehe zB Kernel.

Falls jemand noch etwas einfällt bitte eintragen.
Ich denke das meiste ist damit erschlagen ;)

von G. O. (aminox86)


Lesenswert?

@progger,
der Linker beschwert sich, weil er einige Objekte in deinem
(Programm)-Fragment nicht finden kann. Kann ich ihm nachfühlen, denn der 
Quelltext, so wie in deinem Beitrag abgebildet, ist nicht vollständig 
übernommen bzw verstümmelt.
Bitte daran denken, dass das Beispiel unter Windows als 
'Konsole-Anwendung'(Dos läßt grüßen) laufen sollte.
mfg

von Klaus W. (mfgkw)


Lesenswert?

Was soll man da noch machen?

Das obige Beispiel von G.O.
(Beitrag "Re: State Machine in Ansi C")
IST lauffähig unter VC++ 6.0 (zumindest wenn man den Aufruf von
clrscr() entfernt, ein echter "Progger" schafft das, und sogar
ich als Maschinenbauer habe es hinbekommen).
Auch wenn man es genau so nicht übersetzt bekommt, könnte man
mit etwas gutem Willen daran sehen, wie ein Automat funktionieren
kann - deiner wird sowieso etwas anderes machen müssen als Tasten
abzufragen.


Progger schrieb:
> Für den Einstieg bräuchte ich ein lauffähiges Beispielprogramm in AnsiC.

ANSI-C und Visual C++ 6.0 sind zwei verschiedene Dinge.
Das obige Beispiel ist wie gesagt lauffähig (vom clrscr() abgesehen).
Wenn du es nicht schaffst, das zu übernehmen, ohne es zu verhunzen,
kann ich dir ein günstiges Angebot machen, vor Ort den Quelltext
in deinen Editor zu kopieren. Spesen gehen extra.

> Kennt jemand da eine gute Quelle?

Ja.

BTW:
Wofür steht eigentlich der schöne Name "Progger"?

von Karl H. (kbuchegg)


Lesenswert?

Progger schrieb:
> Ich hab nun mal den Code vom obigen Beitrag "Datum: 25.06.2009 15:14 "
> in ein neus Visual Studio 6.0 C++ Projekt eingefügt. Der Compiler spuckt
> mir immer folgende Fehlermeldungen aus:

Die sollte mann dann auch lesen und wenigstens versuchen zu verstehen. 
So schwer ist das nicht.
Der Linker (nicht der Compiler) beschwert sich, dass er print_e nicht 
finden kann. Also vergleichst du einfach mal wo in deinem Programm 
print_e vorkommt und wo in deiner Vorlage print_e vorkommt.

Ich habe nichts gegen copy&paste Programmierung. Ab und an braucht man 
einfach eine Vorlage, die man studieren kann. Aber dann sollte man 
wenigstens in der Lage sein, die Vorlage komplett zu kopieren!

Und ob du bei derartigen Problemen eine Statemachine, die auf 
Funktionspointern beruht in Eigenregie zum laufen kriegst, da hab ich so 
meine Zweifel.

von Progger (Gast)


Lesenswert?

Sorry wenn ich nochmals nerve. Ich verstehe nicht woran dies liegen 
könnte.
1
FSM maschine[]={
2
/*0*/  {1, 0, print_ok, print_nok, compare, 'q'},
3
/*1*/  {2, 1, print_ok, print_nok, compare, 'u'},
4
/*2*/  {3, 2, print_ok, print_nok, compare, 'e'},
5
/*3*/  {4, 3, print_ok, print_nok, compare, 'r'},
6
/*4*/  {5, 4, print_ok, print_nok, compare, 't'},
7
/*5*/  {0, 6, print_d, print_nok, compare, 'z'},
8
/*6*/  {0, 6, print_e, print_nok, compare, 'y'}};

von Progger (Gast)


Lesenswert?

Ich hab meinen Fehler gefunden. Ich danke euch viel mals.

von G. O. (aminox86)


Lesenswert?

@Progger
Na dann funktionierts ja wohl.

@Karl heinz Buchegger
Nicht so streng, jeder hat 'mal angefangen.

An dieser Stelle soll noch der erwähnte Artikel nachgereicht werden:
Alan Cline: Build Applications Faster with State Transition Automatons, 
erschienen Dezember 1992 ab Seite 93 im 'C Users Journal'. Der Quellcode 
war oder ist im Rahmen der Wal-Nut-Creek Cd-Rom im Internet verfügbar.

Ich finde Clines Konzept einfach genial. Von allen Methoden 
Zustandsmaschinen zu konstruieren, ist diese die einfachste und 
flexibelste, die ich kenne. Mit ihr lassen sich besonders gut Tokeniser, 
Parser, Protokollstack(zB Carson: PPP Design, Implementation and 
Debugging (!mehrdimensionale Maschinentafel!)) usw. aufbauen. Ich 
verwende sie hauptsächlich für die Programmierung umfangreicher 
Menüstrukturen (Wettbewerbsbeitrag 2008: Tinykon).
mfg

von Peter Mueller (Gast)


Lesenswert?

Hallo,

schau mal hier: www.sinelabore.com

Gruß,
Peter Mueller

von Vaclav Cechticky (Gast)


Lesenswert?

Hi,

Have a look here: http://code.google.com/p/fwprofile/

It's an open source version (GNU GPLv3) of the state machine implemented 
in C. The concept and implementation is well-suited for use in 
mission-critical applications. There are deployments in industrial 
applications.

Regards
Vaclav

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

We're not really keen on digging up threads that have been dead or 
dormant for a long time. You've just managed to do so with a thread 
three years old.
Do you really think that the participants were sitting in front of their 
computers for three years, waiting for your contribution to arrive?

So, please refrain from attempts to rise the un-dead.

von Anton (Gast)


Lesenswert?

Yes, someone is still redanig it  The point is, though, that currently 
we have 2 different  editions . One with full Unicode capabilities and 
one with ANSI only. ANSI is also always slower, because all the ANSI 
APIs on Windows NT and later convert the string to Unicode anyway (well, 
as much as a mapping is possible). 64bit and ANSI is pointless. But 
should we drop ANSI ultimately (not necessarily the option to get back 
to it, but cease to develop that branch, so to speak   or should we 
continue ANSI +  Unicode 32bit + Unicode 64bit.BTW: The performance 
impact on WOW64 programs is minimal. Has to do with how the stuff is 
passed to the kernel. And depending on what you consider low-level, I 
cannot really see what information is hidden from WDS that would be 
interesting for this kind of application

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Anton schrieb:
> (well, as much as a mapping is possible)

Are you really sure that you know what you're writing about?

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Progger schrieb:
> Für den Einstieg bräuchte ich ein lauffähiges Beispielprogramm in AnsiC.

Das ist schon mal kein ANSI-C

#include <conio.h>
#include <windows.h>

> Kennt jemand da eine gute Quelle?

Die besten Quellen die ich kenne sind meine eigenen :-))

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Noch mal ein DEUTLICHER HINWEIS:

Dieser Thread ist seit drei Jahren verwaist.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.