Classe ActionPlan

Esta é a classe de mais alto nível entre as bibliotecas de uso geral. Foi desenvolvida pra ser a estrutura de decisão de um robô, mas pode ser aproveitada para qualquer aplicação que use microcontroladores. É uma classe bem enxuta que ocupa pouca memória e não exige muito poder de processamento. É especialmente indicada para ambientes como o do arduino.

Assim como qualquer microcontrolador, um programa no arduino funciona em *loop* infinito. As instruções dentro da função *loop()* são executadas até o final da função e depois a execução volta ao início e executa tudo novamente. Isso continua indefinidamente até que o microcontrolador seja desligado. Esse esquema de funcionamento obriga a um tipo de programação que não é familiar para quem está acostumado a programar computadores, mas que é bastante comum em programação de jogos. É algo como programação orientada a eventos, mas vai além disso, porque inclui também o monitoramento. O programa precisa ficar o tempo todo monitorando dispositivos variados, reagir a determinadas condições ou leituras e também responder a eventos internos ou externos que ocorrem frequentemente. Por isso é extremamente importante que as funções implementadas no programa não sejam bloqueantes, ou seja, não obriguem que o processamento fique parado esperando que a tarefa seja completada. É claro que tecnicamente todas as funções são bloqueantes já que é o mesmo processador que irá executar todas elas. Não existe processamento paralelo no arduino ou na maioria dos microcontroladores, mas uma coisa é esperar alguns microssegundos pela leitura de um sensor de distância e outra muito diferente é esperar vários segundos até que o robô se mova de um ponto a outro. É por essa razão que a função *delay()* deve ser evitada a todo custo. Enquanto o processador está esperando o tempo passar, não está fazendo mais nada e o *loop* fica bloqueado até a função retornar.

A classe *ActionPlan* cria um mecanismo de execução de ações em sequencia que roda com um mínimo de interferência com o *loop* principal. As ações ficam armazenadas em um buffer circular e vão sendo executadas uma a uma e permitindo a reavaliação da sequencia a cada ação executada. Mas como a classe sabe executar essas ações? Ela tem conhecimento de todos os dispositivos incluídos no robô? A resposta é não, ela não sabe executar as ações, e é por isso que ela deve ser derivada em uma classe descendente que saiba não só executar as ações, mas também identificar quando uma ação foi completada para que a próxima seja iniciada. Vamos supor um algoritmo bem simples em um robô também simples. Este robô tem apenas os motores de tração e um sensor de distancia apontado diretamente para a frente. O algoritmo que o robô irá executar será bem simples:

1. Leia o sensor de distância.
2. Se a distância for maior ou igual a 50cm, ande 40 cm e volte para o passo 1, senão vá para o passo 3.
3. Gire 25 graus para a direita e volte para o passo 1.

Em resumo o que temos que fazer é ler o sensor e decidir se vamos para a frente ou se giramos para a direita. Para identificar estas ações no programa criaremos *#define's* para as ações:

```
#define act_readAndGo   1
#define act_moveAndBack 2
#define act_move        3

```

Agora derivamos a classe *ActionPlan* e implementamos o método virtual *executeAction()*:

```
class MyRobot: public ActionPlan
{
  public:
    MyRobot(): ActionPlan(){ }

    TExecResult executeAction(TAction *action)
    {
      switch (action->actionID)
      {
        case act_readAndGo:
          {
             int dist= sensor.read();
             if( dist >= 50 )
                addActionI( act_moveAndBack, mvAhead, 40 );
             else
                addActionI( act_moveAndBack, mvSpinR, 25 );
          }
          return ear_actionDone;

        case act_moveAndBack:
          addActionI( act_move, action->par.iparam1, action->par.iparam2);
          addActionI( act_readAndGo, 0 );
          return ear_actionDone;

        case act_move:
          move(action->par.iparam1, action->par.iparam2);
          return ear_actionStarted;

        default:
          return ear_nothingDone;
      }
    }

```

O método *executeAction()* recebe como parâmetro a estrutura *TAction* que contém o ID da ação, que corresponde aos defines criados e os parâmetros para a ação. Cada ação pode ter até 8 bytes de parâmetros, que podem ser formatados em 4 inteiros, ou 2 longos ou 2 *floats*. Para incluir ações na lista de ações use *addActionI()* para fornecer até 4 inteiros como parâmetros, *addActionL()* para até dois longos, ou *addActionF()* para até dois parâmetros *float*.

