#ifndef CONFIG_PARSER_H #define CONFIG_PARSER_H #include #include #include #include #include // Core inclusion #include "Equipment/Equipment.h" #include "ModbusPoints/Modbus_Point.h" #include "ModbusPoints/Modbus_PointFactory.h" #include "Storage.h" // Current Strategies inclusions #include "Strategies/Strategy_Behavior.h" #include "Strategies/Strategy_Ramp.h" #include "Strategies/Strategy_Random.h" #include "Strategies/Strategy_Saw.h" #include "Strategies/Strategy_Square.h" #include "Strategies/Strategy_SingleValue.h" class ConfigParser { private: // Create the Equipment generic instance using ModbusIP as default // Note: The original Equipment code use templates, it is possible to instantiate with ModbusIP static Equipment* _equipmentInstance; static WiFiServer* _pythonServer; static int _pythonPort; static int _modeIsTCP; public: static Equipment* getEquipmentInstance() { if (_equipmentInstance == nullptr) { _equipmentInstance = new Equipment(); } return _equipmentInstance; } /** * @brief Start the TCP socket server to listen Python though WiFi */ static void initPythonTCPServer(int port = 8888){ _pythonPort = port; _pythonServer = new WiFiServer(_pythonPort); _pythonServer->begin(); Serial.print(F("[ConfigParser] Python server started on port: ")); Serial.println(_pythonPort); } /** * @brief Check if Python send Json through Wi-Fi and apply */ static void checkPythonTCPClient(ModbusIP& mbIP, ModbusRTU& mbRTU, bool* modeIsTCP){ if (_pythonServer==nullptr) return; WiFiClient client = _pythonServer->available(); if (client) { Serial.println(F("[ConfigParser] Python connected over Wi-Fi...")); String jsonConfig = client.readString(); // Apply configuration if it is a valid JSON apply(jsonConfig, mbIP, mbRTU, modeIsTCP); // Store backup configuration in LittleFS Storage::saveConfigFile(jsonConfig); client.println("OK"); client.stop(); } } /** * @brief Main fucntion: Deserialize the Json and configure Modbus and strategies */ static void apply(const String& jsonConfig, ModbusIP& mbIP, ModbusRTU& mbRTU, bool* modeIsTCP) { Serial.println(F("[ConfigParser] Apply new configuration...")); JsonDocument doc; DeserializationError error = deserializeJson(doc, jsonConfig); if (error) { Serial.print(F("[ConfigParser] Deserialize Json Error: ")); Serial.println(error.f_str()); return; } // 1. Clean Hardware and RAM: Clear all Modbus points and strategies in the Equipment instance Equipment* eq = getEquipmentInstance(); eq->clearEquipment(); // 2. Network and Modbus protocol configuration. if (doc.containsKey("connection")){ JsonObject connection = doc["connection"]; String type = connection["type"] | "TCP"; if (type.equalsIgnoreCase("TCP")){ *modeIsTCP = true; int port = connection["port"] | 502; mbIP.server(port); Serial.print(F("[ConfigParser] Modbus TCP server started in port: ")); Serial.println(port); } else { *modeIsTCP = false; int slaveId = connection["slave_id"] | 1; mbRTU.server(slaveId); Serial.print(F("[ConfigParser] Modbus RTU server started in slave ID: ")); Serial.println(slaveId); } } // 2. Rebuild the tags and strategies. if (doc.containsKey("points")){ JsonArray pointsArray = doc["points"]; Serial.print(F("[ConfigParser] Mapping requester registers: ")); Serial.println(pointsArray.size()); for(JsonObject p : pointsArray){ int address = p["address"]; int category = p["cateogry"]; int initVal = p["init_val"] | 0; const char* desc = p["desc"] | "Empty_Desc"; Modbus_Point* newPoint = nullptr; if (*modeIsTCP) { newPoint = createModbus_Point(&mbIP, category, address, initVal, desc); } else { newPoint = createModbus_Point((ModbusIP*)&mbRTU, category, address, initVal, desc); } if (newPoint != nullptr) { newPoint->addToModbusServer(); if (p.containsKey("strategy")){ const char* stratType = p["strategy"]; unsigned long interval = p["interval"] | 1000; Strategy_Behavior* strategy = nullptr; if (strcmp(stratType, "ramp") == 0) { strategy = new RampStrategy((float)(p["target"] | 0.0f), (float)(p["step"] | 100.0f), (int)(p["interval"] | 1000)); } else if (strcmp(stratType, "random") == 0) { strategy = new RandomStrategy(interval); } else if (strcmp(stratType, "saw") == 0) { strategy = new SawStrategy((float)(p["min"] | 0.0f), (float)(p["max"] | 100.0f), (float)(p["step"] | 5.0f), (int)(p["interval"] | 1000)); } else if (strcmp(stratType, "square") == 0) { strategy = new SquareStrategy((float)(p["lower"] | 0.0f), (float)(p["upper"] | 100.0f), (int)(p["interval"] | 1000)); } else if (strcmp(stratType, "single") == 0 || strcmp(stratType, "none") == 0) { strategy = new SingleValueStrategy((float)(p["setpoint"] | 50.0f), (float)(p["noise"] | 0.5f), (int)(p["interval"] | 1000)); } } eq->addModbus_Point(desc, newPoint); } } Serial.println(F("[ConfigParser] Tag deployment completed successfully.")); } } }; inline Equipment* ConfigParser::_equipmentInstance = nullptr; inline WiFiServer* ConfigParser::_pythonServer = nullptr; inline int ConfigParser::_modeIsTCP = true; inline int ConfigParser::_pythonPort = 8888; #endif