diff --git a/.kbot/params.json b/.kbot/params.json index a1d6ab54..efe95305 100644 --- a/.kbot/params.json +++ b/.kbot/params.json @@ -11,13 +11,8 @@ }, { "role": "user", - "path": "src/modbus/ModbusTypes.h", - "content": "#ifndef MODBUS_TYPES_H\r\n#define MODBUS_TYPES_H\r\n#include \r\n#include \r\n#include \r\n#include \r\n#include \r\n\r\n#include \"macros.h\"\r\n#include \"constants.h\"\r\n#include \"config-modbus.h\"\r\n\r\nenum E_FN_CODE : uint8_t\r\n{\r\n FN_ANY_FUNCTION_CODE = 0x00, // Only valid for server to register function codes\r\n FN_READ_COIL = 0x01,\r\n FN_READ_DISCR_INPUT = 0x02,\r\n FN_READ_HOLD_REGISTER = 0x03,\r\n FN_READ_INPUT_REGISTER = 0x04,\r\n FN_WRITE_COIL = 0x05,\r\n FN_WRITE_HOLD_REGISTER = 0x06,\r\n FN_READ_EXCEPTION_SERIAL = 0x07,\r\n FN_DIAGNOSTICS_SERIAL = 0x08,\r\n FN_READ_COMM_CNT_SERIAL = 0x0B,\r\n FN_READ_COMM_LOG_SERIAL = 0x0C,\r\n FN_WRITE_MULT_COILS = 0x0F,\r\n FN_WRITE_MULT_REGISTERS = 0x10,\r\n FN_REPORT_SERVER_ID_SERIAL = 0x11,\r\n FN_READ_FILE_RECORD = 0x14,\r\n FN_WRITE_FILE_RECORD = 0x15,\r\n FN_MASK_WRITE_REGISTER = 0x16,\r\n FN_R_W_MULT_REGISTERS = 0x17,\r\n FN_READ_FIFO_QUEUE = 0x18,\r\n FN_ENCAPSULATED_INTERFACE = 0x2B,\r\n FN_USER_DEFINED_41 = 0x41,\r\n FN_USER_DEFINED_42 = 0x42,\r\n FN_USER_DEFINED_43 = 0x43,\r\n FN_USER_DEFINED_44 = 0x44,\r\n FN_USER_DEFINED_45 = 0x45,\r\n FN_USER_DEFINED_46 = 0x46,\r\n FN_USER_DEFINED_47 = 0x47,\r\n FN_USER_DEFINED_48 = 0x48,\r\n FN_USER_DEFINED_64 = 0x64,\r\n FN_USER_DEFINED_65 = 0x65,\r\n FN_USER_DEFINED_66 = 0x66,\r\n FN_USER_DEFINED_67 = 0x67,\r\n FN_USER_DEFINED_68 = 0x68,\r\n FN_USER_DEFINED_69 = 0x69,\r\n FN_USER_DEFINED_6A = 0x6A,\r\n FN_USER_DEFINED_6B = 0x6B,\r\n FN_USER_DEFINED_6C = 0x6C,\r\n FN_USER_DEFINED_6D = 0x6D,\r\n FN_USER_DEFINED_6E = 0x6E,\r\n FN_NONE = 0xFF,\r\n};\r\n\r\n// Define Modbus operation types\r\nenum E_MB_OpType\r\n{\r\n MB_READ_COIL,\r\n MB_READ_REGISTER,\r\n MB_WRITE_COIL,\r\n MB_WRITE_REGISTER,\r\n MB_WRITE_MULTIPLE_REGISTERS\r\n};\r\n\r\n// Define operation status\r\nenum E_MB_OpStatus\r\n{\r\n MB_PENDING,\r\n MB_SUCCESS,\r\n MB_FAILED,\r\n MB_RETRYING\r\n};\r\n\r\n// Define operation error codes - incorporates internal errors and eModbus errors\r\nenum class MB_Error : uint8_t\r\n{\r\n // Standard Modbus Exception Codes (as defined by eModbus)\r\n Success = 0x00, // Also used for internal success\r\n IllegalFunction = 0x01,\r\n IllegalDataAddress = 0x02,\r\n IllegalDataValue = 0x03,\r\n ServerDeviceFailure = 0x04,\r\n Acknowledge = 0x05,\r\n ServerDeviceBusy = 0x06,\r\n NegativeAcknowledge = 0x07,\r\n MemoryParityError = 0x08,\r\n GatewayPathUnavailable = 0x0A,\r\n GatewayTargetNoResp = 0x0B,\r\n\r\n // Internal Operation/Queue Errors (remapped to avoid conflicts)\r\n OpNotReady = 0x10, // Renamed from NotReady\r\n OpQueueFull = 0x11, // Renamed from QueueFull\r\n OpClientQueueFull = 0x12, // Renamed from ClientQueueFull\r\n OpExecutionFailed = 0x13, // Renamed from ExecutionFailed\r\n OpInvalidParameter = 0x14, // Renamed from InvalidParameter\r\n OpRetrying = 0x15, // Renamed from Retrying\r\n OpMaxRetriesExceeded = 0x16, // Renamed from MaxRetriesExceeded\r\n\r\n // eModbus Specific Communication Errors\r\n Timeout = 0xE0,\r\n InvalidServer = 0xE1,\r\n CrcError = 0xE2, // RTU only\r\n FcMismatch = 0xE3,\r\n ServerIdMismatch = 0xE4,\r\n PacketLengthError = 0xE5,\r\n ParameterCountError = 0xE6,\r\n ParameterLimitError = 0xE7,\r\n RequestQueueFull = 0xE8, // eModbus client queue\r\n IllegalIpOrPort = 0xE9,\r\n IpConnectionFailed = 0xEA,\r\n TcpHeadMismatch = 0xEB,\r\n EmptyMessage = 0xEC,\r\n AsciiFrameError = 0xED,\r\n AsciiCrcError = 0xEE,\r\n AsciiInvalidChar = 0xEF,\r\n BroadcastError = 0xF0,\r\n UndefinedError = 0xFF // Other communication error\r\n};\r\n\r\n/**\r\n * @brief Structure to hold Modbus registration details for a component.\r\n */\r\nstruct MB_Registers\r\n{\r\n ushort startAddress = -1; // Starting address (-1 indicates invalid/not set)\r\n ushort count = 0; // Number of consecutive addresses\r\n ushort slaveId = 0; // Slave ID of the device\r\n E_FN_CODE type = E_FN_CODE::FN_NONE; // Type of Modbus object\r\n E_ModbusAccess access = MB_ACCESS_NONE; // Read/Write access\r\n ushort componentId = 0; // ID of the owning component (MAY NOT BE SET in data returned by mb_tcp_blocks)\r\n const char *name = nullptr; // Optional descriptive name for the register block\r\n const char *group = nullptr; // Optional group name for the register block\r\n ComponentFnPtr writeCallbackFn = nullptr; // Optional component member function pointer for write callbacks\r\n \r\n MB_Registers(ushort addr = 0xFFFF, // Use 0xFFFF for unsigned invalid marker\r\n ushort ct = 0,\r\n E_FN_CODE t = E_FN_CODE::FN_NONE,\r\n E_ModbusAccess a = MB_ACCESS_NONE,\r\n ushort cId = 0,\r\n ushort sId = 0,\r\n const char *n = nullptr,\r\n const char *g = nullptr,\r\n ComponentFnPtr cb = nullptr) // Add to constructor\r\n : startAddress(addr), count(ct), type(t), access(a), componentId(cId), slaveId(sId), name(n), group(g), writeCallbackFn(cb)\r\n {\r\n }\r\n};\r\n\r\n/**\r\n * @brief A non-owning view of a collection of MB_Registers blocks.\r\n */\r\nstruct ModbusBlockView\r\n{\r\n const MB_Registers *data; // Pointer to the first block. Never null if count > 0.\r\n int count; // Total number of blocks in the view.\r\n};\r\n\r\n// Forward declarations\r\nclass ModbusRTU;\r\nclass ModbusOperation;\r\n\r\n// Define filter types for type identification without RTTI\r\nenum E_FilterType\r\n{\r\n FILTER_DUPLICATE,\r\n FILTER_RATE_LIMIT,\r\n FILTER_PRIORITY,\r\n FILTER_LIFECYCLE,\r\n FILTER_CUSTOM\r\n};\r\n\r\n// Filter chain base class\r\nclass ModbusOperationFilter\r\n{\r\npublic:\r\n ModbusOperationFilter() : nextFilter(nullptr) {}\r\n virtual ~ModbusOperationFilter() = default;\r\n\r\n // Returns true if operation should be queued, false if it should be dropped\r\n virtual bool filter(const ModbusOperation &op) = 0;\r\n\r\n // Get the filter type\r\n virtual E_FilterType getType() const = 0;\r\n\r\n // Set the next filter in the chain\r\n void setNext(ModbusOperationFilter *next) { nextFilter = next; }\r\n\r\n // Get the next filter in the chain\r\n ModbusOperationFilter *getNext() const { return nextFilter; }\r\n\r\n // Process the operation through this filter and subsequent filters\r\n bool process(const ModbusOperation &op)\r\n {\r\n if (!filter(op))\r\n return false;\r\n return nextFilter ? nextFilter->process(op) : true;\r\n }\r\n\r\n // Method to notify filter that an operation was executed\r\n // Used by filters that need to track operation status\r\n virtual void notifyOperationExecuted(const ModbusOperation &op) {}\r\n\r\n // Method to notify filter that an operation was completed\r\n // Used by filters that need to track operation status\r\n virtual void notifyOperationCompleted(const ModbusOperation &op) {}\r\n\r\nprivate:\r\n ModbusOperationFilter *nextFilter;\r\n};\r\n\r\n// Structure to hold a Modbus operation\r\n// Optimized memory layout: larger members first, booleans last\r\nstruct ModbusOperation\r\n{\r\n unsigned long timestamp; // 4 bytes\r\n uint32_t token; // 4 bytes\r\n uint16_t address; // 2 bytes\r\n uint16_t value; // 2 bytes\r\n uint16_t quantity; // 2 bytes\r\n uint8_t slaveId; // 1 byte\r\n uint8_t retries; // 1 byte\r\n uint8_t flags; // 1 byte for all boolean flags\r\n E_FN_CODE type; // 1 byte enum\r\n E_MB_OpStatus status; // 1 byte enum\r\n\r\n ModbusOperation()\r\n : timestamp(0), token(0), address(0), value(0), quantity(1),\r\n slaveId(0), retries(0), flags(0), type(E_FN_CODE::FN_READ_COIL), status(MB_PENDING) {}\r\n\r\n ModbusOperation(E_FN_CODE t, uint8_t s, uint16_t a, uint16_t v = 0, uint16_t q = 1, bool hp = false)\r\n : timestamp(millis()), token(0), address(a), value(v), quantity(q),\r\n slaveId(s), retries(0), flags(0), type(t), status(MB_PENDING)\r\n {\r\n if (hp)\r\n flags |= OP_HIGH_PRIORITY_BIT;\r\n }\r\n\r\n // Getter/setter methods for flags - Update to use TEST/SET_BIT_TO\r\n bool isUsed() const { return TEST(flags, OP_USED_BIT); }\r\n bool isHighPriority() const { return TEST(flags, OP_HIGH_PRIORITY_BIT); }\r\n bool isInProgress() const { return TEST(flags, OP_IN_PROGRESS_BIT); }\r\n bool isBroadcast() const { return TEST(flags, OP_BROADCAST_BIT); }\r\n bool isSynchronized() const { return TEST(flags, OP_SYNCHRONIZED_BIT); }\r\n\r\n void setUsed(bool value) { SET_BIT_TO(flags, OP_USED_BIT, value); }\r\n void setHighPriority(bool value) { SET_BIT_TO(flags, OP_HIGH_PRIORITY_BIT, value); }\r\n void setInProgress(bool value) { SET_BIT_TO(flags, OP_IN_PROGRESS_BIT, value); }\r\n void setBroadcast(bool value) { SET_BIT_TO(flags, OP_BROADCAST_BIT, value); }\r\n void setSynchronized(bool value) { SET_BIT_TO(flags, OP_SYNCHRONIZED_BIT, value); }\r\n};\r\n\r\n// Structure to represent a register or coil value entry\r\nstruct ModbusValueEntry\r\n{\r\n unsigned long lastUpdate; // 4 bytes\r\n uint16_t address; // 2 bytes\r\n uint16_t value; // 2 bytes\r\n uint8_t flags; // 1 byte for flags (used, synchronized)\r\n\r\n ModbusValueEntry()\r\n : lastUpdate(0), address(0), value(0), flags(0) {} // Default: not used, not synchronized\r\n\r\n ModbusValueEntry(uint16_t addr, uint16_t val, bool sync = true)\r\n : lastUpdate(millis()), address(addr), value(val), flags(0)\r\n { // Initialize flags to 0 first\r\n SBI(flags, VALUE_USED_BIT); // Set used bit\r\n if (sync)\r\n SBI(flags, VALUE_SYNCHRONIZED_BIT); // Set synchronized bit if needed\r\n }\r\n};\r\n\r\n// Structure to represent a Modbus slave's data\r\nstruct SlaveData\r\n{\r\n ModbusValueEntry coils[MAX_ADDRESSES_PER_SLAVE];\r\n ModbusValueEntry registers[MAX_ADDRESSES_PER_SLAVE];\r\n uint8_t coilCount;\r\n uint8_t registerCount;\r\n\r\n SlaveData() : coilCount(0), registerCount(0)\r\n {\r\n clear();\r\n }\r\n\r\n void clear()\r\n {\r\n coilCount = 0;\r\n registerCount = 0;\r\n for (int i = 0; i < MAX_ADDRESSES_PER_SLAVE; ++i)\r\n {\r\n CBI(coils[i].flags, VALUE_USED_BIT); // Use CBI\r\n CBI(registers[i].flags, VALUE_USED_BIT); // Use CBI\r\n CBI(coils[i].flags, VALUE_SYNCHRONIZED_BIT); // Use CBI\r\n CBI(registers[i].flags, VALUE_SYNCHRONIZED_BIT); // Use CBI\r\n }\r\n }\r\n};\r\n\r\n// Add callback function type definition\r\ntypedef void (*ResponseCallback)(uint8_t slaveId);\r\n\r\n// Callback function types for notifications\r\ntypedef void (*OnRegisterChangeCallback)(const ModbusOperation &op, uint16_t oldValue, uint16_t newValue);\r\ntypedef void (*OnWriteCallback)(const ModbusOperation &op);\r\ntypedef void (*OnErrorCallback)(const ModbusOperation &op, int errorCode, const char *errorMessage);\r\n\r\n// Struct for passing RTU update data via onMessage(void*)\r\n// Used for synchronous message passing - data copied immediately by receiver.\r\nstruct MB_UpdateData {\r\n uint8_t slaveId; // Original RTU Slave ID\r\n uint16_t address; // RTU Address OR Calculated TCP Address (depending on context)\r\n uint16_t value;\r\n uint16_t componentId;\r\n E_FN_CODE functionCode;\r\n};\r\n\r\n// Define empty callbacks for default behavior\r\ninline void emptyRegisterChangeCallback(const ModbusOperation &, uint16_t, uint16_t) {}\r\ninline void emptyWriteCallback(const ModbusOperation &) {}\r\ninline void emptyErrorCallback(const ModbusOperation &, int, const char *) {}\r\n\r\n// Function to convert Modbus Error enum to human-readable string\r\nconst char *modbusErrorToString(MB_Error error);\r\n\r\n// Structure for defining mandatory read blocks\r\nstruct ModbusReadBlock\r\n{\r\n uint16_t startAddress; // Starting address of the block\r\n uint16_t count; // Number of registers/coils in the block\r\n E_FN_CODE type; // Modbus function code for reading (e.g., FN_READ_HOLD_REGISTER)\r\n unsigned long readInterval; // Minimum interval between reads (ms)\r\n unsigned long lastReadTime; // Timestamp of the last read attempt (millis())\r\n uint8_t flags; // Status flags (e.g., used)\r\n\r\n ModbusReadBlock()\r\n : startAddress(0), count(0), type(E_FN_CODE::FN_NONE), readInterval(0), lastReadTime(0), flags(0) {}\r\n\r\n ModbusReadBlock(uint16_t start, uint16_t ct, E_FN_CODE t, unsigned long interval = 1000)\r\n : startAddress(start), count(ct), type(t), readInterval(interval), lastReadTime(0), flags(0)\r\n {\r\n SBI(flags, BLOCK_USED_BIT);\r\n }\r\n\r\n bool isUsed() const { return TEST(flags, BLOCK_USED_BIT); }\r\n void setUsed(bool value) { SET_BIT_TO(flags, BLOCK_USED_BIT, value); }\r\n};\r\n\r\n// Base class for register states\r\nclass RegisterState\r\n{\r\npublic:\r\n RegisterState(E_FN_CODE type, uint16_t address, uint16_t value = 0)\r\n : type(type), address(address), value(value),\r\n priority(PRIORITY_MEDIUM) {}\r\n\r\n E_FN_CODE type;\r\n uint16_t address;\r\n uint16_t value;\r\n uint8_t priority;\r\n\r\n bool getBoolValue() const\r\n {\r\n return (type == E_FN_CODE::FN_READ_COIL || type == E_FN_CODE::FN_READ_DISCR_INPUT) && value != 0;\r\n }\r\n\r\n void setBoolValue(bool boolValue)\r\n {\r\n // Only applicable for Coil types (writeable)\r\n if (type == E_FN_CODE::FN_WRITE_COIL)\r\n {\r\n value = boolValue ? 1 : 0; // Simplified - Modbus uses 0xFF00/0x0000 but internal might use 1/0\r\n }\r\n }\r\n\r\n // Read value from device\r\n MB_Error readFromDevice(ModbusRTU &manager, uint8_t slaveId);\r\n\r\n // Write value to device\r\n MB_Error writeToDevice(ModbusRTU &manager, uint8_t slaveId, bool forceWrite = false);\r\n\r\n // Print the current state\r\n void printState(ModbusRTU &manager, uint8_t slaveId);\r\n};\r\n\r\nclass RTU_Base : public Component\r\n{\r\npublic:\r\n // Device state enum\r\n typedef enum\r\n {\r\n UNINITIALIZED,\r\n INITIALIZING,\r\n IDLE,\r\n RUNNING,\r\n ERROR\r\n } E_DeviceState;\r\n\r\n // Constructor with device identification & owner\r\n RTU_Base(Component* owner, uint8_t _slaveId = 1, short _componentId = 1000) \r\n : Component(\"RTU_Base_Device\", _componentId, COMPONENT_DEFAULT, owner),\r\n slaveId(_slaveId),\r\n state(UNINITIALIZED),\r\n errorCount(0),\r\n lastResponseTime(0),\r\n responseTimeout(5000),\r\n lastSyncTime(0),\r\n syncInterval(10),\r\n componentId(_componentId),\r\n registerCount(0),\r\n lastErrorCode(0),\r\n mandatoryReadBlocks(readBlockStorage)\r\n {\r\n // Initialize name more specifically if desired, e.g., after this->id is set by Component base\r\n // For now, Component base takes care of ID and a generic name if not overridden.\r\n // If a more specific name like \"RTU_Device_sidXX_cidYY\" is needed, do it after base construction.\r\n for (int i = 0; i < MAX_REGISTERS; ++i)\r\n registers[i] = nullptr;\r\n }\r\n\r\n virtual ~RTU_Base()\r\n {\r\n // Clean up register objects\r\n for (int i = 0; i < registerCount; i++)\r\n {\r\n if (registers[i] != nullptr)\r\n {\r\n delete registers[i];\r\n registers[i] = nullptr;\r\n }\r\n }\r\n }\r\n\r\n // Timing control\r\n unsigned long lastResponseTime;\r\n unsigned long responseTimeout;\r\n unsigned long lastSyncTime;\r\n unsigned long syncInterval;\r\n\r\n // Error tracking\r\n unsigned int errorCount;\r\n\r\n // Device identification\r\n uint8_t slaveId;\r\n\r\n // Component mapping\r\n uint16_t componentId;\r\n\r\n // State tracking\r\n E_DeviceState state;\r\n\r\n RegisterState *registers[MAX_REGISTERS];\r\n int registerCount;\r\n virtual const char *getStateString() const;\r\n\r\n virtual void setState(E_DeviceState newState);\r\n\r\n void handleResponseReceived();\r\n\r\n bool addInputRegister(uint16_t address, E_FN_CODE type, uint8_t priority)\r\n {\r\n if (registerCount >= MAX_REGISTERS)\r\n {\r\n Log.warningln(\"Max total registers (%d) reached for device %d\", MAX_REGISTERS, slaveId);\r\n return false;\r\n }\r\n // Use appropriate E_FN_CODE for input register (assuming FN_READ_INPUT_REGISTER)\r\n RegisterState *reg = new RegisterState(type, address, 0);\r\n reg->priority = priority;\r\n reg->type = type;\r\n registers[registerCount++] = reg;\r\n Log.traceln(\"Added input register addr=%d, priority=%d to device %d\", address, priority, slaveId);\r\n return true;\r\n }\r\n\r\n // Add an output register using MAX_REGISTERS from constants.h\r\n bool addOutputRegister(uint16_t address, E_FN_CODE type, uint16_t defaultValue, uint8_t priority)\r\n {\r\n if (registerCount >= MAX_REGISTERS)\r\n {\r\n Log.warningln(\"Max total registers (%d) reached for device %d\", MAX_REGISTERS, slaveId);\r\n return false;\r\n }\r\n // Use appropriate E_FN_CODE for holding register (assuming FN_WRITE_HOLD_REGISTER)\r\n RegisterState *reg = new RegisterState(type, address, defaultValue);\r\n reg->priority = priority;\r\n registers[registerCount++] = reg;\r\n Log.traceln(\"Added output register addr=%d, default=%d, priority=%d to device %d\", address, defaultValue, priority, slaveId);\r\n return true;\r\n }\r\n\r\n // Update device state based on Modbus operations\r\n void updateState(ModbusRTU &manager);\r\n\r\n // Initialize state by writing output values to the device\r\n bool initialize(ModbusRTU &manager);\r\n\r\n // Read readable registers from device\r\n virtual void read(ModbusRTU &manager);\r\n\r\n // Get a pointer to a register by its address (const version)\r\n const RegisterState *getRegisterByAddress(uint16_t address) const;\r\n\r\n // Set a specific output register value by ADDRESS\r\n void setOutputRegisterValue(uint16_t address, uint16_t value);\r\n\r\n virtual void onRegisterUpdate(uint16_t address, uint16_t newValue);\r\n\r\n virtual void onError(ushort errorCode, const char *errorMessage);\r\n ushort mb_tcp_error(MB_Registers *reg) { return lastErrorCode; }\r\n\r\n // Write writable registers to the device (queues writes based on local state)\r\n virtual void write(ModbusRTU &manager);\r\n\r\n // Print current state values\r\n void printState();\r\n\r\n // Reset state\r\n void reset();\r\n\r\n // Run a test sequence (Declaration might be removed if implementation is gone)\r\n // void runTestSequence(ModbusRTU& manager); // Keep commented/removed if impl is gone\r\n\r\n // --- Modbus TCP Mapping Support ---\r\n /**\r\n * @brief Gets the base Modbus TCP address allocated for this RTU device instance.\r\n *\r\n * This is the starting address in the TCP address space from which the\r\n * device's own TCP register offsets are calculated.\r\n *\r\n * @return The base TCP address for this device instance.\r\n * Returns 0 if TCP mapping is not applicable or configured.\r\n */\r\n virtual uint16_t mb_tcp_base_address() const { return 0; } // Default: No TCP mapping\r\n\r\n /**\r\n * @brief Calculates the Modbus TCP offset corresponding to a given RTU address update.\r\n *\r\n * This function is intended to map an address from the RTU bus (which triggered an update)\r\n * back to the corresponding offset within the device's allocated TCP address block.\r\n *\r\n * @param rtuAddress The RTU register address that was updated.\r\n * @return The corresponding TCP offset (relative to mb_tcp_base_address()), or 0 if no direct mapping exists for broadcast.\r\n */\r\n virtual uint16_t mb_tcp_offset_for_rtu_address(uint16_t rtuAddress) const { return 0; } // Default: No mapping\r\n\r\n // --- End Modbus TCP Mapping Support ---\r\n\r\n // Add a mandatory read block definition\r\n ModbusReadBlock *addMandatoryReadBlock(uint16_t startAddress, uint16_t count, E_FN_CODE type, unsigned long interval = 1000);\r\n\r\n ushort lastErrorCode;\r\n \r\n bool triggerRTUWrite();\r\n \r\nprotected:\r\n\r\nprivate:\r\n // Storage for the mandatory read blocks Vector\r\n ModbusReadBlock readBlockStorage[MAX_READ_BLOCKS];\r\n // Vector to hold mandatory read block definitions\r\n Vector mandatoryReadBlocks;\r\n};\r\n\r\n// Callback type for checking if operation already exists\r\ntypedef bool (*OperationExistsCallback)(const ModbusOperation &op, void *context);\r\n\r\n// Filter that removes duplicate operations\r\nclass DuplicateOperationFilter : public ModbusOperationFilter\r\n{\r\npublic:\r\n // Update constructor to take ModbusRTU reference\r\n DuplicateOperationFilter(ModbusRTU *rtu);\r\n ~DuplicateOperationFilter();\r\n\r\n bool filter(const ModbusOperation &op) override;\r\n\r\n // These methods aren't needed anymore as we'll directly check the queues\r\n void notifyOperationExecuted(const ModbusOperation &op) override {}\r\n void notifyOperationCompleted(const ModbusOperation &op) override {}\r\n\r\n // Get the filter type\r\n E_FilterType getType() const override { return FILTER_DUPLICATE; }\r\n\r\nprivate:\r\n // Check if operation is already in the ModbusRTU queues\r\n bool isOperationAlreadyPending(const ModbusOperation &op) const;\r\n\r\n // Reference to ModbusRTU for queue access\r\n ModbusRTU *modbusRTU;\r\n\r\n // Timeout for considering operations as duplicates\r\n unsigned long operationTimeout;\r\n};\r\n\r\n// Filter that limits the rate of operations\r\nclass RateLimitFilter : public ModbusOperationFilter\r\n{\r\npublic:\r\n RateLimitFilter(unsigned long minIntervalMs = 100);\r\n\r\n bool filter(const ModbusOperation &op) override;\r\n\r\n // Get the filter type\r\n E_FilterType getType() const override { return FILTER_RATE_LIMIT; }\r\n\r\nprivate:\r\n unsigned long minInterval;\r\n unsigned long lastOperationTime;\r\n};\r\n\r\n// Filter that prioritizes operations based on type or address\r\nclass PriorityFilter : public ModbusOperationFilter\r\n{\r\npublic:\r\n PriorityFilter();\r\n\r\n bool filter(const ModbusOperation &op) override;\r\n\r\n // Get the filter type\r\n E_FilterType getType() const override { return FILTER_PRIORITY; }\r\n\r\n // Adjusts the priority based on operation properties\r\n // Returns true to continue processing, false to abort\r\n // This doesn't change filtering, but could modify the op\r\n bool adjustPriority(ModbusOperation &op);\r\n};\r\n\r\n// Filter that handles operation lifecycle (expiration, retries)\r\nclass OperationLifecycleFilter : public ModbusOperationFilter\r\n{\r\npublic:\r\n OperationLifecycleFilter(unsigned long timeoutMs = OPERATION_TIMEOUT, uint8_t maxRetries = MAX_RETRIES)\r\n : timeout(timeoutMs), maxRetries(maxRetries) {}\r\n\r\n bool filter(const ModbusOperation &op) override\r\n {\r\n if (op.retries >= maxRetries)\r\n {\r\n return false;\r\n }\r\n\r\n unsigned long now = millis();\r\n if (now - op.timestamp > timeout)\r\n {\r\n return false;\r\n }\r\n\r\n return true; // Let operation through\r\n }\r\n\r\n E_FilterType getType() const override { return FILTER_LIFECYCLE; }\r\n\r\nprivate:\r\n unsigned long timeout;\r\n uint8_t maxRetries;\r\n};\r\n\r\n#endif // MODBUS_TYPES_H\r\n" - }, - { - "role": "user", - "path": "src/modbus/ModbusTypes.cpp", - "content": "#include \r\n#include \r\n#include \r\n#include \r\n\r\nbool RTU_Base::triggerRTUWrite()\r\n{\r\n RS485 *rs485 = (RS485 *)owner;\r\n if (rs485)\r\n {\r\n write(rs485->modbus);\r\n return true;\r\n }\r\n Log.errorln(\"Device %d: Failed to cast owner to RS485*! Cannot trigger RTU write.\", slaveId);\r\n return false;\r\n}\r\n\r\nMB_Error RegisterState::readFromDevice(ModbusRTU &manager, uint8_t slaveId)\r\n{\r\n switch (type)\r\n {\r\n case E_FN_CODE::FN_READ_INPUT_REGISTER:\r\n case E_FN_CODE::FN_READ_HOLD_REGISTER:\r\n return manager.readRegister(slaveId, address);\r\n case E_FN_CODE::FN_READ_COIL:\r\n case E_FN_CODE::FN_READ_DISCR_INPUT:\r\n return manager.readCoil(slaveId, address);\r\n }\r\n return MB_Error::UndefinedError;\r\n}\r\n\r\nMB_Error RegisterState::writeToDevice(ModbusRTU &manager, uint8_t slaveId, bool forceWrite)\r\n{\r\n switch (type)\r\n {\r\n case E_FN_CODE::FN_WRITE_HOLD_REGISTER:\r\n return manager.writeRegister(slaveId, address, value, forceWrite);\r\n case E_FN_CODE::FN_WRITE_COIL:\r\n return manager.writeCoil(slaveId, address, getBoolValue());\r\n case E_FN_CODE::FN_READ_INPUT_REGISTER:\r\n case E_FN_CODE::FN_READ_DISCR_INPUT:\r\n case E_FN_CODE::FN_READ_HOLD_REGISTER:\r\n case E_FN_CODE::FN_READ_COIL:\r\n return MB_Error::IllegalDataAddress;\r\n default:\r\n Log.warningln(\"Attempted writeToDevice with unhandled/read-only type: %X\", (int)type);\r\n return MB_Error::IllegalFunction;\r\n }\r\n return MB_Error::UndefinedError;\r\n}\r\n\r\nvoid RegisterState::printState(ModbusRTU &manager, uint8_t slaveId)\r\n{\r\n switch (type)\r\n {\r\n case E_FN_CODE::FN_READ_INPUT_REGISTER:\r\n Log.noticeln(\"Input Register %d: %d (Priority: %d, %s)\",\r\n address, value, priority,\r\n manager.isRegisterSynchronized(slaveId, address) ? \"Synchronized\" : \"Not synchronized\");\r\n break;\r\n\r\n case E_FN_CODE::FN_READ_HOLD_REGISTER:\r\n Log.noticeln(\"Holding Register %d: %d (Priority: %d, %s)\",\r\n address, value, priority,\r\n manager.isRegisterSynchronized(slaveId, address) ? \"Synchronized\" : \"Not synchronized\");\r\n break;\r\n\r\n case E_FN_CODE::FN_READ_COIL:\r\n Log.noticeln(\"Coil %d: %s (Priority: %d, %s)\",\r\n address, getBoolValue() ? \"ON\" : \"OFF\", priority,\r\n manager.isCoilSynchronized(slaveId, address) ? \"Synchronized\" : \"Not synchronized\");\r\n break;\r\n\r\n case E_FN_CODE::FN_READ_DISCR_INPUT:\r\n Log.noticeln(\"Discrete Input %d: %s (Priority: %d, %s)\",\r\n address, getBoolValue() ? \"ON\" : \"OFF\", priority,\r\n manager.isCoilSynchronized(slaveId, address) ? \"Synchronized\" : \"Not synchronized\");\r\n break;\r\n\r\n default:\r\n Log.noticeln(\"Register Address %d: Value %d (Type: %X, Priority: %d, %s)\",\r\n address, value, (int)type, priority,\r\n manager.isRegisterSynchronized(slaveId, address) ? \"Synchronized\" : \"Not synchronized\");\r\n break;\r\n }\r\n}\r\n\r\nDuplicateOperationFilter::DuplicateOperationFilter(ModbusRTU *rtu)\r\n : modbusRTU(rtu), operationTimeout(OPERATION_TIMEOUT)\r\n{\r\n if (modbusRTU == nullptr)\r\n {\r\n Log.errorln(\"DuplicateOperationFilter created with null ModbusRTU pointer!\");\r\n }\r\n}\r\n\r\nDuplicateOperationFilter::~DuplicateOperationFilter()\r\n{\r\n // Nothing to clean up\r\n}\r\n\r\nbool DuplicateOperationFilter::filter(const ModbusOperation &op)\r\n{\r\n // Check if this operation is already pending in the ModbusRTU queues\r\n // Use the stored modbusRTU pointer\r\n if (modbusRTU && modbusRTU->isOperationAlreadyPending(op))\r\n {\r\n // Log.traceln(\"Filter: Dropping duplicate operation (slave: %d, type: %d, address: %d)\", op.slaveId, op.type, op.address);\r\n return false; // Filter out the operation\r\n }\r\n\r\n return true; // Let the operation through\r\n}\r\n\r\nRateLimitFilter::RateLimitFilter(unsigned long minIntervalMs)\r\n : minInterval(minIntervalMs), lastOperationTime(0) {}\r\n\r\nbool RateLimitFilter::filter(const ModbusOperation &op)\r\n{\r\n unsigned long now = millis();\r\n\r\n // Check if enough time has passed since the last operation\r\n if (now - lastOperationTime < minInterval)\r\n {\r\n Log.traceln(\"Filter: Rate limiting operation (slave: %d, type: %d, address: %d)\",\r\n op.slaveId, op.type, op.address);\r\n return false; // Filter out the operation\r\n }\r\n\r\n // Update the last operation time\r\n lastOperationTime = now;\r\n return true; // Let the operation through\r\n}\r\n\r\nPriorityFilter::PriorityFilter() {}\r\n\r\nbool PriorityFilter::filter(const ModbusOperation &op)\r\n{\r\n return true;\r\n}\r\n\r\nbool PriorityFilter::adjustPriority(ModbusOperation &op)\r\n{\r\n if (op.type == E_FN_CODE::FN_WRITE_COIL ||\r\n op.type == E_FN_CODE::FN_WRITE_HOLD_REGISTER ||\r\n op.type == E_FN_CODE::FN_WRITE_MULT_COILS ||\r\n op.type == E_FN_CODE::FN_WRITE_MULT_REGISTERS)\r\n {\r\n // Write operations are considered high priority\r\n // The actual priority value might be set elsewhere or this could return a priority level.\r\n // For now, returning true indicates it meets the high-priority condition.\r\n return true;\r\n }\r\n return false;\r\n}\r\n\r\nvoid RTU_Base::updateState(ModbusRTU &manager)\r\n{\r\n if (state != UNINITIALIZED && lastResponseTime > 0 && millis() - lastResponseTime > responseTimeout)\r\n {\r\n if (state != ERROR)\r\n {\r\n errorCount++;\r\n setState(ERROR);\r\n }\r\n return; // If timeout occurred, don't process further state changes here\r\n }\r\n bool hasPendingOps = manager.hasPendingOperations(slaveId);\r\n switch (state)\r\n {\r\n case UNINITIALIZED:\r\n break;\r\n case INITIALIZING:\r\n if (!hasPendingOps)\r\n {\r\n setState(IDLE);\r\n }\r\n break;\r\n case IDLE:\r\n if (hasPendingOps)\r\n {\r\n setState(RUNNING);\r\n }\r\n break;\r\n case RUNNING:\r\n if (!hasPendingOps)\r\n {\r\n setState(IDLE);\r\n }\r\n break;\r\n case ERROR:\r\n break;\r\n }\r\n}\r\n\r\nbool RTU_Base::initialize(ModbusRTU &manager)\r\n{\r\n if (state == UNINITIALIZED)\r\n {\r\n setState(INITIALIZING);\r\n write(manager);\r\n return true; // Return true indicating initialization was started\r\n }\r\n else\r\n {\r\n return false; // Return false as it wasn't in UNINITIALIZED state\r\n }\r\n}\r\n\r\nvoid RTU_Base::read(ModbusRTU &manager)\r\n{\r\n if (state == RUNNING)\r\n {\r\n return;\r\n }\r\n unsigned long currentTime = millis();\r\n bool readQueued = false;\r\n int numBlocks = mandatoryReadBlocks.size();\r\n for (int i = 0; i < numBlocks; ++i)\r\n {\r\n ModbusReadBlock &block = mandatoryReadBlocks[i];\r\n\r\n if (!block.isUsed())\r\n {\r\n continue;\r\n }\r\n\r\n if (currentTime - block.lastReadTime >= block.readInterval)\r\n {\r\n MB_Error err = MB_Error::Success;\r\n switch (block.type)\r\n {\r\n case E_FN_CODE::FN_READ_HOLD_REGISTER:\r\n err = manager.readHoldingRegisters(slaveId, block.startAddress, block.count);\r\n break;\r\n case E_FN_CODE::FN_READ_INPUT_REGISTER:\r\n err = manager.readInputRegisters(slaveId, block.startAddress, block.count);\r\n break;\r\n case E_FN_CODE::FN_READ_COIL:\r\n err = manager.readCoils(slaveId, block.startAddress, block.count);\r\n break;\r\n case E_FN_CODE::FN_READ_DISCR_INPUT:\r\n err = manager.readDiscreteInputs(slaveId, block.startAddress, block.count);\r\n break;\r\n default:\r\n Log.warningln(\"Device %d: Mandatory read block has invalid type (%X). Skipping.\", slaveId, (int)block.type);\r\n err = MB_Error::IllegalFunction; // Mark as error to prevent state change if this was the only block\r\n break;\r\n }\r\n\r\n if (err == MB_Error::Success)\r\n {\r\n readQueued = true;\r\n block.lastReadTime = currentTime;\r\n }\r\n else\r\n {\r\n if (err != MB_Error::OpNotReady)\r\n {\r\n Log.warningln(\"Device %d: Read queue is full. Skipping read for block - Start: %d, Count: %d.\",\r\n slaveId, block.startAddress, block.count);\r\n }\r\n }\r\n }\r\n }\r\n if (readQueued && state == IDLE)\r\n {\r\n setState(RUNNING);\r\n }\r\n}\r\n\r\nvoid RTU_Base::write(ModbusRTU &manager)\r\n{\r\n if (state == RUNNING)\r\n {\r\n // Log.infoln(\"Device %d: Write operation already in progress. Skipping.\", slaveId);\r\n // return;\r\n }\r\n bool writeQueued = false;\r\n for (int i = 0; i < registerCount; i++)\r\n {\r\n if (registers[i] != nullptr)\r\n {\r\n if (registers[i]->type == E_FN_CODE::FN_WRITE_HOLD_REGISTER ||\r\n registers[i]->type == E_FN_CODE::FN_WRITE_COIL)\r\n {\r\n MB_Error err = registers[i]->writeToDevice(manager, slaveId, registers[i]->priority == PRIORITY_HIGHEST);\r\n if (err == MB_Error::Success)\r\n {\r\n // Log.traceln(\"Device %d: Write operation queued for register %d (addr %d) | Value: %d.\", slaveId, i, registers[i]->address, registers[i]->value);\r\n writeQueued = true;\r\n }\r\n else\r\n {\r\n Log.warningln(\"Device %d: Failed to queue write for register %d (addr %d). Error: %u\",\r\n slaveId, i, registers[i]->address, (unsigned int)err);\r\n }\r\n }\r\n }\r\n else\r\n {\r\n Log.errorln(\"Device %d: Register %d (addr %d) is not writable.\", slaveId, i, registers[i]->address);\r\n }\r\n }\r\n if (writeQueued && state == IDLE)\r\n {\r\n setState(RUNNING);\r\n }\r\n}\r\n\r\nvoid RTU_Base::printState()\r\n{\r\n for (int i = 0; i < registerCount; i++)\r\n {\r\n if (registers[i] != nullptr)\r\n {\r\n Log.traceln(\"Register %d: Type %d, Address %d, Value %d\", i, registers[i]->type, registers[i]->address, registers[i]->value);\r\n }\r\n }\r\n}\r\n\r\nvoid RTU_Base::reset()\r\n{\r\n Log.noticeln(\"Device %d: Resetting state...\", slaveId);\r\n setState(UNINITIALIZED);\r\n lastResponseTime = 0;\r\n errorCount = 0;\r\n lastSyncTime = 0;\r\n}\r\n\r\nconst char *RTU_Base::getStateString() const\r\n{\r\n switch (state)\r\n {\r\n case UNINITIALIZED:\r\n return \"Uninitialized\";\r\n case INITIALIZING:\r\n return \"Initializing\";\r\n case IDLE:\r\n return \"Idle\";\r\n case RUNNING:\r\n return \"Running\";\r\n case ERROR:\r\n return \"Error\";\r\n default:\r\n return \"Unknown\";\r\n }\r\n}\r\n\r\nvoid RTU_Base::setState(E_DeviceState newState)\r\n{\r\n if (state != newState)\r\n {\r\n E_DeviceState oldState = state;\r\n const char *oldStateStr = getStateString();\r\n state = newState;\r\n if ((oldState == ERROR) && (newState == IDLE || newState == RUNNING))\r\n {\r\n if (errorCount > 0)\r\n {\r\n errorCount = 0;\r\n }\r\n }\r\n }\r\n}\r\n\r\nvoid RTU_Base::handleResponseReceived()\r\n{\r\n lastResponseTime = millis();\r\n if (state == ERROR)\r\n {\r\n setState(IDLE);\r\n }\r\n else if (state == IDLE || state == RUNNING || state == INITIALIZING)\r\n {\r\n // Log.traceln(\"Device %d received response in state %s.\", slaveId, getStateString());\r\n // Log.traceln(\"Device %d received response in state %s. = %d\", slaveId, getStateString());\r\n // printState();\r\n }\r\n}\r\n\r\nconst RegisterState *RTU_Base::getRegisterByAddress(uint16_t address) const\r\n{\r\n for (int i = 0; i < registerCount; i++)\r\n {\r\n if (registers[i] != nullptr && registers[i]->address == address)\r\n {\r\n return registers[i];\r\n }\r\n }\r\n return nullptr;\r\n}\r\n\r\nvoid RTU_Base::setOutputRegisterValue(uint16_t address, uint16_t value)\r\n{\r\n bool found = false;\r\n for (int i = 0; i < registerCount; i++)\r\n {\r\n if (registers[i] != nullptr && registers[i]->address == address)\r\n {\r\n if (registers[i]->type == E_FN_CODE::FN_WRITE_HOLD_REGISTER || registers[i]->type == E_FN_CODE::FN_WRITE_COIL)\r\n {\r\n registers[i]->value = value;\r\n found = true;\r\n break;\r\n }\r\n else\r\n {\r\n Log.warningln(\"Device %d: Register with address %d found, but cannot set value - not a Holding/Coil type (%d).\",\r\n slaveId, address, registers[i]->type);\r\n found = true;\r\n break;\r\n }\r\n }\r\n }\r\n if (!found)\r\n {\r\n Log.warningln(\"Device %d: Writable register with address %d not found for setting value.\", slaveId, address);\r\n }\r\n else\r\n {\r\n triggerRTUWrite();\r\n }\r\n}\r\n\r\nModbusReadBlock *RTU_Base::addMandatoryReadBlock(uint16_t startAddress, uint16_t count, E_FN_CODE type, unsigned long interval)\r\n{\r\n if (count == 0)\r\n {\r\n Log.warningln(\"Device %d: Cannot add mandatory read block with count 0.\", slaveId);\r\n return nullptr;\r\n }\r\n if (type != E_FN_CODE::FN_READ_HOLD_REGISTER &&\r\n type != E_FN_CODE::FN_READ_INPUT_REGISTER &&\r\n type != E_FN_CODE::FN_READ_COIL &&\r\n type != E_FN_CODE::FN_READ_DISCR_INPUT)\r\n {\r\n Log.warningln(\"Device %d: Invalid type (%X) for mandatory read block.\", slaveId, (int)type);\r\n return nullptr;\r\n }\r\n if (mandatoryReadBlocks.full())\r\n {\r\n Log.errorln(\"Device %d: Cannot add mandatory read block - maximum (%d) reached.\", slaveId, MAX_READ_BLOCKS);\r\n return nullptr;\r\n }\r\n ModbusReadBlock newBlock(startAddress, count, type, interval);\r\n mandatoryReadBlocks.push_back(newBlock);\r\n Log.traceln(\"Device %d: Added mandatory read block - Start: %d, Count: %d, Type: %X, Interval: %lu ms\",\r\n slaveId, startAddress, count, (int)type, interval);\r\n\r\n newBlock.setUsed(true);\r\n return &mandatoryReadBlocks.back();\r\n}\r\nvoid RTU_Base::onError(ushort errorCode, const char *errorMessage)\r\n{\r\n lastErrorCode = errorCode;\r\n}\r\nvoid RTU_Base::onRegisterUpdate(uint16_t address, uint16_t newValue)\r\n{\r\n bool updated = false;\r\n for (int i = 0; i < registerCount; ++i)\r\n {\r\n if (registers[i] != nullptr && registers[i]->address == address)\r\n {\r\n if (registers[i]->value != newValue)\r\n {\r\n registers[i]->value = newValue;\r\n }\r\n updated = true;\r\n break;\r\n }\r\n }\r\n}\r\nconst char *modbusErrorToString(MB_Error error)\r\n{\r\n switch (error)\r\n {\r\n // Standard Modbus Exceptions\r\n case MB_Error::Success:\r\n return \"Success\";\r\n case MB_Error::IllegalFunction:\r\n return \"Illegal Function\";\r\n case MB_Error::IllegalDataAddress:\r\n return \"Illegal Data Address\";\r\n case MB_Error::IllegalDataValue:\r\n return \"Illegal Data Value\";\r\n case MB_Error::ServerDeviceFailure:\r\n return \"Server Device Failure\";\r\n case MB_Error::Acknowledge:\r\n return \"Acknowledge\";\r\n case MB_Error::ServerDeviceBusy:\r\n return \"Server Device Busy\";\r\n case MB_Error::NegativeAcknowledge:\r\n return \"Negative Acknowledge\";\r\n case MB_Error::MemoryParityError:\r\n return \"Memory Parity Error\";\r\n case MB_Error::GatewayPathUnavailable:\r\n return \"Gateway Path Unavailable\";\r\n case MB_Error::GatewayTargetNoResp:\r\n return \"Gateway Target Device Failed to Respond\";\r\n\r\n // Internal Operation/Queue Errors\r\n case MB_Error::OpNotReady:\r\n return \"Operation Not Ready\";\r\n case MB_Error::OpQueueFull:\r\n return \"ModbusRTU Operation Queue Full\";\r\n case MB_Error::OpClientQueueFull:\r\n return \"eModbus Client Queue Full\";\r\n case MB_Error::OpExecutionFailed:\r\n return \"Operation Execution Failed\";\r\n case MB_Error::OpInvalidParameter:\r\n return \"Invalid Parameter\";\r\n case MB_Error::OpRetrying:\r\n return \"Operation Retrying\";\r\n case MB_Error::OpMaxRetriesExceeded:\r\n return \"Max Retries Exceeded\";\r\n\r\n // eModbus Specific Communication Errors\r\n case MB_Error::Timeout:\r\n return \"Timeout\";\r\n case MB_Error::InvalidServer:\r\n return \"Invalid Server Response\";\r\n case MB_Error::CrcError:\r\n return \"CRC Error\"; // RTU\r\n case MB_Error::FcMismatch:\r\n return \"Function Code Mismatch\";\r\n case MB_Error::ServerIdMismatch:\r\n return \"Server ID Mismatch\";\r\n case MB_Error::PacketLengthError:\r\n return \"Packet Length Error\";\r\n case MB_Error::ParameterCountError:\r\n return \"Parameter Count Error\";\r\n case MB_Error::ParameterLimitError:\r\n return \"Parameter Limit Error\";\r\n case MB_Error::RequestQueueFull:\r\n return \"eModbus Request Queue Full\"; // Same as OpClientQueueFull? Check enum vals\r\n case MB_Error::IllegalIpOrPort:\r\n return \"Illegal IP or Port\"; // TCP\r\n case MB_Error::IpConnectionFailed:\r\n return \"IP Connection Failed\"; // TCP\r\n case MB_Error::TcpHeadMismatch:\r\n return \"TCP Header Mismatch\"; // TCP\r\n case MB_Error::EmptyMessage:\r\n return \"Empty Message Received\";\r\n case MB_Error::AsciiFrameError:\r\n return \"ASCII Frame Error\"; // ASCII\r\n case MB_Error::AsciiCrcError:\r\n return \"ASCII LRC Error\"; // ASCII\r\n case MB_Error::AsciiInvalidChar:\r\n return \"ASCII Invalid Character\"; // ASCII\r\n case MB_Error::BroadcastError:\r\n return \"Broadcast Error\";\r\n\r\n case MB_Error::UndefinedError:\r\n default:\r\n return \"Undefined or Unknown Error\";\r\n }\r\n}" + "path": "src/xtypes.h", + "content": "#ifndef TYPES_H\n#define TYPES_H\n\n#include \n\ntypedef const char cchar;\ntypedef unsigned char uchar;\n\ntypedef unsigned long millis_t;\ntypedef unsigned short ushort;\ntypedef unsigned long ulong;\ntypedef long int lint;\ntypedef long long int llint;\n\n\n#endif" } ], "tools": [] diff --git a/docs-c/core/Addon.md b/docs-c/core/Addon.md new file mode 100644 index 00000000..21290a0e --- /dev/null +++ b/docs-c/core/Addon.md @@ -0,0 +1,82 @@ +# Addon + +**Path**: [`src/modbus/Addon.h`](../src/modbus/Addon.h) + +**Revision History**: Initial documentation + +Addon is a base class that extends the Component class, providing a foundation for creating modular extensions within the modbus system. It serves as a backward compatibility layer while ensuring consistent behavior across different types of add-ons. + +## REQUIREMENTS + +- ESP-32 microcontroller +- Platform.io development environment + +## PROVIDES + +- `Addon` class: Base class for creating modular components +- `Addons` type: A Vector container for storing Addon pointers +- `AddonFnPtr` type: Function pointer type for addon methods +- `byId()` utility function: Find an addon by its ID in a collection + +## FEATURES + +- Inheritance from Component class for consistent behavior +- Default run flags for standard execution modes +- Support for identifying addons through unique IDs +- Vector-based storage and retrieval of addons + +## DEPENDENCIES + +- [Component.h](../src/modbus/Component.h) +- [enums.h](../src/modbus/enums.h) +- [error_codes.h](../src/modbus/error_codes.h) +- [macros.h](../src/modbus/macros.h) +- [WString.h](https://github.com/espressif/arduino-esp32/blob/master/cores/esp32/WString.h) +- [Vector.h](https://github.com/janelia-arduino/Vector) +- [Arduino.h](https://github.com/espressif/arduino-esp32/blob/master/cores/esp32/Arduino.h) + +```mermaid +graph TD + Addon --> Component + Addon --> enums + Addon --> error_codes + Addon --> macros + Addon --> WString + Addon --> Vector +``` + +## BEHAVIOUR + +The Addon class extends Component, inheriting its lifecycle methods and adding specific functionality for modular components. + +```mermaid +graph TD + Start[Initialize Addon] --> Construct[Constructor sets name, id, flags] + Construct --> Setup[Setup phase] + Setup --> Loop[Loop function execution] + Loop --> Info[Info reporting] + Info --> Loop +``` + +## TODOS + +### PERFORMANCE + +- Consider optimizing the `byId()` function for large collections using more efficient data structures or search algorithms +- Evaluate memory usage when multiple addons are instantiated + +### SECURITY + +- Add validation for addon IDs to prevent duplication +- Consider implementing access control mechanisms for sensitive addons + +### COMPLIANCE + +- Ensure compatibility with industrial Modbus-485 specifications +- Maintain backward compatibility with existing addon implementations + +### RECOMMENDATIONS + +- Use meaningful, unique IDs for each addon to simplify management +- Consider implementing a registration system to avoid manual management of addon collections +- Document specific addon implementations with clear usage examples \ No newline at end of file diff --git a/docs-c/core/App.md b/docs-c/core/App.md new file mode 100644 index 00000000..4ef5928f --- /dev/null +++ b/docs-c/core/App.md @@ -0,0 +1,95 @@ +--- +title: "App - Application Component" +description: "The App class is the main application component that manages the lifecycle and interactions of all components in the system." +keywords: ["ESP32", "App", "Component", "PlatformIO", "ModbusRTU", "embedded system"] +--- + +## App + +**Path**: [`src/App.cpp`](src/App.cpp) + +**Revision History**: Initial documentation + +The App class serves as the main application component responsible for managing the lifecycle and execution of all components in the system. It is derived from the Component base class and provides functionality for setting up, running the main loop, debugging, and retrieving information about the application and its components. The App acts as a container and orchestrator for all child components. + +## REQUIREMENTS + +- ESP32 microcontroller +- PlatformIO build environment +- C++17 support + +## PROVIDES + +- `App` class that manages the application lifecycle +- Component management functionality +- Component registration with Bridge for communication +- Debugging infrastructure + +## FEATURES + +- Component management (adding, retrieving, and querying components) +- Application lifecycle management (setup, loop) +- Component setup and execution orchestration +- Debug facilities with configurable intervals +- Component registration with Bridge for communication +- Component filtering by flags + +## DEPENDENCIES + +- [Component](./Component.md) +- [Bridge](./Bridge.md) +- [Vector](./Vector.md) +- [xtypes](./xtypes.md) +- [xtimer](./xtimer.md) + +```mermaid +graph TD + App --> Component + App --> Bridge + App --> Vector + App --> xtypes + App --> xtimer +``` + +## BEHAVIOUR + +The App class follows a standard application lifecycle pattern: + +```mermaid +graph TD + Start[Start] --> Constructor[Constructor] + Constructor --> Setup[Setup] + Setup --> RegisterComponents[Register Components] + RegisterComponents --> OnRun[onRun] + OnRun --> Loop[Loop] + Loop --> Debug[Debug if interval passed] + Debug --> Loop + Loop --> End[End] +``` + +## TODOS + +### PERFORMANCE + +- Consider implementing component prioritization for the main loop +- Optimize component lookup by ID using a hashmap instead of linear search +- Implement a more efficient mechanism for component flag filtering + +### SECURITY + +- Implement component access control or validation mechanisms +- Add proper error handling for component operations +- Consider adding checks for component validity before operations + +### COMPLIANCE + +- Ensure memory management follows best practices for embedded systems +- Validate that the application meets real-time requirements for industrial settings + +### RECOMMENDATIONS + +- Use the `byId` method to retrieve components when you know their IDs +- Configure appropriate debug intervals based on the application's performance requirements +- Group related components with similar flags for easier management +- Implement a more structured approach for component initialization and dependency management +- Consider implementing a component health monitoring system \ No newline at end of file diff --git a/docs-c/core/Bridge.md b/docs-c/core/Bridge.md new file mode 100644 index 00000000..d52e9496 --- /dev/null +++ b/docs-c/core/Bridge.md @@ -0,0 +1,99 @@ +# Bridge + +**Path**: [src/modbus/Bridge.h](../../../src/modbus/Bridge.h) + +**Revision History**: +- Initial documentation + +A Bridge component that enables inter-component messaging and remote method invocation in an ESP32-based system. It allows components to register member functions that can be called via a message passing mechanism, typically triggered by Modbus commands. + +## REQUIREMENTS + +- No specific hardware pins required +- Software dependencies for messaging and component management + +## PROVIDES + +- `SComponentInfo`: A structure that stores information about registered component methods + - Stores component key, instance pointer, method name, and function pointer +- `Bridge`: Main class that manages component method registration and message handling + - Inherits from `Component` base class + +## FEATURES + +- Register component member functions for remote invocation +- Message routing between components +- Method lookup by component ID and method name +- Debug support for listing registered methods +- Support for component discovery for Modbus management + +## DEPENDENCIES + +- [Component](./Component.md): Base class for all components +- [WString](https://github.com/espressif/arduino-esp32): String manipulation +- [xtypes](./xtypes.md): Type definitions +- [enums](./enums.md): Enumeration definitions +- [macros](./macros.md): Macro definitions +- [Vector](./Vector.md): Container for storing component information +- [Streaming](./Streaming.md): Stream output utilities + +```mermaid +graph TD + Bridge --> Component + Bridge --> WString + Bridge --> xtypes + Bridge --> enums + Bridge --> macros + Bridge --> Vector + Bridge --> Streaming +``` + +## BEHAVIOUR + +The Bridge acts as a message router between components. It maintains a registry of component methods and handles method invocation based on message parameters. + +```mermaid +sequenceDiagram + participant Caller + participant Bridge + participant Component + + Caller->>Bridge: onMessage(id, verb, flags, user, src) + alt EC_METHOD verb + Bridge->>Bridge: Parse method parameters + Bridge->>Bridge: hasMethod(id, methodName) + alt Method found + Bridge->>Component: Invoke method + Component-->>Bridge: Return result + Bridge-->>Caller: Return result + else Method not found + Bridge-->>Caller: Return E_NOT_FOUND + end + else Other verbs + Bridge-->>Caller: Return E_OK + end +``` + +## TODOS + +### PERFORMANCE + +- The current implementation uses dynamic memory allocation for component registration which could lead to heap fragmentation. Consider using a fixed-size pool for `SComponentInfo` objects. +- Message parsing could be optimized for cases with large numbers of registered components. + +### SECURITY + +- There is no authentication mechanism for method invocation. Consider adding a permission system. +- Method invocation through Modbus should be validated to prevent buffer overflows and other security issues. + +### COMPLIANCE + +- Ensure proper memory management to comply with embedded system best practices. +- Test for compliance with industrial communication standards when used with Modbus. + +### RECOMMENDATIONS + +- Use descriptive method names and consistent component IDs to make debugging easier. +- Keep registered method count below the `MAX_COMPONENTS` limit (defined in configuration). +- When adding new components, always register their methods during initialization. +- Consider implementing an unregister mechanism for dynamically loaded/unloaded components. \ No newline at end of file diff --git a/docs-c/core/CommandMessage.md b/docs-c/core/CommandMessage.md new file mode 100644 index 00000000..7aa52f06 --- /dev/null +++ b/docs-c/core/CommandMessage.md @@ -0,0 +1,93 @@ +--- +title: "CommandMessage Class - Message Parsing System" +description: "Documentation for the CommandMessage class used to parse and structure messages in a Modbus system" +keywords: ["ESP-32", "Modbus", "communication", "command parsing", "messaging"] +--- + +## CommandMessage + +**Path**: [src/modbus/command_message.h](../src/modbus/command_message.h) + +**Revision History**: Initial documentation + +The CommandMessage class provides a structured approach to parse, validate, and manage command messages within the system. It supports message serialization and deserialization with defined start/end markers and delimiters. This class is designed for efficient messaging in industrial Modbus-485 communications. + +## REQUIREMENTS + +- No specific hardware pins required +- Requires StringUtils and enums support + +## PROVIDES + +- **CommandMessage**: The main class for message handling +- **DEBUG_MESSAGES_PARSE**: Conditional compilation flag for verbose message parsing output +- **DEBUG_MESSAGES_PARSER**: Macro for debug logging of message parsing operations +- **MESSAGE_TOKENS**: Constant defining the expected number of tokens in a valid message + +## FEATURES + +- Structured message storage with ID, verb, flags, and payload +- Timestamp tracking for message receipt +- Validation of message format with start/end markers +- Parsing of delimited message content +- Clear method for resetting message state +- Match checking for message format validation + +## DEPENDENCIES + +- [Vector](../src/Vector.h) +- [ArduinoLog](../src/ArduinoLog.h) +- [StringUtils](../src/StringUtils.h) +- [Enums](../src/modbus/enums.h) + +```mermaid +graph TD + CommandMessage --> StringUtils + CommandMessage --> ArduinoLog + CommandMessage --> Vector + CommandMessage --> Enums +``` + +## BEHAVIOUR + +```mermaid +sequenceDiagram + participant Client + participant CommandMessage + + Client->>CommandMessage: Create message with id, verb, flags + Client->>CommandMessage: parse(message) + CommandMessage->>CommandMessage: clear() + CommandMessage->>CommandMessage: Extract content between markers + CommandMessage->>CommandMessage: Split by delimiters + CommandMessage->>CommandMessage: Validate token count + CommandMessage->>CommandMessage: Assign id, verb, flags, payload + CommandMessage->>CommandMessage: Set timestamp + CommandMessage-->>Client: Return success/failure +``` + +## TODOS + +### PERFORMANCE + +- Consider pre-allocating memory for the message parsing to avoid heap fragmentation +- Evaluate the use of String vs. char arrays for payload handling in memory-constrained environments +- Potential for optimizing token parsing with a more efficient approach than strtok + +### SECURITY + +- Consider implementing message integrity checking (checksums or CRC) +- Add length validation to prevent buffer overflows +- Implement validation of message payload content based on expected verb/command + +### COMPLIANCE + +- Ensure all message processing complies with Modbus protocol standards where applicable +- Consider adding support for standard industrial message formats + +### RECOMMENDATIONS + +- Add support for binary payloads for more efficient communication +- Consider implementing a message queue system to handle multiple messages +- Add methods to serialize a CommandMessage back to a string for bidirectional communication +- Consider adding timeout functionality for message handling \ No newline at end of file diff --git a/docs-c/core/Component.md b/docs-c/core/Component.md new file mode 100644 index 00000000..a228fec6 --- /dev/null +++ b/docs-c/core/Component.md @@ -0,0 +1,97 @@ +--- +title: "Component Class" +description: "A generic component class for embedded systems" +keywords: ["component", "ESP-32", "modbus", "industrial application"] +--- + +## Component + +**Path**: [`src/modbus/Component.h`](../src/modbus/Component.h) + +**Revision History**: Initial documentation + +The Component class represents a generic component for industrial applications. It provides a foundation for creating various types of components with consistent behavior and interface. These components can be integrated into a modbus-based industrial control system. + +## REQUIREMENTS + +- ESP-32 platform +- No specific hardware pins required as this is a base class + +## PROVIDES + +- `Component` class - A base class for creating components in an industrial application +- Constants: + - `COMPONENT_DEFAULT` - Default run flags for a component + - `COMPONENT_NO_ID` - Default ID for a component + +## FEATURES + +- Name-based component identification +- ID-based component identification +- Configurable run flags +- Support for owner-child relationship between components +- Type identification through enumeration +- Integration with Modbus communication + +## DEPENDENCIES + +- [`WString.h`](../src/WString.h) - String handling +- [`ArduinoLog.h`](../src/ArduinoLog.h) - Logging functionality +- [`Vector.h`](../src/Vector.h) - Dynamic array implementation +- [`enums.h`](../src/modbus/enums.h) - Enumeration definitions +- [`constants.h`](../src/modbus/constants.h) - Constant definitions +- [`error_codes.h`](../src/modbus/error_codes.h) - Error code definitions +- [`macros.h`](../src/modbus/macros.h) - Macro definitions +- [`xtypes.h`](../src/modbus/xtypes.h) - Extended type definitions + +```mermaid +graph TD + Component --> WString + Component --> ArduinoLog + Component --> Vector + Component --> enums + Component --> constants + Component --> error_codes + Component --> macros + Component --> xtypes + ModbusBlock[ModbusBlockView] --> Component + ModbusTCP --> Component + RS485 --> Component + Bridge --> Component +``` + +## BEHAVIOUR + +The Component class operates as a base class that defines common behavior for various industrial components. + +```mermaid +graph TD + Init[Initialize Component] --> Setup[Setup Component] + Setup --> Loop[Run Loop] + Loop --> |Run Flags| Loop + Loop --> |End of loop| Cleanup[Cleanup] +``` + +## TODOS + +### PERFORMANCE + +- Consider implementing lazy initialization for components with high initialization costs +- Evaluate the memory footprint of components in resource-constrained environments + +### SECURITY + +- Implement access control mechanisms for sensitive components +- Ensure proper validation of component IDs and types to prevent misuse + +### COMPLIANCE + +- Review compliance with industrial standards for component interfaces +- Ensure compatibility with Modbus protocol specifications + +### RECOMMENDATIONS + +- Extend this base class for specific component types rather than modifying the base class +- Use meaningful names and IDs for components to improve system maintainability +- Set appropriate run flags based on the component's intended behavior +- Organize components in a logical hierarchy using the owner-child relationship \ No newline at end of file diff --git a/docs-c/core/Logger.md b/docs-c/core/Logger.md new file mode 100644 index 00000000..9b6ecec8 --- /dev/null +++ b/docs-c/core/Logger.md @@ -0,0 +1,108 @@ +--- +title: "CircularLogPrinter - In-Memory Logging with Optional Output" +description: "A circular buffer for storing log messages with ESP-IDF integration" +keywords: ["ESP32", "logging", "circular buffer", "ESP-IDF", "modbus"] +--- + +## CircularLogPrinter + +**Path**: [`src/modbus/circular_log_printer.h`](../../src/modbus/circular_log_printer.h) + +**Revision History**: +- 2023-11-15: Initial documentation + +CircularLogPrinter is an Arduino-compatible Print implementation that stores the last N log lines in a circular buffer. It can optionally mirror all output to another stream (like Serial). The component is designed to be 100% reinterpret-cast-free for MISRA/CPPCHECK compliance, making it suitable for industrial applications. + +## REQUIREMENTS + +- FreeRTOS (for optional thread safety) +- ESP32 IDF framework +- Global Serial object (when using default constructor) + +## PROVIDES + +- `using LogRingBuffer = char[LOG_BUFFER_LINES][LOG_BUFFER_LINE_LENGTH]` +- `class CircularLogPrinter`: A Print implementation storing log lines in a circular buffer + +## FEATURES + +- Configurable circular buffer size via macros +- Thread-safe operation (optional) +- Ability to attach to ESP-IDF's logging system +- No pointer type casting (MISRA compliant) +- Retrieves log history by line number +- Optional mirroring to any Print-compatible output +- Efficient multi-byte writes + +## DEPENDENCIES + +- [Arduino.h](https://github.com/espressif/arduino-esp32) +- [Print.h](https://github.com/espressif/arduino-esp32) +- [esp_log.h](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/system/log.html) + +```mermaid +graph TD + CircularLogPrinter --> Arduino + CircularLogPrinter --> Print + CircularLogPrinter --> ESP_Log +``` + +## BEHAVIOUR + +```mermaid +stateDiagram-v2 + [*] --> Ready + Ready --> Accumulating: write(byte) + Accumulating --> Accumulating: write non-newline + Accumulating --> CommittingLine: write newline + CommittingLine --> Ready: Store line & update indices + Ready --> Cleared: clear() + Cleared --> Ready: write(byte) +``` + +## TODOS + +### PERFORMANCE + +- Consider using a more efficient buffer strategy for high-volume logging +- Potential optimization by pre-allocating buffer space when attaching to ESP-IDF log + +### SECURITY + +- No direct security concerns as this is an output-only component +- Consider adding sanitization for control characters that could affect terminal displays + +### COMPLIANCE + +- Already designed for MISRA/CPPCHECK compliance with no reinterpret casts +- Consider adding formal verification of buffer access bounds + +### RECOMMENDATIONS + +- Adjust `LOG_BUFFER_LINES` and `LOG_BUFFER_LINE_LENGTH` based on available memory and application needs +- For performance-critical applications, consider disabling thread safety with `LOG_BUFFER_THREAD_SAFE=0` +- When using with Modbus, attach to ESP-IDF logging to capture all system messages including Modbus communication logs + +## Example + +```cpp +#include "circular_log_printer.h" + +// Create logger with default output (Serial) +CircularLogPrinter logger; + +void setup() { + Serial.begin(115200); + + // Attach to ESP-IDF logging system + logger.attachToEspLog(); + + // Log some messages + logger.println("System initializing..."); + + // Later, retrieve historical logs + for (size_t i = 0; i < logger.lines(); i++) { + Serial.println(logger.getLine(i)); + } +} +``` \ No newline at end of file diff --git a/docs-c/core/SerialMessage.md b/docs-c/core/SerialMessage.md new file mode 100644 index 00000000..21a179ec --- /dev/null +++ b/docs-c/core/SerialMessage.md @@ -0,0 +1,97 @@ +--- +title: "SerialMessage - Serial Communication Component" +description: "A component that handles serial communication over a stream, providing methods for reading and parsing command messages." +keywords: "SerialMessage, serial communication, command parsing, ESP32, modbus" +--- + +## SerialMessage + +**Path**: [`src/modbus/SerialMessage.h`](../src/modbus/SerialMessage.h) + +**Revision History**: Initial documentation + +SerialMessage is a component that handles serial communication over a provided Stream. It is responsible for reading incoming data from the serial port, parsing it into structured command messages, and forwarding these messages to its owner component. The class periodically checks for new messages on the configured serial interface. + +## REQUIREMENTS + +- A valid Stream object (like Serial, Serial1, etc.) for communication +- Sufficient memory for the receive buffer (SERIAL_RX_BUFFER_SIZE, defaults to 256 bytes) + +## PROVIDES + +- **SerialMessage class**: Main component for handling serial communication + +## FEATURES + +- Periodic checking for new serial commands (configurable interval) +- Parsing of raw serial data into structured CommandMessage objects +- Event-based message handling through owner component notification +- Configurable message delimiter (default: newline) +- Debug capabilities for troubleshooting communication issues + +## DEPENDENCIES + +- [Vector](../src/Vector.h) +- [ArduinoLog](../src/ArduinoLog.h) +- [Arduino.h](https://www.arduino.cc/reference/en/) +- [xtypes.h](../src/xtypes.h) +- [Component.h](../src/Component.h) +- [CommandMessage.h](../src/CommandMessage.h) +- [config.h](../src/config.h) + +```mermaid +graph TD + SerialMessage --> Component + SerialMessage --> CommandMessage + SerialMessage --> Vector + SerialMessage --> ArduinoLog + SerialMessage --> xtypes + SerialMessage --> config +``` + +## BEHAVIOUR + +The SerialMessage component operates on a time-based polling mechanism: + +```mermaid +graph TD + Start([Start]) --> Setup[Setup SerialMessage] + Setup --> Loop[Loop] + Loop --> CheckInterval{Time to check?} + CheckInterval -->|No| Loop + CheckInterval -->|Yes| ReadSerial[Read from serial] + ReadSerial --> DataAvailable{Data available?} + DataAvailable -->|No| UpdateTime[Update lastRead time] + DataAvailable -->|Yes| ParseMessage[Parse to CommandMessage] + ParseMessage --> ValidMessage{Valid message?} + ValidMessage -->|No| UpdateTime + ValidMessage -->|Yes| NotifyOwner[Notify owner component] + NotifyOwner --> UpdateTime + UpdateTime --> Loop +``` + +## TODOS + +### PERFORMANCE + +- Consider implementing interrupt-driven serial reading rather than polling for high-throughput applications +- Optimize buffer handling for memory-constrained environments +- Evaluate the impact of the polling interval on application responsiveness + +### SECURITY + +- Add input validation to prevent buffer overflow attacks +- Consider implementing message authentication for secure applications +- Evaluate the need for encryption for sensitive data transmission + +### COMPLIANCE + +- Ensure compliance with relevant industrial communication standards when used in Modbus-485 applications +- Verify error handling mechanisms meet application reliability requirements + +### RECOMMENDATIONS + +- Set an appropriate SERIAL_COMMAND_PARSE_INTERVAL based on your application's requirements +- For debugging communication issues, enable DEBUG_SERIAL_MESSAGES in your build configuration +- When extending this component, consider implementing additional message validation logic +- For applications with multiple serial interfaces, create separate SerialMessage instances for each interface \ No newline at end of file diff --git a/docs-c/core/StringUtils.md b/docs-c/core/StringUtils.md new file mode 100644 index 00000000..98bb706c --- /dev/null +++ b/docs-c/core/StringUtils.md @@ -0,0 +1,89 @@ +--- +title: "StringUtils - String Manipulation Utilities" +description: "A utility class for string manipulation and type conversion for ESP-32 applications" +keywords: "ESP-32, C++17, string utilities, type conversion, industrial application, Modbus" +--- + +## StringUtils + +**Path**: [`src/modbus/StringUtils.h`](../src/modbus/StringUtils.h) + +**Revision History**: Initial documentation + +StringUtils provides a set of utility functions for string manipulation and type conversion in an industrial ESP-32 application. It enables parsing and converting string values to native C types, which is particularly useful for configuration and communication scenarios in Modbus applications. + +## REQUIREMENTS + +No specific hardware pins are required as this is a software utility helper. + +## PROVIDES + +- `E_VALUE_TYPE` enumeration for representing different value types +- Template function `convertTo` for converting strings to various native types +- Type detection functions (`isInteger`, `isFloat`, `detectType`) +- Hex data printing utility + +## FEATURES + +- Specialized template implementations for converting strings to: + - Integer types (`int`, `short`, `long int`) + - Floating-point (`float`) + - Boolean values (`bool`) +- Type detection to determine if a string represents a valid integer or floating-point value +- Platform-specific adaptations for string tokenization across different compilers +- Hex data visualization for debugging + +## DEPENDENCIES + +- [`./xtypes.h`](../src/modbus/xtypes.h) +- Standard C libraries: ``, ``, ``, `` + +```mermaid +graph TD + StringUtils --> xtypes + StringUtils --> "Standard C Libraries" +``` + +## BEHAVIOUR + +The StringUtils component follows a simple conversion and detection workflow: + +```mermaid +graph LR + Input[Input String] --> Detect[Detect Type] + Detect --> |Integer| ConvertInt[Convert to Integer] + Detect --> |Float| ConvertFloat[Convert to Float] + Detect --> |Boolean| ConvertBool[Convert to Boolean] + Detect --> |Text| HandleText[Handle as String] + ConvertInt --> Result[Native Type Result] + ConvertFloat --> Result + ConvertBool --> Result + HandleText --> Result +``` + +## TODOS + +### PERFORMANCE + +- Consider using `strtol`, `strtof`, etc. for all conversions to provide better error handling +- Optimize the string scanning operations for large strings +- Add string length validation to avoid buffer overruns + +### SECURITY + +- Add input validation to prevent exploitation through malformed strings +- Consider adding bounds checking for numeric conversions +- Implement sanitization for strings that will be used in sensitive contexts + +### COMPLIANCE + +- Ensure compatibility with C17 standard +- Verify that all functions work correctly in the target ESP-32 environment +- Audit for potential compliance issues with industrial standards + +### RECOMMENDATIONS + +- Extend type support to include additional numeric types as needed +- Add more robust error handling for conversion failures +- Consider adding string formatting utilities for consistent output generation +- For improved performance, use static or cached conversions for frequently used values \ No newline at end of file diff --git a/docs-c/core/ValueWrapper.md b/docs-c/core/ValueWrapper.md new file mode 100644 index 00000000..c390a43e --- /dev/null +++ b/docs-c/core/ValueWrapper.md @@ -0,0 +1,96 @@ +--- +title: "ValueWrapper Template Class" +description: "A templated class for managing values with threshold-based notifications through Modbus" +keywords: "ValueWrapper, Modbus, threshold, notification, embedded systems, ESP32" +--- + +## ValueWrapper + +**Path**: [`src/modbus/ValueWrapper.h`](../src/modbus/ValueWrapper.h) + +**Revision History**: Initial documentation + +A template class that wraps values of any type with threshold-based update notifications through Modbus. This class monitors changes in a value and sends notifications to the owner component when changes exceed specified thresholds, using different comparison modes. It helps reduce network traffic by only sending updates when significant changes occur. + +## REQUIREMENTS + +- An owning Component instance that receives notifications +- A Modbus address space for notifications +- Function code configuration for the Modbus messages + +## PROVIDES + +- `ValueWrapper` - Template class for wrapping values of any type +- `ThresholdMode` enumeration: + - `DIFFERENCE` - Triggers when absolute difference between old and new values exceeds threshold + - `INTERVAL_STEP` - Triggers when values cross step boundaries defined by the threshold + +- Macros: + - `DEFAULT_VW_POST_NOTIFY_LAMBDA` - Helper for creating standard post-notification logging lambdas + - `INIT_COMPONENT_VALUE_WRAPPER` - Simplifies ValueWrapper initialization + +## FEATURES + +- Wraps any data type, including enums, with transparent type conversion +- Provides automatic threshold-based notifications through Modbus +- Supports different threshold modes for different types of data +- Handles both arithmetic types and enumerations correctly +- Provides callbacks when notifications are sent +- Automatic typecasting for Modbus message values + +## DEPENDENCIES + +- [`Component.h`](../src/Component.md) +- [`modbus/ModbusTypes.h`](../src/modbus/ModbusTypes.md) +- [`enums.h`](../src/enums.md) +- [`Logger.h`](../src/Logger.md) + +```mermaid +graph TD + ValueWrapper --> Component + ValueWrapper --> ModbusTypes + ValueWrapper --> enums + ValueWrapper --> Logger +``` + +## BEHAVIOUR + +The ValueWrapper monitors changes to a value and sends notifications when thresholds are exceeded. + +```mermaid +stateDiagram-v2 + [*] --> Initialized: Constructor + Initialized --> Comparing: update(newValue) + Comparing --> ThresholdNotMet: Below threshold + Comparing --> ThresholdExceeded: Above threshold + ThresholdNotMet --> ValueUpdated: Update value without notification + ThresholdExceeded --> NotifyOwner: Send Modbus update + NotifyOwner --> CallbackExecution: Execute post-notification callback + CallbackExecution --> ValueUpdated: Value updated + ValueUpdated --> Initialized: Ready for next update +``` + +## TODOS + +### PERFORMANCE + +- Consider providing specialized implementations for common types to avoid template instantiation overhead +- Investigate using std::variant for better type safety and potentially reduced memory footprint +- Add an optional hysteresis parameter to prevent rapid oscillations around threshold values + +### SECURITY + +- Add bounds checking for Modbus addresses to prevent accidental overwrites +- Consider adding validation callbacks to ensure values remain within acceptable ranges + +### COMPLIANCE + +- Ensure all Modbus messages conform to the Modbus specification requirements +- Consider adding support for additional Modbus function codes as needed + +### RECOMMENDATIONS + +- Use the `INIT_COMPONENT_VALUE_WRAPPER` macro to simplify instance creation +- Choose appropriate threshold values to balance responsiveness and network traffic +- For enum types, use threshold value of 1 to notify on any state change +- For float values, consider the INTERVAL_STEP mode to create logical boundaries for notifications \ No newline at end of file diff --git a/docs-c/core/constants.md b/docs-c/core/constants.md new file mode 100644 index 00000000..b51f7ab6 --- /dev/null +++ b/docs-c/core/constants.md @@ -0,0 +1,58 @@ +# Constants + +**Path**: [`src/modbus/constants.h`](../src/modbus/constants.h) + +**Revision History**: Initial documentation + +This file defines global constants used throughout the application, particularly for component management and debug settings. + +## REQUIREMENTS + +No specific hardware requirements. This is a header-only file that provides constant values. + +## PROVIDES + +- Global constant definitions: + - `MAX_COMPONENTS`: Maximum number of components in the system + - `DEFAULT_DEBUG_INTERVAL`: Default interval for debug operations in milliseconds + +## FEATURES + +- Defines system-wide constants for configuration +- Sets limits for component management +- Establishes default timing values + +## DEPENDENCIES + +None. This is a standalone header file. + +```mermaid +graph TD + Constants["Constants"] +``` + +## BEHAVIOUR + +This component doesn't have behavior as it only provides constant definitions. + +## TODOS + +### PERFORMANCE + +- Consider evaluating if `MAX_COMPONENTS` is appropriately sized for the application's needs +- Review if any constants should be configurable at compile time based on target hardware + +### SECURITY + +- No direct security concerns as these are compile-time constants + +### COMPLIANCE + +- Ensure constant values comply with any relevant industrial standards or specifications +- Consider documenting the reasoning behind specific values for future reference + +### RECOMMENDATIONS + +- Use these constants throughout the application rather than hard-coding values +- Consider moving to a more structured configuration system if the number of constants grows substantially +- Document the impact of changing these values on system behavior and resource usage \ No newline at end of file diff --git a/docs-c/core/enums.md b/docs-c/core/enums.md new file mode 100644 index 00000000..6b7bf24d --- /dev/null +++ b/docs-c/core/enums.md @@ -0,0 +1,87 @@ +--- +title: "Modbus Enums and Constants" +description: "Documentation for the Modbus enumerations and constants used in the industrial control application" +keywords: "Modbus, ESP32, Industrial Control, Enumerations, Constants, Error Codes" +--- + +## Modbus Enumerations and Constants + +**Path**: [/src/modbus/enums.h](../../../src/modbus/enums.h) + +**Revision History**: Initial documentation + +This file defines the core enumerations and constants used throughout the industrial control application, particularly for Modbus communications, component management, and error handling. + +## REQUIREMENTS + +- No direct hardware requirements +- Consistent implementation across the codebase + +## PROVIDES + +- `COMPONENT_KEY_BASE` - Base key identifiers for system components +- `COMPONENT_TYPE` - Component type classification +- Error Code Constants - System-wide error definitions +- `OBJECT_RUN_FLAGS` - Runtime behavior flags for components +- `OBJECT_NET_CAPS` - Network capability flags +- `MB_REGISTER_MODE` - Modbus register access modes +- `E_CALLS` - Call type definitions +- `E_MessageFlags` - Message processing flags +- `MB_FC` - Modbus function codes +- `MODBUS_ERRORS` - Standard Modbus error codes +- `E_ModbusType` - Modbus data type definitions +- `E_ModbusAccess` - Modbus access permissions + +## FEATURES + +- Comprehensive error code system with reserved ranges for subsystems +- Modbus protocol constants aligned with industry standards +- Component type classification for system organization +- Runtime behavior flags for component lifecycle management +- Message handling flags for inter-component communication + +## DEPENDENCIES + +- No direct dependencies on other components + +```mermaid +graph TD + Enums[Modbus Enumerations and Constants] + OtherComponents[Other System Components] + OtherComponents --> Enums +``` + +## BEHAVIOUR + +The enumerations and constants in this file define the protocol and behavior parameters used by other components in the system. + +```mermaid +graph TD + Constants[Constants Definition] --> Usage[Used by Components] + Usage --> ComponentInteraction[Component Interaction] + Usage --> ErrorHandling[Error Handling] + Usage --> ModbusProtocol[Modbus Protocol Execution] +``` + +## TODOS + +### PERFORMANCE + +- Consider using more compact types for memory-constrained applications +- Evaluate if any enumerations could be consolidated to reduce memory usage + +### SECURITY + +- Ensure error codes don't leak sensitive information in production environments +- Consider adding authentication/authorization related constants for secure Modbus implementations + +### COMPLIANCE + +- Maintain alignment with Modbus specification for all Modbus-related constants +- Consider adding comments with references to specific sections of the Modbus specification + +### RECOMMENDATIONS + +- When extending error codes, maintain the hierarchical structure +- When adding new component types, ensure they follow the established naming convention +- Consider adding range validation macros alongside the constants \ No newline at end of file diff --git a/docs-c/core/error_codes.md b/docs-c/core/error_codes.md new file mode 100644 index 00000000..d05fdf74 --- /dev/null +++ b/docs-c/core/error_codes.md @@ -0,0 +1,75 @@ +# Error Codes + +**Path**: [`src/modbus/error_codes.h`](../../src/modbus/error_codes.h) + +**Revision History**: Initial documentation + +A comprehensive collection of error codes used throughout the Modbus implementation. This header defines specific error code enumerations for different types of errors that can occur during Modbus operations. + +## REQUIREMENTS + +No specific hardware pins required. This is a pure software component providing error code definitions. + +## PROVIDES + +- `ModbusErrorCode` - Enumeration for general Modbus error codes +- `ModbusDeviceErrorCode` - Enumeration for device-specific Modbus error codes +- `ModbusExceptionCode` - Enumeration for standard Modbus exception codes + +## FEATURES + +- Standardized error code definitions for Modbus protocol implementation +- Clear separation between different categories of errors +- Compliance with standard Modbus exception codes +- Error codes to handle both protocol-level and device-specific errors + +## DEPENDENCIES + +None. This is a standalone header file defining error code enumerations. + +```mermaid +graph TD + ErrorCodes[Error Codes] +``` + +## BEHAVIOUR + +The error codes are used to report and handle various error conditions throughout the Modbus implementation. + +```mermaid +graph TD + Start[Operation Start] --> Operation[Modbus Operation] + Operation --> CheckError{Error?} + CheckError -->|No| Success[Operation Successful] + CheckError -->|Yes| Identify[Identify Error Type] + Identify -->|Protocol Error| ProtocolError[ModbusErrorCode] + Identify -->|Device Error| DeviceError[ModbusDeviceErrorCode] + Identify -->|Exception| Exception[ModbusExceptionCode] + ProtocolError --> Handle[Handle Error] + DeviceError --> Handle + Exception --> Handle + Handle --> End[End Operation] + Success --> End +``` + +## TODOS + +### PERFORMANCE + +- Consider optimizing error handling mechanisms for minimal runtime overhead + +### SECURITY + +- Review error codes to ensure they don't expose sensitive system information +- Consider adding error codes for security-related issues (authentication failures, access violations) + +### COMPLIANCE + +- Verify that all Modbus exception codes match the latest Modbus specification +- Consider adding compliance with IEC 61131-3 error handling recommendations + +### RECOMMENDATIONS + +- Use appropriate error codes consistently across the codebase +- Consider extending error codes with additional information for debugging purposes +- Implement comprehensive error logging using these error codes \ No newline at end of file diff --git a/docs-c/core/json.md b/docs-c/core/json.md new file mode 100644 index 00000000..242b7122 --- /dev/null +++ b/docs-c/core/json.md @@ -0,0 +1,81 @@ +--- +title: "JSON Utilities" +description: "Utility functions for parsing JSON data in embedded applications" +keywords: ["JSON", "ArduinoJson", "parsing", "ESP32", "embedded"] +--- + +## JSON Utilities + +**Path**: [`src/utils/json_utils.h`](../../src/utils/json_utils.h) + +**Revision History**: +- Initial documentation + +A collection of inline utility functions for parsing JSON data in embedded applications. These functions provide a consistent way to extract values from JSON objects, with built-in type validation and error reporting. + +## REQUIREMENTS + +- ArduinoJson library +- ArduinoLog library + +## PROVIDES + +- `JsonUtils` namespace containing utility functions: + - `parseJsonFieldUint32`: Parses a 32-bit unsigned integer from JSON + - `parseJsonFieldUint8`: Parses an 8-bit unsigned integer from JSON + - `parseJsonFieldBool`: Parses a boolean value from JSON + +## FEATURES + +- Type validation for JSON field parsing +- Maintains default values when fields are missing or of incorrect type +- Debug logging for parsing errors +- Consistent parsing interface across different data types + +## DEPENDENCIES + +- [ArduinoJson](https://arduinojson.org/) +- [ArduinoLog](https://github.com/thijse/Arduino-Log) + +```mermaid +graph TD + JsonUtils --> ArduinoJson + JsonUtils --> ArduinoLog +``` + +## BEHAVIOUR + +```mermaid +graph TD + Start([Parse JSON Field]) --> CheckField{Field exists?} + CheckField -- Yes --> CheckType{Correct type?} + CheckField -- No --> UseDefault[Use default value] + CheckType -- Yes --> AssignValue[Assign parsed value] + CheckType -- No --> LogWarning[Log warning] --> UseDefault + AssignValue --> End([End]) + UseDefault --> End +``` + +## TODOS + +### PERFORMANCE + +- Consider providing batch parsing functions to reduce parsing overhead for multiple fields +- Evaluate memory usage when parsing large JSON objects + +### SECURITY + +- Currently no validation for value ranges; consider adding range validation options +- Implement protection against malformed JSON that could lead to buffer overflows + +### COMPLIANCE + +- Ensure all error messages follow consistent formatting standards +- Consider adding more documentation for compliance with project coding standards + +### RECOMMENDATIONS + +- Add support for additional data types (e.g., float, string) +- Consider a more structured approach to error handling (e.g., error codes or exceptions) +- Add functions for array parsing and nested object traversal +- Provide examples of common JSON parsing scenarios \ No newline at end of file diff --git a/docs-c/core/macros.md b/docs-c/core/macros.md new file mode 100644 index 00000000..8702f0d9 --- /dev/null +++ b/docs-c/core/macros.md @@ -0,0 +1,87 @@ +--- +title: "Macros Utility Header" +description: "Documentation for the macros utility header which provides various helper macros for ESP32 C17 development" +keywords: "ESP32, C17, macros, utilities, embedded, industrial" +--- + +# Macros Utility + +**Path**: [`src/modbus/macros.h`](../src/modbus/macros.h) + +**Revision History**: Initial documentation + +A comprehensive collection of utility macros for ESP-32 development. This header provides macro definitions for commonly used operations in embedded systems programming, avoiding the use of the standard library. It includes macros for type manipulation, bit operations, mathematical functions, array handling, compile-time optimizations, and more. + +## Requirements + +- ESP-32 platform +- C17 compliant compiler +- No standard library dependencies + +## Provides + +- **Incrementation/Decrementation**: `INC_X`, `DEC_X`, `INCREMENT`, `DECREMENT` +- **Compiler Optimizations**: `FORCE_INLINE`, `_UNUSED`, `NOOP` +- **Argument Counting/Testing**: `NUM_ARGS`, `TWO_ARGS` +- **String Helpers**: `CAT`, `_CAT` +- **Conditional Compilation**: `ENABLED`, `DISABLED`, `ANY`, `ALL`, `NONE` +- **Bit Manipulation**: `__BV`, `TEST`, `SBI`, `CBI`, `SET_BIT_TO` +- **Math Operations**: `WITHIN`, `RADIANS`, `DEGREES`, `NEAR_ZERO` +- **Time Helpers**: `PENDING`, `ELAPSED`, `HOUR_MS`, `MIN_MS` +- **Array Initialization**: `ARRAY_N`, `ARRAY_1` through `ARRAY_6` +- **C++11 Template Utilities**: Min/Max templates, SFINAE helpers + +## Features + +- Preprocessor macros for common calculations and operations +- Bit manipulation utilities +- Compile-time optimizations +- Safe type conversion helpers +- Template-based compile-time checks +- Time management utilities +- Array initialization helpers +- Conditional execution based on feature flags + +## Dependencies + +- [`./xtypes.h`](../src/modbus/xtypes.h) + +```mermaid +graph TD + Macros --> XTypes +``` + +## Behavior + +The macros in this header provide utility functions that are used throughout the codebase. They do not have state or flow by themselves but enable cleaner, more efficient code. + +```mermaid +graph LR + Application[Application Code] -->|Uses macros| Macros + Macros -->|Expand at compile time| Optimized[Optimized Binary] +``` + +## TODOs + +### Performance + +- Consider separating into multiple headers for faster compilation +- Add pre-processor optimization for frequently used macro combinations + +### Security + +- No direct security concerns as these are compile-time macros +- Ensure macros don't introduce unintended behaviors when used with user input + +### Compliance + +- Compatible with C17 standard +- Designed for ESP-32 and industrial Modbus applications +- No use of standard library functions + +### Recommendations + +- Group related macros when using in implementation files +- Use the ENABLED/DISABLED macros for feature toggling +- Prefer the template-based MIN/MAX over macro versions when in C++ code +- Consider the resource constraints of ESP-32 when using complex macro expansions \ No newline at end of file diff --git a/docs-c/core/pid_constants.md b/docs-c/core/pid_constants.md new file mode 100644 index 00000000..f0d8bb13 --- /dev/null +++ b/docs-c/core/pid_constants.md @@ -0,0 +1,84 @@ +--- +title: "Cassandra 650 Test Data Constants" +description: "Reference constants and verified test data for the Cassandra 650 hot-plate system" +keywords: ["modbus", "cassandra", "hotplate", "test data", "constants"] +--- + +## Cassandra 650 Test Data + +**Path**: [`src/modbus/cassandra650_testdata.h`](../../../src/modbus/cassandra650_testdata.h) + +**Revision History**: +- 2025-05-01: Initial verified version with full equation markup. + +This header file provides verified test data constants for the Cassandra 650 hot-plate system, serving as a reference for simulation, testing, and validation of the hot-plate control system. + +The constants follow specific notation: +- Values ending in `_X100` are stored multiplied by 100 (e.g., 1.25 becomes 125) +- Dimensions are in millimeters unless otherwise noted +- Time units: s = seconds, min = minutes +- Frequency: mHz = milli-hertz (10⁻³ Hz) + +## REQUIREMENTS + +- No specific hardware pins required as this is a constants-only header +- Compatible with C17 standard +- ESP-32 platform as target + +## PROVIDES + +- **Heat-plate geometry constants**: Dimensions of the heat plate +- **Heater configuration constants**: Specifications for the heating elements +- **Cooling performance constants**: Natural cooling rates +- **Temperature-loop precision constants**: Control precision specifications +- **Oscillation constants**: Timing parameters for temperature holding +- **Heating performance constants**: Rate of temperature increase under different conditions +- **Derived thermodynamic properties**: Calculated constants based on measured data +- **Sequential heating constants**: Parameters for multi-partition heating systems + +## FEATURES + +- Comprehensive set of validated constants for the Cassandra 650 hot-plate system +- Clear organization of constants by functional category +- Documented equations showing relationships between constants +- Scaled values (×100) to maintain precision with integer constants + +## DEPENDENCIES + +None - this is a self-contained constants header + +```mermaid +graph TD + cassandra650_testdata[cassandra650_testdata.h] + + style cassandra650_testdata fill:#f9f,stroke:#333,stroke-width:2px +``` + +## BEHAVIOUR + +As a constants header file, this component doesn't have behavior or state transitions. + +## TODOS + +### PERFORMANCE + +- Consider adding temperature gradient constants across the plate surface +- Add thermal profile constants for different materials/loads + +### SECURITY + +- No security concerns for constant definitions + +### COMPLIANCE + +- Constants should be verified periodically against actual measurements to ensure continued accuracy +- Consider adding IEC or other industrial standards references for measurement methodologies + +### RECOMMENDATIONS + +- When updating constant values, maintain the equation references to document the relationships +- Consider adding a table or graph representation of heating/cooling curves in documentation +- To extend this module, consider adding constants for: + - Different material loads (e.g., glass, metal, ceramic) + - Energy consumption metrics + - Wear and aging factors \ No newline at end of file diff --git a/docs-c/core/xmath.md b/docs-c/core/xmath.md new file mode 100644 index 00000000..82daf94b --- /dev/null +++ b/docs-c/core/xmath.md @@ -0,0 +1,83 @@ +--- +title: "XMath - Utility Functions for Mathematical Operations" +description: "Lightweight mathematical functions including template-based clamping and range checking" +keywords: ["ESP-32", "math", "clamp", "range", "normalize"] +--- + +## XMath + +**Path**: [`src/modbus/xmath.h`](../src/modbus/xmath.h) + +**Revision History**: +- Initial documentation + +A lightweight utility header that provides essential mathematical operations for embedded systems. It includes a template-based clamp function and macros for range checking and normalized clamping. + +## REQUIREMENTS + +- C++17 compiler support +- No hardware-specific requirements + +## PROVIDES + +- `clamp()` - Template function for clamping values within specified bounds +- `RANGE()` - Macro for checking if a value is within a specified range +- `NCLAMP()` - Macro for normalizing a value within a range to [0.0, 1.0] + +## FEATURES + +- Type-generic clamping through template implementation +- Range checking for value validation +- Value normalization to a [0.0, 1.0] range +- Conditional compilation for C++ environments +- No standard library dependencies + +## DEPENDENCIES + +- None + +```mermaid +graph TD + Application --> XMath +``` + +## BEHAVIOUR + +The XMath utilities provide deterministic mathematical operations: + +```mermaid +graph LR + A[Input Value] --> B{clamp} + B --> |value < low| C[Return low] + B --> |value > high| D[Return high] + B --> |low ≤ value ≤ high| E[Return value] + + F[Input Value] --> G{RANGE} + G --> |min < value < max| H[Return true] + G --> |else| I[Return false] + + J[Input Value] --> K{NCLAMP} + K --> L[Normalize to 0.0-1.0 range] +``` + +## TODOS + +### PERFORMANCE + +- Consider specialized implementations for common numeric types to avoid template instantiation overhead +- Evaluate potential for SIMD optimizations on compatible platforms + +### SECURITY + +- No known security concerns as the utilities perform pure mathematical operations without side effects + +### COMPLIANCE + +- Ensure all mathematical operations maintain expected precision for safety-critical applications +- Verify behavior with edge cases (e.g., handling of NaN, Inf values) + +### RECOMMENDATIONS + +- Use `clamp()` when working with specific types to leverage compile-time type checking +- Prefer the NCLAMP macro when normalizing sensor readings or other input values to a standardized range +- Consider adding additional mathematical utilities as needed for specific application domains \ No newline at end of file diff --git a/docs-c/core/xstatistics.md b/docs-c/core/xstatistics.md new file mode 100644 index 00000000..57abdaab --- /dev/null +++ b/docs-c/core/xstatistics.md @@ -0,0 +1,90 @@ +--- +title: "XStatistics Class - Statistical Analysis Toolkit" +description: "A lightweight C++ class for statistical analysis, providing functionality for calculating mean, min/max, variance, and standard deviation of numerical datasets" +keywords: ["ESP32", "statistics", "C++", "industrial", "standard deviation", "variance", "average"] +--- + +## XStatistics + +**Path**: [`src/modbus/xstatistics.h`](../src/modbus/xstatistics.h) + +**Revision History**: Initial documentation + +The XStatistics module provides a lightweight statistical analysis toolkit for numerical datasets. It is designed for use in industrial applications on ESP32 platforms where standard C++ libraries might not be available. The Statistic class enables efficient calculation of basic statistical measures like average, minimum, maximum, variance, and standard deviations without requiring dynamic memory allocation. + +## REQUIREMENTS + +- No specific hardware requirements +- C++ compiler supporting C++11 features +- Arduino core for ESP32 + +## PROVIDES + +- `Statistic` class for collecting and analyzing numerical datasets +- Template functions for basic operations (`MIN`, `MAX`, `ABS`) +- Conditional compilation for standard deviation functionality using the `STAT_USE_STDEV` flag + +## FEATURES + +- Calculation of basic statistical measures (count, sum, min, max, average) +- Optional calculation of variance and standard deviation +- Memory-efficient implementation suitable for embedded systems +- No dynamic memory allocation required +- Incremental calculation methods that avoid storing the entire dataset + +## DEPENDENCIES + +- [Arduino.h](https://www.arduino.cc/reference/en/) +- [math.h](https://en.cppreference.com/w/cpp/header/cmath) + +```mermaid +graph TD + XStatistics --> Arduino + XStatistics --> Math +``` + +## BEHAVIOUR + +The Statistic class collects numerical data points one at a time and maintains running calculations of statistical measures. + +```mermaid +graph TD + Start[Create Statistic object] --> Clear[Initialize counters] + Clear --> Add[Add data point] + Add --> Calculate[Calculate statistics] + Add --> |More data points| Add + Calculate --> |mean/average| Mean + Calculate --> |min/max| MinMax + Calculate --> |count| Count + Calculate --> |sum| Sum + + subgraph "Optional functionality" + Calculate --> |STAT_USE_STDEV| Variance + Variance --> PopStdev[Population standard deviation] + Variance --> UnbiasedStdev[Unbiased standard deviation] + end +``` + +## TODOS + +### PERFORMANCE + +- The code includes an optimization comment indicating a 10% faster calculation method for standard deviation, but it limits samples to 65K due to potential overflow. Consider implementing a version that handles larger datasets. +- For larger datasets, consider adding functionality to discard outliers or implement online algorithms that require less memory. + +### SECURITY + +- No significant security concerns as this is a mathematical utility class without I/O operations. +- Consider adding bounds checking to prevent potential numerical overflows in extreme use cases. + +### COMPLIANCE + +- The implementation follows standard computational formulas for statistical measures. +- The class might need additional methods like confidence intervals or hypothesis testing for compliance with specific industrial data analysis standards. + +### RECOMMENDATIONS + +- Use this class when you need to monitor trends and statistics for sensor data or control variables. +- When using on systems with limited floating-point performance, consider monitoring the computational overhead. +- The optional standard deviation features can be disabled by removing the `STAT_USE_STDEV` definition to reduce code size and execution time. +- Consider extending the class with additional statistical functions like median, mode, or quartile calculations for more comprehensive analysis. \ No newline at end of file diff --git a/docs-c/core/xtimer.md b/docs-c/core/xtimer.md new file mode 100644 index 00000000..bd5140e7 --- /dev/null +++ b/docs-c/core/xtimer.md @@ -0,0 +1,93 @@ +--- +title: "XTimer - A Lightweight Timer Implementation" +description: "A template-based timer utility for scheduling tasks at specific intervals or times" +keywords: ["timer", "ESP-32", "embedded", "C++", "scheduling", "task management"] +--- + +## XTimer + +**Path**: [`src/modbus/xtimer.h`](../src/modbus/xtimer.h) + +**Revision History**: +- Initial documentation + +A lightweight, template-based timer implementation designed for embedded systems, specifically targeting ESP-32. The timer provides functionality for scheduling tasks to run after a delay, at a specific time, or at regular intervals. It avoids using the standard library, making it suitable for resource-constrained environments. + +## REQUIREMENTS + +- No specific hardware pins required +- C++17 support +- Arduino framework or equivalent (`millis()` function) + +## PROVIDES + +- `Timer` - Main timer class template +- `handler_t` - Function pointer type for task callbacks +- `timer_create_default()` - Utility function to create a timer with default settings +- `task` struct - Internal structure to represent scheduled tasks + +## FEATURES + +- Schedule tasks to run after a specified delay +- Schedule tasks to run at specific time points +- Schedule recurring tasks at regular intervals +- Configurable number of maximum concurrent tasks +- Customizable time function (defaults to `millis()`) +- Small memory footprint with fixed-size task array +- No dynamic memory allocation + +## DEPENDENCIES + +- [`src/modbus/macros.h`](../src/modbus/macros.h) +- Arduino core (`Arduino.h` or `WProgram.h` for older versions) + +```mermaid +graph TD + XTimer --> Macros + XTimer --> Arduino["Arduino Core"] +``` + +## BEHAVIOUR + +The timer operates by tracking tasks and their scheduled execution times: + +```mermaid +graph TD + Start[Initialize Timer] --> AddTask[Add Task to Queue] + AddTask --> Tick[Timer Tick] + Tick --> CheckTasks[Check Task Expiry] + CheckTasks --> TaskExpired{Task Expired?} + TaskExpired -- Yes --> Execute[Execute Task Handler] + TaskExpired -- No --> Tick + Execute --> IsRepeat{Is Recurring?} + IsRepeat -- Yes --> ResetTask[Reset Timer for Next Run] + ResetTask --> Tick + IsRepeat -- No --> RemoveTask[Remove Task from Queue] + RemoveTask --> Tick +``` + +## TODOS + +### PERFORMANCE + +- Consider a more efficient data structure than a simple array for tasks, especially for systems with many scheduled tasks +- Implement a priority queue to optimize the `tick()` function when handling many tasks +- Provide options to reduce memory usage for systems with very limited RAM + +### SECURITY + +- No major security concerns as this is a local timer implementation +- Ensure task handlers do not introduce timing vulnerabilities in security-critical applications + +### COMPLIANCE + +- Review for MISRA C++ compliance if used in safety-critical applications +- Ensure thread safety if used in multi-threaded environments + +### RECOMMENDATIONS + +- Keep task handlers lightweight to avoid blocking the main loop +- Avoid using too many concurrent tasks as it may impact performance +- For time-critical operations, consider using hardware timers instead +- When using the `every()` function for periodic tasks, be aware of potential timing drift over long periods +- Consider implementing a task ID return value to allow for cancellation of scheduled tasks \ No newline at end of file diff --git a/docs-c/core/xtypes.md b/docs-c/core/xtypes.md new file mode 100644 index 00000000..e8918576 --- /dev/null +++ b/docs-c/core/xtypes.md @@ -0,0 +1,64 @@ +# Types + +**Path**: [`src/modbus/types.h`](../src/modbus/types.h) + +**Revision History**: +- Initial documentation + +A core utility header that defines fundamental type aliases used throughout the codebase. This file provides standardized type definitions to ensure consistent data type usage across the application. + +## REQUIREMENTS + +No specific hardware requirements. This is a pure software utility. + +## PROVIDES + +- `cchar`: A type alias for constant character data +- `uchar`: A type alias for unsigned character data +- `millis_t`: A type alias for millisecond time representations +- `ushort`: A type alias for unsigned short integers +- `ulong`: A type alias for unsigned long integers +- `lint`: A type alias for long integers +- `llint`: A type alias for long long integers + +## FEATURES + +- Simple, standardized type definitions +- Cross-platform compatibility +- Consistent naming convention for derived types +- Avoids usage of standard library types + +## DEPENDENCIES + +- [``](https://en.cppreference.com/w/c/types/integer) - Standard C library for integer types + +```mermaid +graph TD + Types --> stdint +``` + +## BEHAVIOUR + +This component is a passive header-only utility that doesn't have runtime behavior. + +## TODOS + +### PERFORMANCE + +- No specific performance considerations as these are basic type aliases + +### SECURITY + +- Consider defining overflow-safe numeric types for sensitive operations + +### COMPLIANCE + +- Verify that these type definitions are consistent with the target hardware architecture +- Ensure compliance with C17 standard + +### RECOMMENDATIONS + +- Use these type aliases consistently throughout the codebase +- Consider adding more explicit sizing (e.g., `uint32_t`) for architecture-dependent types where exact bit sizes are critical +- When adding new types, maintain the established naming convention +- Document the expected size (in bits) for each type to avoid platform-specific issues \ No newline at end of file diff --git a/scripts/docs-m.sh b/scripts/docs-m.sh index bb9e5af8..b6a03eb9 100644 --- a/scripts/docs-m.sh +++ b/scripts/docs-m.sh @@ -1,10 +1,9 @@ kbot-d --model=anthropic/claude-3.7-sonnet \ --prompt=./scripts/docs-m.md \ - --each=./src/modbus/*.h \ + --each=./src/*.h \ --globExtension=match-cpp \ --mode=completion \ --filters=markdown \ --preferences=none \ - --exclude='./docs-c/modbus/Modbus.h' \ - --exclude='./docs-c/modbus/${SRC_NAME}.md' \ - --dst='./docs-c/modbus/${SRC_NAME}.md' + --exclude='./docs-c/core/${SRC_NAME}.md' \ + --dst='./docs-c/core/${SRC_NAME}.md'