♻️ Centralizes socket serialization
This commit is contained in:
@@ -93,61 +93,7 @@ esp_err_t EventSocket::onFrame(PsychicWebSocketRequest *request, httpd_ws_frame
|
|||||||
|
|
||||||
bool EventSocket::hasSubscribers(const char *event) { return !client_subscriptions[event].empty(); }
|
bool EventSocket::hasSubscribers(const char *event) { return !client_subscriptions[event].empty(); }
|
||||||
|
|
||||||
void EventSocket::emit(const char *event, const char *payload, const char *originId, bool onlyToSameOrigin) {
|
void EventSocket::emit(const char *event, JsonVariant &payload, const char *originId, bool onlyToSameOrigin) {
|
||||||
int originSubscriptionId = originId[0] ? atoi(originId) : -1;
|
|
||||||
xSemaphoreTake(clientSubscriptionsMutex, portMAX_DELAY);
|
|
||||||
auto &subscriptions = client_subscriptions[event];
|
|
||||||
if (subscriptions.empty()) {
|
|
||||||
xSemaphoreGive(clientSubscriptionsMutex);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
JsonDocument doc;
|
|
||||||
auto a = doc.to<JsonArray>();
|
|
||||||
a.add(static_cast<uint8_t>(message_type_t::EVENT));
|
|
||||||
a.add(event);
|
|
||||||
|
|
||||||
JsonDocument payloadDoc;
|
|
||||||
if (deserializeJson(payloadDoc, payload) == DeserializationError::Ok)
|
|
||||||
a.add(payloadDoc.as<JsonVariant>());
|
|
||||||
else
|
|
||||||
a.add(payload); // fallback: insert as plain string if not valid JSON
|
|
||||||
|
|
||||||
String out;
|
|
||||||
#if USE_MSGPACK
|
|
||||||
serializeMsgPack(doc, out);
|
|
||||||
#else
|
|
||||||
serializeJson(doc, out);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
const char *msg = out.c_str();
|
|
||||||
|
|
||||||
// if onlyToSameOrigin == true, send the message back to the origin
|
|
||||||
if (onlyToSameOrigin && originSubscriptionId > 0) {
|
|
||||||
auto *client = _socket.getClient(originSubscriptionId);
|
|
||||||
if (client) {
|
|
||||||
ESP_LOGV("EventSocket", "Emitting event: %s to %s, Message: %s", event,
|
|
||||||
client->remoteIP().toString().c_str(), msg);
|
|
||||||
send(client, msg, strlen(msg));
|
|
||||||
}
|
|
||||||
} else { // else send the message to all other clients
|
|
||||||
|
|
||||||
for (int subscription : client_subscriptions[event]) {
|
|
||||||
if (subscription == originSubscriptionId) continue;
|
|
||||||
auto *client = _socket.getClient(subscription);
|
|
||||||
if (!client) {
|
|
||||||
subscriptions.remove(subscription);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
ESP_LOGV("EventSocket", "Emitting event: %s to %s, Message: %s", event,
|
|
||||||
client->remoteIP().toString().c_str(), msg);
|
|
||||||
send(client, msg, strlen(msg));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
xSemaphoreGive(clientSubscriptionsMutex);
|
|
||||||
}
|
|
||||||
|
|
||||||
void EventSocket::emit(const char *event, JsonObject &payload, const char *originId, bool onlyToSameOrigin) {
|
|
||||||
int originSubscriptionId = originId[0] ? atoi(originId) : -1;
|
int originSubscriptionId = originId[0] ? atoi(originId) : -1;
|
||||||
xSemaphoreTake(clientSubscriptionsMutex, portMAX_DELAY);
|
xSemaphoreTake(clientSubscriptionsMutex, portMAX_DELAY);
|
||||||
auto &subscriptions = client_subscriptions[event];
|
auto &subscriptions = client_subscriptions[event];
|
||||||
|
|||||||
@@ -24,9 +24,7 @@ class EventSocket {
|
|||||||
|
|
||||||
void onSubscribe(String event, SubscribeCallback callback);
|
void onSubscribe(String event, SubscribeCallback callback);
|
||||||
|
|
||||||
void emit(const char *event, const char *payload, const char *originId = "", bool onlyToSameOrigin = false);
|
void emit(const char *event, JsonVariant &payload, const char *originId = "", bool onlyToSameOrigin = false);
|
||||||
|
|
||||||
void emit(const char *event, JsonObject &root, const char *originId = "", bool onlyToSameOrigin = false);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
PsychicWebSocketHandler _socket;
|
PsychicWebSocketHandler _socket;
|
||||||
|
|||||||
@@ -16,22 +16,19 @@
|
|||||||
extern const uint8_t rootca_crt_bundle_start[] asm("_binary_src_certs_x509_crt_bundle_bin_start");
|
extern const uint8_t rootca_crt_bundle_start[] asm("_binary_src_certs_x509_crt_bundle_bin_start");
|
||||||
|
|
||||||
static int previousProgress = 0;
|
static int previousProgress = 0;
|
||||||
JsonDocument doc;
|
JsonVariant obj;
|
||||||
|
|
||||||
void update_started() {
|
void update_started() {
|
||||||
String output;
|
obj["status"] = "preparing";
|
||||||
doc["status"] = "preparing";
|
socket.emit(EVENT_DOWNLOAD_OTA, obj);
|
||||||
serializeJson(doc, output);
|
|
||||||
socket.emit(EVENT_DOWNLOAD_OTA, output.c_str());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void update_progress(int currentBytes, int totalBytes) {
|
void update_progress(int currentBytes, int totalBytes) {
|
||||||
String output;
|
obj["status"] = "progress";
|
||||||
doc["status"] = "progress";
|
|
||||||
int progress = ((currentBytes * 100) / totalBytes);
|
int progress = ((currentBytes * 100) / totalBytes);
|
||||||
if (progress > previousProgress) {
|
if (progress > previousProgress) {
|
||||||
doc["progress"] = progress;
|
obj["progress"] = progress;
|
||||||
socket.emit(EVENT_DOWNLOAD_OTA, output.c_str());
|
socket.emit(EVENT_DOWNLOAD_OTA, obj);
|
||||||
ESP_LOGV("Download OTA", "HTTP update process at %d of %d bytes... (%d %%)", currentBytes, totalBytes,
|
ESP_LOGV("Download OTA", "HTTP update process at %d of %d bytes... (%d %%)", currentBytes, totalBytes,
|
||||||
progress);
|
progress);
|
||||||
}
|
}
|
||||||
@@ -39,10 +36,8 @@ void update_progress(int currentBytes, int totalBytes) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void update_finished() {
|
void update_finished() {
|
||||||
String output;
|
obj["status"] = "finished";
|
||||||
doc["status"] = "finished";
|
socket.emit(EVENT_DOWNLOAD_OTA, obj);
|
||||||
serializeJson(doc, output);
|
|
||||||
socket.emit(EVENT_DOWNLOAD_OTA, output.c_str());
|
|
||||||
|
|
||||||
// delay to allow the event to be sent out
|
// delay to allow the event to be sent out
|
||||||
vTaskDelay(100 / portTICK_PERIOD_MS);
|
vTaskDelay(100 / portTICK_PERIOD_MS);
|
||||||
@@ -57,7 +52,6 @@ void updateTask(void *param) {
|
|||||||
httpUpdate.rebootOnUpdate(true);
|
httpUpdate.rebootOnUpdate(true);
|
||||||
|
|
||||||
String url = *((String *)param);
|
String url = *((String *)param);
|
||||||
String output;
|
|
||||||
// httpUpdate.onStart(update_started);
|
// httpUpdate.onStart(update_started);
|
||||||
// httpUpdate.onProgress(update_progress);
|
// httpUpdate.onProgress(update_progress);
|
||||||
// httpUpdate.onEnd(update_finished);
|
// httpUpdate.onEnd(update_finished);
|
||||||
@@ -66,21 +60,18 @@ void updateTask(void *param) {
|
|||||||
|
|
||||||
switch (ret) {
|
switch (ret) {
|
||||||
case HTTP_UPDATE_FAILED:
|
case HTTP_UPDATE_FAILED:
|
||||||
|
obj["status"] = "error";
|
||||||
doc["status"] = "error";
|
obj["error"] = httpUpdate.getLastErrorString().c_str();
|
||||||
doc["error"] = httpUpdate.getLastErrorString().c_str();
|
socket.emit(EVENT_DOWNLOAD_OTA, obj);
|
||||||
serializeJson(doc, output);
|
|
||||||
socket.emit(EVENT_DOWNLOAD_OTA, output.c_str());
|
|
||||||
|
|
||||||
ESP_LOGE("Download OTA", "HTTP Update failed with error (%d): %s", httpUpdate.getLastError(),
|
ESP_LOGE("Download OTA", "HTTP Update failed with error (%d): %s", httpUpdate.getLastError(),
|
||||||
httpUpdate.getLastErrorString().c_str());
|
httpUpdate.getLastErrorString().c_str());
|
||||||
break;
|
break;
|
||||||
case HTTP_UPDATE_NO_UPDATES:
|
case HTTP_UPDATE_NO_UPDATES:
|
||||||
|
|
||||||
doc["status"] = "error";
|
obj["status"] = "error";
|
||||||
doc["error"] = "Update failed, has same firmware version";
|
obj["error"] = "Update failed, has same firmware version";
|
||||||
serializeJson(doc, output);
|
socket.emit(EVENT_DOWNLOAD_OTA, obj);
|
||||||
socket.emit(EVENT_DOWNLOAD_OTA, output.c_str());
|
|
||||||
|
|
||||||
ESP_LOGE("Download OTA", "HTTP Update failed, has same firmware version");
|
ESP_LOGE("Download OTA", "HTTP Update failed, has same firmware version");
|
||||||
break;
|
break;
|
||||||
@@ -99,14 +90,10 @@ esp_err_t DownloadFirmwareService::handleDownloadUpdate(PsychicRequest *request,
|
|||||||
String downloadURL = json["download_url"];
|
String downloadURL = json["download_url"];
|
||||||
ESP_LOGI("Download OTA", "Starting OTA from: %s", downloadURL.c_str());
|
ESP_LOGI("Download OTA", "Starting OTA from: %s", downloadURL.c_str());
|
||||||
|
|
||||||
doc["status"] = "preparing";
|
obj["status"] = "preparing";
|
||||||
doc["progress"] = 0;
|
obj["progress"] = 0;
|
||||||
doc["error"] = "";
|
obj["error"] = "";
|
||||||
|
socket.emit(EVENT_DOWNLOAD_OTA, obj);
|
||||||
String output;
|
|
||||||
serializeJson(doc, output);
|
|
||||||
|
|
||||||
socket.emit(EVENT_DOWNLOAD_OTA, output.c_str());
|
|
||||||
|
|
||||||
const BaseType_t taskResult = g_taskManager.createTask(&updateTask, "Firmware download", OTA_TASK_STACK_SIZE,
|
const BaseType_t taskResult = g_taskManager.createTask(&updateTask, "Firmware download", OTA_TASK_STACK_SIZE,
|
||||||
&downloadURL, (configMAX_PRIORITIES - 1), NULL, 1);
|
&downloadURL, (configMAX_PRIORITIES - 1), NULL, 1);
|
||||||
|
|||||||
@@ -85,17 +85,19 @@ esp_err_t FirmwareUploadService::handleUpload(PsychicRequest *request, const Str
|
|||||||
if (Update.write(data, len) != len) {
|
if (Update.write(data, len) != len) {
|
||||||
handleError(request, 500);
|
handleError(request, 500);
|
||||||
} else {
|
} else {
|
||||||
char buffer[64];
|
JsonVariant obj;
|
||||||
snprintf(buffer, sizeof(buffer), "{\"status\":\"progress\",\"progress\":%.1f}",
|
obj["status"] = "progress";
|
||||||
(float)Update.progress() / (float)fsize * 100.f);
|
obj["progress"] = (float)Update.progress() / (float)fsize * 100.f;
|
||||||
socket.emit("otastatus", buffer);
|
socket.emit("otastatus", obj);
|
||||||
delay(20);
|
delay(20);
|
||||||
}
|
}
|
||||||
if (final) {
|
if (final) {
|
||||||
if (!Update.end(true)) {
|
if (!Update.end(true)) {
|
||||||
handleError(request, 500);
|
handleError(request, 500);
|
||||||
} else {
|
} else {
|
||||||
socket.emit("otastatus", "{\"status\":\"finished\",\"progress\":100}");
|
JsonVariant obj;
|
||||||
|
obj["status"] = "finished", obj["progress"] = 100;
|
||||||
|
socket.emit("otastatus", obj);
|
||||||
ESP_LOGI(TAG, "Finish writing update");
|
ESP_LOGI(TAG, "Finish writing update");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -134,9 +136,9 @@ esp_err_t FirmwareUploadService::uploadComplete(PsychicRequest *request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
esp_err_t FirmwareUploadService::handleError(PsychicRequest *request, int code) {
|
esp_err_t FirmwareUploadService::handleError(PsychicRequest *request, int code) {
|
||||||
char buffer[64];
|
JsonVariant obj;
|
||||||
snprintf(buffer, sizeof(buffer), "{\"status\":\"error\",\"error\":\"%d\"}", Update.getError());
|
obj["status"] = "error", obj["error"] = Update.getError();
|
||||||
socket.emit("otastatus", buffer);
|
socket.emit("otastatus", obj);
|
||||||
// if we have had an error already, do nothing
|
// if we have had an error already, do nothing
|
||||||
if (request->_tempObject) {
|
if (request->_tempObject) {
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
|
|||||||
@@ -82,20 +82,21 @@ class MotionService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void handleMode(JsonObject &root, int originId) {
|
void handleMode(JsonObject &root, int originId) {
|
||||||
motionState = (MOTION_STATE)root["data"].as<int>();
|
motionState = static_cast<MOTION_STATE>(root["data"].as<int>());
|
||||||
ESP_LOGV("MotionService", "Mode %d", motionState);
|
ESP_LOGV("MotionService", "Mode %d", motionState);
|
||||||
char output[2];
|
|
||||||
itoa((int)motionState, output, 10);
|
|
||||||
motionState == MOTION_STATE::DEACTIVATED ? _servoController->deactivate() : _servoController->activate();
|
motionState == MOTION_STATE::DEACTIVATED ? _servoController->deactivate() : _servoController->activate();
|
||||||
socket.emit(MODE_EVENT, output, String(originId).c_str());
|
JsonDocument doc;
|
||||||
|
doc.set(static_cast<int>(motionState));
|
||||||
|
JsonVariant data = doc.as<JsonVariant>();
|
||||||
|
socket.emit(MODE_EVENT, data, String(originId).c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void emitAngles(const String &originId = "", bool sync = false) {
|
void emitAngles(const String &originId = "", bool sync = false) {
|
||||||
char output[100];
|
JsonDocument doc;
|
||||||
snprintf(output, sizeof(output), "[%.1f,%.1f,%.1f,%.1f,%.1f,%.1f,%.1f,%.1f,%.1f,%.1f,%.1f,%.1f]", angles[0],
|
auto arr = doc.to<JsonArray>();
|
||||||
angles[1], angles[2], angles[3], angles[4], angles[5], angles[6], angles[7], angles[8], angles[9],
|
for (int i = 0; i < 12; i++) arr.add(angles[i]);
|
||||||
angles[10], angles[11]);
|
JsonVariant data = doc.as<JsonVariant>();
|
||||||
socket.emit(ANGLES_EVENT, output, originId.c_str());
|
socket.emit(ANGLES_EVENT, data, originId.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void syncAngles(const String &originId = "", bool sync = false) {
|
void syncAngles(const String &originId = "", bool sync = false) {
|
||||||
|
|||||||
@@ -113,9 +113,9 @@ class Peripherals : public StatefulService<PeripheralsConfiguration> {
|
|||||||
for (auto &address : addressList) {
|
for (auto &address : addressList) {
|
||||||
addresses.add(address);
|
addresses.add(address);
|
||||||
}
|
}
|
||||||
serializeJson(root, output);
|
|
||||||
ESP_LOGI("Peripherals", "Emitting I2C scan results, %s %d", originId.c_str(), sync);
|
ESP_LOGI("Peripherals", "Emitting I2C scan results, %s %d", originId.c_str(), sync);
|
||||||
socket.emit(EVENT_I2C_SCAN, output, originId.c_str(), sync);
|
JsonVariant data = doc.as<JsonVariant>();
|
||||||
|
socket.emit(EVENT_I2C_SCAN, data, originId.c_str(), sync);
|
||||||
}
|
}
|
||||||
|
|
||||||
void scanI2C(uint8_t lower = 1, uint8_t higher = 127) {
|
void scanI2C(uint8_t lower = 1, uint8_t higher = 127) {
|
||||||
@@ -190,15 +190,17 @@ class Peripherals : public StatefulService<PeripheralsConfiguration> {
|
|||||||
#if FT_ENABLED(USE_BMP180)
|
#if FT_ENABLED(USE_BMP180)
|
||||||
_bmp.readBarometer(root);
|
_bmp.readBarometer(root);
|
||||||
#endif
|
#endif
|
||||||
serializeJson(doc, message);
|
JsonVariant data = doc.as<JsonVariant>();
|
||||||
socket.emit(EVENT_IMU, message);
|
socket.emit(EVENT_IMU, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
void emitSonar() {
|
void emitSonar() {
|
||||||
#if FT_ENABLED(USE_USS)
|
#if FT_ENABLED(USE_USS)
|
||||||
char output[16];
|
doc.clear();
|
||||||
snprintf(output, sizeof(output), "[%.1f,%.1f]", _left_distance, _right_distance);
|
JsonArray root = doc.to<JsonArray>();
|
||||||
socket.emit("sonar", output);
|
root[0] = _left_distance, root[1] = _right_distance;
|
||||||
|
JsonVariant data = doc.as<JsonVariant>();
|
||||||
|
socket.emit("sonar", data);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -86,14 +86,6 @@ class ServoController : public StatefulService<ServoSettings> {
|
|||||||
ESP_LOGI("SERVO_CONTROLLER", "Setting servo %d to %d", servo_id, pwm);
|
ESP_LOGI("SERVO_CONTROLLER", "Setting servo %d to %d", servo_id, pwm);
|
||||||
}
|
}
|
||||||
|
|
||||||
void syncAngles(const String &originId) {
|
|
||||||
char output[100];
|
|
||||||
snprintf(output, sizeof(output), "[%.1f,%.1f,%.1f,%.1f,%.1f,%.1f,%.1f,%.1f,%.1f,%.1f,%.1f,%.1f]", angles[0],
|
|
||||||
angles[1], angles[2], angles[3], angles[4], angles[5], angles[6], angles[7], angles[8], angles[9],
|
|
||||||
angles[10], angles[11]);
|
|
||||||
socket.emit("angles", output, String(originId).c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
void updateActiveState() { is_active ? activate() : deactivate(); }
|
void updateActiveState() { is_active ? activate() : deactivate(); }
|
||||||
|
|
||||||
void setAngles(float new_angles[12]) {
|
void setAngles(float new_angles[12]) {
|
||||||
|
|||||||
@@ -140,8 +140,8 @@ void emitMetrics() {
|
|||||||
analyticsDoc.clear();
|
analyticsDoc.clear();
|
||||||
JsonObject root = analyticsDoc.to<JsonObject>();
|
JsonObject root = analyticsDoc.to<JsonObject>();
|
||||||
system_service::metrics(root);
|
system_service::metrics(root);
|
||||||
serializeJson(analyticsDoc, analyticsMessage);
|
JsonVariant data = analyticsDoc.as<JsonVariant>();
|
||||||
socket.emit(EVENT_ANALYTICS, analyticsMessage);
|
socket.emit(EVENT_ANALYTICS, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *resetReason(esp_reset_reason_t reason) {
|
const char *resetReason(esp_reset_reason_t reason) {
|
||||||
@@ -173,7 +173,7 @@ const char *resetReason(esp_reset_reason_t reason) {
|
|||||||
case ESP_RST_CPU_LOCKUP: return "Reset due to CPU lock up (double exception)";
|
case ESP_RST_CPU_LOCKUP: return "Reset due to CPU lock up (double exception)";
|
||||||
#endif
|
#endif
|
||||||
default:
|
default:
|
||||||
char buffer[50];
|
static char buffer[48];
|
||||||
snprintf(buffer, sizeof(buffer), "Unknown reset reason (%d)", reason);
|
snprintf(buffer, sizeof(buffer), "Unknown reset reason (%d)", reason);
|
||||||
return buffer;
|
return buffer;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,8 +36,7 @@ class EventEndpoint {
|
|||||||
JsonObject root = jsonDocument.to<JsonObject>();
|
JsonObject root = jsonDocument.to<JsonObject>();
|
||||||
String output;
|
String output;
|
||||||
_statefulService->read(root, _stateReader);
|
_statefulService->read(root, _stateReader);
|
||||||
serializeJson(root, output);
|
JsonVariant obj = jsonDocument.as<JsonVariant>();
|
||||||
ESP_LOGV("EventEndpoint", "Syncing state: %s", output.c_str());
|
socket.emit(_event, obj, originId.c_str(), sync);
|
||||||
socket.emit(_event, output.c_str(), originId.c_str(), sync);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user