mirror of
https://github.com/dalathegreat/Battery-Emulator.git
synced 2025-10-04 10:19:29 +02:00
add lib/eModbus-eModbus
This commit is contained in:
parent
86decfbc51
commit
8a1d3c1da5
29 changed files with 4 additions and 4 deletions
199
Software/src/lib/eModbus-eModbus/ModbusBridgeTemp.h
Normal file
199
Software/src/lib/eModbus-eModbus/ModbusBridgeTemp.h
Normal file
|
@ -0,0 +1,199 @@
|
|||
// =================================================================================================
|
||||
// eModbus: Copyright 2020 by Michael Harwerth, Bert Melis and the contributors to eModbus
|
||||
// MIT license - see license.md for details
|
||||
// =================================================================================================
|
||||
#ifndef _MODBUS_BRIDGE_TEMP_H
|
||||
#define _MODBUS_BRIDGE_TEMP_H
|
||||
|
||||
#include <map>
|
||||
#include <functional>
|
||||
#include "ModbusClient.h"
|
||||
#include "ModbusClientTCP.h" // Needed for client.setTarget()
|
||||
#include "RTUutils.h" // Needed for RTScallback
|
||||
|
||||
#undef LOCAL_LOG_LEVEL
|
||||
#define LOCAL_LOG_LEVEL LOG_LEVEL_VERBOSE
|
||||
#include "Logging.h"
|
||||
|
||||
using std::bind;
|
||||
using std::placeholders::_1;
|
||||
|
||||
// Known server types: TCP (client, host/port) and RTU (client)
|
||||
enum ServerType : uint8_t { TCP_SERVER, RTU_SERVER };
|
||||
|
||||
// Bridge class template, takes one of ModbusServerRTU, ModbusServerWiFi, ModbusServerEthernet or ModbusServerTCPasync as parameter
|
||||
template<typename SERVERCLASS>
|
||||
class ModbusBridge : public SERVERCLASS {
|
||||
public:
|
||||
// Constructor for TCP server variants.
|
||||
ModbusBridge();
|
||||
|
||||
// Constructors for the RTU variant. Parameters as are for ModbusServerRTU
|
||||
ModbusBridge(HardwareSerial& serial, uint32_t timeout, int rtsPin = -1);
|
||||
ModbusBridge(HardwareSerial& serial, uint32_t timeout, RTScallback rts);
|
||||
|
||||
// Destructor
|
||||
~ModbusBridge();
|
||||
|
||||
// Method to link external servers to the bridge
|
||||
bool attachServer(uint8_t aliasID, uint8_t serverID, uint8_t functionCode, ModbusClient *client, IPAddress host = IPAddress(0, 0, 0, 0), uint16_t port = 0);
|
||||
|
||||
// Link another function code to the server
|
||||
bool addFunctionCode(uint8_t aliasID, uint8_t functionCode);
|
||||
|
||||
// Block a function code (respond with ILLEGAL_FUNCTION error)
|
||||
bool denyFunctionCode(uint8_t aliasID, uint8_t functionCode);
|
||||
|
||||
protected:
|
||||
// ServerData holds all data necessary to address a single server
|
||||
struct ServerData {
|
||||
uint8_t serverID; // External server id
|
||||
ModbusClient *client; // client to be used to request the server
|
||||
ServerType serverType; // TCP_SERVER or RTU_SERVER
|
||||
IPAddress host; // TCP: host IP address, else 0.0.0.0
|
||||
uint16_t port; // TCP: host port number, else 0
|
||||
|
||||
// RTU constructor
|
||||
ServerData(uint8_t sid, ModbusClient *c) :
|
||||
serverID(sid),
|
||||
client(c),
|
||||
serverType(RTU_SERVER),
|
||||
host(IPAddress(0, 0, 0, 0)),
|
||||
port(0) {}
|
||||
|
||||
// TCP constructor
|
||||
ServerData(uint8_t sid, ModbusClient *c, IPAddress h, uint16_t p) :
|
||||
serverID(sid),
|
||||
client(c),
|
||||
serverType(TCP_SERVER),
|
||||
host(h),
|
||||
port(p) {}
|
||||
};
|
||||
|
||||
// Default worker functions
|
||||
ModbusMessage bridgeWorker(ModbusMessage msg);
|
||||
ModbusMessage bridgeDenyWorker(ModbusMessage msg);
|
||||
|
||||
// Map of servers attached
|
||||
std::map<uint8_t, ServerData *> servers;
|
||||
};
|
||||
|
||||
// Constructor for TCP variants
|
||||
template<typename SERVERCLASS>
|
||||
ModbusBridge<SERVERCLASS>::ModbusBridge() :
|
||||
SERVERCLASS() { }
|
||||
|
||||
// Constructors for RTU variant
|
||||
template<typename SERVERCLASS>
|
||||
ModbusBridge<SERVERCLASS>::ModbusBridge(HardwareSerial& serial, uint32_t timeout, int rtsPin) :
|
||||
SERVERCLASS(serial, timeout, rtsPin) { }
|
||||
|
||||
// Alternate constructors for RTU variant
|
||||
template<typename SERVERCLASS>
|
||||
ModbusBridge<SERVERCLASS>::ModbusBridge(HardwareSerial& serial, uint32_t timeout, RTScallback rts) :
|
||||
SERVERCLASS(serial, timeout, rts) { }
|
||||
|
||||
// Destructor
|
||||
template<typename SERVERCLASS>
|
||||
ModbusBridge<SERVERCLASS>::~ModbusBridge() {
|
||||
// Release ServerData storage in servers array
|
||||
for (auto itr = servers.begin(); itr != servers.end(); itr++) {
|
||||
delete (itr->second);
|
||||
}
|
||||
servers.clear();
|
||||
}
|
||||
|
||||
// attachServer: memorize the access data for an external server with ID serverID under bridge ID aliasID
|
||||
template<typename SERVERCLASS>
|
||||
bool ModbusBridge<SERVERCLASS>::attachServer(uint8_t aliasID, uint8_t serverID, uint8_t functionCode, ModbusClient *client, IPAddress host, uint16_t port) {
|
||||
|
||||
// Is there already an entry for the aliasID?
|
||||
if (servers.find(aliasID) == servers.end()) {
|
||||
// No. Store server data in map.
|
||||
|
||||
// Do we have a port number?
|
||||
if (port != 0) {
|
||||
// Yes. Must be a TCP client
|
||||
servers[aliasID] = new ServerData(serverID, static_cast<ModbusClient *>(client), host, port);
|
||||
LOG_D("(TCP): %02X->%02X %d.%d.%d.%d:%d\n", aliasID, serverID, host[0], host[1], host[2], host[3], port);
|
||||
} else {
|
||||
// No - RTU client required
|
||||
servers[aliasID] = new ServerData(serverID, static_cast<ModbusClient *>(client));
|
||||
LOG_D("(RTU): %02X->%02X\n", aliasID, serverID);
|
||||
}
|
||||
}
|
||||
|
||||
// Register the server/FC combination for the bridgeWorker
|
||||
addFunctionCode(aliasID, functionCode);
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename SERVERCLASS>
|
||||
bool ModbusBridge<SERVERCLASS>::addFunctionCode(uint8_t aliasID, uint8_t functionCode) {
|
||||
// Is there already an entry for the aliasID?
|
||||
if (servers.find(aliasID) != servers.end()) {
|
||||
// Yes. Link server to own worker function
|
||||
this->registerWorker(aliasID, functionCode, std::bind(&ModbusBridge<SERVERCLASS>::bridgeWorker, this, std::placeholders::_1));
|
||||
LOG_D("FC %02X added for server %02X\n", functionCode, aliasID);
|
||||
} else {
|
||||
LOG_E("Server %d not attached to bridge!\n", aliasID);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename SERVERCLASS>
|
||||
bool ModbusBridge<SERVERCLASS>::denyFunctionCode(uint8_t aliasID, uint8_t functionCode) {
|
||||
// Is there already an entry for the aliasID?
|
||||
if (servers.find(aliasID) != servers.end()) {
|
||||
// Yes. Link server to own worker function
|
||||
this->registerWorker(aliasID, functionCode, std::bind(&ModbusBridge<SERVERCLASS>::bridgeDenyWorker, this, std::placeholders::_1));
|
||||
LOG_D("FC %02X blocked for server %02X\n", functionCode, aliasID);
|
||||
} else {
|
||||
LOG_E("Server %d not attached to bridge!\n", aliasID);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// bridgeWorker: default worker function to process bridge requests
|
||||
template<typename SERVERCLASS>
|
||||
ModbusMessage ModbusBridge<SERVERCLASS>::bridgeWorker(ModbusMessage msg) {
|
||||
uint8_t aliasID = msg.getServerID();
|
||||
uint8_t functionCode = msg.getFunctionCode();
|
||||
ModbusMessage response;
|
||||
|
||||
// Find the (alias) serverID
|
||||
if (servers.find(aliasID) != servers.end()) {
|
||||
// Found it. We may use servers[aliasID] now without allocating a new map slot
|
||||
|
||||
// Set real target server ID
|
||||
msg.setServerID(servers[aliasID]->serverID);
|
||||
|
||||
// Issue the request
|
||||
LOG_D("Request (%02X/%02X) sent\n", servers[aliasID]->serverID, functionCode);
|
||||
// TCP servers have a target host/port that needs to be set in the client
|
||||
if (servers[aliasID]->serverType == TCP_SERVER) {
|
||||
response = reinterpret_cast<ModbusClientTCP *>(servers[aliasID]->client)->syncRequestMT(msg, (uint32_t)millis(), servers[aliasID]->host, servers[aliasID]->port);
|
||||
} else {
|
||||
response = servers[aliasID]->client->syncRequestM(msg, (uint32_t)millis());
|
||||
}
|
||||
|
||||
// Re-set the requested server ID
|
||||
response.setServerID(aliasID);
|
||||
} else {
|
||||
// If we get here, something has gone wrong internally. We send back an error response anyway.
|
||||
response.setError(aliasID, functionCode, INVALID_SERVER);
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
// bridgeDenyWorker: worker function to block function codes
|
||||
template<typename SERVERCLASS>
|
||||
ModbusMessage ModbusBridge<SERVERCLASS>::bridgeDenyWorker(ModbusMessage msg) {
|
||||
ModbusMessage response;
|
||||
response.setError(msg.getServerID(), msg.getFunctionCode(), ILLEGAL_FUNCTION);
|
||||
return response;
|
||||
}
|
||||
|
||||
#endif
|
Loading…
Add table
Add a link
Reference in a new issue