O método deve retornar um dos seguintes valores *enum*:
**ear\_nothingDone:** Significa que nada foi feito, será tentado novamente no próximo *loop*.
**ear\_actionDone:** A ação foi completada, a classe pode executar a próxima ação da lista. Isso será feito no próximo loop.
**ear\_actionStarted:** A ação foi iniciada, mas ainda não completada. A classe irá esperar uma notificação de que a ação foi concluída para executar a próxima ação da lista. Em geral são as ações de movimento do robô. Essas ações demoram um bom tempo para serem executadas.

A notificação de conclusão de uma ação deve ser feita chamando o método *actionDoneReport()*. Esta chamada informa à classe que a ação corrente foi concluída. No caso de ações de movimento, a notificação poderia ser incluída no evento *onAfterMove()* que é disparado sempre que um movimento é completado, como no exemplo abaixo:

```
void MyRobot::onAfterMove(char movType, float param)
{
  if (planRunning())
  {
    TAction* cr = currentAction();
    if ((cr->actionID == act_move) && (cr->par.iparam1 == movType))
      actionDoneReport();
  }
}

```

Finalmente é preciso citar que o método *run()* da classe deve ser chamado frequentemente, pelo menos uma vez a cada *loop*. É nesse método que a classe verifica as ações que precisam ser executadas.

### Conclusão

Esse foi um exemplo bem simples, feito para mostrar o funcionamento básico da classe *ActionPlan*. Em geral os algoritmos implementados para o comportamento do robô são muito mais complexos e podem crescer muito rapidamente na fase de testes quando são identificadas situações que não foram previstas inicialmente. Por exemplo, o que acontece se o teu bichano resolver entrar na frente do robô no momento em que ele está se movendo pra frente? O ideal seria que o robô identifique um obstáculo inesperado no caminho e pare antes de atropelar o gatinho, mas esse comportamento não está previsto no algoritmo acima.

Abaixo a declaração pública da classe *TAction*:

```
#define PlanBufLen 16  // Tamanho do buffer circular. Pode ser reduzido para economizar memória.
                       // Até o momento nunca usei mais do que 5 ou 6 ações enfileiradas.

class ActionPlan{
public:
  ActionPlan();  // construtor
  void run();    // chamar pelo menos uma vez a cada loop

  // métodos para incluir ações na lista
  boolean addAction(int actionID, TParam64 *par, int tag=0);
  boolean addActionL(int actionID, long  param1, long  param2=0, int tag=0);
  boolean addActionF(int actionID, float param1, float param2=0, int tag=0);
  boolean addActionI(int actionID, int param1, int param2=0, int param3=0, int param4=0, int tag=0);

  // método para facilitar a inclusão de ações de movimento, só funciona se o método setMoveActionID() for
  // chamado antes para informar qual o ID da ação associada ao movimento.
  boolean addMoveAction(int movType, float param, int tag=0);
  void setMoveActionID(int moveActionID);

  // Deve ser reescrito para implementar a execução das ações.
  virtual TExecResult executeAction(TAction *action);

  // eventos
  virtual void onActionDone(TAction* action){};
  virtual void onAllActionsDone(int tag){};

  TAction *currentAction(); // Retorna a estrutura completa da ação corrente
  int currentActionID();    // Retorna o ID da ação corrente
  int currentActionTag();   // Retorna o tag da ação corrente
  void startPlan();         // inicia a execução das ações
  void resetPlan();         // cancela e limpa a lista de ações

  void actionDoneReport();  // informa que a ação corrente foi concluída

  // verificação de status
  boolean actionWaiting();
  boolean planRunning();
  boolean actionRunning();
};

```

Abaixo um exemplo de como poderiam ser as funções setup() e loop():

```
MyRobot robot;

void setup()
{
  ...
  robot.init();
  ...
}

void loop() {
  ...
  robot.run();

  if (!robot.isMoving()&& !robot.planRunning())
  {
    robot.addActionI(act_readAndGo, 0);
    robot.startPlan();
  }
  ...
}

```