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:
@@ -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
|
||||
Reference in New Issue
Block a user