Add serial configuration support to Gateway Modbus

- Introduced GatewayModbusSerialConfig structure to encapsulate serial communication settings.
- Added clamping functions for integer and size values to ensure valid configuration ranges.
- Updated GatewayModbusConfigFromValue to parse serial configuration from JSON input.
- Implemented transport type checking functions for TCP, RTU, ASCII, and Serial.
- Enhanced GatewayModbusConfigToValue to include serial configuration in output.

Signed-off-by: Tony <tonylu@tony-cloud.com>
This commit is contained in:
Tony
2026-05-04 14:34:05 +08:00
parent 640e78f688
commit 34d2d9caa0
10 changed files with 1364 additions and 204 deletions
@@ -18,14 +18,43 @@ constexpr uint16_t kGatewayModbusMaxReadBits = 2000;
constexpr uint16_t kGatewayModbusMaxReadRegisters = 125;
constexpr uint16_t kGatewayModbusMaxWriteBits = 1968;
constexpr uint16_t kGatewayModbusMaxWriteRegisters = 123;
constexpr uint32_t kGatewayModbusDefaultSerialBaudrate = 9600;
constexpr uint32_t kGatewayModbusDefaultSerialResponseTimeoutMs = 20;
constexpr uint32_t kGatewayModbusDefaultSerialInterFrameGapUs = 4000;
struct GatewayModbusRs485Config {
bool enabled{false};
int de_pin{-1};
};
struct GatewayModbusSerialConfig {
int uart_port{1};
int tx_pin{-1};
int rx_pin{-1};
uint32_t baudrate{kGatewayModbusDefaultSerialBaudrate};
int data_bits{8};
std::string parity{"none"};
int stop_bits{1};
size_t rx_buffer_size{512};
size_t tx_buffer_size{512};
uint32_t response_timeout_ms{kGatewayModbusDefaultSerialResponseTimeoutMs};
uint32_t inter_frame_gap_us{kGatewayModbusDefaultSerialInterFrameGapUs};
GatewayModbusRs485Config rs485;
};
struct GatewayModbusConfig {
std::string transport{"tcp-server"};
std::string host;
uint16_t port{kGatewayModbusDefaultTcpPort};
uint8_t unit_id{1};
GatewayModbusSerialConfig serial;
};
bool GatewayModbusTransportIsTcp(const std::string& transport);
bool GatewayModbusTransportIsRtu(const std::string& transport);
bool GatewayModbusTransportIsAscii(const std::string& transport);
bool GatewayModbusTransportIsSerial(const std::string& transport);
enum class GatewayModbusSpace : uint8_t {
kCoil = 1,
kDiscreteInput = 2,
@@ -396,8 +396,44 @@ GatewayModbusPointBinding toBinding(const GatewayModbusPoint& point) {
point.diagnostic_device_type};
}
int clampedInt(const DaliValue::Object& json, const std::string& key, int fallback,
int min_value, int max_value) {
const int value = getObjectInt(json, key).value_or(fallback);
return std::clamp(value, min_value, max_value);
}
uint32_t clampedU32(const DaliValue::Object& json, const std::string& key, uint32_t fallback,
uint32_t min_value, uint32_t max_value) {
const int value = getObjectInt(json, key).value_or(static_cast<int>(fallback));
return static_cast<uint32_t>(std::clamp(value, static_cast<int>(min_value),
static_cast<int>(max_value)));
}
size_t clampedSize(const DaliValue::Object& json, const std::string& key, size_t fallback,
size_t min_value, size_t max_value) {
const int value = getObjectInt(json, key).value_or(static_cast<int>(fallback));
return static_cast<size_t>(std::clamp(value, static_cast<int>(min_value),
static_cast<int>(max_value)));
}
} // namespace
bool GatewayModbusTransportIsTcp(const std::string& transport) {
return transport.empty() || transport == "tcp" || transport == "tcp-server";
}
bool GatewayModbusTransportIsRtu(const std::string& transport) {
return transport == "rtu" || transport == "rtu-server" || transport == "modbus-rtu";
}
bool GatewayModbusTransportIsAscii(const std::string& transport) {
return transport == "ascii" || transport == "ascii-server" || transport == "modbus-ascii";
}
bool GatewayModbusTransportIsSerial(const std::string& transport) {
return GatewayModbusTransportIsRtu(transport) || GatewayModbusTransportIsAscii(transport);
}
std::optional<GatewayModbusConfig> GatewayModbusConfigFromValue(const DaliValue* value) {
if (value == nullptr || value->asObject() == nullptr) {
return std::nullopt;
@@ -410,6 +446,35 @@ std::optional<GatewayModbusConfig> GatewayModbusConfigFromValue(const DaliValue*
getObjectInt(json, "port").value_or(kGatewayModbusDefaultTcpPort));
config.unit_id = static_cast<uint8_t>(getObjectInt(json, "unitID").value_or(
getObjectInt(json, "unitId").value_or(getObjectInt(json, "unit_id").value_or(1))));
if (const auto* serial_value = getObjectValue(json, "serial")) {
if (const auto* serial = serial_value->asObject()) {
config.serial.uart_port = clampedInt(*serial, "uartPort", config.serial.uart_port, 0, 2);
config.serial.tx_pin = clampedInt(*serial, "txPin", config.serial.tx_pin, -1, 48);
config.serial.rx_pin = clampedInt(*serial, "rxPin", config.serial.rx_pin, -1, 48);
config.serial.baudrate = clampedU32(*serial, "baudrate", config.serial.baudrate,
1200, 921600);
config.serial.data_bits = clampedInt(*serial, "dataBits", config.serial.data_bits, 7, 8);
config.serial.parity = getObjectString(*serial, "parity").value_or(config.serial.parity);
config.serial.stop_bits = clampedInt(*serial, "stopBits", config.serial.stop_bits, 1, 2);
config.serial.rx_buffer_size = clampedSize(*serial, "rxBufferBytes",
config.serial.rx_buffer_size, 128, 4096);
config.serial.tx_buffer_size = clampedSize(*serial, "txBufferBytes",
config.serial.tx_buffer_size, 0, 4096);
config.serial.response_timeout_ms = clampedU32(*serial, "responseTimeoutMs",
config.serial.response_timeout_ms, 1, 1000);
config.serial.inter_frame_gap_us = clampedU32(*serial, "interFrameGapUs",
config.serial.inter_frame_gap_us, 1000,
100000);
if (const auto* rs485_value = getObjectValue(*serial, "rs485")) {
if (const auto* rs485 = rs485_value->asObject()) {
config.serial.rs485.enabled = getObjectBool(*rs485, "enabled")
.value_or(config.serial.rs485.enabled);
config.serial.rs485.de_pin = clampedInt(*rs485, "dePin",
config.serial.rs485.de_pin, -1, 48);
}
}
}
}
return config;
}
@@ -419,6 +484,23 @@ DaliValue GatewayModbusConfigToValue(const GatewayModbusConfig& config) {
out["host"] = config.host;
out["port"] = static_cast<int>(config.port);
out["unitID"] = static_cast<int>(config.unit_id);
DaliValue::Object serial;
serial["uartPort"] = config.serial.uart_port;
serial["txPin"] = config.serial.tx_pin;
serial["rxPin"] = config.serial.rx_pin;
serial["baudrate"] = static_cast<int>(config.serial.baudrate);
serial["dataBits"] = config.serial.data_bits;
serial["parity"] = config.serial.parity;
serial["stopBits"] = config.serial.stop_bits;
serial["rxBufferBytes"] = static_cast<int>(config.serial.rx_buffer_size);
serial["txBufferBytes"] = static_cast<int>(config.serial.tx_buffer_size);
serial["responseTimeoutMs"] = static_cast<int>(config.serial.response_timeout_ms);
serial["interFrameGapUs"] = static_cast<int>(config.serial.inter_frame_gap_us);
DaliValue::Object rs485;
rs485["enabled"] = config.serial.rs485.enabled;
rs485["dePin"] = config.serial.rs485.de_pin;
serial["rs485"] = std::move(rs485);
out["serial"] = std::move(serial);
return DaliValue(std::move(out));
}