Add Gateway Modbus component with configuration and bridge implementation

- Created CMakeLists.txt for the Gateway Modbus component.
- Added header file `gateway_modbus.hpp` defining configuration structures, enums, and point structures.
- Implemented the `gateway_modbus.cpp` source file containing the logic for managing Modbus points, including reading and writing operations.
- Introduced utility functions for converting configurations to and from DaliValue, and for handling Modbus space and access types.
- Established a bridge class to manage Modbus points and their interactions with the DaliBridgeEngine.

Co-authored-by: Copilot <copilot@github.com>
This commit is contained in:
Tony
2026-05-04 01:19:05 +08:00
parent 8aa5a451a4
commit 694217eb2c
9 changed files with 1244 additions and 63 deletions
+7
View File
@@ -0,0 +1,7 @@
idf_component_register(
SRCS "src/gateway_modbus.cpp"
INCLUDE_DIRS "include"
REQUIRES dali_cpp
)
set_property(TARGET ${COMPONENT_LIB} PROPERTY CXX_STANDARD 17)
@@ -0,0 +1,147 @@
#pragma once
#include "bridge.hpp"
#include "bridge_model.hpp"
#include "model_value.hpp"
#include <cstddef>
#include <cstdint>
#include <optional>
#include <string>
#include <vector>
namespace gateway {
constexpr uint16_t kGatewayModbusDefaultTcpPort = 1502;
constexpr size_t kGatewayModbusMaxPduBytes = 252;
constexpr uint16_t kGatewayModbusMaxReadBits = 2000;
constexpr uint16_t kGatewayModbusMaxReadRegisters = 125;
constexpr uint16_t kGatewayModbusMaxWriteBits = 1968;
constexpr uint16_t kGatewayModbusMaxWriteRegisters = 123;
struct GatewayModbusConfig {
std::string transport{"tcp-server"};
std::string host;
uint16_t port{kGatewayModbusDefaultTcpPort};
uint8_t unit_id{1};
};
enum class GatewayModbusSpace : uint8_t {
kCoil = 1,
kDiscreteInput = 2,
kHoldingRegister = 3,
kInputRegister = 4,
};
enum class GatewayModbusAccess : uint8_t {
kReadOnly = 0,
kWriteOnly = 1,
kReadWrite = 2,
};
enum class GatewayModbusGeneratedKind : uint8_t {
kNone = 0,
kShortOn,
kShortOff,
kShortRecallMax,
kShortRecallMin,
kShortDiscovered,
kShortOnline,
kShortSupportsDt1,
kShortSupportsDt4,
kShortSupportsDt5,
kShortSupportsDt6,
kShortSupportsDt8,
kShortGroupMaskKnown,
kShortActualLevelKnown,
kShortSceneKnown,
kShortSettingsKnown,
kShortControlGearPresent,
kShortLampFailure,
kShortLampPowerOn,
kShortLimitError,
kShortFadingCompleted,
kShortResetState,
kShortMissingShortAddress,
kShortPowerSupplyFault,
kShortBrightness,
kShortColorTemperature,
kShortGroupMask,
kShortPowerOnLevel,
kShortSystemFailureLevel,
kShortMinLevel,
kShortMaxLevel,
kShortFadeTime,
kShortFadeRate,
kShortInventoryState,
kShortPrimaryType,
kShortTypeMask,
kShortActualLevel,
kShortSceneId,
kShortRawStatus,
};
struct GatewayModbusPoint {
GatewayModbusSpace space{GatewayModbusSpace::kHoldingRegister};
GatewayModbusAccess access{GatewayModbusAccess::kReadWrite};
uint16_t address{0};
std::string id;
std::string name;
bool generated{false};
GatewayModbusGeneratedKind generated_kind{GatewayModbusGeneratedKind::kNone};
int short_address{-1};
std::string model_id;
BridgeOperation operation{BridgeOperation::unknown};
std::optional<int> bit_index;
};
struct GatewayModbusPointBinding {
std::string model_id;
GatewayModbusSpace space{GatewayModbusSpace::kHoldingRegister};
uint16_t address{0};
std::string id;
std::string name;
bool generated{false};
GatewayModbusGeneratedKind generated_kind{GatewayModbusGeneratedKind::kNone};
int short_address{-1};
GatewayModbusAccess access{GatewayModbusAccess::kReadWrite};
};
std::optional<GatewayModbusConfig> GatewayModbusConfigFromValue(const DaliValue* value);
DaliValue GatewayModbusConfigToValue(const GatewayModbusConfig& config);
const char* GatewayModbusSpaceToString(GatewayModbusSpace space);
const char* GatewayModbusAccessToString(GatewayModbusAccess access);
const char* GatewayModbusGeneratedKindToString(GatewayModbusGeneratedKind kind);
int GatewayModbusHumanAddressFromWire(GatewayModbusSpace space, uint16_t zero_based_address);
std::optional<GatewayModbusSpace> GatewayModbusReadSpaceForFunction(uint8_t function_code);
std::optional<GatewayModbusSpace> GatewayModbusWriteSpaceForFunction(uint8_t function_code);
class GatewayModbusBridge {
public:
explicit GatewayModbusBridge(DaliBridgeEngine& engine);
void setConfig(const GatewayModbusConfig& config);
const GatewayModbusConfig& config() const;
void rebuildMap();
std::optional<GatewayModbusPoint> findPoint(GatewayModbusSpace space,
uint16_t address) const;
std::vector<GatewayModbusPointBinding> describePoints() const;
std::vector<GatewayModbusPointBinding> describeHoldingRegisters() const;
DaliBridgeResult readModelPoint(const GatewayModbusPoint& point) const;
DaliBridgeResult writeRegisterPoint(const GatewayModbusPoint& point, uint16_t value) const;
DaliBridgeResult writeCoilPoint(const GatewayModbusPoint& point, bool value) const;
private:
DaliBridgeResult executeModelPoint(const GatewayModbusPoint& point,
std::optional<int> value) const;
DaliBridgeEngine& engine_;
GatewayModbusConfig config_;
std::vector<GatewayModbusPoint> points_;
};
} // namespace gateway
@@ -0,0 +1,516 @@
#include "gateway_modbus.hpp"
#include <algorithm>
#include <array>
#include <cstdio>
#include <map>
#include <utility>
namespace gateway {
namespace {
constexpr uint16_t kCoilBase = 1;
constexpr uint16_t kDiscreteInputBase = 10001;
constexpr uint16_t kInputRegisterBase = 30001;
constexpr uint16_t kHoldingRegisterBase = 40001;
constexpr uint16_t kShortAddressCount = 64;
constexpr uint16_t kShortStride = 32;
struct PointKey {
GatewayModbusSpace space{GatewayModbusSpace::kHoldingRegister};
uint16_t address{0};
bool operator<(const PointKey& other) const {
if (space != other.space) {
return static_cast<uint8_t>(space) < static_cast<uint8_t>(other.space);
}
return address < other.address;
}
};
struct GeneratedPointSpec {
uint16_t offset;
GatewayModbusSpace space;
GatewayModbusAccess access;
GatewayModbusGeneratedKind kind;
const char* suffix;
const char* name;
};
constexpr std::array<GeneratedPointSpec, 4> kGeneratedCoils{{
{0, GatewayModbusSpace::kCoil, GatewayModbusAccess::kWriteOnly,
GatewayModbusGeneratedKind::kShortOn, "on", "recall max"},
{1, GatewayModbusSpace::kCoil, GatewayModbusAccess::kWriteOnly,
GatewayModbusGeneratedKind::kShortOff, "off", "off"},
{2, GatewayModbusSpace::kCoil, GatewayModbusAccess::kWriteOnly,
GatewayModbusGeneratedKind::kShortRecallMax, "recall_max", "recall max"},
{3, GatewayModbusSpace::kCoil, GatewayModbusAccess::kWriteOnly,
GatewayModbusGeneratedKind::kShortRecallMin, "recall_min", "recall min"},
}};
constexpr std::array<GeneratedPointSpec, 18> kGeneratedDiscreteInputs{{
{0, GatewayModbusSpace::kDiscreteInput, GatewayModbusAccess::kReadOnly,
GatewayModbusGeneratedKind::kShortDiscovered, "discovered", "discovered"},
{1, GatewayModbusSpace::kDiscreteInput, GatewayModbusAccess::kReadOnly,
GatewayModbusGeneratedKind::kShortOnline, "online", "online"},
{2, GatewayModbusSpace::kDiscreteInput, GatewayModbusAccess::kReadOnly,
GatewayModbusGeneratedKind::kShortSupportsDt1, "supports_dt1", "supports DT1"},
{3, GatewayModbusSpace::kDiscreteInput, GatewayModbusAccess::kReadOnly,
GatewayModbusGeneratedKind::kShortSupportsDt4, "supports_dt4", "supports DT4"},
{4, GatewayModbusSpace::kDiscreteInput, GatewayModbusAccess::kReadOnly,
GatewayModbusGeneratedKind::kShortSupportsDt5, "supports_dt5", "supports DT5"},
{5, GatewayModbusSpace::kDiscreteInput, GatewayModbusAccess::kReadOnly,
GatewayModbusGeneratedKind::kShortSupportsDt6, "supports_dt6", "supports DT6"},
{6, GatewayModbusSpace::kDiscreteInput, GatewayModbusAccess::kReadOnly,
GatewayModbusGeneratedKind::kShortSupportsDt8, "supports_dt8", "supports DT8"},
{7, GatewayModbusSpace::kDiscreteInput, GatewayModbusAccess::kReadOnly,
GatewayModbusGeneratedKind::kShortGroupMaskKnown, "group_mask_known", "group mask known"},
{8, GatewayModbusSpace::kDiscreteInput, GatewayModbusAccess::kReadOnly,
GatewayModbusGeneratedKind::kShortActualLevelKnown, "actual_level_known", "actual level known"},
{9, GatewayModbusSpace::kDiscreteInput, GatewayModbusAccess::kReadOnly,
GatewayModbusGeneratedKind::kShortSceneKnown, "scene_known", "scene known"},
{10, GatewayModbusSpace::kDiscreteInput, GatewayModbusAccess::kReadOnly,
GatewayModbusGeneratedKind::kShortSettingsKnown, "settings_known", "settings known"},
{16, GatewayModbusSpace::kDiscreteInput, GatewayModbusAccess::kReadOnly,
GatewayModbusGeneratedKind::kShortControlGearPresent, "control_gear_present",
"control gear present"},
{17, GatewayModbusSpace::kDiscreteInput, GatewayModbusAccess::kReadOnly,
GatewayModbusGeneratedKind::kShortLampFailure, "lamp_failure", "lamp failure"},
{18, GatewayModbusSpace::kDiscreteInput, GatewayModbusAccess::kReadOnly,
GatewayModbusGeneratedKind::kShortLampPowerOn, "lamp_power_on", "lamp power on"},
{19, GatewayModbusSpace::kDiscreteInput, GatewayModbusAccess::kReadOnly,
GatewayModbusGeneratedKind::kShortLimitError, "limit_error", "limit error"},
{20, GatewayModbusSpace::kDiscreteInput, GatewayModbusAccess::kReadOnly,
GatewayModbusGeneratedKind::kShortFadingCompleted, "fading_completed", "fading completed"},
{21, GatewayModbusSpace::kDiscreteInput, GatewayModbusAccess::kReadOnly,
GatewayModbusGeneratedKind::kShortResetState, "reset_state", "reset state"},
{22, GatewayModbusSpace::kDiscreteInput, GatewayModbusAccess::kReadOnly,
GatewayModbusGeneratedKind::kShortMissingShortAddress, "missing_short_address",
"missing short address"},
}};
constexpr std::array<GeneratedPointSpec, 8> kGeneratedHoldingRegisters{{
{0, GatewayModbusSpace::kHoldingRegister, GatewayModbusAccess::kReadWrite,
GatewayModbusGeneratedKind::kShortBrightness, "brightness", "brightness"},
{1, GatewayModbusSpace::kHoldingRegister, GatewayModbusAccess::kReadWrite,
GatewayModbusGeneratedKind::kShortColorTemperature, "color_temperature",
"color temperature"},
{2, GatewayModbusSpace::kHoldingRegister, GatewayModbusAccess::kReadWrite,
GatewayModbusGeneratedKind::kShortGroupMask, "group_mask", "group mask"},
{3, GatewayModbusSpace::kHoldingRegister, GatewayModbusAccess::kReadWrite,
GatewayModbusGeneratedKind::kShortPowerOnLevel, "power_on_level", "power-on level"},
{4, GatewayModbusSpace::kHoldingRegister, GatewayModbusAccess::kReadWrite,
GatewayModbusGeneratedKind::kShortSystemFailureLevel, "system_failure_level",
"system-failure level"},
{5, GatewayModbusSpace::kHoldingRegister, GatewayModbusAccess::kReadWrite,
GatewayModbusGeneratedKind::kShortMinLevel, "min_level", "minimum level"},
{6, GatewayModbusSpace::kHoldingRegister, GatewayModbusAccess::kReadWrite,
GatewayModbusGeneratedKind::kShortMaxLevel, "max_level", "maximum level"},
{7, GatewayModbusSpace::kHoldingRegister, GatewayModbusAccess::kReadWrite,
GatewayModbusGeneratedKind::kShortFadeTime, "fade_time", "fade time"},
}};
constexpr std::array<GeneratedPointSpec, 13> kGeneratedInputRegisters{{
{0, GatewayModbusSpace::kInputRegister, GatewayModbusAccess::kReadOnly,
GatewayModbusGeneratedKind::kShortInventoryState, "inventory_state", "inventory state"},
{1, GatewayModbusSpace::kInputRegister, GatewayModbusAccess::kReadOnly,
GatewayModbusGeneratedKind::kShortPrimaryType, "primary_type", "primary type"},
{2, GatewayModbusSpace::kInputRegister, GatewayModbusAccess::kReadOnly,
GatewayModbusGeneratedKind::kShortTypeMask, "type_mask", "device type mask"},
{3, GatewayModbusSpace::kInputRegister, GatewayModbusAccess::kReadOnly,
GatewayModbusGeneratedKind::kShortActualLevel, "actual_level", "actual level"},
{4, GatewayModbusSpace::kInputRegister, GatewayModbusAccess::kReadOnly,
GatewayModbusGeneratedKind::kShortSceneId, "scene_id", "scene id"},
{5, GatewayModbusSpace::kInputRegister, GatewayModbusAccess::kReadOnly,
GatewayModbusGeneratedKind::kShortRawStatus, "raw_status", "raw status"},
{6, GatewayModbusSpace::kInputRegister, GatewayModbusAccess::kReadOnly,
GatewayModbusGeneratedKind::kShortGroupMask, "group_mask", "group mask"},
{7, GatewayModbusSpace::kInputRegister, GatewayModbusAccess::kReadOnly,
GatewayModbusGeneratedKind::kShortPowerOnLevel, "power_on_level", "power-on level"},
{8, GatewayModbusSpace::kInputRegister, GatewayModbusAccess::kReadOnly,
GatewayModbusGeneratedKind::kShortSystemFailureLevel, "system_failure_level",
"system-failure level"},
{9, GatewayModbusSpace::kInputRegister, GatewayModbusAccess::kReadOnly,
GatewayModbusGeneratedKind::kShortMinLevel, "min_level", "minimum level"},
{10, GatewayModbusSpace::kInputRegister, GatewayModbusAccess::kReadOnly,
GatewayModbusGeneratedKind::kShortMaxLevel, "max_level", "maximum level"},
{11, GatewayModbusSpace::kInputRegister, GatewayModbusAccess::kReadOnly,
GatewayModbusGeneratedKind::kShortFadeTime, "fade_time", "fade time"},
{12, GatewayModbusSpace::kInputRegister, GatewayModbusAccess::kReadOnly,
GatewayModbusGeneratedKind::kShortFadeRate, "fade_rate", "fade rate"},
}};
uint16_t baseForSpace(GatewayModbusSpace space) {
switch (space) {
case GatewayModbusSpace::kCoil:
return kCoilBase;
case GatewayModbusSpace::kDiscreteInput:
return kDiscreteInputBase;
case GatewayModbusSpace::kInputRegister:
return kInputRegisterBase;
case GatewayModbusSpace::kHoldingRegister:
return kHoldingRegisterBase;
}
return kHoldingRegisterBase;
}
std::optional<GatewayModbusSpace> spaceForObjectType(BridgeObjectType type) {
switch (type) {
case BridgeObjectType::coil:
return GatewayModbusSpace::kCoil;
case BridgeObjectType::discreteInput:
return GatewayModbusSpace::kDiscreteInput;
case BridgeObjectType::inputRegister:
return GatewayModbusSpace::kInputRegister;
case BridgeObjectType::holdingRegister:
return GatewayModbusSpace::kHoldingRegister;
default:
return std::nullopt;
}
}
GatewayModbusAccess accessForSpace(GatewayModbusSpace space) {
switch (space) {
case GatewayModbusSpace::kCoil:
case GatewayModbusSpace::kHoldingRegister:
return GatewayModbusAccess::kReadWrite;
case GatewayModbusSpace::kDiscreteInput:
case GatewayModbusSpace::kInputRegister:
return GatewayModbusAccess::kReadOnly;
}
return GatewayModbusAccess::kReadOnly;
}
std::string generatedId(uint8_t short_address, const char* suffix) {
char buffer[64];
std::snprintf(buffer, sizeof(buffer), "dali_%02u_%s", static_cast<unsigned>(short_address),
suffix == nullptr ? "point" : suffix);
return buffer;
}
std::string generatedName(uint8_t short_address, const char* name) {
char buffer[96];
std::snprintf(buffer, sizeof(buffer), "DALI %u %s", static_cast<unsigned>(short_address),
name == nullptr ? "point" : name);
return buffer;
}
void addGeneratedPoint(std::map<PointKey, GatewayModbusPoint>* points, uint8_t short_address,
const GeneratedPointSpec& spec) {
if (points == nullptr) {
return;
}
const uint16_t address = static_cast<uint16_t>(baseForSpace(spec.space) +
short_address * kShortStride + spec.offset);
GatewayModbusPoint point;
point.space = spec.space;
point.access = spec.access;
point.address = address;
point.id = generatedId(short_address, spec.suffix);
point.name = generatedName(short_address, spec.name);
point.generated = true;
point.generated_kind = spec.kind;
point.short_address = short_address;
(*points)[PointKey{spec.space, address}] = std::move(point);
}
GatewayModbusPointBinding toBinding(const GatewayModbusPoint& point) {
return GatewayModbusPointBinding{point.model_id,
point.space,
point.address,
point.id,
point.name,
point.generated,
point.generated_kind,
point.short_address,
point.access};
}
} // namespace
std::optional<GatewayModbusConfig> GatewayModbusConfigFromValue(const DaliValue* value) {
if (value == nullptr || value->asObject() == nullptr) {
return std::nullopt;
}
const auto& json = *value->asObject();
GatewayModbusConfig config;
config.transport = getObjectString(json, "transport").value_or("tcp-server");
config.host = getObjectString(json, "host").value_or("");
config.port = static_cast<uint16_t>(
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))));
return config;
}
DaliValue GatewayModbusConfigToValue(const GatewayModbusConfig& config) {
DaliValue::Object out;
out["transport"] = config.transport;
out["host"] = config.host;
out["port"] = static_cast<int>(config.port);
out["unitID"] = static_cast<int>(config.unit_id);
return DaliValue(std::move(out));
}
const char* GatewayModbusSpaceToString(GatewayModbusSpace space) {
switch (space) {
case GatewayModbusSpace::kCoil:
return "coil";
case GatewayModbusSpace::kDiscreteInput:
return "discrete_input";
case GatewayModbusSpace::kHoldingRegister:
return "holding_register";
case GatewayModbusSpace::kInputRegister:
return "input_register";
}
return "unknown";
}
const char* GatewayModbusAccessToString(GatewayModbusAccess access) {
switch (access) {
case GatewayModbusAccess::kReadOnly:
return "read_only";
case GatewayModbusAccess::kWriteOnly:
return "write_only";
case GatewayModbusAccess::kReadWrite:
return "read_write";
}
return "unknown";
}
const char* GatewayModbusGeneratedKindToString(GatewayModbusGeneratedKind kind) {
switch (kind) {
case GatewayModbusGeneratedKind::kShortOn:
return "short_on";
case GatewayModbusGeneratedKind::kShortOff:
return "short_off";
case GatewayModbusGeneratedKind::kShortRecallMax:
return "short_recall_max";
case GatewayModbusGeneratedKind::kShortRecallMin:
return "short_recall_min";
case GatewayModbusGeneratedKind::kShortDiscovered:
return "short_discovered";
case GatewayModbusGeneratedKind::kShortOnline:
return "short_online";
case GatewayModbusGeneratedKind::kShortSupportsDt1:
return "short_supports_dt1";
case GatewayModbusGeneratedKind::kShortSupportsDt4:
return "short_supports_dt4";
case GatewayModbusGeneratedKind::kShortSupportsDt5:
return "short_supports_dt5";
case GatewayModbusGeneratedKind::kShortSupportsDt6:
return "short_supports_dt6";
case GatewayModbusGeneratedKind::kShortSupportsDt8:
return "short_supports_dt8";
case GatewayModbusGeneratedKind::kShortGroupMaskKnown:
return "short_group_mask_known";
case GatewayModbusGeneratedKind::kShortActualLevelKnown:
return "short_actual_level_known";
case GatewayModbusGeneratedKind::kShortSceneKnown:
return "short_scene_known";
case GatewayModbusGeneratedKind::kShortSettingsKnown:
return "short_settings_known";
case GatewayModbusGeneratedKind::kShortControlGearPresent:
return "short_control_gear_present";
case GatewayModbusGeneratedKind::kShortLampFailure:
return "short_lamp_failure";
case GatewayModbusGeneratedKind::kShortLampPowerOn:
return "short_lamp_power_on";
case GatewayModbusGeneratedKind::kShortLimitError:
return "short_limit_error";
case GatewayModbusGeneratedKind::kShortFadingCompleted:
return "short_fading_completed";
case GatewayModbusGeneratedKind::kShortResetState:
return "short_reset_state";
case GatewayModbusGeneratedKind::kShortMissingShortAddress:
return "short_missing_short_address";
case GatewayModbusGeneratedKind::kShortPowerSupplyFault:
return "short_power_supply_fault";
case GatewayModbusGeneratedKind::kShortBrightness:
return "short_brightness";
case GatewayModbusGeneratedKind::kShortColorTemperature:
return "short_color_temperature";
case GatewayModbusGeneratedKind::kShortGroupMask:
return "short_group_mask";
case GatewayModbusGeneratedKind::kShortPowerOnLevel:
return "short_power_on_level";
case GatewayModbusGeneratedKind::kShortSystemFailureLevel:
return "short_system_failure_level";
case GatewayModbusGeneratedKind::kShortMinLevel:
return "short_min_level";
case GatewayModbusGeneratedKind::kShortMaxLevel:
return "short_max_level";
case GatewayModbusGeneratedKind::kShortFadeTime:
return "short_fade_time";
case GatewayModbusGeneratedKind::kShortFadeRate:
return "short_fade_rate";
case GatewayModbusGeneratedKind::kShortInventoryState:
return "short_inventory_state";
case GatewayModbusGeneratedKind::kShortPrimaryType:
return "short_primary_type";
case GatewayModbusGeneratedKind::kShortTypeMask:
return "short_type_mask";
case GatewayModbusGeneratedKind::kShortActualLevel:
return "short_actual_level";
case GatewayModbusGeneratedKind::kShortSceneId:
return "short_scene_id";
case GatewayModbusGeneratedKind::kShortRawStatus:
return "short_raw_status";
case GatewayModbusGeneratedKind::kNone:
default:
return "none";
}
}
int GatewayModbusHumanAddressFromWire(GatewayModbusSpace space, uint16_t zero_based_address) {
return baseForSpace(space) + static_cast<int>(zero_based_address);
}
std::optional<GatewayModbusSpace> GatewayModbusReadSpaceForFunction(uint8_t function_code) {
switch (function_code) {
case 0x01:
return GatewayModbusSpace::kCoil;
case 0x02:
return GatewayModbusSpace::kDiscreteInput;
case 0x03:
return GatewayModbusSpace::kHoldingRegister;
case 0x04:
return GatewayModbusSpace::kInputRegister;
default:
return std::nullopt;
}
}
std::optional<GatewayModbusSpace> GatewayModbusWriteSpaceForFunction(uint8_t function_code) {
switch (function_code) {
case 0x05:
case 0x0F:
return GatewayModbusSpace::kCoil;
case 0x06:
case 0x10:
return GatewayModbusSpace::kHoldingRegister;
default:
return std::nullopt;
}
}
GatewayModbusBridge::GatewayModbusBridge(DaliBridgeEngine& engine) : engine_(engine) {
rebuildMap();
}
void GatewayModbusBridge::setConfig(const GatewayModbusConfig& config) { config_ = config; }
const GatewayModbusConfig& GatewayModbusBridge::config() const { return config_; }
void GatewayModbusBridge::rebuildMap() {
std::map<PointKey, GatewayModbusPoint> next;
for (uint8_t short_address = 0; short_address < kShortAddressCount; ++short_address) {
for (const auto& spec : kGeneratedCoils) {
addGeneratedPoint(&next, short_address, spec);
}
for (const auto& spec : kGeneratedDiscreteInputs) {
addGeneratedPoint(&next, short_address, spec);
}
for (const auto& spec : kGeneratedHoldingRegisters) {
addGeneratedPoint(&next, short_address, spec);
}
for (const auto& spec : kGeneratedInputRegisters) {
addGeneratedPoint(&next, short_address, spec);
}
}
for (const auto& model : engine_.listModels()) {
if (model.protocol != BridgeProtocolKind::modbus || !model.external.registerAddress.has_value()) {
continue;
}
const auto space = spaceForObjectType(model.external.objectType);
if (!space.has_value()) {
continue;
}
GatewayModbusPoint point;
point.space = space.value();
point.access = accessForSpace(space.value());
point.address = static_cast<uint16_t>(model.external.registerAddress.value());
point.id = model.id;
point.name = model.displayName();
point.generated = false;
point.generated_kind = GatewayModbusGeneratedKind::kNone;
point.model_id = model.id;
point.operation = model.operation;
point.bit_index = model.external.bitIndex;
if (model.dali.kind == BridgeDaliTargetKind::shortAddress && model.dali.shortAddress.has_value()) {
point.short_address = model.dali.shortAddress.value();
}
next[PointKey{point.space, point.address}] = std::move(point);
}
points_.clear();
points_.reserve(next.size());
for (auto& entry : next) {
points_.push_back(std::move(entry.second));
}
}
std::optional<GatewayModbusPoint> GatewayModbusBridge::findPoint(GatewayModbusSpace space,
uint16_t address) const {
const auto found = std::find_if(points_.begin(), points_.end(), [space, address](const auto& point) {
return point.space == space && point.address == address;
});
if (found == points_.end()) {
return std::nullopt;
}
return *found;
}
std::vector<GatewayModbusPointBinding> GatewayModbusBridge::describePoints() const {
std::vector<GatewayModbusPointBinding> bindings;
bindings.reserve(points_.size());
for (const auto& point : points_) {
bindings.push_back(toBinding(point));
}
return bindings;
}
std::vector<GatewayModbusPointBinding> GatewayModbusBridge::describeHoldingRegisters() const {
std::vector<GatewayModbusPointBinding> bindings;
for (const auto& point : points_) {
if (point.space == GatewayModbusSpace::kHoldingRegister) {
bindings.push_back(toBinding(point));
}
}
return bindings;
}
DaliBridgeResult GatewayModbusBridge::readModelPoint(const GatewayModbusPoint& point) const {
return executeModelPoint(point, std::nullopt);
}
DaliBridgeResult GatewayModbusBridge::writeRegisterPoint(const GatewayModbusPoint& point,
uint16_t value) const {
return executeModelPoint(point, static_cast<int>(value));
}
DaliBridgeResult GatewayModbusBridge::writeCoilPoint(const GatewayModbusPoint& point,
bool value) const {
return executeModelPoint(point, value ? 1 : 0);
}
DaliBridgeResult GatewayModbusBridge::executeModelPoint(const GatewayModbusPoint& point,
std::optional<int> value) const {
DaliBridgeRequest request;
request.sequence = "modbus-" + std::to_string(point.address);
request.modelID = point.model_id;
if (value.has_value()) {
request.value = value.value();
}
if (point.model_id.empty()) {
DaliBridgeResult result;
result.sequence = request.sequence;
result.error = "generated Modbus point requires gateway handler";
return result;
}
return engine_.execute(request);
}
} // namespace gateway