Questa è la terza volta in cui ho dovuto scrivere software per controllare un modem cellulare. Per coloro che non hanno familiarità con il processo, hai una sequenza di passaggi che devi seguire. Ogni passaggio richiede una certa quantità di tempo e ci sono alcune risposte che dovresti ricevere in quel lasso di tempo. Ci sono anche alcune risposte che puoi ricevere in qualsiasi momento, indipendentemente dal passo in cui ti trovi. In base alla risposta, devi passare ad un'altra fase del processo. Se dovesse scadere, devi andare in una fase diversa. In alcuni casi, i passaggi vengono tentati più volte prima di passare a un altro stage.
Queste devono essere funzioni non bloccanti, che posso eseguire come attività su una singola macchina con thread. Quindi il programma principale chiamerebbe questo modemTask()
poche centinaia di volte al secondo, controlla se deve fare qualcosa, quindi esegue una funzione se necessario ed esce.
In passato ho scritto questo come una semplice macchina di stato basata su switch
, con fasi enumerate, un po 'come la seguente:
switch(stage){
case Power:
powerOn(); // Turn the modem on
nextstage = ResetCmd; // Go perform a reset
attemptsLeft = 5; // Send the reset command up to five times
break;
case ResetCmd;
modem.write("ATZ\n"); // ATZ - reset
attemptsLeft--; // Use one of our attempts
nextstage = ResetReply; // Next wait for a response (should be OK)
timeout = millis() + 5000; // Wait for up to 5 seconds each attempt
break;
case ResetReply;
if(receivedResponse() == OK) // Success
{
nextStage = NetworkAttachCmd; // Attach to the cellular network
} elseif(receivedResponse() == ERROR || timeout < millis())
{ // If we get an error or timeout, reattempt if we can, power on if we can't
if(attemptsLeft > 0)
{
nextStage = ResetCmd;
} else {
nextStage = Power;
}
}
break;
case NetworkAttachCmd:
...
}
stage = nextstage; // Assign stage indirectly for debug purposes - nice to know where we came from at this point
È difficile tenere traccia dell'intero flusso del sistema, l'inserimento di un passaggio aggiuntivo richiede modifiche ai passaggi prima e dopo, e sembra proprio che ci dovrebbe essere un modo più semplice. Il più grande che ho dovuto progettare aveva meno di 60 livelli, quindi non è ingestibile, ma non posso fare a meno di pensare che ci sia una strategia o uno schema migliore per questo tipo di lavoro.
Mentre uso un po 'di #define
per la maggior parte dei timeout e dei tentativi, sarebbe un po' più bello se non fosse incorporato nella macchina a stati. Forse una struttura di qualche tipo potrebbe essere fatta per contenere ogni stato, ma poiché le risposte variano, sembra altrettanto complicato. La maggior parte dei passaggi avrà un semplice "OK", ma alcuni contengono lo stato e i dati sui quali si deve agire, in cui lo stage cambierà in base alla risposta esatta.