feat: enhance gateway channel management with serial command handling and gateway ID updates
Signed-off-by: Tony <tonylu@tony-cloud.com>
This commit is contained in:
@@ -48,9 +48,26 @@ commands, address allocation, live management reads, bridge traffic, and any raw
|
|||||||
DALI bus activity from another master until the bus has been idle. Group and
|
DALI bus activity from another master until the bus has been idle. Group and
|
||||||
broadcast targets are never queried for refresh.
|
broadcast targets are never queried for refresh.
|
||||||
|
|
||||||
Gateway feature opcode `0x06` advertises cache support with bit `0x40`. Gateway
|
Gateway feature opcode `0x06` keeps the Lua-compatible low-byte feature bits,
|
||||||
opcode `0x39` returns cache summary and target snapshots so frontend clients can
|
advertises cache support with bit `0x40`, and advertises the native C++ gateway
|
||||||
read cached state without issuing live DALI queries on supported gateways.
|
type with bit `0x0100`. Gateway opcode `0x39` returns cache summary and target
|
||||||
|
snapshots so frontend clients can read cached state without issuing live DALI
|
||||||
|
queries on supported gateways.
|
||||||
|
|
||||||
|
Gateway opcode `0x09` with address/data `0x00/0x00` is a chip-level channel-id
|
||||||
|
report. It returns the enabled DALI channel ids so clients do not need to probe
|
||||||
|
every possible gateway id one by one. For native C++ gateways, channel number is
|
||||||
|
the fixed 1-based Kconfig slot (`1` to `16`) and channel id is the persisted,
|
||||||
|
Lua-compatible gateway id used by normal `0x28 0x01 ...` command frames.
|
||||||
|
|
||||||
|
Gateway opcode `0x0B` is the serial-scoped channel command. The serial is the
|
||||||
|
last three bytes of the ESP base MAC. Operation `0x00` reports serial plus
|
||||||
|
`(channel number, channel id)` pairs. Operations `0x01` and `0x02` get and set
|
||||||
|
the channel id for the fixed channel number in the command frame gateway byte.
|
||||||
|
Operation `0x03` wraps an existing gateway command and dispatches it by fixed
|
||||||
|
channel number after the serial matches, so BLE/Wi-Fi configuration and DALI
|
||||||
|
send/query commands can target a channel even when its variable channel id is
|
||||||
|
unknown.
|
||||||
|
|
||||||
## Current status
|
## Current status
|
||||||
|
|
||||||
|
|||||||
@@ -893,14 +893,22 @@ bool ValidateChannelBindings() {
|
|||||||
|
|
||||||
esp_err_t BindConfiguredChannels(gateway::DaliDomainService& dali_domain,
|
esp_err_t BindConfiguredChannels(gateway::DaliDomainService& dali_domain,
|
||||||
const gateway::GatewayRuntime& runtime) {
|
const gateway::GatewayRuntime& runtime) {
|
||||||
|
std::array<bool, 256> used_gateway_ids{};
|
||||||
for (const auto& channel : BuildChannelBindings()) {
|
for (const auto& channel : BuildChannelBindings()) {
|
||||||
if (!channel.enabled) {
|
if (!channel.enabled) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
const uint8_t gateway_id =
|
||||||
|
runtime.gatewayIdForChannel(channel.channel_index, channel.gateway_id);
|
||||||
|
if (used_gateway_ids[gateway_id]) {
|
||||||
|
ESP_LOGE(kTag, "duplicate runtime gateway id configured: %u", gateway_id);
|
||||||
|
return ESP_ERR_INVALID_STATE;
|
||||||
|
}
|
||||||
|
used_gateway_ids[gateway_id] = true;
|
||||||
if (channel.native_phy) {
|
if (channel.native_phy) {
|
||||||
gateway::DaliHardwareBusConfig config{};
|
gateway::DaliHardwareBusConfig config{};
|
||||||
config.channel_index = channel.channel_index;
|
config.channel_index = channel.channel_index;
|
||||||
config.gateway_id = channel.gateway_id;
|
config.gateway_id = gateway_id;
|
||||||
config.bus_id = channel.native_bus_id;
|
config.bus_id = channel.native_bus_id;
|
||||||
config.tx_pin = static_cast<uint8_t>(channel.native_tx_pin);
|
config.tx_pin = static_cast<uint8_t>(channel.native_tx_pin);
|
||||||
config.rx_pin = static_cast<uint8_t>(channel.native_rx_pin);
|
config.rx_pin = static_cast<uint8_t>(channel.native_rx_pin);
|
||||||
@@ -914,7 +922,7 @@ esp_err_t BindConfiguredChannels(gateway::DaliDomainService& dali_domain,
|
|||||||
} else if (channel.serial_phy) {
|
} else if (channel.serial_phy) {
|
||||||
gateway::DaliSerialBusConfig config{};
|
gateway::DaliSerialBusConfig config{};
|
||||||
config.channel_index = channel.channel_index;
|
config.channel_index = channel.channel_index;
|
||||||
config.gateway_id = channel.gateway_id;
|
config.gateway_id = gateway_id;
|
||||||
config.uart_port = channel.uart_port;
|
config.uart_port = channel.uart_port;
|
||||||
config.tx_pin = channel.serial_tx_pin;
|
config.tx_pin = channel.serial_tx_pin;
|
||||||
config.rx_pin = channel.serial_rx_pin;
|
config.rx_pin = channel.serial_rx_pin;
|
||||||
@@ -966,8 +974,8 @@ extern "C" void app_main(void) {
|
|||||||
},
|
},
|
||||||
s_dali_domain.get());
|
s_dali_domain.get());
|
||||||
ESP_ERROR_CHECK(s_runtime->start());
|
ESP_ERROR_CHECK(s_runtime->start());
|
||||||
s_runtime->setGatewayCount(CONFIG_GATEWAY_CHANNEL_COUNT);
|
|
||||||
ESP_ERROR_CHECK(BindConfiguredChannels(*s_dali_domain, *s_runtime));
|
ESP_ERROR_CHECK(BindConfiguredChannels(*s_dali_domain, *s_runtime));
|
||||||
|
s_runtime->setGatewayCount(s_dali_domain->channelCount());
|
||||||
|
|
||||||
gateway::GatewayCacheConfig cache_config;
|
gateway::GatewayCacheConfig cache_config;
|
||||||
cache_config.cache_enabled = kCacheSupported && kCacheStartupEnabled && s_runtime->cacheEnabled();
|
cache_config.cache_enabled = kCacheSupported && kCacheStartupEnabled && s_runtime->cacheEnabled();
|
||||||
|
|||||||
@@ -192,6 +192,8 @@ class DaliDomainService {
|
|||||||
std::optional<uint8_t> level) const;
|
std::optional<uint8_t> level) const;
|
||||||
bool applyAddressSettings(uint8_t gateway_id, int short_address,
|
bool applyAddressSettings(uint8_t gateway_id, int short_address,
|
||||||
const DaliAddressSettingsSnapshot& settings) const;
|
const DaliAddressSettingsSnapshot& settings) const;
|
||||||
|
std::optional<uint8_t> gatewayIdForChannelIndex(uint8_t channel_index) const;
|
||||||
|
bool updateChannelGatewayId(uint8_t channel_index, uint8_t gateway_id);
|
||||||
bool updateChannelName(uint8_t gateway_id, std::string_view name);
|
bool updateChannelName(uint8_t gateway_id, std::string_view name);
|
||||||
bool allocateAllAddr(uint8_t gateway_id, int start_address = 0) const;
|
bool allocateAllAddr(uint8_t gateway_id, int start_address = 0) const;
|
||||||
void stopAllocAddr(uint8_t gateway_id) const;
|
void stopAllocAddr(uint8_t gateway_id) const;
|
||||||
@@ -212,6 +214,7 @@ class DaliDomainService {
|
|||||||
DaliChannel* findChannelByGateway(uint8_t gateway_id);
|
DaliChannel* findChannelByGateway(uint8_t gateway_id);
|
||||||
const DaliChannel* findChannelByGateway(uint8_t gateway_id) const;
|
const DaliChannel* findChannelByGateway(uint8_t gateway_id) const;
|
||||||
DaliChannel* findChannelByIndex(uint8_t channel_index);
|
DaliChannel* findChannelByIndex(uint8_t channel_index);
|
||||||
|
const DaliChannel* findChannelByIndex(uint8_t channel_index) const;
|
||||||
const DaliChannel* findChannelByHardwareBus(uint8_t bus_id) const;
|
const DaliChannel* findChannelByHardwareBus(uint8_t bus_id) const;
|
||||||
bool hasSerialPort(int uart_port) const;
|
bool hasSerialPort(int uart_port) const;
|
||||||
esp_err_t startSerialRxTask(DaliChannel& channel);
|
esp_err_t startSerialRxTask(DaliChannel& channel);
|
||||||
|
|||||||
@@ -635,6 +635,31 @@ std::vector<DaliChannelInfo> DaliDomainService::channelInfo() const {
|
|||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::optional<uint8_t> DaliDomainService::gatewayIdForChannelIndex(uint8_t channel_index) const {
|
||||||
|
const auto* channel = findChannelByIndex(channel_index);
|
||||||
|
if (channel == nullptr) {
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
return channel->config.gateway_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DaliDomainService::updateChannelGatewayId(uint8_t channel_index, uint8_t gateway_id) {
|
||||||
|
auto* channel = findChannelByIndex(channel_index);
|
||||||
|
if (channel == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (const auto& other : channels_) {
|
||||||
|
if (other.get() != channel && other->config.gateway_id == gateway_id) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
channel->config.gateway_id = gateway_id;
|
||||||
|
if (channel->dali != nullptr) {
|
||||||
|
channel->dali = std::make_unique<Dali>(*channel->comm, gateway_id, channel->config.name);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void DaliDomainService::addRawFrameSink(std::function<void(const DaliRawFrame& frame)> sink) {
|
void DaliDomainService::addRawFrameSink(std::function<void(const DaliRawFrame& frame)> sink) {
|
||||||
if (!sink) {
|
if (!sink) {
|
||||||
return;
|
return;
|
||||||
@@ -1674,6 +1699,15 @@ DaliDomainService::DaliChannel* DaliDomainService::findChannelByIndex(uint8_t ch
|
|||||||
return it == channels_.end() ? nullptr : it->get();
|
return it == channels_.end() ? nullptr : it->get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const DaliDomainService::DaliChannel* DaliDomainService::findChannelByIndex(
|
||||||
|
uint8_t channel_index) const {
|
||||||
|
const auto it = std::find_if(channels_.begin(), channels_.end(),
|
||||||
|
[channel_index](const auto& channel) {
|
||||||
|
return channel->config.channel_index == channel_index;
|
||||||
|
});
|
||||||
|
return it == channels_.end() ? nullptr : it->get();
|
||||||
|
}
|
||||||
|
|
||||||
const DaliDomainService::DaliChannel* DaliDomainService::findChannelByHardwareBus(
|
const DaliDomainService::DaliChannel* DaliDomainService::findChannelByHardwareBus(
|
||||||
uint8_t bus_id) const {
|
uint8_t bus_id) const {
|
||||||
const auto it = std::find_if(channels_.begin(), channels_.end(), [bus_id](const auto& channel) {
|
const auto it = std::find_if(channels_.begin(), channels_.end(), [bus_id](const auto& channel) {
|
||||||
|
|||||||
@@ -139,6 +139,7 @@ class GatewayController {
|
|||||||
|
|
||||||
bool hasGateway(uint8_t gateway_id) const;
|
bool hasGateway(uint8_t gateway_id) const;
|
||||||
std::vector<uint8_t> gatewayIds() const;
|
std::vector<uint8_t> gatewayIds() const;
|
||||||
|
std::optional<uint8_t> gatewayIdForChannelNumber(uint8_t channel_number) const;
|
||||||
std::string gatewayName(uint8_t gateway_id) const;
|
std::string gatewayName(uint8_t gateway_id) const;
|
||||||
void refreshRuntimeGatewayNames();
|
void refreshRuntimeGatewayNames();
|
||||||
void publishPayload(uint8_t gateway_id, const std::vector<uint8_t>& payload);
|
void publishPayload(uint8_t gateway_id, const std::vector<uint8_t>& payload);
|
||||||
@@ -181,6 +182,11 @@ class GatewayController {
|
|||||||
bool executeGroup(uint8_t gateway_id, uint8_t group_id);
|
bool executeGroup(uint8_t gateway_id, uint8_t group_id);
|
||||||
|
|
||||||
void handleGatewayNameCommand(uint8_t gateway_id, const std::vector<uint8_t>& command);
|
void handleGatewayNameCommand(uint8_t gateway_id, const std::vector<uint8_t>& command);
|
||||||
|
void handleGatewaySerialCommand(uint8_t channel_number, const std::vector<uint8_t>& command);
|
||||||
|
void publishGatewaySerialReport();
|
||||||
|
void publishGatewaySerialResponse(uint8_t status, uint8_t op,
|
||||||
|
const std::vector<uint8_t>& data);
|
||||||
|
bool gatewaySerialMatches(const std::vector<uint8_t>& command) const;
|
||||||
void handleGatewayIdentityCommand(uint8_t gateway_id, uint8_t op);
|
void handleGatewayIdentityCommand(uint8_t gateway_id, uint8_t op);
|
||||||
void handleAllocationCommand(uint8_t gateway_id, const std::vector<uint8_t>& command);
|
void handleAllocationCommand(uint8_t gateway_id, const std::vector<uint8_t>& command);
|
||||||
void handleInternalSceneCommand(uint8_t gateway_id, const std::vector<uint8_t>& command);
|
void handleInternalSceneCommand(uint8_t gateway_id, const std::vector<uint8_t>& command);
|
||||||
|
|||||||
@@ -28,13 +28,23 @@ constexpr uint8_t kBridgeTransportRequestOpcode = 0xB0;
|
|||||||
constexpr uint8_t kBridgeTransportResponseOpcode = 0xB1;
|
constexpr uint8_t kBridgeTransportResponseOpcode = 0xB1;
|
||||||
constexpr uint8_t kBridgeTransportVersion = 1;
|
constexpr uint8_t kBridgeTransportVersion = 1;
|
||||||
constexpr size_t kBridgeTransportMaxChunkBytes = 120;
|
constexpr size_t kBridgeTransportMaxChunkBytes = 120;
|
||||||
|
constexpr uint8_t kGatewaySerialOpcode = 0x0B;
|
||||||
|
constexpr uint8_t kGatewaySerialOpReport = 0x00;
|
||||||
|
constexpr uint8_t kGatewaySerialOpGetChannelId = 0x01;
|
||||||
|
constexpr uint8_t kGatewaySerialOpSetChannelId = 0x02;
|
||||||
|
constexpr uint8_t kGatewaySerialOpDispatchByChannelNumber = 0x03;
|
||||||
|
constexpr uint8_t kGatewaySerialStatusOk = 0x00;
|
||||||
|
constexpr uint8_t kGatewaySerialStatusInvalidArgument = 0x02;
|
||||||
|
constexpr uint8_t kGatewaySerialStatusDuplicateChannelId = 0x03;
|
||||||
|
constexpr uint8_t kGatewaySerialStatusStorageError = 0x04;
|
||||||
constexpr uint8_t kDali103SendOpcode = 0x60;
|
constexpr uint8_t kDali103SendOpcode = 0x60;
|
||||||
constexpr uint8_t kDali103SendTwiceOpcode = 0x61;
|
constexpr uint8_t kDali103SendTwiceOpcode = 0x61;
|
||||||
constexpr uint8_t kDali103QueryOpcode = 0x62;
|
constexpr uint8_t kDali103QueryOpcode = 0x62;
|
||||||
constexpr uint8_t kDali103QueryResponseOpcode = 0x63;
|
constexpr uint8_t kDali103QueryResponseOpcode = 0x63;
|
||||||
constexpr uint8_t kDali103NoResponseOpcode = 0x64;
|
constexpr uint8_t kDali103NoResponseOpcode = 0x64;
|
||||||
constexpr uint8_t kDali103RawFrameOpcode = 0x65;
|
constexpr uint8_t kDali103RawFrameOpcode = 0x65;
|
||||||
constexpr uint8_t kGatewayFeatureCache = 0x40;
|
constexpr uint16_t kGatewayFeatureCache = 0x0040;
|
||||||
|
constexpr uint16_t kGatewayFeatureNativeCpp = 0x0100;
|
||||||
constexpr uint8_t kGatewayCacheOpcode = 0x39;
|
constexpr uint8_t kGatewayCacheOpcode = 0x39;
|
||||||
constexpr uint8_t kGatewayCacheProtocolVersion = 1;
|
constexpr uint8_t kGatewayCacheProtocolVersion = 1;
|
||||||
constexpr uint8_t kGatewayCacheOpSummary = 0x00;
|
constexpr uint8_t kGatewayCacheOpSummary = 0x00;
|
||||||
@@ -165,6 +175,13 @@ void AppendLe32(std::vector<uint8_t>& out, uint32_t value) {
|
|||||||
out.push_back(static_cast<uint8_t>((value >> 24) & 0xFF));
|
out.push_back(static_cast<uint8_t>((value >> 24) & 0xFF));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AppendFeatureBits(std::vector<uint8_t>& out, uint16_t feature) {
|
||||||
|
if (feature > 0xFF) {
|
||||||
|
out.push_back(static_cast<uint8_t>((feature >> 8) & 0xFF));
|
||||||
|
}
|
||||||
|
out.push_back(static_cast<uint8_t>(feature & 0xFF));
|
||||||
|
}
|
||||||
|
|
||||||
uint8_t CacheByte(std::optional<uint8_t> value) {
|
uint8_t CacheByte(std::optional<uint8_t> value) {
|
||||||
return value.value_or(0xFF);
|
return value.value_or(0xFF);
|
||||||
}
|
}
|
||||||
@@ -759,6 +776,10 @@ void GatewayController::dispatchCommand(const std::vector<uint8_t>& command) {
|
|||||||
publishPayload(gateway_id, payload);
|
publishPayload(gateway_id, payload);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (opcode == kGatewaySerialOpcode) {
|
||||||
|
handleGatewaySerialCommand(gateway_id, command);
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (!hasGateway(gateway_id)) {
|
if (!hasGateway(gateway_id)) {
|
||||||
ESP_LOGW(kTag, "command for unknown gateway=%u opcode=0x%02x", gateway_id, opcode);
|
ESP_LOGW(kTag, "command for unknown gateway=%u opcode=0x%02x", gateway_id, opcode);
|
||||||
return;
|
return;
|
||||||
@@ -825,7 +846,7 @@ void GatewayController::dispatchCommand(const std::vector<uint8_t>& command) {
|
|||||||
handleGatewayNameCommand(gateway_id, command);
|
handleGatewayNameCommand(gateway_id, command);
|
||||||
break;
|
break;
|
||||||
case 0x06: {
|
case 0x06: {
|
||||||
uint8_t feature = 0;
|
uint16_t feature = kGatewayFeatureNativeCpp;
|
||||||
if (setup_mode_ && config_.setup_supported) {
|
if (setup_mode_ && config_.setup_supported) {
|
||||||
feature |= 0x01;
|
feature |= 0x01;
|
||||||
}
|
}
|
||||||
@@ -844,7 +865,9 @@ void GatewayController::dispatchCommand(const std::vector<uint8_t>& command) {
|
|||||||
if (config_.cache_supported) {
|
if (config_.cache_supported) {
|
||||||
feature |= kGatewayFeatureCache;
|
feature |= kGatewayFeatureCache;
|
||||||
}
|
}
|
||||||
publishPayload(gateway_id, {0x03, gateway_id, feature});
|
std::vector<uint8_t> payload{0x03, gateway_id};
|
||||||
|
AppendFeatureBits(payload, feature);
|
||||||
|
publishPayload(gateway_id, payload);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 0x07:
|
case 0x07:
|
||||||
@@ -995,6 +1018,23 @@ std::vector<uint8_t> GatewayController::gatewayIds() const {
|
|||||||
return ids;
|
return ids;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::optional<uint8_t> GatewayController::gatewayIdForChannelNumber(
|
||||||
|
uint8_t channel_number) const {
|
||||||
|
if (channel_number == 0) {
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
const uint8_t channel_index = static_cast<uint8_t>(channel_number - 1);
|
||||||
|
const auto channels = dali_domain_.channelInfo();
|
||||||
|
const auto it =
|
||||||
|
std::find_if(channels.begin(), channels.end(), [channel_index](const auto& channel) {
|
||||||
|
return channel.channel_index == channel_index;
|
||||||
|
});
|
||||||
|
if (it == channels.end()) {
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
return it->gateway_id;
|
||||||
|
}
|
||||||
|
|
||||||
std::string GatewayController::gatewayName(uint8_t gateway_id) const {
|
std::string GatewayController::gatewayName(uint8_t gateway_id) const {
|
||||||
const auto channels = dali_domain_.channelInfo();
|
const auto channels = dali_domain_.channelInfo();
|
||||||
const auto it = std::find_if(channels.begin(), channels.end(), [gateway_id](const auto& channel) {
|
const auto it = std::find_if(channels.begin(), channels.end(), [gateway_id](const auto& channel) {
|
||||||
@@ -1618,6 +1658,117 @@ void GatewayController::handleGatewayNameCommand(uint8_t gateway_id,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GatewayController::publishGatewaySerialResponse(uint8_t status, uint8_t op,
|
||||||
|
const std::vector<uint8_t>& data) {
|
||||||
|
std::vector<uint8_t> payload{kGatewaySerialOpcode, status};
|
||||||
|
const auto serial = runtime_.serialNumberBytes();
|
||||||
|
payload.insert(payload.end(), serial.begin(), serial.end());
|
||||||
|
payload.push_back(op);
|
||||||
|
payload.insert(payload.end(), data.begin(), data.end());
|
||||||
|
publishPayload(0, payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GatewayController::gatewaySerialMatches(const std::vector<uint8_t>& command) const {
|
||||||
|
if (command.size() < 9) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const auto serial = runtime_.serialNumberBytes();
|
||||||
|
return command[5] == serial[0] && command[6] == serial[1] && command[7] == serial[2];
|
||||||
|
}
|
||||||
|
|
||||||
|
void GatewayController::publishGatewaySerialReport() {
|
||||||
|
const auto serial = runtime_.serialNumberBytes();
|
||||||
|
std::vector<uint8_t> payload{kGatewaySerialOpcode,
|
||||||
|
kGatewaySerialStatusOk,
|
||||||
|
serial[0],
|
||||||
|
serial[1],
|
||||||
|
serial[2],
|
||||||
|
kGatewaySerialOpReport};
|
||||||
|
const auto channels = dali_domain_.channelInfo();
|
||||||
|
const auto count = std::min<size_t>(channels.size(), 16);
|
||||||
|
payload.push_back(static_cast<uint8_t>(count));
|
||||||
|
for (size_t index = 0; index < count; ++index) {
|
||||||
|
payload.push_back(static_cast<uint8_t>(channels[index].channel_index + 1));
|
||||||
|
payload.push_back(channels[index].gateway_id);
|
||||||
|
}
|
||||||
|
publishPayload(0, payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GatewayController::handleGatewaySerialCommand(uint8_t channel_number,
|
||||||
|
const std::vector<uint8_t>& command) {
|
||||||
|
const uint8_t op = command.size() > 4 ? command[4] : kGatewaySerialOpReport;
|
||||||
|
if (op == kGatewaySerialOpReport) {
|
||||||
|
if (command.size() >= 9 && !gatewaySerialMatches(command)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
publishGatewaySerialReport();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!gatewaySerialMatches(command)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto gateway_id = gatewayIdForChannelNumber(channel_number);
|
||||||
|
if (!gateway_id.has_value()) {
|
||||||
|
publishGatewaySerialResponse(kGatewaySerialStatusInvalidArgument, op, {channel_number});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (op) {
|
||||||
|
case kGatewaySerialOpGetChannelId:
|
||||||
|
publishGatewaySerialResponse(kGatewaySerialStatusOk, op,
|
||||||
|
{channel_number, gateway_id.value()});
|
||||||
|
return;
|
||||||
|
case kGatewaySerialOpSetChannelId: {
|
||||||
|
if (command.size() < 10) {
|
||||||
|
publishGatewaySerialResponse(kGatewaySerialStatusInvalidArgument, op, {channel_number});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const uint8_t new_gateway_id = command[8];
|
||||||
|
const uint8_t channel_index = static_cast<uint8_t>(channel_number - 1);
|
||||||
|
const uint8_t old_gateway_id = gateway_id.value();
|
||||||
|
if (!dali_domain_.updateChannelGatewayId(channel_index, new_gateway_id)) {
|
||||||
|
publishGatewaySerialResponse(kGatewaySerialStatusDuplicateChannelId, op,
|
||||||
|
{channel_number, new_gateway_id});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!runtime_.setGatewayIdForChannel(channel_index, new_gateway_id)) {
|
||||||
|
dali_domain_.updateChannelGatewayId(channel_index, old_gateway_id);
|
||||||
|
publishGatewaySerialResponse(kGatewaySerialStatusStorageError, op,
|
||||||
|
{channel_number, new_gateway_id});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
cache_.preloadChannel(new_gateway_id);
|
||||||
|
reconciliation_jobs_.erase(old_gateway_id);
|
||||||
|
cache_refresh_jobs_.erase(old_gateway_id);
|
||||||
|
refreshRuntimeGatewayNames();
|
||||||
|
publishGatewaySerialResponse(kGatewaySerialStatusOk, op,
|
||||||
|
{channel_number, new_gateway_id});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case kGatewaySerialOpDispatchByChannelNumber: {
|
||||||
|
if (command.size() < 12) {
|
||||||
|
publishGatewaySerialResponse(kGatewaySerialStatusInvalidArgument, op, {channel_number});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const uint8_t inner_opcode = command[8];
|
||||||
|
if (inner_opcode == kGatewaySerialOpcode) {
|
||||||
|
publishGatewaySerialResponse(kGatewaySerialStatusInvalidArgument, op,
|
||||||
|
{channel_number, inner_opcode});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
std::vector<uint8_t> inner{0x28, 0x01, gateway_id.value(), inner_opcode};
|
||||||
|
inner.insert(inner.end(), command.begin() + 9, command.end() - 1);
|
||||||
|
dispatchCommand(GatewayRuntime::checksum(std::move(inner)));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
publishGatewaySerialResponse(kGatewaySerialStatusInvalidArgument, op, {channel_number});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void GatewayController::handleGatewayIdentityCommand(uint8_t gateway_id, uint8_t op) {
|
void GatewayController::handleGatewayIdentityCommand(uint8_t gateway_id, uint8_t op) {
|
||||||
std::string value;
|
std::string value;
|
||||||
if (op == 0x00) {
|
if (op == 0x00) {
|
||||||
|
|||||||
@@ -76,11 +76,14 @@ class GatewaySettingsStore {
|
|||||||
|
|
||||||
std::string getGatewayName(uint8_t gateway_id, std::string_view fallback) const;
|
std::string getGatewayName(uint8_t gateway_id, std::string_view fallback) const;
|
||||||
bool setGatewayName(uint8_t gateway_id, std::string_view name);
|
bool setGatewayName(uint8_t gateway_id, std::string_view name);
|
||||||
|
uint8_t getChannelGatewayId(uint8_t channel_index, uint8_t fallback) const;
|
||||||
|
bool setChannelGatewayId(uint8_t channel_index, uint8_t gateway_id);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::optional<std::string> readString(std::string_view key) const;
|
std::optional<std::string> readString(std::string_view key) const;
|
||||||
bool writeString(std::string_view key, std::string_view value);
|
bool writeString(std::string_view key, std::string_view value);
|
||||||
std::string makeGatewayNameKey(uint8_t gateway_id) const;
|
std::string makeGatewayNameKey(uint8_t gateway_id) const;
|
||||||
|
std::string makeChannelGatewayIdKey(uint8_t channel_index) const;
|
||||||
|
|
||||||
mutable nvs_handle_t handle_{0};
|
mutable nvs_handle_t handle_{0};
|
||||||
};
|
};
|
||||||
@@ -135,8 +138,11 @@ class GatewayRuntime {
|
|||||||
bool setBleEnabled(bool enabled);
|
bool setBleEnabled(bool enabled);
|
||||||
bool cacheEnabled() const;
|
bool cacheEnabled() const;
|
||||||
bool setCacheEnabled(bool enabled);
|
bool setCacheEnabled(bool enabled);
|
||||||
|
uint8_t gatewayIdForChannel(uint8_t channel_index, uint8_t fallback) const;
|
||||||
|
bool setGatewayIdForChannel(uint8_t channel_index, uint8_t gateway_id);
|
||||||
std::string gatewayName(uint8_t gateway_id) const;
|
std::string gatewayName(uint8_t gateway_id) const;
|
||||||
bool setGatewayName(uint8_t gateway_id, std::string_view name);
|
bool setGatewayName(uint8_t gateway_id, std::string_view name);
|
||||||
|
std::vector<uint8_t> serialNumberBytes() const;
|
||||||
std::string gatewaySerialHex(uint8_t gateway_id) const;
|
std::string gatewaySerialHex(uint8_t gateway_id) const;
|
||||||
std::string bleMacHex() const;
|
std::string bleMacHex() const;
|
||||||
std::string bleGatewayName(uint8_t gateway_id, std::string_view gateway_name) const;
|
std::string bleGatewayName(uint8_t gateway_id, std::string_view gateway_name) const;
|
||||||
@@ -162,6 +168,7 @@ class GatewayRuntime {
|
|||||||
std::deque<std::vector<uint8_t>> normal_commands_;
|
std::deque<std::vector<uint8_t>> normal_commands_;
|
||||||
std::deque<std::vector<uint8_t>> maintenance_commands_;
|
std::deque<std::vector<uint8_t>> maintenance_commands_;
|
||||||
mutable std::map<uint8_t, std::string> gateway_names_;
|
mutable std::map<uint8_t, std::string> gateway_names_;
|
||||||
|
mutable std::map<uint8_t, uint8_t> channel_gateway_ids_;
|
||||||
size_t gateway_count_{0};
|
size_t gateway_count_{0};
|
||||||
bool ble_enabled_{false};
|
bool ble_enabled_{false};
|
||||||
bool cache_enabled_{true};
|
bool cache_enabled_{true};
|
||||||
|
|||||||
@@ -57,7 +57,8 @@ esp_err_t InitializeRuntimeNvs() {
|
|||||||
|
|
||||||
std::string ReadRuntimeSerialId() {
|
std::string ReadRuntimeSerialId() {
|
||||||
uint8_t mac[6] = {0};
|
uint8_t mac[6] = {0};
|
||||||
if (esp_read_mac(mac, ESP_MAC_WIFI_STA) != ESP_OK) {
|
if (esp_read_mac(mac, ESP_MAC_BASE) != ESP_OK &&
|
||||||
|
esp_read_mac(mac, ESP_MAC_WIFI_STA) != ESP_OK) {
|
||||||
return "DALIGW";
|
return "DALIGW";
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -188,6 +189,31 @@ bool GatewaySettingsStore::setGatewayName(uint8_t gateway_id,
|
|||||||
return writeString(makeGatewayNameKey(gateway_id), name);
|
return writeString(makeGatewayNameKey(gateway_id), name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint8_t GatewaySettingsStore::getChannelGatewayId(uint8_t channel_index,
|
||||||
|
uint8_t fallback) const {
|
||||||
|
if (handle_ == 0) {
|
||||||
|
return fallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t gateway_id = fallback;
|
||||||
|
if (nvs_get_u8(handle_, makeChannelGatewayIdKey(channel_index).c_str(), &gateway_id) !=
|
||||||
|
ESP_OK) {
|
||||||
|
return fallback;
|
||||||
|
}
|
||||||
|
return gateway_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GatewaySettingsStore::setChannelGatewayId(uint8_t channel_index,
|
||||||
|
uint8_t gateway_id) {
|
||||||
|
if (handle_ == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nvs_set_u8(handle_, makeChannelGatewayIdKey(channel_index).c_str(), gateway_id) ==
|
||||||
|
ESP_OK &&
|
||||||
|
nvs_commit(handle_) == ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
std::optional<std::string> GatewaySettingsStore::readString(std::string_view key) const {
|
std::optional<std::string> GatewaySettingsStore::readString(std::string_view key) const {
|
||||||
if (handle_ == 0) {
|
if (handle_ == 0) {
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
@@ -222,6 +248,12 @@ std::string GatewaySettingsStore::makeGatewayNameKey(uint8_t gateway_id) const {
|
|||||||
return std::string(key);
|
return std::string(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string GatewaySettingsStore::makeChannelGatewayIdKey(uint8_t channel_index) const {
|
||||||
|
char key[24] = {0};
|
||||||
|
std::snprintf(key, sizeof(key), "dali_ch_id_%u", channel_index);
|
||||||
|
return std::string(key);
|
||||||
|
}
|
||||||
|
|
||||||
GatewayRuntime::GatewayRuntime(BootProfile profile, GatewayRuntimeConfig config,
|
GatewayRuntime::GatewayRuntime(BootProfile profile, GatewayRuntimeConfig config,
|
||||||
DaliDomainService* dali_domain)
|
DaliDomainService* dali_domain)
|
||||||
: profile_(profile),
|
: profile_(profile),
|
||||||
@@ -323,7 +355,7 @@ GatewayRuntime::CommandPriority GatewayRuntime::classifyCommandPriority(
|
|||||||
}
|
}
|
||||||
if (opcode == 0x00 || opcode == 0x01 || opcode == 0x03 || opcode == 0x04 || opcode == 0x07 ||
|
if (opcode == 0x00 || opcode == 0x01 || opcode == 0x03 || opcode == 0x04 || opcode == 0x07 ||
|
||||||
opcode == 0x08 || opcode == 0x10 || opcode == 0x11 || opcode == 0x12 || opcode == 0x13 ||
|
opcode == 0x08 || opcode == 0x10 || opcode == 0x11 || opcode == 0x12 || opcode == 0x13 ||
|
||||||
opcode == 0x17 || opcode == 0x18 || opcode == 0x37 || opcode == 0x38 ||
|
opcode == 0x0B || opcode == 0x17 || opcode == 0x18 || opcode == 0x37 || opcode == 0x38 ||
|
||||||
opcode == 0x60 || opcode == 0x61 || opcode == 0x62 || (opcode == 0x30 && addr == 0)) {
|
opcode == 0x60 || opcode == 0x61 || opcode == 0x62 || (opcode == 0x30 && addr == 0)) {
|
||||||
return CommandPriority::kControl;
|
return CommandPriority::kControl;
|
||||||
}
|
}
|
||||||
@@ -531,6 +563,27 @@ bool GatewayRuntime::setCacheEnabled(bool enabled) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint8_t GatewayRuntime::gatewayIdForChannel(uint8_t channel_index, uint8_t fallback) const {
|
||||||
|
LockGuard guard(command_lock_);
|
||||||
|
const auto cached = channel_gateway_ids_.find(channel_index);
|
||||||
|
if (cached != channel_gateway_ids_.end()) {
|
||||||
|
return cached->second;
|
||||||
|
}
|
||||||
|
const uint8_t gateway_id = settings_.getChannelGatewayId(channel_index, fallback);
|
||||||
|
channel_gateway_ids_[channel_index] = gateway_id;
|
||||||
|
return gateway_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GatewayRuntime::setGatewayIdForChannel(uint8_t channel_index, uint8_t gateway_id) {
|
||||||
|
if (!settings_.setChannelGatewayId(channel_index, gateway_id)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
LockGuard guard(command_lock_);
|
||||||
|
channel_gateway_ids_[channel_index] = gateway_id;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
std::string GatewayRuntime::gatewayName(uint8_t gateway_id) const {
|
std::string GatewayRuntime::gatewayName(uint8_t gateway_id) const {
|
||||||
LockGuard guard(command_lock_);
|
LockGuard guard(command_lock_);
|
||||||
const auto cached = gateway_names_.find(gateway_id);
|
const auto cached = gateway_names_.find(gateway_id);
|
||||||
@@ -577,6 +630,11 @@ std::string GatewayRuntime::gatewaySerialHex(uint8_t gateway_id) const {
|
|||||||
return toHex(serial);
|
return toHex(serial);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<uint8_t> GatewayRuntime::serialNumberBytes() const {
|
||||||
|
const auto bytes = serialBytes();
|
||||||
|
return {bytes[3], bytes[4], bytes[5]};
|
||||||
|
}
|
||||||
|
|
||||||
std::string GatewayRuntime::bleMacHex() const {
|
std::string GatewayRuntime::bleMacHex() const {
|
||||||
return toHex(serialBytes());
|
return toHex(serialBytes());
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user