34d2d9caa0
- 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>
777 lines
40 KiB
C++
777 lines
40 KiB
C++
#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;
|
|
constexpr uint16_t kDiagnosticDiscreteInputBase = kDiscreteInputBase +
|
|
kShortAddressCount * kShortStride;
|
|
constexpr uint16_t kDiagnosticStride = 128;
|
|
|
|
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;
|
|
};
|
|
|
|
struct GeneratedDiagnosticBitSpec {
|
|
uint16_t offset;
|
|
int device_type;
|
|
const char* snapshot;
|
|
const char* bool_key;
|
|
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, 19> 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"},
|
|
{23, GatewayModbusSpace::kDiscreteInput, GatewayModbusAccess::kReadOnly,
|
|
GatewayModbusGeneratedKind::kShortPowerSupplyFault, "power_supply_fault",
|
|
"power supply fault"},
|
|
}};
|
|
|
|
constexpr std::array<GeneratedPointSpec, 9> 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"},
|
|
{8, GatewayModbusSpace::kHoldingRegister, GatewayModbusAccess::kReadWrite,
|
|
GatewayModbusGeneratedKind::kShortFadeRate, "fade_rate", "fade rate"},
|
|
}};
|
|
|
|
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"},
|
|
}};
|
|
|
|
constexpr GeneratedDiagnosticBitSpec kGeneratedDiagnosticBits[] = {
|
|
{0, 1, "dt1", "circuitFailure", "dt1_circuit_failure", "DT1 circuit failure"},
|
|
{1, 1, "dt1", "batteryDurationFailure", "dt1_battery_duration_failure", "DT1 battery duration failure"},
|
|
{2, 1, "dt1", "batteryFailure", "dt1_battery_failure", "DT1 battery failure"},
|
|
{3, 1, "dt1", "emergencyLampFailure", "dt1_emergency_lamp_failure", "DT1 emergency lamp failure"},
|
|
{4, 1, "dt1", "functionTestMaxDelayExceeded", "dt1_function_test_delay_exceeded", "DT1 function test delay exceeded"},
|
|
{5, 1, "dt1", "durationTestMaxDelayExceeded", "dt1_duration_test_delay_exceeded", "DT1 duration test delay exceeded"},
|
|
{6, 1, "dt1", "functionTestFailed", "dt1_function_test_failed", "DT1 function test failed"},
|
|
{7, 1, "dt1", "durationTestFailed", "dt1_duration_test_failed", "DT1 duration test failed"},
|
|
{8, 1, "dt1", "inhibitMode", "dt1_inhibit_mode", "DT1 inhibit mode"},
|
|
{9, 1, "dt1", "functionTestResultValid", "dt1_function_result_valid", "DT1 function result valid"},
|
|
{10, 1, "dt1", "durationTestResultValid", "dt1_duration_result_valid", "DT1 duration result valid"},
|
|
{11, 1, "dt1", "batteryFullyCharged", "dt1_battery_fully_charged", "DT1 battery fully charged"},
|
|
{12, 1, "dt1", "functionTestRequestPending", "dt1_function_request_pending", "DT1 function request pending"},
|
|
{13, 1, "dt1", "durationTestRequestPending", "dt1_duration_request_pending", "DT1 duration request pending"},
|
|
{14, 1, "dt1", "identificationActive", "dt1_identification_active", "DT1 identification active"},
|
|
{15, 1, "dt1", "physicallySelected", "dt1_physically_selected", "DT1 physically selected"},
|
|
{16, 1, "dt1", "restModeActive", "dt1_rest_mode_active", "DT1 rest mode active"},
|
|
{17, 1, "dt1", "normalModeActive", "dt1_normal_mode_active", "DT1 normal mode active"},
|
|
{18, 1, "dt1", "emergencyModeActive", "dt1_emergency_mode_active", "DT1 emergency mode active"},
|
|
{19, 1, "dt1", "extendedEmergencyModeActive", "dt1_extended_mode_active", "DT1 extended emergency mode active"},
|
|
{20, 1, "dt1", "functionTestInProgress", "dt1_function_test_in_progress", "DT1 function test in progress"},
|
|
{21, 1, "dt1", "durationTestInProgress", "dt1_duration_test_in_progress", "DT1 duration test in progress"},
|
|
{22, 1, "dt1", "hardwiredInhibitActive", "dt1_hardwired_inhibit_active", "DT1 hardwired inhibit active"},
|
|
{23, 1, "dt1", "hardwiredSwitchOn", "dt1_hardwired_switch_on", "DT1 hardwired switch on"},
|
|
{24, 1, "dt1", "integralEmergencyControlGear", "dt1_integral_emergency", "DT1 integral emergency gear"},
|
|
{25, 1, "dt1", "maintainedControlGear", "dt1_maintained", "DT1 maintained gear"},
|
|
{26, 1, "dt1", "switchedMaintainedControlGear", "dt1_switched_maintained", "DT1 switched maintained gear"},
|
|
{27, 1, "dt1", "autoTestCapability", "dt1_auto_test_capability", "DT1 auto test capability"},
|
|
{28, 1, "dt1", "adjustableEmergencyLevel", "dt1_adjustable_level", "DT1 adjustable emergency level"},
|
|
{29, 1, "dt1", "hardwiredInhibitSupported", "dt1_hardwired_inhibit_supported", "DT1 hardwired inhibit supported"},
|
|
{30, 1, "dt1", "physicalSelectionSupported", "dt1_physical_selection_supported", "DT1 physical selection supported"},
|
|
{31, 1, "dt1", "relightInRestModeSupported", "dt1_relight_rest_supported", "DT1 relight in rest mode supported"},
|
|
{32, 4, "dt4", "leadingEdgeModeRunning", "dt4_leading_edge_running", "DT4 leading edge running"},
|
|
{33, 4, "dt4", "trailingEdgeModeRunning", "dt4_trailing_edge_running", "DT4 trailing edge running"},
|
|
{34, 4, "dt4", "referenceMeasurementRunning", "dt4_reference_running", "DT4 reference measurement running"},
|
|
{35, 4, "dt4", "nonLogarithmicDimmingCurveActive", "dt4_non_log_curve_active", "DT4 non-log curve active"},
|
|
{36, 4, "dt4", "canQueryLoadOverCurrentShutdown", "dt4_can_load_over_current_shutdown", "DT4 can query over-current shutdown"},
|
|
{37, 4, "dt4", "canQueryOpenCircuitDetection", "dt4_can_open_circuit", "DT4 can query open circuit"},
|
|
{38, 4, "dt4", "canQueryLoadDecrease", "dt4_can_load_decrease", "DT4 can query load decrease"},
|
|
{39, 4, "dt4", "canQueryLoadIncrease", "dt4_can_load_increase", "DT4 can query load increase"},
|
|
{40, 4, "dt4", "canQueryThermalShutdown", "dt4_can_thermal_shutdown", "DT4 can query thermal shutdown"},
|
|
{41, 4, "dt4", "canQueryThermalOverloadReduction", "dt4_can_thermal_overload", "DT4 can query thermal overload"},
|
|
{42, 4, "dt4", "physicalSelectionSupported", "dt4_physical_selection_supported", "DT4 physical selection supported"},
|
|
{43, 4, "dt4", "canQueryTemperature", "dt4_can_temperature", "DT4 can query temperature"},
|
|
{44, 4, "dt4", "canQuerySupplyVoltage", "dt4_can_supply_voltage", "DT4 can query supply voltage"},
|
|
{45, 4, "dt4", "canQuerySupplyFrequency", "dt4_can_supply_frequency", "DT4 can query supply frequency"},
|
|
{46, 4, "dt4", "canQueryLoadVoltage", "dt4_can_load_voltage", "DT4 can query load voltage"},
|
|
{47, 4, "dt4", "canQueryLoadCurrent", "dt4_can_load_current", "DT4 can query load current"},
|
|
{48, 4, "dt4", "canQueryRealLoadPower", "dt4_can_load_power", "DT4 can query load power"},
|
|
{49, 4, "dt4", "canQueryLoadRating", "dt4_can_load_rating", "DT4 can query load rating"},
|
|
{50, 4, "dt4", "canQueryCurrentOverloadReduction", "dt4_can_current_overload", "DT4 can query current overload"},
|
|
{51, 4, "dt4", "canSelectNonLogarithmicDimmingCurve", "dt4_can_non_log_curve", "DT4 can select non-log curve"},
|
|
{52, 4, "dt4", "canQueryUnsuitableLoad", "dt4_can_unsuitable_load", "DT4 can query unsuitable load"},
|
|
{53, 4, "dt4", "loadOverCurrentShutdown", "dt4_load_over_current_shutdown", "DT4 load over-current shutdown"},
|
|
{54, 4, "dt4", "openCircuitDetected", "dt4_open_circuit_detected", "DT4 open circuit detected"},
|
|
{55, 4, "dt4", "loadDecreaseDetected", "dt4_load_decrease_detected", "DT4 load decrease detected"},
|
|
{56, 4, "dt4", "loadIncreaseDetected", "dt4_load_increase_detected", "DT4 load increase detected"},
|
|
{57, 4, "dt4", "thermalShutdown", "dt4_thermal_shutdown", "DT4 thermal shutdown"},
|
|
{58, 4, "dt4", "thermalOverloadReduction", "dt4_thermal_overload", "DT4 thermal overload reduction"},
|
|
{59, 4, "dt4", "referenceMeasurementFailedStatus", "dt4_reference_failed", "DT4 reference failed"},
|
|
{60, 4, "dt4", "loadUnsuitableForSelectedMethod", "dt4_unsuitable_load", "DT4 unsuitable load"},
|
|
{61, 4, "dt4", "supplyVoltageOutOfLimits", "dt4_supply_voltage_limits", "DT4 supply voltage out of limits"},
|
|
{62, 4, "dt4", "supplyFrequencyOutOfLimits", "dt4_supply_frequency_limits", "DT4 supply frequency out of limits"},
|
|
{63, 4, "dt4", "loadVoltageOutOfLimits", "dt4_load_voltage_limits", "DT4 load voltage out of limits"},
|
|
{64, 4, "dt4", "loadCurrentOverloadReduction", "dt4_load_current_overload", "DT4 load current overload"},
|
|
{65, 5, "dt5", "outputRange0To10VSelectable", "dt5_output_range_selectable", "DT5 output range selectable"},
|
|
{66, 5, "dt5", "internalPullUpSelectable", "dt5_pullup_selectable", "DT5 pull-up selectable"},
|
|
{67, 5, "dt5", "outputFaultDetectionSelectable", "dt5_fault_detection_selectable", "DT5 fault detection selectable"},
|
|
{68, 5, "dt5", "mainsRelay", "dt5_mains_relay", "DT5 mains relay"},
|
|
{69, 5, "dt5", "outputLevelQueryable", "dt5_output_level_queryable", "DT5 output level queryable"},
|
|
{70, 5, "dt5", "nonLogarithmicDimmingCurveSupported", "dt5_non_log_supported", "DT5 non-log curve supported"},
|
|
{71, 5, "dt5", "physicalSelectionByOutputLossSupported", "dt5_output_loss_selection", "DT5 output-loss selection supported"},
|
|
{72, 5, "dt5", "physicalSelectionSwitchSupported", "dt5_selection_switch", "DT5 selection switch supported"},
|
|
{73, 5, "dt5", "outputFaultDetected", "dt5_output_fault", "DT5 output fault detected"},
|
|
{74, 5, "dt5", "zeroToTenVoltOperation", "dt5_zero_to_ten", "DT5 0-10V operation"},
|
|
{75, 5, "dt5", "internalPullUpOn", "dt5_pullup_on", "DT5 pull-up on"},
|
|
{76, 5, "dt5", "nonLogarithmicDimmingCurveActive", "dt5_non_log_active", "DT5 non-log curve active"},
|
|
{77, 6, "dt6", "ledPowerSupplyIntegrated", "dt6_power_supply_integrated", "DT6 power supply integrated"},
|
|
{78, 6, "dt6", "ledModuleIntegrated", "dt6_module_integrated", "DT6 LED module integrated"},
|
|
{79, 6, "dt6", "acSupplyPossible", "dt6_ac_supply_possible", "DT6 AC supply possible"},
|
|
{80, 6, "dt6", "dcSupplyPossible", "dt6_dc_supply_possible", "DT6 DC supply possible"},
|
|
{81, 6, "dt6", "pwmModePossible", "dt6_pwm_possible", "DT6 PWM possible"},
|
|
{82, 6, "dt6", "amModePossible", "dt6_am_possible", "DT6 AM possible"},
|
|
{83, 6, "dt6", "currentControlledOutputPossible", "dt6_current_control_possible", "DT6 current control possible"},
|
|
{84, 6, "dt6", "highCurrentPulseModePossible", "dt6_high_current_possible", "DT6 high current possible"},
|
|
{85, 6, "dt6", "canQueryShortCircuit", "dt6_can_short_circuit", "DT6 can query short circuit"},
|
|
{86, 6, "dt6", "canQueryOpenCircuit", "dt6_can_open_circuit", "DT6 can query open circuit"},
|
|
{87, 6, "dt6", "canQueryLoadDecrease", "dt6_can_load_decrease", "DT6 can query load decrease"},
|
|
{88, 6, "dt6", "canQueryLoadIncrease", "dt6_can_load_increase", "DT6 can query load increase"},
|
|
{89, 6, "dt6", "canQueryCurrentProtector", "dt6_can_current_protector", "DT6 can query current protector"},
|
|
{90, 6, "dt6", "canQueryThermalShutdown", "dt6_can_thermal_shutdown", "DT6 can query thermal shutdown"},
|
|
{91, 6, "dt6", "canQueryThermalOverloadReduction", "dt6_can_thermal_overload", "DT6 can query thermal overload"},
|
|
{92, 6, "dt6", "shortCircuit", "dt6_short_circuit", "DT6 short circuit"},
|
|
{93, 6, "dt6", "openCircuit", "dt6_open_circuit", "DT6 open circuit"},
|
|
{94, 6, "dt6", "loadDecrease", "dt6_load_decrease", "DT6 load decrease"},
|
|
{95, 6, "dt6", "loadIncrease", "dt6_load_increase", "DT6 load increase"},
|
|
{96, 6, "dt6", "currentProtectorActive", "dt6_current_protector_active", "DT6 current protector active"},
|
|
{97, 6, "dt6", "thermalShutdown", "dt6_thermal_shutdown", "DT6 thermal shutdown"},
|
|
};
|
|
|
|
constexpr GeneratedDiagnosticBitSpec kGeneratedDiagnosticBitsTail[] = {
|
|
{98, 6, "dt6", "thermalOverloadReduction", "dt6_thermal_overload", "DT6 thermal overload"},
|
|
{99, 6, "dt6", "referenceMeasurementFailed", "dt6_reference_failed", "DT6 reference failed"},
|
|
{100, 6, "dt6", "pwmModeActive", "dt6_pwm_active", "DT6 PWM active"},
|
|
{101, 6, "dt6", "amModeActive", "dt6_am_active", "DT6 AM active"},
|
|
{102, 6, "dt6", "currentControlledOutput", "dt6_current_controlled_output", "DT6 current controlled output"},
|
|
{103, 6, "dt6", "highCurrentPulseModeActive", "dt6_high_current_active", "DT6 high current active"},
|
|
{104, 6, "dt6", "nonLogarithmicDimmingCurveActive", "dt6_non_log_active", "DT6 non-log curve active"},
|
|
{105, 8, "dt8_status", "xyOutOfRange", "dt8_xy_out_of_range", "DT8 xy out of range"},
|
|
{106, 8, "dt8_status", "ctOutOfRange", "dt8_ct_out_of_range", "DT8 CT out of range"},
|
|
{107, 8, "dt8_status", "autoCalibrationActive", "dt8_auto_cal_active", "DT8 auto calibration active"},
|
|
{108, 8, "dt8_status", "autoCalibrationSuccess", "dt8_auto_cal_success", "DT8 auto calibration success"},
|
|
{109, 8, "dt8_status", "xyActive", "dt8_xy_active", "DT8 xy active"},
|
|
{110, 8, "dt8_status", "ctActive", "dt8_ct_active", "DT8 CT active"},
|
|
{111, 8, "dt8_status", "primaryNActive", "dt8_primary_active", "DT8 primary-N active"},
|
|
{112, 8, "dt8_status", "rgbwafActive", "dt8_rgbwaf_active", "DT8 RGBWAF active"},
|
|
{113, 8, "dt8_status", "xyCapable", "dt8_xy_capable", "DT8 xy capable"},
|
|
{114, 8, "dt8_status", "ctCapable", "dt8_ct_capable", "DT8 CT capable"},
|
|
{115, 8, "dt8_status", "primaryNCapable", "dt8_primary_capable", "DT8 primary-N capable"},
|
|
{116, 8, "dt8_status", "rgbwafCapable", "dt8_rgbwaf_capable", "DT8 RGBWAF capable"},
|
|
{117, 6, "dt6", "physicalSelectionSupported", "dt6_physical_selection_supported", "DT6 physical selection supported"},
|
|
{118, 6, "dt6", "currentProtectorEnabled", "dt6_current_protector_enabled", "DT6 current protector enabled"},
|
|
{119, 1, "dt1", "controlGearFailure", "dt1_control_gear_failure", "DT1 control gear failure"},
|
|
};
|
|
|
|
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);
|
|
}
|
|
|
|
void addGeneratedDiagnosticPoint(std::map<PointKey, GatewayModbusPoint>* points,
|
|
uint8_t short_address,
|
|
const GeneratedDiagnosticBitSpec& spec) {
|
|
if (points == nullptr) {
|
|
return;
|
|
}
|
|
const uint16_t address = static_cast<uint16_t>(kDiagnosticDiscreteInputBase +
|
|
short_address * kDiagnosticStride + spec.offset);
|
|
GatewayModbusPoint point;
|
|
point.space = GatewayModbusSpace::kDiscreteInput;
|
|
point.access = GatewayModbusAccess::kReadOnly;
|
|
point.address = address;
|
|
point.id = generatedId(short_address, spec.suffix);
|
|
point.name = generatedName(short_address, spec.name);
|
|
point.generated = true;
|
|
point.generated_kind = GatewayModbusGeneratedKind::kShortDiagnosticBit;
|
|
point.short_address = short_address;
|
|
point.diagnostic_snapshot = spec.snapshot == nullptr ? "" : spec.snapshot;
|
|
point.diagnostic_bool = spec.bool_key == nullptr ? "" : spec.bool_key;
|
|
point.diagnostic_device_type = spec.device_type;
|
|
(*points)[PointKey{point.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,
|
|
point.bit_index,
|
|
point.diagnostic_snapshot,
|
|
point.diagnostic_bool,
|
|
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;
|
|
}
|
|
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))));
|
|
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;
|
|
}
|
|
|
|
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);
|
|
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));
|
|
}
|
|
|
|
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::kShortDiagnosticBit:
|
|
return "short_diagnostic_bit";
|
|
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& spec : kGeneratedDiagnosticBits) {
|
|
addGeneratedDiagnosticPoint(&next, short_address, spec);
|
|
}
|
|
for (const auto& spec : kGeneratedDiagnosticBitsTail) {
|
|
addGeneratedDiagnosticPoint(&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
|