Máquina de estados en C

Waqar Aslam 12 octubre 2023
  1. una descripción general de la máquina de estado
  2. Use Puntero de función para crear una máquina de estado
  3. Utilice la instrucción Switch para crear una máquina de estado
Máquina de estados en C

Este artículo demuestra la implementación de la máquina de estado en el lenguaje de programación C.

una descripción general de la máquina de estado

El uso de una máquina de estado para implementar código es una estrategia de diseño valiosa para resolver problemas de ingeniería complejos. Las máquinas de estado toman el diseño general y lo dividen en etapas, denominadas estados en la jerga de las máquinas de estado.

Cada estado es responsable de llevar a cabo una función particular. Por otro lado, los eventos son los estímulos que hacen que la máquina de estados cambie entre estados. También se conoce como una transición.

Cuando se escriben por primera vez, la mayoría de los sistemas son sencillos y están bien organizados, pero cuando se agregan nuevas funciones, se crean banderas y variables adicionales para realizar un seguimiento del historial de eventos.

Luego, se agregan declaraciones if y else para probar las expresiones lógicas cada vez más complejas creadas a partir de las muchas variables y banderas.

En este sentido, las máquinas de estado son de ayuda. Cuando se utilizan adecuadamente, las máquinas de estado simplifican las condiciones probadas en cada punto de bifurcación y facilitan el cambio entre varios modos de ejecución del programa.

Resulta que el comportamiento de la mayoría de los sistemas en tiempo real se puede dividir en un número relativamente pequeño de fragmentos (estados) que no se superponen.

Las respuestas de eventos dentro de cada fragmento dependen solo del evento actual y ya no dependen de la secuencia de eventos que ocurrieron en el pasado.

Use Puntero de función para crear una máquina de estado

El siguiente es un ejemplo de código para construir una máquina de estado en C. Este ejemplo específico genera dos cambios de estados entre ellos diez veces.

Luego, muestra el número de índice y el estado correspondiente a ese índice.

#include <stdio.h>

struct state;
typedef void state_fn(struct state *);

struct state {
  state_fn *next;
  int i;
};

state_fn off_state, on_state;

void off_state(struct state *state) {
  printf("%s %i\n", __func__, ++state->i);
  state->next = on_state;
}

void on_state(struct state *state) {
  printf("%s %i\n", __func__, ++state->i);
  state->next = state->i < 10 ? off_state : 0;
}

int main(void) {
  struct state state = {off_state, 0};
  while (state.next) state.next(&state);
}

Producción :

off_state 1
on_state 2
off_state 3
on_state 4
off_state 5
on_state 6
off_state 7
on_state 8
off_state 9
on_state 10

Utilice la instrucción Switch para crear una máquina de estado

Para que las cosas tengan un comienzo sólido, debe enumerar el conjunto de estados en los que puede estar nuestro programa y el conjunto de eventos que puede manejar.

Este programa puede estar en uno de estos tres estados: START, ITERATE o END.

enum states {
  START,
  ITERATE,
  END,
} state;

Puede manejar los eventos START LOOPING, SHOW_MESSAGE y STOP LOOPING. Después de recibir el evento START LOOPING mientras está en el estado START, nuestro programa pasará al estado ITERATE.

Mientras esté en el estado ITERATE, mostrará el mensaje cuando reciba el evento SHOW_MESSAGE. Finalmente, pasará al estado END después de recibir el evento STOP LOOPING.

enum events {
  START_LOOPING,
  SHOW_MESSAGE,
  STOP_LOOPING,
};

Usemos una instrucción switch que mire el estado actual y ejecute el caso apropiado. Tenemos tres situaciones principales, que se denominan START, ITERATE y END.

Dentro del caso START, si el evento recibido es START LOOPING, cambiará el estado a ITERATE. El caso se romperá si el evento no es START LOOPING.

Asimismo, dentro del caso ITERATE, si el evento recibido es SHOW MESSAGE, mostrará el mensaje; de lo contrario, buscará el caso STOP LOOPING y cambiará el estado a END.

Si no se recibe ninguno de estos eventos, se romperá y pasará al siguiente caso. Finalmente, el caso END hace que finalice la declaración switch y el programa sale.

void switchState(enum events event) {
  switch (state) {
    case START:
      switch (event) {
        case START_LOOPING:
          state = ITERATE;
          break;
        default:
          exit(1);
          break;
      }
      break;
    case ITERATE:
      switch (event) {
        case SHOW_MESSAGE:
          printf("State Machine Ready!\n");
          break;
        case STOP_LOOPING:
          state = END;
          break;
        default:
          exit(1);
          break;
      }
      break;
    case END:
      exit(1);
      break;
  }
}

Por último, invoquemos el método conocido como switchState() y proporcionemos los eventos uno a la vez en forma de argumentos.

int main(void) {
  switchState(START_LOOPING);
  switchState(SHOW_MESSAGE);
  switchState(STOP_LOOPING);
  return 0;
}

A continuación se muestra el código fuente completo utilizando la instrucción switch.

#include <stdio.h>
#include <stdlib.h>

enum states {
  START,
  ITERATE,
  END,
} state;

enum events {
  START_LOOPING,
  SHOW_MESSAGE,
  STOP_LOOPING,
};

void switchState(enum events event) {
  switch (state) {
    case START:
      switch (event) {
        case START_LOOPING:
          state = ITERATE;
          break;
        default:
          exit(1);
          break;
      }
      break;
    case ITERATE:
      switch (event) {
        case SHOW_MESSAGE:
          printf("State Machine Ready!\n");
          break;
        case STOP_LOOPING:
          state = END;
          break;
        default:
          exit(1);
          break;
      }
      break;
    case END:
      exit(1);
      break;
  }
}

int main(void) {
  switchState(START_LOOPING);
  switchState(SHOW_MESSAGE);
  switchState(STOP_LOOPING);
  return 0;
}

Producción :

State Machine Ready!
Waqar Aslam avatar Waqar Aslam avatar

I am Waqar having 5+ years of software engineering experience. I have been in the industry as a javascript web and mobile developer for 3 years working with multiple frameworks such as nodejs, react js, react native, Ionic, and angular js. After which I Switched to flutter mobile development. I have 2 years of experience building android and ios apps with flutter. For the backend, I have experience with rest APIs, Aws, and firebase. I have also written articles related to problem-solving and best practices in C, C++, Javascript, C#, and power shell.

LinkedIn