156 lines
6.5 KiB
C++
156 lines
6.5 KiB
C++
#ifndef CONFIG_PARSER_H
|
|
#define CONFIG_PARSER_H
|
|
#include <Arduino.h>
|
|
#include <WiFi.h>
|
|
#include <ArduinoJson.h>
|
|
#include <ModbusIP_ESP8266.h>
|
|
#include <ModbusRTU.h>
|
|
// 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<ModbusIP>* _equipmentInstance;
|
|
static WiFiServer* _pythonServer;
|
|
static int _pythonPort;
|
|
static int _modeIsTCP;
|
|
public:
|
|
|
|
static Equipment<ModbusIP>* getEquipmentInstance() {
|
|
if (_equipmentInstance == nullptr) {
|
|
_equipmentInstance = new Equipment<ModbusIP>();
|
|
}
|
|
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<ModbusIP>* 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<ModbusIP>* 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<ModbusIP>* ConfigParser::_equipmentInstance = nullptr;
|
|
inline WiFiServer* ConfigParser::_pythonServer = nullptr;
|
|
inline int ConfigParser::_modeIsTCP = true;
|
|
inline int ConfigParser::_pythonPort = 8888;
|
|
#endif |