feat(gateway): Update SDK configuration and add 485 control bridge

- Changed flash size configuration from 16MB to 4MB and updated partition table filename.
- Introduced two gateway channels with UART configurations for communication.
- Added support for gateway cache and startup services including BLE and Wi-Fi.
- Enabled SPI RAM and configured its parameters for better memory management.
- Enhanced the gateway bridge service to handle generated Modbus points more efficiently.
- Refactored the gateway Modbus component to improve point management and added new methods for point description and generation.
- Implemented a new Gateway485ControlBridge for handling 485 control communication with UART.
- Added necessary files for the 485 control bridge including configuration and implementation.

Signed-off-by: Tony <tonylu@tony-cloud.com>
This commit is contained in:
Tony
2026-05-06 00:39:58 +08:00
parent 34d2d9caa0
commit 029785ff1d
13 changed files with 925 additions and 181 deletions
@@ -119,12 +119,14 @@ struct GatewayModbusPoint {
std::string name;
bool generated{false};
GatewayModbusGeneratedKind generated_kind{GatewayModbusGeneratedKind::kNone};
const char* generated_suffix{"point"};
const char* generated_name{"point"};
int short_address{-1};
std::string model_id;
BridgeOperation operation{BridgeOperation::unknown};
std::optional<int> bit_index;
std::string diagnostic_snapshot;
std::string diagnostic_bool;
const char* diagnostic_snapshot{""};
const char* diagnostic_bool{""};
int diagnostic_device_type{-1};
};
@@ -165,8 +167,12 @@ class GatewayModbusBridge {
void rebuildMap();
std::optional<GatewayModbusPoint> findPoint(GatewayModbusSpace space,
uint16_t address) const;
GatewayModbusPointBinding describePoint(const GatewayModbusPoint& point) const;
void appendGeneratedPointsForShortAddress(uint8_t short_address,
std::vector<GatewayModbusPoint>* points) const;
std::vector<GatewayModbusPointBinding> describePoints() const;
std::vector<GatewayModbusPointBinding> describeHoldingRegisters() const;
const std::vector<GatewayModbusPoint>& points() const;
DaliBridgeResult readModelPoint(const GatewayModbusPoint& point) const;
DaliBridgeResult writeRegisterPoint(const GatewayModbusPoint& point, uint16_t value) const;
+231 -66
View File
@@ -3,7 +3,6 @@
#include <algorithm>
#include <array>
#include <cstdio>
#include <map>
#include <utility>
namespace gateway {
@@ -283,6 +282,16 @@ constexpr GeneratedDiagnosticBitSpec kGeneratedDiagnosticBitsTail[] = {
{119, 1, "dt1", "controlGearFailure", "dt1_control_gear_failure", "DT1 control gear failure"},
};
constexpr size_t kGeneratedDiagnosticBitCount =
sizeof(kGeneratedDiagnosticBits) / sizeof(kGeneratedDiagnosticBits[0]) +
sizeof(kGeneratedDiagnosticBitsTail) / sizeof(kGeneratedDiagnosticBitsTail[0]);
constexpr size_t kGeneratedPointsPerShort = kGeneratedCoils.size() +
kGeneratedDiscreteInputs.size() +
kGeneratedHoldingRegisters.size() +
kGeneratedInputRegisters.size() +
kGeneratedDiagnosticBitCount;
constexpr size_t kGeneratedPointCount = kShortAddressCount * kGeneratedPointsPerShort;
uint16_t baseForSpace(GatewayModbusSpace space) {
switch (space) {
case GatewayModbusSpace::kCoil:
@@ -338,62 +347,165 @@ std::string generatedName(uint8_t short_address, const char* name) {
return buffer;
}
void addGeneratedPoint(std::map<PointKey, GatewayModbusPoint>* points, uint8_t short_address,
const GeneratedPointSpec& spec) {
if (points == nullptr) {
return;
const char* literalOrEmpty(const char* value) {
return value == nullptr ? "" : value;
}
PointKey keyForPoint(const GatewayModbusPoint& point) {
return PointKey{point.space, point.address};
}
bool pointLess(const GatewayModbusPoint& lhs, const GatewayModbusPoint& rhs) {
return keyForPoint(lhs) < keyForPoint(rhs);
}
bool pointKeyEqual(const GatewayModbusPoint& lhs, const GatewayModbusPoint& rhs) {
return lhs.space == rhs.space && lhs.address == rhs.address;
}
bool pointKeyEqual(const GatewayModbusPoint& point, const PointKey& key) {
return point.space == key.space && point.address == key.address;
}
std::vector<GatewayModbusPoint>::const_iterator findStoredPoint(
const std::vector<GatewayModbusPoint>& points, PointKey key) {
const auto found = std::lower_bound(
points.begin(), points.end(), key,
[](const GatewayModbusPoint& point, const PointKey& value) {
return keyForPoint(point) < value;
});
if (found == points.end() || !pointKeyEqual(*found, key)) {
return points.end();
}
return found;
}
GatewayModbusPoint makeGeneratedPoint(uint8_t short_address,
const GeneratedPointSpec& spec) {
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.generated_suffix = spec.suffix == nullptr ? "point" : spec.suffix;
point.generated_name = spec.name == nullptr ? "point" : spec.name;
point.short_address = short_address;
(*points)[PointKey{spec.space, address}] = std::move(point);
return point;
}
void addGeneratedDiagnosticPoint(std::map<PointKey, GatewayModbusPoint>* points,
uint8_t short_address,
const GeneratedDiagnosticBitSpec& spec) {
if (points == nullptr) {
return;
}
GatewayModbusPoint makeGeneratedDiagnosticPoint(uint8_t short_address,
const GeneratedDiagnosticBitSpec& spec) {
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.generated_suffix = spec.suffix == nullptr ? "point" : spec.suffix;
point.generated_name = spec.name == nullptr ? "point" : spec.name;
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_snapshot = literalOrEmpty(spec.snapshot);
point.diagnostic_bool = literalOrEmpty(spec.bool_key);
point.diagnostic_device_type = spec.device_type;
(*points)[PointKey{point.space, address}] = std::move(point);
return point;
}
void appendIfNotOverridden(const std::vector<GatewayModbusPoint>& stored_points,
std::vector<GatewayModbusPoint>* points,
GatewayModbusPoint point) {
if (points == nullptr) {
return;
}
if (findStoredPoint(stored_points, keyForPoint(point)) != stored_points.end()) {
return;
}
points->push_back(std::move(point));
}
std::optional<GatewayModbusPoint> generatedPointForAddress(GatewayModbusSpace space,
uint16_t address) {
if (space == GatewayModbusSpace::kDiscreteInput &&
address >= kDiagnosticDiscreteInputBase) {
const uint16_t relative = static_cast<uint16_t>(address - kDiagnosticDiscreteInputBase);
const uint8_t short_address = static_cast<uint8_t>(relative / kDiagnosticStride);
const uint16_t offset = static_cast<uint16_t>(relative % kDiagnosticStride);
if (short_address >= kShortAddressCount) {
return std::nullopt;
}
for (const auto& spec : kGeneratedDiagnosticBits) {
if (spec.offset == offset) {
return makeGeneratedDiagnosticPoint(short_address, spec);
}
}
for (const auto& spec : kGeneratedDiagnosticBitsTail) {
if (spec.offset == offset) {
return makeGeneratedDiagnosticPoint(short_address, spec);
}
}
return std::nullopt;
}
const uint16_t base = baseForSpace(space);
if (address < base) {
return std::nullopt;
}
const uint16_t relative = static_cast<uint16_t>(address - base);
const uint8_t short_address = static_cast<uint8_t>(relative / kShortStride);
const uint16_t offset = static_cast<uint16_t>(relative % kShortStride);
if (short_address >= kShortAddressCount) {
return std::nullopt;
}
const auto find_regular_point = [short_address, offset](const auto& specs)
-> std::optional<GatewayModbusPoint> {
for (const auto& spec : specs) {
if (spec.offset == offset) {
return makeGeneratedPoint(short_address, spec);
}
}
return std::nullopt;
};
switch (space) {
case GatewayModbusSpace::kCoil:
return find_regular_point(kGeneratedCoils);
case GatewayModbusSpace::kDiscreteInput:
return find_regular_point(kGeneratedDiscreteInputs);
case GatewayModbusSpace::kHoldingRegister:
return find_regular_point(kGeneratedHoldingRegisters);
case GatewayModbusSpace::kInputRegister:
return find_regular_point(kGeneratedInputRegisters);
}
return std::nullopt;
}
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};
GatewayModbusPointBinding binding;
binding.model_id = point.model_id;
binding.space = point.space;
binding.address = point.address;
if (point.generated) {
const auto short_address = static_cast<uint8_t>(point.short_address < 0 ? 0 : point.short_address);
binding.id = generatedId(short_address, point.generated_suffix);
binding.name = generatedName(short_address, point.generated_name);
} else {
binding.id = point.id;
binding.name = point.name;
}
binding.generated = point.generated;
binding.generated_kind = point.generated_kind;
binding.short_address = point.short_address;
binding.access = point.access;
binding.bit_index = point.bit_index;
binding.diagnostic_snapshot = literalOrEmpty(point.diagnostic_snapshot);
binding.diagnostic_bool = literalOrEmpty(point.diagnostic_bool);
binding.diagnostic_device_type = point.diagnostic_device_type;
return binding;
}
int clampedInt(const DaliValue::Object& json, const std::string& key, int fallback,
@@ -657,29 +769,11 @@ void GatewayModbusBridge::setConfig(const GatewayModbusConfig& 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);
}
}
auto models = engine_.listModels();
points_.clear();
points_.reserve(models.size());
for (const auto& model : engine_.listModels()) {
for (const auto& model : models) {
if (model.protocol != BridgeProtocolKind::modbus || !model.external.registerAddress.has_value()) {
continue;
}
@@ -701,46 +795,117 @@ void GatewayModbusBridge::rebuildMap() {
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_.push_back(std::move(point));
}
points_.clear();
points_.reserve(next.size());
for (auto& entry : next) {
points_.push_back(std::move(entry.second));
std::stable_sort(points_.begin(), points_.end(), pointLess);
auto write = points_.begin();
for (auto read = points_.begin(); read != points_.end();) {
auto next = read + 1;
while (next != points_.end() && pointKeyEqual(*read, *next)) {
++next;
}
if (write != next - 1) {
*write = std::move(*(next - 1));
}
++write;
read = next;
}
points_.erase(write, points_.end());
}
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;
const PointKey key{space, address};
const auto found = findStoredPoint(points_, key);
if (found != points_.end()) {
return *found;
}
return generatedPointForAddress(space, address);
}
GatewayModbusPointBinding GatewayModbusBridge::describePoint(
const GatewayModbusPoint& point) const {
return toBinding(point);
}
void GatewayModbusBridge::appendGeneratedPointsForShortAddress(
uint8_t short_address, std::vector<GatewayModbusPoint>* points) const {
if (points == nullptr || short_address >= kShortAddressCount) {
return;
}
for (const auto& spec : kGeneratedCoils) {
appendIfNotOverridden(points_, points, makeGeneratedPoint(short_address, spec));
}
for (const auto& spec : kGeneratedDiscreteInputs) {
appendIfNotOverridden(points_, points, makeGeneratedPoint(short_address, spec));
}
for (const auto& spec : kGeneratedHoldingRegisters) {
appendIfNotOverridden(points_, points, makeGeneratedPoint(short_address, spec));
}
for (const auto& spec : kGeneratedInputRegisters) {
appendIfNotOverridden(points_, points, makeGeneratedPoint(short_address, spec));
}
for (const auto& spec : kGeneratedDiagnosticBits) {
appendIfNotOverridden(points_, points, makeGeneratedDiagnosticPoint(short_address, spec));
}
for (const auto& spec : kGeneratedDiagnosticBitsTail) {
appendIfNotOverridden(points_, points, makeGeneratedDiagnosticPoint(short_address, spec));
}
return *found;
}
std::vector<GatewayModbusPointBinding> GatewayModbusBridge::describePoints() const {
std::vector<GatewayModbusPointBinding> bindings;
bindings.reserve(points_.size());
bindings.reserve(kGeneratedPointCount + points_.size());
std::vector<GatewayModbusPoint> generated_points;
generated_points.reserve(kGeneratedPointsPerShort);
for (uint8_t short_address = 0; short_address < kShortAddressCount; ++short_address) {
generated_points.clear();
appendGeneratedPointsForShortAddress(short_address, &generated_points);
for (const auto& point : generated_points) {
bindings.push_back(toBinding(point));
}
}
for (const auto& point : points_) {
bindings.push_back(toBinding(point));
}
std::sort(bindings.begin(), bindings.end(), [](const auto& lhs, const auto& rhs) {
if (lhs.space != rhs.space) {
return static_cast<uint8_t>(lhs.space) < static_cast<uint8_t>(rhs.space);
}
return lhs.address < rhs.address;
});
return bindings;
}
std::vector<GatewayModbusPointBinding> GatewayModbusBridge::describeHoldingRegisters() const {
std::vector<GatewayModbusPointBinding> bindings;
std::vector<GatewayModbusPoint> generated_points;
generated_points.reserve(kGeneratedPointsPerShort);
for (uint8_t short_address = 0; short_address < kShortAddressCount; ++short_address) {
generated_points.clear();
appendGeneratedPointsForShortAddress(short_address, &generated_points);
for (const auto& point : generated_points) {
if (point.space == GatewayModbusSpace::kHoldingRegister) {
bindings.push_back(toBinding(point));
}
}
}
for (const auto& point : points_) {
if (point.space == GatewayModbusSpace::kHoldingRegister) {
bindings.push_back(toBinding(point));
}
}
std::sort(bindings.begin(), bindings.end(), [](const auto& lhs, const auto& rhs) {
return lhs.address < rhs.address;
});
return bindings;
}
const std::vector<GatewayModbusPoint>& GatewayModbusBridge::points() const {
return points_;
}
DaliBridgeResult GatewayModbusBridge::readModelPoint(const GatewayModbusPoint& point) const {
return executeModelPoint(point, std::nullopt);
}