Ciao,
sono riuscito ad implementare quasi tutto, il sistema gira più o meno come dovrebbe. Ora vorrei fare qualche miglioria e non essendo un programmatore C/C++ (programmo altro di professione), sono in cerca di consigli.
Il sistema riceve comandi json-rpc usando come trasporto sia MQTT che WebSocket, entrambi in modalità asincrona . Nel mio loop c'è solamente un controllo periodico per riconnettere il wifi in caso la connessione dovesse cadere, tutto il resto gira in task rtos separati.
La domanda è: appurato che i comandi che json-rpc dovrà eseguire sono in comune ad entrambi i trasporti, come posso strutturare il tutto in maniera ottimale ? Al momento ho una classe Executor
che viene instanziata dall'evento wsOnEvent
del server websocket. Dentro l'evento faccio il parse del json-rpc, leggo il comando da eseguire, instanzio la Executor
passando un puntatore alla funzione comando vera e propria (il comando è una semplice funzione dentro un namespace Commands
). la Executor
quindi mi crea un task dedicato con il comando stesso come funzione da eseguire. Tutto il codice è dentro il comando, compreso quello per rispondere al websocket.
Il sistema funziona, ma non mi piace. in particolare non mi piace il grossolano namespace con i vari comandi all'interno. La classe Executor
mi serve perchè la uso come "appoggio" per passarmi dei parametri all'interno del task, diversamente non appena il task viene instanziato, perderei i parametri (che sono passati per forza come puntatori al task freertos) venendo eliminati. Così facendo invece l'Executor mi resta instanziato (e con esso tutti i puntatori) finchè non lo distruggo da dentro il task stesso.
Pseudocodice (ma neanche troppo) per rendere l'idea:
if (method.equals("type.mioComando"))
{
Executor *exec = new Executor();
exec->execute((const char *)json, &Commands::mioComando, wsClientId);
}
typedef void (*execTask)(void *ptr);
void Executor::execute(String json, execTask task, uint32_t wsClientId)
{
this->wsClientId = wsClientId;
this->requestJSON = json;
DynamicJsonDocument docRequest(2048);
deserializeJson(docRequest, json);
String requestId = docRequest["id"];
this->taskId = requestId ? requestId : "";
BaseType_t result = xTaskCreatePinnedToCore(
task, // Task function.
this->taskId.c_str(), // name of task.
4 * 1024, // Stack size of task
this, // parameter of the task
1, // priority of the task
&this->taskHandle, // Task handle to keep track of created task
1);
if (result != pdPASS)
{
Serial.printf("executor.cpp: task creation failed: %i\n", result);
} else {
Serial.println("executor.cpp: created task with id " + this->taskId);
}
}
namespace Commands
{
void mioComando(void *pvParameters)
{
{
UBaseType_t uxHighWaterMarkStart = uxTaskGetStackHighWaterMark(NULL);
Executor *exec = (Executor *)pvParameters;
uint32_t wsClientId = (uint32_t)exec->wsClientId;
String jsonRequest = (String)exec->requestJSON;
// Faccio cose
// ............
DynamicJsonDocument docRequest(2048);
deserializeJson(docRequest, jsonRequest);
String jsonrpc = docRequest["jsonrpc"];
String method = docRequest["method"];
String requestId = docRequest["id"];
DynamicJsonDocument docResponse(2048);
docResponse["jsonrpc"] = jsonrpc;
docResponse["id"] = requestId;
docResponse["result"] = true;
String jsonResponse;
serializeJson(docResponse, jsonResponse);
webserver.websocket->text(wsClientId, jsonResponse);
delete exec;
}
vTaskDelete(NULL);
}
}
Consigli ? Così non mi piace proprio. Si può fare sicuramente meglio. Saprei farlo in php senza impazzire con sti puntatori, passerei i parametri necessari direttamente al task e finirebbe li....