// ================================================================================================= // eModbus: Copyright 2020 by Michael Harwerth, Bert Melis and the contributors to eModbus // MIT license - see license.md for details // ================================================================================================= #ifndef _MODBUS_CLIENT_H #define _MODBUS_CLIENT_H #include #include #include "options.h" #include "ModbusMessage.h" #if HAS_FREERTOS extern "C" { #include #include } #elif IS_LINUX #include #endif #if USE_MUTEX #include // NOLINT using std::mutex; using std::lock_guard; #endif typedef std::function MBOnData; typedef std::function MBOnError; typedef std::function MBOnResponse; class ModbusClient { public: bool onDataHandler(MBOnData handler); // Accept onData handler bool onErrorHandler(MBOnError handler); // Accept onError handler bool onResponseHandler(MBOnResponse handler); // Accept onResponse handler uint32_t getMessageCount(); // Informative: return number of messages created uint32_t getErrorCount(); // Informative: return number of errors received void resetCounts(); // Set both message and error counts to zero inline Error addRequest(ModbusMessage m, uint32_t token) { return addRequestM(m, token); } inline ModbusMessage syncRequest(ModbusMessage m, uint32_t token) { return syncRequestM(m, token); } // Template function to generate syncRequest functions as long as there is a // matching ModbusMessage::setMessage() call template ModbusMessage syncRequest(uint32_t token, Args&&... args) { Error rc = SUCCESS; // Create request, if valid ModbusMessage m; rc = m.setMessage(std::forward(args) ...); // Add it to the queue and wait for a response, if valid if (rc == SUCCESS) { return syncRequestM(m, token); } // Else return the error as a message return buildErrorMsg(rc, std::forward(args) ...); } // Template function to create an error response message from a variadic pattern template ModbusMessage buildErrorMsg(Error e, uint8_t serverID, uint8_t functionCode, Args&&... args) { ModbusMessage m; m.setError(serverID, functionCode, e); return m; } // Template function to generate addRequest functions as long as there is a // matching ModbusMessage::setMessage() call template Error addRequest(uint32_t token, Args&&... args) { Error rc = SUCCESS; // Return value // Create request, if valid ModbusMessage m; rc = m.setMessage(std::forward(args) ...); // Add it to the queue, if valid if (rc == SUCCESS) { return addRequestM(m, token); } // Else return the error return rc; } protected: ModbusClient(); // Default constructor virtual void isInstance() = 0; // Make class abstract ModbusMessage waitSync(uint8_t serverID, uint8_t functionCode, uint32_t token); // wait for syncRequest response to arrive // Virtual addRequest variant needed internally. All others done by template! virtual Error addRequestM(ModbusMessage msg, uint32_t token) = 0; // Virtual syncRequest variant following the same pattern virtual ModbusMessage syncRequestM(ModbusMessage msg, uint32_t token) = 0; // Prevent copy construction or assignment ModbusClient(ModbusClient& other) = delete; ModbusClient& operator=(ModbusClient& other) = delete; uint32_t messageCount; // Number of requests generated. Used for transactionID in TCPhead uint32_t errorCount; // Number of errors received #if HAS_FREERTOS TaskHandle_t worker; // Interface instance worker task #elif IS_LINUX pthread_t worker; #endif MBOnData onData; // Data response handler MBOnError onError; // Error response handler MBOnResponse onResponse; // Uniform response handler static uint16_t instanceCounter; // Number of ModbusClients created std::map syncResponse; // Map to hold response messages on synchronous requests #if USE_MUTEX std::mutex syncRespM; // Mutex protecting syncResponse map against race conditions std::mutex countAccessM; // Mutex protecting access to the message and error counts #endif // Let any ModbusBridge class use protected members template friend class ModbusBridge; }; #endif