Domanda

Sono un po 'confuso su come implementare la mia macchina a stati.
So già che è gerarchica in quanto alcuni Stati condividono la stessa azione.
Determino quello che ho bisogno di fare da questi parametri:

  • Class (I valori sono: Base , Derivato , specifico )
  • OpCode
  • Parametro 1 - opzionale
  • Parametro 2 - opzionale

La mia gerarchia è determinata dalla classe e la OpCode rappresenta l'azione.
Derivato può usare il OpCodes di Base e specifico può usare OpCodes sia < em> Base e derivato .
L'implementazione ingenuo è il seguente:

void (*const state_table [MAX_CLASSES][MAX_OPCODES]) (state *) {
  {base_state1, base_state2, NULL, NULL},
  {base_state1, base_state2, derived_state1, NULL},
  {base_state1,base_state2, derived_state1, specific_state3},
};

void dispatch(state *s)
{
  if (state_table[s->Class][s->OpCode] != NULL)
    state_table[s->Class][s->OpCode](s);
}

Questo si trasformerà impossibile da mantenere davvero veloce.
C'è un altro modo per mappare lo stato di una superclasse?

EDIT:
Ulteriori calcualtion mi porta a pensare che io probabilmente usare la maggior parte se non tutti i OpCodes , ma non userò tutti i Classi a mia disposizione.
Un altro chiarimento:
Alcuni OpCodes potrebbe essere condivisi attraverso molteplici derivati ??e la base di Classi .
Ad esempio:

  • Ho un Class denominato Qualsiasi che è un Base di classe. Ha la OpCodes :. STATE_ON , STATE_OFF , STATE_SET
  • Ho un altro Class chiamato MyGroup , che è un Derivato di classe. Ha la OpCodes : STATE_FLIP , STATE_FLOP .

  • Il terzo Class è una specifico classe denominata ThingInMyGroup , che ha la OpCode : STATE_FLIP_FLOP_AND_FLOOP .

Quindi, un messaggio con la classe di Qualsiasi viene inviato dal server, ricevuto in tutti i clienti ed elaborati.

Un messaggio alla classe di MyGroup viene inviato dal server, ricevuto in tutti i clienti e trattati solo sui client che appartengono a MyGroup , ogni OpCodes che sono validi per il Qualsiasi di classe sono validi per il strong> MyGroup di classe <.

Un messaggio alla classe di ThingInMyGroup viene inviato dal server, ricevuto in tutti i clienti e trattati solo sui client che appartengono a MyGroup e sono un ThingInMyGroup *, eventuali OpCodes ** che sono validi per il Qualsiasi di classe e MyGroup di classe sono validi per il di classe ThingInMyGroup.

Dopo che un messaggio viene ricevuto il cliente sarà ACK / NACK di conseguenza.

preferisco non corpi interruttore di utilizzo o array const come diventeranno impossibile da mantenere quando diventano più grandi.
Ho bisogno di un design flessibile che mi consente:

  1. Per specificare quali OpCodes sono disponibili per ogni Class .
  2. Per specificare un superclasse per ogni Class e attraverso quella specifica per permettere che chiami il puntatore alla funzione che è rappresentato dalla corrente OpCode .
È stato utile?

Soluzione

Ci sono diversi modi per affrontare questo. Qui è uno:

modifica - con la gerarchia uso generale aggiunto

typedef unsigned op_code_type;
typedef void (*dispatch_type)(op_code_type);
typedef struct hierarchy_stack hierarchy_stack;
struct hierarchy_stack {
       dispatch_type func;
       hierarchy_stack *tail;
};

void dispatch(state *s, hierarchy_stack *stk) {
    if (!stk) {
          printf("this shouldn't have happened");
    } else {
          stk->func(s, stk->tail);
    }
}

void Base(state *s, hierarchy_stack *stk ) {
    switch (s->OpCode) {
          case bstate1:
               base_state1(s);
               break;
          case bstate2:
               base_state(2);
               break;
          default:
               dispatch(s, stk);
    }
}
void Derived(state *s, hierarchy_stack *stk ) {
    switch(s->opcode) {
           case dstate1:
                deriveds_state1(s);
                break;
           default:
                dispatch(s, stk);
    }
}
... 

NOTA: Tutte le chiamate di funzione sono chiamate coda

.

Questa localizza la tua "classe" es un bel po 'in modo che se si decide che le esigenze derivate 100 più metodi / codici operativi allora si hanno solo a metodi di modifica e l'enum (o altro) che si utilizza per definire codici operativi.

Un altro modo più dinamico, per affrontare questo sarebbe quello di avere un puntatore genitore all'interno di ogni "classe" che ha indicato la "classe" che avrebbe gestire qualsiasi cosa che non poteva gestire.

L'approccio tavola 2D è veloce e flessibile (Derivato potrebbe avere un gestore diverso da quello Base per codice operativo 0), ma cresce rapidamente.

Altri suggerimenti

ho scritto un piccolo strumento che genera il codice simile al vostro implementazione ingenuo sulla base di un mini-lingua. La lingua appena specificato le relazioni Stato-opcode-azione, tutte le azioni erano solo funzioni C conformi a un typedef.

E non ha gestito l'aspetto HSM, ma questo sarebbe relativamente facile da aggiungere a una lingua.

Vi consiglio di prendere questo approccio - creare un po 'la lingua che ti dà un modo pulito per descrivere la macchina dello Stato, e quindi generare il codice sulla base di tale descrizione della macchina. In questo modo quando è necessario inserire un nuovo stato un mese da ora, la cosa non è un pasticcio aggrovigliato da modificare.

Fammi sapere se si desidera che il codice e mi assicurerò che è ancora disponibile da qualche parte.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top