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?
Hier in den Foren gibt es etliche Beiträge. Im Web kenne ich den: http://www.embedded.com/columns/technicalinsights/10700829?_requestid=42049
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?
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.
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 | }
|
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.
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
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 | }
|
Für den Einstieg bräuchte ich ein lauffähiges Beispielprogramm in AnsiC. Kennt jemand da eine gute Quelle?
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 ;)
@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
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"?
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.
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'}}; |
@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
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
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.
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
Anton schrieb: > (well, as much as a mapping is possible) Are you really sure that you know what you're writing about?
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 :-))
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.