W jaki sposób podrzędna maszyna stanów może zrezygnować z kontroli z powrotem na maszynę nadrzędną?

9

Moja maszyna stanu najwyższego poziomu ma pewne stany i krawędzie. Nazywam to nadrzędną maszyną stanu.

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

Dowolny stan w nadrzędnej maszynie stanów może być również maszyną stanów. Nazwę te dzieci stanowymi maszynami.

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

Jeśli nadrzędna maszyna stanu przechodzi z A do B, maszyna stanu B przejmuje kontrolę. Po zakończeniu działania B, w jaki sposób powinien on zrzec się kontroli nad maszyną stanu nadrzędnego i przejść do stanu C? Jakiego wzoru projektu używasz?

Jeśli zastanawiasz się, mam podrzędne maszyny stanu w nadrzędnych maszynach stanu, ponieważ mój dokładny projekt jest dość złożony i naturalne jest, że enkapsuluję wewnętrzne działania stanu podrzędnego.

JoJo
źródło
Sądzę, że B0, B1 i B2 powinny wiedzieć, że są elementami czegoś, co świat zewnętrzny uważa za pojedynczą jednostkę. Więc może będziesz musiał mieć MachineContainerklasę, Bktóra zawiera B0, B1 i B2, a kiedy B2 się skończy, przekazuje kontrolę z powrotem do swojego kontenera, który następnie przechodzi do C ... Nigdy jednak nie próbowałem czegoś takiego. To interesujący problem!
FrustratedWithFormsDesigner
2
Twoje pytanie ma albo oczywistą odpowiedź, albo twoje pytanie nie jest bardzo jasne. Z punktu widzenia rodzica powinieneś zaimplementować go dokładnie tak, jak zaimplementowałbyś maszynę stanu, która nie ma potomnych maszyn stanu. Zdarza się tak, że stany są implementowane przy użyciu potomnych automatów stanów, ale to wcale nie wpływa na element nadrzędny. Nie powinno to również wpływać na automaty stanów potomnych inne niż przy wyjściu generują tylko zdarzenia na poziomie nadrzędnym.
Dunk

Odpowiedzi:

5

Każda maszyna stanów ma jakiś moduł obsługi zdarzeń i sposób wyzwalania tych zdarzeń. Ten moduł obsługi przyjmuje jako dane wejściowe istniejący stan i typ zdarzenia, wybiera nowy stan i opcjonalnie uruchamia kod skutków ubocznych.

Zasadniczo, gdy jest w stanie B, główny moduł obsługi zdarzeń przekazuje wszelkie zdarzenia, których nie rozpoznaje, do Bmodułu obsługi zdarzeń i pozostaje w stanie B. Kiedy Bchce przejść do C, umieszcza odpowiednie zdarzenie w głównej procedurze obsługi zdarzeń.

Karl Bielefeldt
źródło
2

Czy czytałeś już tę sekcję Taoup ? Można to osiągnąć na kilka różnych sposobów, ale wiele z nich zależy od tego, w jaki sposób podzieliłeś maszyny stanu. Czy są to oddzielne procesy? Wątki Obiekty?

Dowiedz się, jak je zbudowałeś i sprawdź, czy istnieje kanoniczny sposób komunikacji. Jeśli nie istnieje, być może źle projektujesz system.

Dla mnie patrzyłbym na osobne procesy, łącząc stdin i stdout razem. Podrzędna maszyna stanu staje się wtedy samodzielna, działając na standardowym wyjściu i wysyłając na standardowe wyjście. Zadaniem nadrzędnego automatu stanów jest uruchomienie procesu potomnego, podłączenie rur, a następnie zrzut danych i oczekiwanie na wyniki. Wszystkie te rzeczy zostały już wykonane we wszystkich współczesnych językach, więc powinno to być łatwe.

Spencer Rathbun
źródło
1

Oddziel dwa automaty stanów i użyj wiadomości przesyłanej między nimi. Zatem automat stanowy 1 przejdzie z ABC, gdzie w stanie B sprawdza bieżące wyniki z automatu stanowego 2. Jeśli dane wyjściowe uległy zmianie, wówczas automat stanowy 1 może to uwzględnić, a automat stanowy 2 nie musi mieć żadnej świadomości tego, jak faktycznie działa maszyna stanu 1. Coś jak:

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
źródło
1

Rozwiązanie zależy od 1) tego, czy podstany A są widoczne dla pod-stanów B. 2) Czy AB i C pochodzą od wspólnego rodzica? Jeśli mają wspólnego rodzica, a widoczność jest uniwersalna, nie powinieneś mieć większych problemów z przejściem ze stanu podrzędnego B do stanu podrzędnego A.

Jeśli wyizolowałeś je za pomocą przestrzeni nazw i / lub A, B i C nie mają wspólnego rodzica, to najlepszym sposobem jest posiadanie zewnętrznego sterownika zmiany stanu dla maszyn A, B i C. Można to zrobić za pomocą modułu obsługi zdarzeń. Wystarczy mieć obserwatora w A, który może nasłuchiwać zdarzeń podniesionych w B i przechodzi do własnego stanu podrzędnego na podstawie zdarzenia.

DPD
źródło