Je crée un petit projet en C et C ++ mixtes. Je construis une petite machine d'état au cœur d'un de mes fils de travail.
Je me demandais si vous, gourous du SO, partageriez vos techniques de conception de machines à états.
REMARQUE: je recherche principalement des techniques d'implémentation éprouvées.
MISE À JOUR: Sur la base de toutes les grandes contributions recueillies sur SO, je me suis installé sur cette architecture:
c++
c
architecture
state-machine
jldupont
la source
la source
Réponses:
Les machines à états que j'ai conçues auparavant (C, pas C ++) se résument toutes à un
struct
tableau et à une boucle. La structure se compose essentiellement d'un état et d'un événement (pour la recherche) et d'une fonction qui renvoie le nouvel état, quelque chose comme:Ensuite, vous définissez vos états et événements avec des définitions simples (
ANY
celles-ci sont des marqueurs spéciaux, voir ci-dessous):Ensuite, vous définissez toutes les fonctions appelées par les transitions:
Toutes ces fonctions sont écrites pour ne prendre aucune variable et retourner le nouvel état de la machine d'état. Dans cet exemple, des variables globales sont utilisées pour transmettre toute information dans les fonctions d'état lorsque cela est nécessaire.
L'utilisation de globaux n'est pas aussi mauvaise qu'il y paraît car le FSM est généralement enfermé dans une seule unité de compilation et toutes les variables sont statiques à cette unité (c'est pourquoi j'ai utilisé des guillemets autour de "global" ci-dessus - ils sont plus partagés dans le FSM, que vraiment mondiale). Comme pour tous les pays du monde, cela nécessite des soins.
Le tableau de transitions définit ensuite toutes les transitions possibles et les fonctions qui sont appelées pour ces transitions (y compris la dernière fourre-tout):
Cela signifie: si vous êtes dans l'
ST_INIT
état et que vous recevez l'EV_KEYPRESS
événement, appelezGotKey
.Le fonctionnement du FSM devient alors une boucle relativement simple:
Comme mentionné ci-dessus, notez l'utilisation de caractères
ST_ANY
génériques, permettant à un événement d'appeler une fonction quel que soit l'état actuel.EV_ANY
fonctionne également de façon similaire, permettant à tout événement à un état spécifique d'appeler une fonction.Il peut également garantir que si vous atteignez la fin du tableau de transitions, vous obtenez une erreur indiquant que votre FSM n'a pas été construit correctement (en utilisant la
ST_ANY/EV_ANY
combinaison.J'ai utilisé un code similaire pour cela sur de nombreux projets de communication, comme une mise en œuvre précoce de piles de communications et de protocoles pour les systèmes embarqués. Le gros avantage était sa simplicité et sa relative facilité à changer le tableau de transitions.
Je ne doute pas qu'il y aura des abstractions de niveau supérieur qui peuvent être plus adaptées de nos jours, mais je pense qu'elles se résumeront toutes à ce même type de structure.
Et, comme l'
ldog
indique un commentaire, vous pouvez éviter les globaux en passant un pointeur de structure à toutes les fonctions (et en l'utilisant dans la boucle d'événement). Cela permettra à plusieurs machines d'état de fonctionner côte à côte sans interférence.Créez simplement un type de structure qui contient les données spécifiques à la machine (état au strict minimum) et utilisez-les à la place des globales.
La raison pour laquelle je l'ai rarement fait est simplement parce que la plupart des machines d'état que j'ai écrites sont de type singleton (one-off, at-process-start, lecture de fichier de configuration par exemple), n'ayant pas besoin d'exécuter plus d'une instance . Mais il a de la valeur si vous devez en exécuter plusieurs.
la source
int (*fn)(void*);
où sevoid*
trouve le pointeur vers les données que chaque fonction d'état prend en paramètre. Ensuite, les fonctions d'état peuvent soit utiliser les données, soit les ignorer.Les autres réponses sont bonnes, mais une implémentation très "légère" que j'ai utilisée lorsque la machine d'état est très simple ressemble à:
J'utiliserais cela lorsque la machine à états est suffisamment simple pour que l'approche du pointeur de fonction et de la table de transition d'état soit exagérée. Ceci est souvent utile pour l'analyse syntaxique caractère par caractère ou mot par mot.
la source
Excusez-moi d'avoir enfreint toutes les règles de l'informatique, mais une machine d'état est l'un des rares endroits (je ne peux en compter que deux à la main) où une
goto
déclaration est non seulement plus efficace, mais rend également votre code plus propre et plus facile à lire. Étant donné que lesgoto
instructions sont basées sur des étiquettes, vous pouvez nommer vos états au lieu d'avoir à suivre un désordre de nombres ou à utiliser une énumération. Cela rend également le code beaucoup plus propre, car vous n'avez pas besoin de toute la cruauté supplémentaire des pointeurs de fonction ou des énormes instructions de commutateur et des boucles while. Ai-je mentionné que c'est aussi plus efficace?Voici à quoi pourrait ressembler une machine d'état:
Vous avez l'idée générale. Le fait est que vous pouvez implémenter la machine d'état d'une manière efficace et qui est relativement facile à lire et qui crie au lecteur qu'il regarde une machine d'état. Notez que si vous utilisez des
goto
instructions, vous devez toujours être prudent car il est très facile de se tirer une balle dans le pied.la source
Vous pourriez envisager le compilateur State Machine http://smc.sourceforge.net/
Ce splendide utilitaire open source accepte la description d'une machine à états dans un langage simple et la compile dans n'importe laquelle d'une douzaine de langages - y compris C et C ++. L'utilitaire lui-même est écrit en Java et peut être inclus dans le cadre d'une build.
La raison de cela, plutôt que de coder manuellement à l'aide du modèle d'état GoF ou de toute autre approche, est qu'une fois que votre machine d'état est exprimée sous forme de code, la structure sous-jacente a tendance à disparaître sous le poids du passe-partout qui doit être généré pour le prendre en charge. L'utilisation de cette approche vous offre une excellente séparation des préoccupations et vous gardez la structure de votre machine d'état «visible». Le code généré automatiquement va dans des modules que vous n'avez pas besoin de toucher, de sorte que vous pouvez revenir en arrière et jouer avec la structure de la machine d'état sans impact sur le code de support que vous avez écrit.
Désolé, je suis trop enthousiaste, et sans doute rebute tout le monde. Mais c'est un utilitaire de premier ordre, et bien documenté aussi.
la source
N'oubliez pas de vérifier le travail de Miro Samek (blog State Space , site Web State Machines & Tools ), dont les articles du C / C ++ Users Journal étaient excellents.
Le site Web contient une implémentation complète (C / C ++) en licence open source et commerciale d'une infrastructure de machine à états (QP Framework) , d'un gestionnaire d'événements (QEP) , d'un outil de modélisation de base (QM) et d'un outil de traçage (QSpy) qui permettent de dessiner des machines à états, de créer du code et de les déboguer.
Le livre contient une explication détaillée sur le quoi / pourquoi de la mise en œuvre et comment l'utiliser et est également un excellent matériel pour acquérir une compréhension des principes fondamentaux des machines à états hiérarchiques et finis.
Le site Web contient également des liens vers plusieurs packages de support de carte pour l'utilisation du logiciel avec des plates-formes intégrées.
la source
J'ai fait quelque chose de similaire à ce que décrit paxdiablo, mais au lieu d'un tableau de transitions état / événement, j'ai mis en place un tableau bidimensionnel de pointeurs de fonction, avec la valeur d'événement comme index d'un axe et la valeur d'état actuelle comme L'autre. Ensuite, j'appelle
state = state_table[event][state](params)
et la bonne chose se produit. Les cellules représentant des combinaisons état / événement invalides obtiennent un pointeur sur une fonction qui le dit, bien sûr.Évidemment, cela ne fonctionne que si les valeurs d'état et d'événement sont toutes les deux des plages contiguës et commencent à 0 ou assez près.
la source
#define STATE_LIST() \STATE_LIST_ENTRY(state1)\STATE_LIST_ENTRY(state2)\...
(une nouvelle ligne implicite après chaque\
) où vous (re) définissez la macro d'entrée lorsque vous utilisez la macro STATE_LIST. Exemple - faire un tableau de noms d'état:#define STATE_LIST_ENTRY(s) #s , \n const char *state_names[] = { STATE_LIST() };\n #undef STATE_LIST_ENTRY
. Certains travaux doivent être effectués en premier, mais c'est extrêmement puissant. Ajouter un nouvel état -> garanti aucun échec.Un très joli "framework" de machine d'état C ++ basé sur un modèle est donné par Stefan Heinzmann dans son article .
Puisqu'il n'y a pas de lien vers un téléchargement de code complet dans l'article, j'ai pris la liberté de coller le code dans un projet et de le vérifier. Le matériel ci-dessous est testé et comprend les quelques pièces manquantes mineures mais à peu près évidentes.
L'innovation majeure ici est que le compilateur génère du code très efficace. Les actions d'entrée / sortie vides sont gratuites. Les actions d'entrée / sortie non vides sont intégrées. Le compilateur vérifie également l'exhaustivité du diagramme d'états. Les actions manquantes génèrent des erreurs de liaison. La seule chose qui n'est pas capturée, ce sont les disparus
Top::init
.C'est une très belle alternative à l'implémentation de Miro Samek, si vous pouvez vivre sans ce qui manque - c'est loin d'être une implémentation UML Statechart complète, bien qu'elle implémente correctement la sémantique UML, alors que le code de Samek par conception ne gère pas la sortie / transition / actions de saisie dans l'ordre correct.
Si ce code fonctionne pour ce que vous devez faire et que vous avez un compilateur C ++ décent pour votre système, il fonctionnera probablement mieux que l'implémentation C / C ++ de Miro. Le compilateur génère pour vous une implémentation de la machine à états de transition O (1) aplatie. Si l'audit des sorties d'assemblage confirme que les optimisations fonctionnent comme vous le souhaitez, vous vous rapprochez des performances théoriques. La meilleure partie: c'est un code relativement petit et facile à comprendre.
Le code de test suit.
la source
La technique que j'aime pour les machines à états (au moins celles pour le contrôle de programme) est d'utiliser des pointeurs de fonction. Chaque état est représenté par une fonction différente. La fonction prend un symbole d'entrée et renvoie le pointeur de fonction pour l'état suivant. Le moniteur de boucle de répartition centrale prend l'entrée suivante, la transmet à l'état actuel et traite le résultat.
Le fait de taper dessus devient un peu étrange, car C n'a pas de moyen d'indiquer les types de pointeurs de fonction qui se retournent, donc les fonctions d'état reviennent
void*
. Mais vous pouvez faire quelque chose comme ça:Ensuite, vos fonctions d'état individuelles peuvent activer leur entrée pour traiter et renvoyer la valeur appropriée.
la source
Cas le plus simple
Points: l'état est privé, non seulement pour l'unité de compilation mais aussi pour le gestionnaire d'événements. Les cas spéciaux peuvent être traités séparément de l'interrupteur principal en utilisant la construction jugée nécessaire.
Cas plus complexe
Lorsque le commutateur est plus grand que deux écrans pleins, divisez-le en fonctions qui gèrent chaque état, en utilisant une table d'états pour rechercher directement la fonction. L'État est toujours privé du gestionnaire d'événements. Les fonctions du gestionnaire d'état renvoient l'état suivant. Si nécessaire, certains événements peuvent toujours recevoir un traitement spécial dans le gestionnaire d'événements principal. J'aime lancer des pseudo-événements pour l'entrée et la sortie d'état et peut-être le démarrage de la machine d'état:
Je ne sais pas si j'ai cloué la syntaxe, en particulier en ce qui concerne le tableau des pointeurs de fonction. Je n'ai exécuté rien de tout cela via un compilateur. Après examen, j'ai remarqué que j'avais oublié de supprimer explicitement l'état suivant lors de la gestion des pseudo-événements (la parenthèse (void) avant l'appel à state_handler ()). C'est quelque chose que j'aime faire même si les compilateurs acceptent l'omission en silence. Il indique aux lecteurs du code que "oui, je voulais en effet appeler la fonction sans utiliser la valeur de retour", et cela peut empêcher les outils d'analyse statique de l'avertir. C'est peut-être idiosyncratique parce que je ne me souviens pas avoir vu quelqu'un d'autre faire cela.
Points: ajouter un tout petit peu de complexité (vérifier si l'état suivant est différent de l'état actuel), peut éviter le code dupliqué ailleurs, car les fonctions du gestionnaire d'état peuvent profiter des pseudo-événements qui se produisent lorsqu'un état est entré et quitté. N'oubliez pas que l'état ne peut pas changer lors de la gestion des pseudo-événements, car le résultat du gestionnaire d'état est ignoré après ces événements. Vous pouvez bien sûr choisir de modifier le comportement.
Un gestionnaire d'état ressemblerait à ceci:
Plus de complexité
Lorsque l'unité de compilation devient trop grande (quelle que soit votre impression, je devrais dire environ 1 000 lignes), placez chaque gestionnaire d'état dans un fichier distinct. Lorsque chaque gestionnaire d'état devient plus long que quelques écrans, divisez chaque événement dans une fonction distincte, similaire à la façon dont le commutateur d'état a été divisé. Vous pouvez le faire de différentes manières, indépendamment de l'état ou en utilisant une table commune ou en combinant différents schémas. Certains d'entre eux ont été couverts ici par d'autres. Triez vos tableaux et utilisez la recherche binaire si la vitesse est une exigence.
Programmation générique
Je voudrais que le préprocesseur traite des problèmes tels que le tri des tables ou même la génération de machines à états à partir de descriptions, vous permettant "d'écrire des programmes sur des programmes". Je crois que c'est pour cela que les gens de Boost exploitent les modèles C ++, mais je trouve la syntaxe cryptique.
Tables bidimensionnelles
J'ai utilisé des tables d'état / d'événements dans le passé, mais je dois dire que pour les cas les plus simples, je ne les trouve pas nécessaires et je préfère la clarté et la lisibilité de l'instruction switch même si elle s'étend au-delà d'un écran complet. Pour les cas plus complexes, les tableaux deviennent rapidement incontrôlables comme d'autres l'ont noté. Les idiomes que je présente ici vous permettent d'ajouter une multitude d'événements et d'états quand vous en avez envie, sans avoir à maintenir une table consommatrice de mémoire (même s'il peut s'agir de mémoire de programme).
Avertissement
Des besoins spéciaux peuvent rendre ces idiomes moins utiles, mais je les ai trouvés très clairs et maintenables.
la source
Extrêmement non testé, mais amusant à coder, maintenant dans une version plus raffinée que ma réponse originale; des versions à jour peuvent être trouvées sur mercurial.intuxication.org :
sm.h
exemple.c
la source
J'ai vraiment aimé la réponse de paxdiable et j'ai décidé d'implémenter toutes les fonctionnalités manquantes pour mon application comme les variables de garde et les données spécifiques à la machine d'état.
J'ai téléchargé mon implémentation sur ce site pour la partager avec la communauté. Il a été testé à l'aide de IAR Embedded Workbench for ARM.
https://sourceforge.net/projects/compactfsm/
la source
Un autre outil open source intéressant est Yakindu Statechart Tools sur statecharts.org . Il utilise les diagrammes d'état Harel et fournit ainsi des états hiérarchiques et parallèles et génère du code C et C ++ (ainsi que Java). Il n'utilise pas de bibliothèques mais suit une approche de «code simple». Le code applique essentiellement des structures de cas de commutation. Les générateurs de code peuvent également être personnalisés. De plus, l'outil offre de nombreuses autres fonctionnalités.
la source
J'arrive tard (comme d'habitude) mais en parcourant les réponses à ce jour, je pense qu'il manque quelque chose d'important;
J'ai trouvé dans mes propres projets qu'il peut être très utile de ne pas avoir de fonction pour chaque combinaison état / événement valide. J'aime l'idée d'avoir effectivement une table 2D d'états / événements. Mais j'aime que les éléments du tableau soient plus qu'un simple pointeur de fonction. Au lieu de cela, j'essaie d'organiser ma conception, donc en son cœur, elle comprend un tas d'éléments ou d'actions atomiques simples. De cette façon, je peux lister ces éléments atomiques simples à chaque intersection de ma table d'état / événement. L'idée est que vous ne le faites pas à définir une masse de N fonctions au carré (généralement très simples). Pourquoi avoir quelque chose d'aussi sujet aux erreurs, de temps, difficile à écrire, difficile à lire, vous l'appelez?
J'inclus également un nouvel état facultatif et un pointeur de fonction facultatif pour chaque cellule du tableau. Le pointeur de fonction est là pour les cas exceptionnels où vous ne voulez pas simplement déclencher une liste d'actions atomiques.
Vous savez que vous le faites correctement lorsque vous pouvez exprimer de nombreuses fonctionnalités différentes, simplement en modifiant votre table, sans nouveau code à écrire.
la source
Bon, je pense que le mien est juste un peu différent de tout le monde. Un peu plus de séparation du code et des données que je ne le vois dans les autres réponses. J'ai vraiment lu la théorie pour écrire ceci, qui implémente un langage régulier complet (sans expressions régulières, malheureusement). Ullman, Minsky, Chomsky. Je ne peux pas dire que j'ai tout compris, mais je me suis inspiré des anciens maîtres aussi directement que possible: à travers leurs mots.
J'utilise un pointeur de fonction vers un prédicat qui détermine la transition vers un état «oui» ou un état «non». Cela facilite la création d'un accepteur à états finis pour un langage normal que vous programmez d'une manière plus proche du langage d'assemblage. S'il vous plaît ne soyez pas rebuté par mes choix de noms stupides. 'czek' == 'vérifier'. 'grok' == [allez le chercher dans le dictionnaire Hacker].
Donc, pour chaque itération, czek appelle une fonction de prédicat avec le caractère courant comme argument. Si le prédicat retourne vrai, le caractère est consommé (le pointeur avancé) et nous suivons la transition «y» pour sélectionner l'état suivant. Si le prédicat renvoie faux, le caractère n'est PAS consommé et nous suivons la transition 'n'. Donc, chaque instruction est une branche à double sens! Je devais lire l'histoire de Mel à l'époque.
Ce code vient directement de mon interprète postscript , et a évolué dans sa forme actuelle avec beaucoup de conseils des boursiers sur comp.lang.c. Étant donné que postscript n'a fondamentalement pas de syntaxe (ne nécessitant que des crochets équilibrés), un accepteur de langage régulier comme celui-ci fonctionne également comme analyseur.
la source
boost.org est livré avec 2 implémentations de diagrammes d'état différents:
Comme toujours, le boost vous transportera dans l'enfer des modèles.
La première bibliothèque est destinée aux machines d'état plus critiques pour les performances. La deuxième bibliothèque vous donne un chemin de transition direct d'un diagramme d'états UML au code.
Voici la question SO demandant une comparaison entre les deux où les deux auteurs répondent.
la source
Cette série d'articles Ars OpenForum sur un peu compliqué de logique de contrôle comprend une implémentation très facile à suivre comme machine d'état en C.
la source
J'ai vu ça quelque part
la source
goto
crée une dépendance à l'égard d'un système d'exploitation multitâche préemptif.Étant donné que vous impliquez que vous pouvez utiliser le code C ++ et donc OO, je suggère d'évaluer le modèle d'état `` GoF '' (GoF = Gang of Four, les gars qui ont écrit le livre sur les modèles de conception qui a mis les modèles de conception sous les projecteurs).
Ce n'est pas particulièrement complexe et il est largement utilisé et discuté, il est donc facile de voir des exemples et des explications en ligne.
Il sera également très probablement reconnaissable par toute autre personne conservant votre code à une date ultérieure.
Si l'efficacité est le souci, il vaudrait la peine de faire une analyse comparative pour s'assurer qu'une approche non OO est plus efficace car de nombreux facteurs affectent les performances et ce n'est pas toujours simplement OO mauvais, le code fonctionnel est bon. De même, si l'utilisation de la mémoire est une contrainte pour vous, cela vaut encore la peine de faire des tests ou des calculs pour voir si cela sera réellement un problème pour votre application particulière si vous utilisez le modèle d'état.
Voici quelques liens vers le modèle d'état «Gof», comme le suggère Craig:
la source
Voici un exemple de machine à états finis pour Linux qui utilise les files d'attente de messages comme événements. Les événements sont placés dans la file d'attente et traités dans l'ordre. L'état change en fonction de ce qui se passe pour chaque événement.
Ceci est un exemple de connexion de données avec des états tels que:
Une petite fonctionnalité supplémentaire que j'ai ajoutée était un horodatage pour chaque message / événement. Le gestionnaire d'événements ignorera les événements trop anciens (ils ont expiré). Cela peut se produire beaucoup dans le monde réel où vous pourriez vous retrouver dans un état inattendu.
Cet exemple fonctionne sous Linux, utilisez le Makefile ci-dessous pour le compiler et jouer avec.
state_machine.c
Makefile
la source
Votre question est assez générique,
voici deux articles de référence qui pourraient être utiles,
Implémentation de la machine à états intégrée
la source
J'ai utilisé avec succès State Machine Compiler dans des projets Java et Python.
la source
Ceci est un ancien article avec beaucoup de réponses, mais j'ai pensé ajouter ma propre approche à la machine à états finis en C. J'ai créé un script Python pour produire le code squelette C pour un certain nombre d'états. Ce script est documenté sur GituHub à FsmTemplateC
Cet exemple est basé sur d'autres approches que j'ai lues. Il n'utilise pas d'instructions goto ou switch mais a plutôt des fonctions de transition dans une matrice de pointeurs (table de correspondance). Le code repose sur une grande macro d'initialisation multi-lignes et des fonctionnalités C99 (initialiseurs désignés et littéraux composés), donc si vous n'aimez pas ces choses, vous n'aimerez peut-être pas cette approche.
Voici un script Python d'un exemple de tourniquet qui génère un code C squelette à l'aide de FsmTemplateC :
L'en-tête de sortie généré contient les typedefs:
eFsmTurnstileCheck
est utilisé pour déterminer si une transition a été bloquée avecEFSM_TURNSTILE_TR_RETREAT
, autorisée à progresser avecEFSM_TURNSTILE_TR_ADVANCE
ou si l'appel de fonction n'a pas été précédé d'une transition avecEFSM_TURNSTILE_TR_CONTINUE
.eFsmTurnstileState
est simplement la liste des états.eFsmTurnstileInput
est simplement la liste des entrées.FsmTurnstile
structure est le cœur de la machine d'état avec la vérification de transition, la table de recherche de fonctions, l'état actuel, l'état commandé et un alias pour la fonction principale qui exécute la machine.FsmTurnstile
ne doit être appelé qu'à partir de la structure et doit avoir sa première entrée en tant que pointeur sur lui-même afin de maintenir un état persistant, un style orienté objet.Maintenant, pour les déclarations de fonction dans l'en-tête:
Les noms de fonction sont au format
{prefix}_{from}_{to}
, où{from}
est l'état précédent (actuel) et{to}
l'état suivant. Notez que si la table de transition ne permet pas certaines transitions, un pointeur NULL au lieu d'un pointeur de fonction sera défini. Enfin, la magie opère avec une macro. Ici, nous construisons la table de transition (matrice des énumérations d'état) et la table de recherche des fonctions de transition d'état (une matrice de pointeurs de fonction):Lors de la création du FSM, la macro
FSM_EXAMPLE_CREATE()
doit être utilisée.Maintenant, dans le code source, chaque fonction de transition d'état déclarée ci-dessus doit être remplie. La
FsmTurnstileFopts
structure peut être utilisée pour transmettre des données vers / depuis la machine d'état. Chaque transition doit être définiefsm->check
sur égale à soitEFSM_EXAMPLE_TR_RETREAT
pour l'empêcher de faire la transition, soitEFSM_EXAMPLE_TR_ADVANCE
pour lui permettre de passer à l'état commandé. Un exemple de travail peut être trouvé sur (FsmTemplateC) [ https://github.com/ChisholmKyle/FsmTemplateC] .Voici l'utilisation réelle très simple dans votre code:
Toutes ces affaires d'en-tête et toutes ces fonctions pour avoir une interface simple et rapide en valent la peine dans mon esprit.
la source
Vous pouvez utiliser la bibliothèque open source OpenFST .
la source
la source
J'utilise personnellement des structures d'auto-référencement en combinaison avec des tableaux de pointeurs. J'ai téléchargé un tutoriel sur github il y a quelque temps, lien:
https://github.com/mmelchger/polling_state_machine_c
Remarque: Je me rends compte que ce fil est assez ancien, mais j'espère obtenir des commentaires et des réflexions sur la conception de la machine à états ainsi que pouvoir fournir un exemple pour une conception possible d'une machine à états en C.
la source
Vous pouvez considérer UML-state-machine-in-c , un framework de machine à états "léger" en C. J'ai écrit ce framework pour supporter à la fois la machine à états finis et la machine à états hiérarchique . Comparé aux tables d'états ou aux cas de commutation simples, une approche de cadre est plus évolutive. Il peut être utilisé pour des machines à états finis simples à des machines à états hiérarchiques complexes.
La machine d'état est représentée par la
state_machine_t
structure. Il ne contient que deux membres "Event" et un pointeur sur "state_t".state_machine_t
doit être le premier membre de la structure de votre machine d'état. par exemplestate_t
contient un gestionnaire pour l'état et également des gestionnaires facultatifs pour l'action d'entrée et de sortie.Si le cadre est configuré pour une machine à états hiérarchique, alors le
state_t
contient un pointeur vers l'état parent et enfant.Framework fournit une API
dispatch_event
pour envoyer l'événement à la machine d'état etswitch_state
déclencher la transition d'état.Pour plus de détails sur la façon d'implémenter une machine d'état hiérarchique, reportez-vous au référentiel GitHub .
exemples de code,
https://github.com/kiishor/UML-State-Machine-in-C/blob/master/demo/simple_state_machine/readme.md https://github.com/kiishor/UML-State-Machine-in-C /blob/master/demo/simple_state_machine_enhanced/readme.md
la source
Voici une méthode pour une machine d'état qui utilise des macros telles que chaque fonction peut avoir son propre ensemble d'états: https://www.codeproject.com/Articles/37037/Macros-to-simulate-multi-tasking-blocking-code -à
Il s'intitule «simuler plusieurs tâches» mais ce n'est pas la seule utilisation.
Cette méthode utilise des rappels pour récupérer dans chaque fonction là où elle s'est arrêtée. Chaque fonction contient une liste d'états propres à chaque fonction. Une "boucle inactive" centrale est utilisée pour exécuter les machines d'état. La "boucle inactive" n'a aucune idée du fonctionnement des machines d'état, ce sont les fonctions individuelles qui "savent quoi faire". Afin d'écrire du code pour les fonctions, on crée simplement une liste d'états et utilise les macros pour "suspendre" et "reprendre". J'ai utilisé ces macros chez Cisco lorsque j'ai écrit la bibliothèque d'émetteurs-récepteurs pour le commutateur Nexus 7000.
la source