diff --git a/Shared/Sensors/BaseSensor.h b/Shared/Sensors/BaseSensor.h index f2f9d6e..b7f6a05 100644 --- a/Shared/Sensors/BaseSensor.h +++ b/Shared/Sensors/BaseSensor.h @@ -2,6 +2,7 @@ #include #include "LoggingSupport.h" +#include "JsonVisitor.h" enum class SensorType : uint8_t { @@ -17,4 +18,6 @@ class BaseSensor : public BaseSensorHandler, public JsonVisitor virtual SensorType getSensorType() const = 0; virtual const char* getSensorCommandId() const = 0; + + virtual const char* getSensorName() const = 0; }; \ No newline at end of file diff --git a/Shared/Sensors/Dht11SensorHandler.h b/Shared/Sensors/Dht11SensorHandler.h index 56c033d..a7d745e 100644 --- a/Shared/Sensors/Dht11SensorHandler.h +++ b/Shared/Sensors/Dht11SensorHandler.h @@ -103,4 +103,9 @@ class Dht11SensorHandler : public BaseSensor, public BroadcastLoggerSupport { return SensorTemperature; } + + const char* getSensorName() const override + { + return "dht11"; + } }; \ No newline at end of file diff --git a/Shared/Sensors/WaterSensorHandler.h b/Shared/Sensors/WaterSensorHandler.h index 3135e95..8f437f6 100644 --- a/Shared/Sensors/WaterSensorHandler.h +++ b/Shared/Sensors/WaterSensorHandler.h @@ -83,7 +83,7 @@ class WaterSensorHandler : public BaseSensor, public BroadcastLoggerSupport void formatStatusJson(char* buffer, size_t size) override { - snprintf(buffer, size, "\"waterLevel\":%d,\"average\":%d", + snprintf(buffer, size, "\"level\":%d,\"average\":%d", _latestWaterLevel, _waterPumpQueue.average()); } @@ -101,4 +101,9 @@ class WaterSensorHandler : public BaseSensor, public BroadcastLoggerSupport { return SensorWaterLevel; } + + const char* getSensorName() const override + { + return "waterLevel"; + } }; \ No newline at end of file diff --git a/Shared/SystemDefinitions.h b/Shared/SystemDefinitions.h index d26a5ea..95adac3 100644 --- a/Shared/SystemDefinitions.h +++ b/Shared/SystemDefinitions.h @@ -4,6 +4,10 @@ constexpr uint8_t DefaultValue = 0xFF; +constexpr uint8_t BufferSuccess = 0x00; +constexpr uint8_t BufferInvalid = 0x01; +constexpr uint8_t BufferOverflow = 0x02; + constexpr char SystemHeartbeatCommand[] = "F0"; constexpr char SystemInitialized[] = "F1"; constexpr char SystemFreeMemory[] = "F2"; diff --git a/SmartFuseBox/INetworkCommandHandler.h b/SmartFuseBox/INetworkCommandHandler.h index 262751e..0bfcdd9 100644 --- a/SmartFuseBox/INetworkCommandHandler.h +++ b/SmartFuseBox/INetworkCommandHandler.h @@ -2,7 +2,7 @@ #include #include #include "SystemDefinitions.h" -#include "JsonVisitor.h" +#include "NetworkJsonVisitor.h" @@ -11,7 +11,7 @@ * * Handlers implement this to expose REST-style endpoints for HTTP/WebSocket access. */ -class INetworkCommandHandler : public JsonVisitor +class INetworkCommandHandler : public NetworkJsonVisitor { public: /** @@ -39,17 +39,33 @@ class INetworkCommandHandler : public JsonVisitor virtual ~INetworkCommandHandler() = default; - void formatJsonResponse(char* buffer, size_t size, bool success, const char* message = nullptr) + uint8_t formatJsonResponse(char* buffer, size_t size, bool success, const char* message = nullptr) { + if (!buffer || size == 0) + { + return BufferInvalid; + } + + int written; if (message) { - snprintf(buffer, size, "{\"success\":%s,\"message\":\"%s\"}", + written = snprintf(buffer, size, "\"success\":%s,\"message\":\"%s\"", success ? "true" : "false", message); } else { - snprintf(buffer, size, "{\"success\":%s}", success ? "true" : "false"); + written = snprintf(buffer, size, "\"success\":%s", success ? "true" : "false"); + } + + // Check if buffer was too small + if (written < 0 || written >= static_cast(size)) + { + // Buffer overflow occurred - truncate gracefully + buffer[size - 1] = '\0'; // Ensure null termination + + return BufferOverflow; } - } + return BufferSuccess; + } }; \ No newline at end of file diff --git a/SmartFuseBox/NetworkJsonVisitor.h b/SmartFuseBox/NetworkJsonVisitor.h new file mode 100644 index 0000000..7e7a060 --- /dev/null +++ b/SmartFuseBox/NetworkJsonVisitor.h @@ -0,0 +1,10 @@ +#pragma once + +#include + +class NetworkJsonVisitor +{ +public: + virtual void formatWifiStatusJson(WiFiClient* client) = 0; + virtual ~NetworkJsonVisitor() = default; +}; \ No newline at end of file diff --git a/SmartFuseBox/RelayNetworkHandler.cpp b/SmartFuseBox/RelayNetworkHandler.cpp index 70dca80..b9a4cab 100644 --- a/SmartFuseBox/RelayNetworkHandler.cpp +++ b/SmartFuseBox/RelayNetworkHandler.cpp @@ -108,3 +108,12 @@ void RelayNetworkHandler::formatStatusJson(char* buffer, size_t size) snprintf(buffer + written, size - written, "]"); } + +void RelayNetworkHandler::formatWifiStatusJson(WiFiClient* client) +{ + char buffer[MaximumJsonResponseBufferSize]; + buffer[0] = '\0'; + + formatStatusJson(buffer, sizeof(buffer)); + client->print(buffer); +} diff --git a/SmartFuseBox/RelayNetworkHandler.h b/SmartFuseBox/RelayNetworkHandler.h index 607413a..7ae2a45 100644 --- a/SmartFuseBox/RelayNetworkHandler.h +++ b/SmartFuseBox/RelayNetworkHandler.h @@ -22,7 +22,9 @@ class RelayNetworkHandler : public INetworkCommandHandler const char* getRoute() const override { return "/api/relay"; } - void formatStatusJson(char* buffer, size_t size) override; + void formatWifiStatusJson(WiFiClient* client) override; + + void formatStatusJson(char* buffer, size_t size); CommandResult handleRequest(const String& method, const String& command, diff --git a/SmartFuseBox/SensorNetworkHandler.cpp b/SmartFuseBox/SensorNetworkHandler.cpp index b9f018b..3c20ea1 100644 --- a/SmartFuseBox/SensorNetworkHandler.cpp +++ b/SmartFuseBox/SensorNetworkHandler.cpp @@ -1,5 +1,3 @@ - - #include "SensorNetworkHandler.h" #include "SystemDefinitions.h" @@ -31,29 +29,76 @@ CommandResult SensorNetworkHandler::handleRequest(const String& method, void SensorNetworkHandler::formatStatusJson(char* buffer, size_t size) { - int written = snprintf(buffer, size, "\"sensor\":["); - - for (uint8_t i = 0; i < _sensorController->sensorCount(); i++) - { - BaseSensor* sensor = _sensorController->sensorGet(i); - - if (sensor == nullptr) - continue; - - char sensorBuffer[MaximumJsonResponseBufferSize]; - sensorBuffer[0] = '\0'; - - sensor->formatStatusJson(sensorBuffer, sizeof(sensorBuffer)); - - // Add comma separator if not the first element - if (i > 0 && written < (int)size) - { - written += snprintf(buffer + written, size - written, ","); - } + if (!buffer || size == 0) + { + return; + } + + int written = snprintf(buffer, size, "\"sensors\":{"); + if (written < 0 || written >= static_cast(size)) + { + buffer[size - 1] = '\0'; + return; // Buffer too small for even the opening + } + + bool firstSensor = true; + for (uint8_t i = 0; i < _sensorController->sensorCount(); i++) + { + BaseSensor* sensor = _sensorController->sensorGet(i); + + if (sensor == nullptr) + continue; + + char sensorBuffer[MaximumJsonResponseBufferSize]; + sensorBuffer[0] = '\0'; + + sensor->formatStatusJson(sensorBuffer, sizeof(sensorBuffer)); + + if (sensorBuffer[0] == '\0') + continue; + + // Add comma separator if not the first element + if (!firstSensor) + { + int n = snprintf(buffer + written, size - written, ","); + if (n < 0 || written + n >= static_cast(size)) + { + buffer[size - 1] = '\0'; + return; // Out of space + } + written += n; + } + + // Write sensor entry (removed duplicate and fixed format) + int n = snprintf(buffer + written, size - written, + "\"%s\":{\"id\":%d,\"type\":%d,%s}", + sensor->getSensorName(), + static_cast(sensor->getSensorId()), + static_cast(sensor->getSensorType()), + sensorBuffer); + + if (n < 0 || written + n >= static_cast(size)) + { + buffer[size - 1] = '\0'; + return; // Out of space + } + written += n; + firstSensor = false; + } + + // Close JSON object + int n = snprintf(buffer + written, size - written, "}"); + if (n < 0 || written + n >= static_cast(size)) + { + buffer[size - 1] = '\0'; + } +} - written += snprintf(buffer + written, size - written, - "%s", sensorBuffer); - } +void SensorNetworkHandler::formatWifiStatusJson(WiFiClient* client) +{ + char buffer[MaximumJsonResponseBufferSize]; + buffer[0] = '\0'; - snprintf(buffer + written, size - written, "]"); + formatStatusJson(buffer, sizeof(buffer)); + client->print(buffer); } diff --git a/SmartFuseBox/SensorNetworkHandler.h b/SmartFuseBox/SensorNetworkHandler.h index 2f1a3e9..b881411 100644 --- a/SmartFuseBox/SensorNetworkHandler.h +++ b/SmartFuseBox/SensorNetworkHandler.h @@ -14,7 +14,9 @@ class SensorNetworkHandler : public INetworkCommandHandler const char* getRoute() const override { return "/api/sensor"; } - void formatStatusJson(char* buffer, size_t size) override; + void formatWifiStatusJson(WiFiClient* client) override; + + void formatStatusJson(char* buffer, size_t size); CommandResult handleRequest(const String& method, const String& command, diff --git a/SmartFuseBox/SmartFuseBox.ino b/SmartFuseBox/SmartFuseBox.ino index b3cf1df..cd6800c 100644 --- a/SmartFuseBox/SmartFuseBox.ino +++ b/SmartFuseBox/SmartFuseBox.ino @@ -200,14 +200,12 @@ void configureWifiSupport(Config* config) // json status visitors for wifi - JsonVisitor* jsonVisitors[] = { + NetworkJsonVisitor* jsonVisitors[] = { &relayNetworkHandler, &soundNetworkHandler, &warningNetworkHandler, &systemNetworkHandler, &sensorNetworkHandler, - &waterSensorHandler, - &dht11SensorHandler }; uint8_t jsonVisitorCount = sizeof(jsonVisitors) / sizeof(jsonVisitors[0]); wifiController.registerJsonVisitors(jsonVisitors, jsonVisitorCount); diff --git a/SmartFuseBox/SmartFuseBox.vcxproj b/SmartFuseBox/SmartFuseBox.vcxproj index 0fcc3db..d6d300c 100644 --- a/SmartFuseBox/SmartFuseBox.vcxproj +++ b/SmartFuseBox/SmartFuseBox.vcxproj @@ -112,6 +112,7 @@ true + diff --git a/SmartFuseBox/SmartFuseBox.vcxproj.filters b/SmartFuseBox/SmartFuseBox.vcxproj.filters index cad2708..e17440a 100644 --- a/SmartFuseBox/SmartFuseBox.vcxproj.filters +++ b/SmartFuseBox/SmartFuseBox.vcxproj.filters @@ -139,6 +139,12 @@ Source Files\SharedCode + + Header Files + + + Source Files\NetworkCommandHandlers + @@ -168,9 +174,6 @@ Header Files\SharedCode\SerialCommandHandlers - - Header Files\SharedCode - Header Files\SharedCode @@ -249,11 +252,23 @@ Header Files - + Header Files - + Header Files + + Header Files + + + Header Files\NetworkCommandHandlers + + + Header Files + + + Header Files\NetworkCommandHandlers + \ No newline at end of file diff --git a/SmartFuseBox/SoundNetworkHandler.cpp b/SmartFuseBox/SoundNetworkHandler.cpp index 7e4e620..efb87a1 100644 --- a/SmartFuseBox/SoundNetworkHandler.cpp +++ b/SmartFuseBox/SoundNetworkHandler.cpp @@ -98,3 +98,12 @@ void SoundNetworkHandler::formatStatusJson(char* buffer, size_t size) snprintf(buffer, size, "\"sound\":{\"active\": %d,\"type\": %d}", _soundController->isPlaying(), static_cast(_soundController->getCurrentSoundType())); } + +void SoundNetworkHandler::formatWifiStatusJson(WiFiClient* client) +{ + char buffer[MaximumJsonResponseBufferSize]; + buffer[0] = '\0'; + + formatStatusJson(buffer, sizeof(buffer)); + client->print(buffer); +} diff --git a/SmartFuseBox/SoundNetworkHandler.h b/SmartFuseBox/SoundNetworkHandler.h index 09ed285..30430a3 100644 --- a/SmartFuseBox/SoundNetworkHandler.h +++ b/SmartFuseBox/SoundNetworkHandler.h @@ -15,7 +15,9 @@ class SoundNetworkHandler : public INetworkCommandHandler const char* getRoute() const override { return "/api/sound"; } - void formatStatusJson(char* buffer, size_t size) override; + void formatWifiStatusJson(WiFiClient* client) override; + + void formatStatusJson(char* buffer, size_t size); CommandResult handleRequest(const String& method, const String& cmd, diff --git a/SmartFuseBox/SystemNetworkHandler.cpp b/SmartFuseBox/SystemNetworkHandler.cpp index d6f2060..6c7a338 100644 --- a/SmartFuseBox/SystemNetworkHandler.cpp +++ b/SmartFuseBox/SystemNetworkHandler.cpp @@ -70,3 +70,12 @@ void SystemNetworkHandler::formatStatusJson(char* buffer, size_t size) rssi, DateTimeManager::formatDateTime().c_str()); } + +void SystemNetworkHandler::formatWifiStatusJson(WiFiClient* client) +{ + char buffer[MaximumJsonResponseBufferSize]; + buffer[0] = '\0'; + + formatStatusJson(buffer, sizeof(buffer)); + client->print(buffer); +} diff --git a/SmartFuseBox/SystemNetworkHandler.h b/SmartFuseBox/SystemNetworkHandler.h index cc11391..2ddb313 100644 --- a/SmartFuseBox/SystemNetworkHandler.h +++ b/SmartFuseBox/SystemNetworkHandler.h @@ -15,7 +15,9 @@ class SystemNetworkHandler : public INetworkCommandHandler const char* getRoute() const override { return "/api/system"; } - void formatStatusJson(char* buffer, size_t size) override; + void formatWifiStatusJson(WiFiClient* client) override; + + void formatStatusJson(char* buffer, size_t size); CommandResult handleRequest(const String& method, const String& cmd, diff --git a/SmartFuseBox/WarningNetworkHandler.cpp b/SmartFuseBox/WarningNetworkHandler.cpp index 3c3a7fe..f8a6df7 100644 --- a/SmartFuseBox/WarningNetworkHandler.cpp +++ b/SmartFuseBox/WarningNetworkHandler.cpp @@ -51,3 +51,12 @@ void WarningNetworkHandler::formatStatusJson(char* buffer, size_t size) snprintf(buffer, size, "\"warning\":{\"active\": \"0x%X\"}", static_cast(_warningManager->getActiveWarningsMask())); } + +void WarningNetworkHandler::formatWifiStatusJson(WiFiClient* client) +{ + char buffer[MaximumJsonResponseBufferSize]; + buffer[0] = '\0'; + + formatStatusJson(buffer, sizeof(buffer)); + client->print(buffer); +} diff --git a/SmartFuseBox/WarningNetworkHandler.h b/SmartFuseBox/WarningNetworkHandler.h index 7720c91..83511b0 100644 --- a/SmartFuseBox/WarningNetworkHandler.h +++ b/SmartFuseBox/WarningNetworkHandler.h @@ -16,7 +16,9 @@ class WarningNetworkHandler : public INetworkCommandHandler const char* getRoute() const override { return "/api/warning"; } - void formatStatusJson(char* buffer, size_t size) override; + void formatWifiStatusJson(WiFiClient* client) override; + + void formatStatusJson(char* buffer, size_t size); CommandResult handleRequest(const String& method, const String& cmd, diff --git a/SmartFuseBox/WifiController.h b/SmartFuseBox/WifiController.h index 2fc79a9..fc5412f 100644 --- a/SmartFuseBox/WifiController.h +++ b/SmartFuseBox/WifiController.h @@ -121,7 +121,7 @@ class WifiController return _wifiServer; } - void registerJsonVisitors(JsonVisitor** jsonVisitors, uint8_t jsonVisitorCount) + void registerJsonVisitors(NetworkJsonVisitor** jsonVisitors, uint8_t jsonVisitorCount) { // owned by caller so no need to clean up _jsonVisitorCount = jsonVisitorCount; @@ -136,7 +136,7 @@ class WifiController INetworkCommandHandler** _handlerObjects; uint8_t _handlerCount = 0; uint16_t _port = 80; - JsonVisitor** _jsonVisitors; + NetworkJsonVisitor** _jsonVisitors; uint8_t _jsonVisitorCount; bool isConfigValid(const Config* cfg) const diff --git a/SmartFuseBox/WifiServer.cpp b/SmartFuseBox/WifiServer.cpp index a66c718..87f82f5 100644 --- a/SmartFuseBox/WifiServer.cpp +++ b/SmartFuseBox/WifiServer.cpp @@ -4,7 +4,7 @@ constexpr uint16_t MaximumRequestSize = 1024; WifiServer::WifiServer(SerialCommandManager* commandMgrComputer, WarningManager* warningManager, uint16_t port, - INetworkCommandHandler** handlers, uint8_t handlerCount, JsonVisitor** jsonVisitors, uint8_t jsonVisitorCount) + INetworkCommandHandler** handlers, uint8_t handlerCount, NetworkJsonVisitor** jsonVisitors, uint8_t jsonVisitorCount) : SingleLoggerSupport(commandMgrComputer), _serverActive(false), _server(port), @@ -605,28 +605,21 @@ bool WifiServer::handleIndex(WiFiClient& client, bool isPersistent, const String // Stream JSON response client.print(F("{")); - bool firstEntry = true; // Track if we've written any content yet + + bool firstEntry = true; for (uint8_t i = 0; i < _jsonVisitorCount; i++) { if (_jsonVisitors[i]) { - char buffer[MaximumJsonResponseBufferSize]; - buffer[0] = '\0'; - - _jsonVisitors[i]->formatStatusJson(buffer, MaximumJsonResponseBufferSize); - - if (buffer[0] != '\0') + // Add comma separator (except before first entry) + if (!firstEntry) { - // Add comma before this entry (but not before the first entry) - if (!firstEntry) - { - client.print(F(",")); - } - - client.print(buffer); - firstEntry = false; + client.print(F(",")); } + + _jsonVisitors[i]->formatWifiStatusJson(&client); + firstEntry = false; } } @@ -832,7 +825,7 @@ void WifiServer::updateClientConnection() } } -void WifiServer::registerJsonVisitors(JsonVisitor** jsonVisitors, uint8_t jsonVisitorCount) +void WifiServer::registerJsonVisitors(NetworkJsonVisitor** jsonVisitors, uint8_t jsonVisitorCount) { if (_jsonVisitors) { @@ -842,7 +835,7 @@ void WifiServer::registerJsonVisitors(JsonVisitor** jsonVisitors, uint8_t jsonVi } _jsonVisitorCount = jsonVisitorCount; - _jsonVisitors = new JsonVisitor * [_jsonVisitorCount]; + _jsonVisitors = new NetworkJsonVisitor * [_jsonVisitorCount]; for (uint8_t i = 0; i < _jsonVisitorCount; i++) { diff --git a/SmartFuseBox/WifiServer.h b/SmartFuseBox/WifiServer.h index ea2140d..d665d64 100644 --- a/SmartFuseBox/WifiServer.h +++ b/SmartFuseBox/WifiServer.h @@ -50,7 +50,7 @@ class WifiServer : public SingleLoggerSupport uint8_t _handlerCount; // json visitors - JsonVisitor** _jsonVisitors; + NetworkJsonVisitor** _jsonVisitors; uint8_t _jsonVisitorCount; // Connection tracking @@ -89,12 +89,12 @@ class WifiServer : public SingleLoggerSupport void stopServer(); bool handleIndex(WiFiClient& client, bool isPersistent, const String& path); bool dispatchToHandler(WiFiClient& client, INetworkCommandHandler* handler, const String& path, const String& method, const String& query); - void registerJsonVisitors(JsonVisitor** jsonVisitors, uint8_t jsonVisitorCount); + void registerJsonVisitors(NetworkJsonVisitor** jsonVisitors, uint8_t jsonVisitorCount); public: WifiServer(SerialCommandManager* commandMgrComputer, WarningManager* warningManager, uint16_t port, INetworkCommandHandler** handlers, uint8_t handlerCount, - JsonVisitor** jsonVisitors, uint8_t jsonVisitorCount); + NetworkJsonVisitor** jsonVisitors, uint8_t jsonVisitorCount); ~WifiServer(); // Configuration methods diff --git a/SmartFuseBox/__vm/Compile.vmps.xml b/SmartFuseBox/__vm/Compile.vmps.xml index 85ff736..0b5edc1 100644 --- a/SmartFuseBox/__vm/Compile.vmps.xml +++ b/SmartFuseBox/__vm/Compile.vmps.xml @@ -2,7 +2,7 @@ - + diff --git a/SmartFuseBox/__vm/Upload.vmps.xml b/SmartFuseBox/__vm/Upload.vmps.xml index 9682e2b..0b5edc1 100644 --- a/SmartFuseBox/__vm/Upload.vmps.xml +++ b/SmartFuseBox/__vm/Upload.vmps.xml @@ -2,31 +2,31 @@ - + - - 144 unsigned long now = millis(); -145 -146 SystemCpuMonitor::startTask(); -147 commandMgrComputer.readCommands(); -148 commandMgrLink.readCommands(); -149 SystemCpuMonitor::endTask(); -150 --->151 SystemCpuMonitor::startTask(); -152 soundController.update(); -153 SystemCpuMonitor::endTask(); -154 -155 SystemCpuMonitor::startTask(); -156 sensorManager.update(now); -157 SystemCpuMonitor::endTask(); -158 -159 SystemCpuMonitor::startTask(); -160 bluetoothController.loop(); + + 159 commandMgrComputer.readCommands(); +160 commandMgrLink.readCommands(); +161 SystemCpuMonitor::endTask(); +162 +163 SystemCpuMonitor::startTask(); +164 soundController.update(); +165 SystemCpuMonitor::endTask(); +-->166 +167 SystemCpuMonitor::startTask(); +168 sensorManager.update(now); +169 SystemCpuMonitor::endTask(); +170 +171 SystemCpuMonitor::startTask(); +172 bluetoothController.loop(); +173 SystemCpuMonitor::endTask(); +174 +175 SystemCpuMonitor::startTask();