// ================================================================================================= // eModbus: Copyright 2020 by Michael Harwerth, Bert Melis and the contributors to eModbus // MIT license - see license.md for details // ================================================================================================= #ifndef _MODBUS_MESSAGE_H #define _MODBUS_MESSAGE_H #include "ModbusTypeDefs.h" #include "ModbusError.h" #include #include using Modbus::Error; using Modbus::FCType; using Modbus::FCT; using std::vector; class ModbusMessage { public: // Default empty message Constructor - optionally takes expected size of MM_data explicit ModbusMessage(uint16_t dataLen = 0); // Special message Constructor - takes a std::vector explicit ModbusMessage(std::vector s); // Message constructors - internally setMessage() is called // WARNING: if parameters are invalid, message will _NOT_ be set up! template ModbusMessage(uint8_t serverID, uint8_t functionCode, Args&&... args) { // NOLINT Error e = SUCCESS; if ((e = setMessage(serverID, functionCode, std::forward(args) ...)) != SUCCESS) { printError(__FILE__, __LINE__, e, serverID, functionCode); } } // Destructor ~ModbusMessage(); // Assignment operator ModbusMessage& operator=(const ModbusMessage& m); // Copy constructor ModbusMessage(const ModbusMessage& m); #ifndef NO_MOVE // Move constructor ModbusMessage(ModbusMessage&& m); // Move assignment ModbusMessage& operator=(ModbusMessage&& m); #endif // Comparison operators bool operator==(const ModbusMessage& m); bool operator!=(const ModbusMessage& m); operator bool(); // Exposed methods of std::vector const uint8_t *data(); // address of MM_data uint16_t size(); // used length in MM_data uint8_t operator[](uint16_t index) const; // provide restricted operator[] interface void push_back(const uint8_t& val); // add a byte at the end of MM_data void clear(); // delete message contents uint16_t resize(uint16_t newSize); // resize MM_data // provide iterator interface on MM_data typedef std::vector::const_iterator const_iterator; const_iterator begin() const { return MM_data.begin(); } const_iterator end() const { return MM_data.end(); } // Add append() for two ModbusMessages or a std::vector to be appended void append(ModbusMessage& m); void append(std::vector& m); // Modbus data extraction uint8_t getServerID() const; // returns Server ID or 0 if MM_data is shorter than 3 uint8_t getFunctionCode() const; // returns FC or 0 if MM_data is shorter than 3 Error getError() const; // getError() - returns error code (MM_data[2], if MM_data[1] > 0x7F, else SUCCESS) // Modbus data manipulation void setServerID(uint8_t serverID); // Change server ID void setFunctionCode(uint8_t FC); // Change function code // add() variant to copy a buffer into MM_data. Returns updated size uint16_t add(const uint8_t *arrayOfBytes, uint16_t count); // add() - add a single data element MSB first to MM_data. Returns updated size template uint16_t add(T v) { uint16_t sz = sizeof(T); // Size of value to be added // Copy it MSB first while (sz) { sz--; MM_data.push_back((v >> (sz << 3)) & 0xFF); } // Return updated size (logical length of message so far) return MM_data.size(); } // Template function to extend add(A) to add(A, B, C, ...) template typename std::enable_if::value, uint16_t>::type add(T v, Args... args) { add(v); return add(args...); } // get() - read a byte array of a given size into a vector. Returns updated index uint16_t get(uint16_t index, vector& v, uint8_t count) const; // get() - recursion stopper for template function below inline uint16_t get(uint16_t index) const { return index; } // Template function to extend getOne(index, A&) to get(index, A&, B&, C&, ...) template typename std::enable_if::value, uint16_t>::type get(uint16_t index, T& v, Args&... args) const { uint16_t pos = getOne(index, v); return get(pos, args...); } // add() variant for vectors of uint8_t uint16_t add(vector v); // add() variants for float and double values uint16_t add(float v, int swapRules = 0); uint16_t add(double v, int swapRules = 0); // get() variants for float and double values uint16_t get(uint16_t index, float& v, int swapRules = 0) const; uint16_t get(uint16_t index, double& v, int swapRules = 0) const; // Message generation methods // 1. no additional parameter (FCs 0x07, 0x0b, 0x0c, 0x11) Error setMessage(uint8_t serverID, uint8_t functionCode); // 2. one uint16_t parameter (FC 0x18) Error setMessage(uint8_t serverID, uint8_t functionCode, uint16_t p1); // 3. two uint16_t parameters (FC 0x01, 0x02, 0x03, 0x04, 0x05, 0x06) Error setMessage(uint8_t serverID, uint8_t functionCode, uint16_t p1, uint16_t p2); // 4. three uint16_t parameters (FC 0x16) Error setMessage(uint8_t serverID, uint8_t functionCode, uint16_t p1, uint16_t p2, uint16_t p3); // 5. two uint16_t parameters, a uint8_t length byte and a uint8_t* pointer to array of words (FC 0x10) Error setMessage(uint8_t serverID, uint8_t functionCode, uint16_t p1, uint16_t p2, uint8_t count, uint16_t *arrayOfWords); // 6. two uint16_t parameters, a uint8_t length byte and a uint16_t* pointer to array of bytes (FC 0x0f) Error setMessage(uint8_t serverID, uint8_t functionCode, uint16_t p1, uint16_t p2, uint8_t count, uint8_t *arrayOfBytes); // 7. generic constructor for preformatted data ==> count is counting bytes! Error setMessage(uint8_t serverID, uint8_t functionCode, uint16_t count, uint8_t *arrayOfBytes); // 8. error response Error setError(uint8_t serverID, uint8_t functionCode, Error errorCode); protected: // Data validation methods - used by the above! // 0. serverID and function code - used by all of the below static Error checkServerFC(uint8_t serverID, uint8_t functionCode); // 1. no additional parameter (FCs 0x07, 0x0b, 0x0c, 0x11) static Error checkData(uint8_t serverID, uint8_t functionCode); // 2. one uint16_t parameter (FC 0x18) static Error checkData(uint8_t serverID, uint8_t functionCode, uint16_t p1); // 3. two uint16_t parameters (FC 0x01, 0x02, 0x03, 0x04, 0x05, 0x06) static Error checkData(uint8_t serverID, uint8_t functionCode, uint16_t p1, uint16_t p2); // 4. three uint16_t parameters (FC 0x16) static Error checkData(uint8_t serverID, uint8_t functionCode, uint16_t p1, uint16_t p2, uint16_t p3); // 5. two uint16_t parameters, a uint8_t length byte and a uint8_t* pointer to array of words (FC 0x10) static Error checkData(uint8_t serverID, uint8_t functionCode, uint16_t p1, uint16_t p2, uint8_t count, uint16_t *arrayOfWords); // 6. two uint16_t parameters, a uint8_t length byte and a uint16_t* pointer to array of bytes (FC 0x0f) static Error checkData(uint8_t serverID, uint8_t functionCode, uint16_t p1, uint16_t p2, uint8_t count, uint8_t *arrayOfBytes); // 7. generic constructor for preformatted data ==> count is counting bytes! static Error checkData(uint8_t serverID, uint8_t functionCode, uint16_t count, uint8_t *arrayOfBytes); // Error output in case a message constructor will fail static void printError(const char *file, int lineNo, Error e, uint8_t serverID, uint8_t functionCode); std::vector MM_data; // Message data buffer static uint8_t floatOrder[sizeof(float)]; // order of bytes in a float variable static uint8_t doubleOrder[sizeof(double)]; // order of bytes in a double variable static uint8_t determineFloatOrder(); static uint8_t determineDoubleOrder(); static float swapFloat(float& f, int swapRule); static double swapDouble(double& f, int swapRule); // getOne() - read a MSB-first value starting at byte index. Returns updated index template uint16_t getOne(uint16_t index, T& retval) const { uint16_t sz = sizeof(retval); // Size of value to be read retval = 0; // return value // Will it fit? if (index <= MM_data.size() - sz) { // Yes. Copy it MSB first while (sz) { sz--; retval <<= 8; retval |= MM_data[index++]; } } return index; } }; #endif