Comment une machine d'état enfant peut-elle abandonner le contrôle à la machine d'état parent?

9

Ma machine d'état de niveau supérieur a des états et des bords. J'appellerai cela la machine d'état parent.

A ----> B ----> C

Tout état dans la machine d'état parent peut également être une machine d'état. J'appellerai ces enfants des machines d'état.

           ___________
         /            \
A ----> |  B0->B1->B2  | ----> C
         \____________/

Si la machine d'état parent passe de A à B, la machine d'état de B prend le relais. Une fois B exécuté, comment doit-il abandonner le contrôle à la machine d'état parent et passer à l'état C? Quel modèle de conception utilisez-vous?

Si vous vous posez la question, j'ai des machines à états enfants dans les machines à états parents parce que mon projet exact est assez complexe et il est naturel d'encapsuler le fonctionnement interne d'un état enfant.

JoJo
la source
Je suppose que B0, B1 et B2 devraient savoir qu'ils sont des composants de quelque chose que le monde extérieur considère comme une seule unité. Alors peut-être que vous auriez à avoir une MachineContainerclasse pour Bqui contient B0, B1 et B2 et quand B2 se termine, il passe le contrôle à son conteneur qui passe ensuite en C ... Je n'ai jamais vraiment essayé quelque chose comme ça. C'est un problème intéressant!
FrustratedWithFormsDesigner
2
Votre question a la réponse évidente ou votre question n'est pas très claire. Du point de vue du parent, vous devez l'implémenter exactement comme vous implémenteriez une machine d'état qui n'a pas de machines d'état enfant. Il se trouve que les états sont implémentés à l'aide de machines d'état enfants, mais cela n'affecte pas du tout le parent. Cela ne devrait pas non plus affecter les machines d'état des enfants, sauf qu'à la sortie, elles ne génèrent que les événements de niveau parent.
Dunk

Réponses:

5

Chaque machine d'état possède une sorte de gestionnaire d'événements et un moyen de déclencher ces événements. Ce gestionnaire prend en entrée l'état et le type d'événement existants, choisit le nouvel état et exécute éventuellement un code d'effet secondaire.

Essentiellement, lorsqu'il est en état B, votre gestionnaire d'événements principal transmet tous les événements qu'il ne reconnaît pas au Bgestionnaire d'événements et reste en état B. Lorsque vous Bsouhaitez effectuer une transition vers C, il publie l'événement approprié dans le gestionnaire d'événements principal.

Karl Bielefeldt
la source
2

Avez-vous lu cette section de Taoup ? Il existe plusieurs façons d'accomplir cela, mais beaucoup d'entre elles dépendent de la façon dont vous avez divisé vos machines d'état. S'agit-il de processus distincts? Des discussions? Objets?

Découvrez comment vous les avez construits et regardez s'il existe un moyen canonique pour eux de communiquer. S'il n'en existe pas, vous avez peut-être mal conçu votre système.

Pour moi, j'examinerais des processus distincts, reliant stdin et stdout ensemble. La machine d'état enfant devient alors autonome, agissant sur stdin et sortie sur stdout. Il devient le travail de la machine d'état parent de démarrer le processus enfant, de raccorder les canaux, puis de vider les données et d'attendre les résultats. Toutes ces choses ont déjà été faites dans toutes les langues modernes, donc cela devrait être facile à faire.

Spencer Rathbun
la source
1

Séparez les deux machines d'état et utilisez le message qui passe entre elles. Ainsi, la machine d'état 1 proviendrait de ABC, où à l'état B elle vérifie les résultats actuels de la machine d'état 2. Si la sortie a changé, alors la machine d'état 1 peut en tenir compte et la machine d'état 2 n'a pas besoin d'avoir aucune connaissance de la façon dont la machine d'état 1 fonctionne réellement. Quelque chose comme:

typedef struct StateMachine {
  void(*Update)(); // function to update the state machine
  int Data;        // generic temp holder to survive state contexts
  int State;       // current state of our state machine
  int *Message;    // pointer to a shared integer for message passing
};

int main(void) {
  int Message = 0;
  /* NewStateMachine would malloc the struct, pass in the int reference
   * and function pointer as well as add it to a circularly linked list */
  NewStateMachine(&Message, MainLoop);
  NewStateMachine(&Message, MinorLoop);
  StateMachine *Current = StateMachine_CLL.First;

  for(;;) {
    Current->Update(Current); /* Update the current state machine */
    Current = Current->Next;  /* And the advance to the next one */
  }
}

void MainLoop(StateMachine *this) {
  switch(this.State) {
  case 0:
    CloseCoolantTank(1); /* safe to call if valve already closed */
    CloseCoolantTank(2); /* safe to call if valve already closed */
    this.State = 1;
    break;
  case 1:
    /* we have a message, do something */
    if(*this.Message) this.State = 2;          
    /* otherwise stall at this state until we get a message */
    else this.State = 1;          
    break;
  case 2:
    if(*this.Message == 1) this.State = 3;      /* warm */
    else if(*this.Message == 2) this.State = 4; /* hot! */
    else this.State = 0;                        /* cooled down, shut off valves */
    this.Message = 0;                           /* clear the message */
    break;
  case 3:
    OpenCoolantTank(1); /* opens the valve, safe to call if already open */
    this.State = 2;     /* recheck for new message */
    break;
  case 4:
    OpenCoolantTank(2); /* opens the valve, safe to call if already open */
    this.State = 3;     /* also open coolant tank 1 for extra cooling */
    break;
  }
}

/* Monitor temperature and send messages on overheat */
void MinorLoop(StateMachine *this) {
  switch(this.State) {
  case 0:
    this.Data = ReadADCValue();
    this.State = 1;
    break;
  case 1:
    if(this.Data > 150) *this.Message = 2;
    else if(this.Data > 100) *this.Message = 1;
    this.State = 0;
    break;
  }
}
Renee Cousins
la source
1

La solution dépend de 1) si les sous-états de A sont visibles aux sous-états de B. 2) Est-ce que AB et C dérivent d'un parent commun. S'ils ont un parent commun et que la visibilité est universelle, vous ne devriez pas avoir trop de mal à passer du sous-état de B au sous-état de A.

Si vous les avez isolés via des espaces de noms et / ou A, B et C n'ont pas de parent commun, alors votre meilleur moyen est d'avoir un pilote de changement d'état externe pour les machines A, B et C. Cela peut être fait via un gestionnaire d'événements. Il suffit d'avoir un observateur dans A qui peut écouter les événements déclenchés dans B et passer à son propre sous-état en fonction de l'événement.

DPD
la source