Files
gateway/components/gateway_runtime/src/gateway_runtime.cpp
T
Tony 1e8bda56e0 feat: Add cache management and USB configuration support
- Implemented `setCacheEnabled` method in `GatewayCache` to enable/disable caching with state preservation.
- Added `setCacheEnabled` and `cacheEnabled` methods in `GatewayController` to manage cache settings.
- Enhanced `GatewayNetworkService` to include new JSON endpoints for getting and setting gateway settings, including cache and USB configurations.
- Introduced `GatewayUsbMode` enum and related methods for managing USB modes in `GatewayRuntime` and `GatewaySettingsStore`.
- Updated runtime configuration to persist USB mode and channel index.
- Added validation for configured channels in USB setup and Tridonic HID components to ensure proper channel usage.

Signed-off-by: Tony <tonylu@tony-cloud.com>
2026-07-28 04:02:58 +08:00

1063 lines
31 KiB
C++

#include "gateway_runtime.hpp"
#include <algorithm>
#include <cctype>
#include <cstdio>
#include <cstdlib>
#include <utility>
#include "dali_domain.hpp"
#include "esp_mac.h"
#include "esp_log.h"
#include "nvs_flash.h"
namespace gateway {
namespace {
constexpr const char* kTag = "gateway_runtime";
constexpr const char* kNamespace = "gateway_rt";
constexpr const char* kBleEnabledKey = "ble_enabled";
constexpr const char* kCacheEnabledKey = "cache_enabled";
constexpr const char* kDeviceNameKey = "device_name";
constexpr const char* kWifiSsidKey = "wifi_ssid";
constexpr const char* kWifiPasswordKey = "wifi_passwd";
constexpr const char* kEthIpKey = "eth_ip";
constexpr const char* kEthMaskKey = "eth_mask";
constexpr const char* kEthGatewayKey = "eth_gw";
constexpr const char* kEthDnsKey = "eth_dns";
constexpr const char* kUsbModeKey = "usb_mode";
constexpr const char* kUsbChannelKey = "usb_channel";
constexpr size_t kMaxGatewayNameBytes = 32;
constexpr uint8_t kCommandFramePrefix0 = 0x28;
constexpr uint8_t kCommandFramePrefix1 = 0x01;
constexpr uint8_t kNotifyFramePrefix = 0x22;
class LockGuard {
public:
explicit LockGuard(SemaphoreHandle_t lock) : lock_(lock) {
if (lock_ != nullptr) {
xSemaphoreTakeRecursive(lock_, portMAX_DELAY);
}
}
~LockGuard() {
if (lock_ != nullptr) {
xSemaphoreGiveRecursive(lock_);
}
}
private:
SemaphoreHandle_t lock_;
};
} // namespace
esp_err_t InitializeRuntimeNvs() {
esp_err_t err = nvs_flash_init();
if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND) {
ESP_ERROR_CHECK(nvs_flash_erase());
err = nvs_flash_init();
}
return err;
}
std::string ReadRuntimeSerialId() {
uint8_t mac[6] = {0};
if (esp_read_mac(mac, ESP_MAC_BASE) != ESP_OK &&
esp_read_mac(mac, ESP_MAC_WIFI_STA) != ESP_OK) {
return "DALIGW";
}
char serial[13] = {0};
std::snprintf(serial, sizeof(serial), "%02X%02X%02X%02X%02X%02X", mac[0], mac[1],
mac[2], mac[3], mac[4], mac[5]);
return std::string(serial);
}
const char* GatewayUsbModeToString(GatewayUsbMode mode) {
switch (mode) {
case GatewayUsbMode::kDebug:
return "debug";
case GatewayUsbMode::kSetup:
return "setup";
case GatewayUsbMode::kTridonic:
return "tridonic";
}
return "debug";
}
std::optional<GatewayUsbMode> GatewayUsbModeFromString(std::string_view value) {
if (value == "debug") {
return GatewayUsbMode::kDebug;
}
if (value == "setup") {
return GatewayUsbMode::kSetup;
}
if (value == "tridonic") {
return GatewayUsbMode::kTridonic;
}
return std::nullopt;
}
GatewaySettingsStore::GatewaySettingsStore() = default;
GatewaySettingsStore::~GatewaySettingsStore() {
close();
}
esp_err_t GatewaySettingsStore::open() {
if (handle_ != 0) {
return ESP_OK;
}
return nvs_open(kNamespace, NVS_READWRITE, &handle_);
}
void GatewaySettingsStore::close() {
if (handle_ != 0) {
nvs_close(handle_);
handle_ = 0;
}
}
bool GatewaySettingsStore::getBleEnabled(bool default_value) const {
if (handle_ == 0) {
return default_value;
}
uint8_t enabled = default_value ? 1 : 0;
if (nvs_get_u8(handle_, kBleEnabledKey, &enabled) != ESP_OK) {
return default_value;
}
return enabled != 0;
}
bool GatewaySettingsStore::setBleEnabled(bool enabled) {
if (handle_ == 0) {
return false;
}
return nvs_set_u8(handle_, kBleEnabledKey, enabled ? 1 : 0) == ESP_OK &&
nvs_commit(handle_) == ESP_OK;
}
bool GatewaySettingsStore::getCacheEnabled(bool default_value) const {
if (handle_ == 0) {
return default_value;
}
uint8_t enabled = default_value ? 1 : 0;
if (nvs_get_u8(handle_, kCacheEnabledKey, &enabled) != ESP_OK) {
return default_value;
}
return enabled != 0;
}
bool GatewaySettingsStore::setCacheEnabled(bool enabled) {
if (handle_ == 0) {
return false;
}
return nvs_set_u8(handle_, kCacheEnabledKey, enabled ? 1 : 0) == ESP_OK &&
nvs_commit(handle_) == ESP_OK;
}
std::optional<std::string> GatewaySettingsStore::getWifiSsid() const {
return readString(kWifiSsidKey);
}
std::optional<std::string> GatewaySettingsStore::getWifiPassword() const {
return readString(kWifiPasswordKey);
}
bool GatewaySettingsStore::setWifiCredentials(std::string_view ssid,
std::string_view password) {
if (handle_ == 0) {
return false;
}
const esp_err_t ssid_err = nvs_set_str(handle_, kWifiSsidKey, std::string(ssid).c_str());
const esp_err_t password_err =
nvs_set_str(handle_, kWifiPasswordKey, std::string(password).c_str());
return ssid_err == ESP_OK && password_err == ESP_OK && nvs_commit(handle_) == ESP_OK;
}
bool GatewaySettingsStore::clearWifiCredentials() {
if (handle_ == 0) {
return false;
}
esp_err_t ssid_err = nvs_erase_key(handle_, kWifiSsidKey);
esp_err_t password_err = nvs_erase_key(handle_, kWifiPasswordKey);
const bool ssid_missing = ssid_err == ESP_ERR_NVS_NOT_FOUND;
const bool password_missing = password_err == ESP_ERR_NVS_NOT_FOUND;
if (ssid_err == ESP_ERR_NVS_NOT_FOUND) {
ssid_err = ESP_OK;
}
if (password_err == ESP_ERR_NVS_NOT_FOUND) {
password_err = ESP_OK;
}
if (ssid_missing && password_missing) {
return true;
}
return ssid_err == ESP_OK && password_err == ESP_OK && nvs_commit(handle_) == ESP_OK;
}
std::optional<EthernetConfig> GatewaySettingsStore::getEthernetConfig() const {
EthernetConfig config;
if (const auto value = readString(kEthIpKey)) {
config.ip = *value;
}
if (const auto value = readString(kEthMaskKey)) {
config.mask = *value;
}
if (const auto value = readString(kEthGatewayKey)) {
config.gateway = *value;
}
if (const auto value = readString(kEthDnsKey)) {
config.dns = *value;
}
if (config.ip.empty() && config.mask.empty() && config.gateway.empty() &&
config.dns.empty()) {
return std::nullopt;
}
return config;
}
bool GatewaySettingsStore::setEthernetConfig(const EthernetConfig& config) {
if (handle_ == 0) {
return false;
}
const esp_err_t ip_err = nvs_set_str(handle_, kEthIpKey, config.ip.c_str());
const esp_err_t mask_err = nvs_set_str(handle_, kEthMaskKey, config.mask.c_str());
const esp_err_t gw_err = nvs_set_str(handle_, kEthGatewayKey, config.gateway.c_str());
const esp_err_t dns_err = nvs_set_str(handle_, kEthDnsKey, config.dns.c_str());
return ip_err == ESP_OK && mask_err == ESP_OK && gw_err == ESP_OK && dns_err == ESP_OK &&
nvs_commit(handle_) == ESP_OK;
}
bool GatewaySettingsStore::clearEthernetConfig() {
if (handle_ == 0) {
return false;
}
auto erase = [this](const char* key) {
const esp_err_t err = nvs_erase_key(handle_, key);
return err == ESP_OK || err == ESP_ERR_NVS_NOT_FOUND;
};
const bool ok = erase(kEthIpKey) && erase(kEthMaskKey) && erase(kEthGatewayKey) &&
erase(kEthDnsKey);
return ok && nvs_commit(handle_) == ESP_OK;
}
GatewayUsbMode GatewaySettingsStore::getUsbMode(GatewayUsbMode default_value) const {
if (handle_ == 0) {
return default_value;
}
uint8_t raw_mode = static_cast<uint8_t>(default_value);
if (nvs_get_u8(handle_, kUsbModeKey, &raw_mode) != ESP_OK ||
raw_mode > static_cast<uint8_t>(GatewayUsbMode::kTridonic)) {
return default_value;
}
return static_cast<GatewayUsbMode>(raw_mode);
}
bool GatewaySettingsStore::setUsbMode(GatewayUsbMode mode) {
if (handle_ == 0 || mode > GatewayUsbMode::kTridonic) {
return false;
}
return nvs_set_u8(handle_, kUsbModeKey, static_cast<uint8_t>(mode)) == ESP_OK &&
nvs_commit(handle_) == ESP_OK;
}
uint8_t GatewaySettingsStore::getUsbChannelIndex(uint8_t default_value) const {
if (handle_ == 0) {
return default_value;
}
uint8_t channel_index = default_value;
if (nvs_get_u8(handle_, kUsbChannelKey, &channel_index) != ESP_OK) {
return default_value;
}
return channel_index;
}
bool GatewaySettingsStore::setUsbChannelIndex(uint8_t channel_index) {
if (handle_ == 0) {
return false;
}
return nvs_set_u8(handle_, kUsbChannelKey, channel_index) == ESP_OK &&
nvs_commit(handle_) == ESP_OK;
}
std::string GatewaySettingsStore::getDeviceName(std::string_view fallback) const {
const auto value = readString(kDeviceNameKey);
if (!value.has_value() || value->empty()) {
return std::string(fallback);
}
return *value;
}
bool GatewaySettingsStore::setDeviceName(std::string_view name) {
return writeString(kDeviceNameKey, name);
}
std::string GatewaySettingsStore::getGatewayName(uint8_t gateway_id,
std::string_view fallback) const {
const auto value = readString(makeGatewayNameKey(gateway_id));
if (!value.has_value() || value->empty()) {
return std::string(fallback);
}
return *value;
}
bool GatewaySettingsStore::setGatewayName(uint8_t gateway_id,
std::string_view 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;
}
uint8_t GatewaySettingsStore::getChannelGatewayGroup(uint8_t channel_index,
uint8_t fallback) const {
if (handle_ == 0) {
return fallback;
}
uint8_t gateway_group = fallback;
if (nvs_get_u8(handle_, makeChannelGatewayGroupKey(channel_index).c_str(),
&gateway_group) != ESP_OK) {
return fallback;
}
return gateway_group;
}
bool GatewaySettingsStore::setChannelGatewayGroup(uint8_t channel_index,
uint8_t gateway_group) {
if (handle_ == 0) {
return false;
}
return nvs_set_u8(handle_, makeChannelGatewayGroupKey(channel_index).c_str(),
gateway_group) == ESP_OK &&
nvs_commit(handle_) == ESP_OK;
}
std::optional<std::string> GatewaySettingsStore::readString(std::string_view key) const {
if (handle_ == 0) {
return std::nullopt;
}
size_t required_size = 0;
const esp_err_t err = nvs_get_str(handle_, std::string(key).c_str(), nullptr, &required_size);
if (err != ESP_OK || required_size == 0) {
return std::nullopt;
}
std::string value(required_size - 1, '\0');
if (nvs_get_str(handle_, std::string(key).c_str(), value.data(), &required_size) != ESP_OK) {
return std::nullopt;
}
return value;
}
bool GatewaySettingsStore::writeString(std::string_view key, std::string_view value) {
if (handle_ == 0) {
return false;
}
return nvs_set_str(handle_, std::string(key).c_str(), std::string(value).c_str()) == ESP_OK &&
nvs_commit(handle_) == ESP_OK;
}
std::string GatewaySettingsStore::makeGatewayNameKey(uint8_t gateway_id) const {
char key[24] = {0};
std::snprintf(key, sizeof(key), "dali_gw_name_%u", gateway_id);
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);
}
std::string GatewaySettingsStore::makeChannelGatewayGroupKey(uint8_t channel_index) const {
char key[24] = {0};
std::snprintf(key, sizeof(key), "dali_ch_grp_%u", channel_index);
return std::string(key);
}
GatewayRuntime::GatewayRuntime(BootProfile profile, GatewayRuntimeConfig config,
DaliDomainService* dali_domain)
: profile_(profile),
config_(std::move(config)),
dali_domain_(dali_domain),
command_address_resolver_([](uint8_t, uint8_t raw_addr) { return raw_addr; }),
command_lock_(xSemaphoreCreateRecursiveMutex()) {}
GatewayRuntime::~GatewayRuntime() {
if (command_lock_ != nullptr) {
vSemaphoreDelete(command_lock_);
command_lock_ = nullptr;
}
}
esp_err_t GatewayRuntime::start() {
const esp_err_t err = settings_.open();
if (err != ESP_OK) {
ESP_LOGE(kTag, "failed to open settings store: %s", esp_err_to_name(err));
return err;
}
ble_enabled_ = settings_.getBleEnabled(config_.default_ble_enabled);
cache_enabled_ = settings_.getCacheEnabled(config_.default_cache_enabled);
usb_mode_ = settings_.getUsbMode(config_.default_usb_mode);
usb_channel_index_ = settings_.getUsbChannelIndex(config_.default_usb_channel_index);
if (usb_channel_index_ > 15) {
usb_channel_index_ = config_.default_usb_channel_index > 15
? 0
: config_.default_usb_channel_index;
}
if (!wireless_info_.has_value()) {
WirelessInfo info;
const auto ssid = settings_.getWifiSsid();
const auto password = settings_.getWifiPassword();
if (ssid.has_value()) {
info.ssid = *ssid;
}
if (password.has_value()) {
info.password = *password;
}
if (!info.ssid.empty() || !info.password.empty()) {
wireless_info_ = std::move(info);
}
}
ESP_LOGI(kTag,
"runtime project=%.*s version=%.*s serial=%s ble=%d cache=%d usb=%s/%u dali_bound=%d",
static_cast<int>(config_.project_name.size()), config_.project_name.data(),
static_cast<int>(config_.version.size()), config_.version.data(),
config_.serial_id.c_str(), ble_enabled_, cache_enabled_,
GatewayUsbModeToString(usb_mode_), usb_channel_index_,
dali_domain_ != nullptr && dali_domain_->isBound());
return ESP_OK;
}
std::vector<uint8_t> GatewayRuntime::checksum(std::vector<uint8_t> frame) {
uint32_t sum = 0;
for (const auto byte : frame) {
sum += byte;
}
frame.push_back(static_cast<uint8_t>(sum & 0xFF));
return frame;
}
bool GatewayRuntime::hasValidChecksum(const std::vector<uint8_t>& frame) {
if (frame.empty()) {
return false;
}
if (frame.back() == 0xFF) {
return true;
}
uint32_t sum = 0;
for (size_t i = 0; i + 1 < frame.size(); ++i) {
sum += frame[i];
}
return static_cast<uint8_t>(sum & 0xFF) == frame.back();
}
bool GatewayRuntime::isGatewayCommandFrame(const std::vector<uint8_t>& frame) {
return frame.size() >= 2 && frame[0] == kCommandFramePrefix0 && frame[1] == kCommandFramePrefix1;
}
std::vector<uint8_t> GatewayRuntime::buildNotificationFrame(const std::vector<uint8_t>& payload) {
std::vector<uint8_t> frame;
frame.reserve(payload.size() + 2);
frame.push_back(kNotifyFramePrefix);
frame.insert(frame.end(), payload.begin(), payload.end());
return checksum(std::move(frame));
}
GatewayRuntime::CommandPriority GatewayRuntime::classifyCommandPriority(
const std::vector<uint8_t>& command) {
if (command.size() < 5 || !isGatewayCommandFrame(command)) {
return CommandPriority::kNormal;
}
const uint8_t opcode = command[3];
const uint8_t addr = command[4];
if (opcode == 0x30 && (addr == 1 || addr == 2)) {
return CommandPriority::kMaintenance;
}
if (opcode == 0x32) {
return CommandPriority::kMaintenance;
}
if (opcode == 0x00 || opcode == 0x01 || opcode == 0x03 || opcode == 0x04 || opcode == 0x07 ||
opcode == 0x08 || opcode == 0x10 || opcode == 0x11 || opcode == 0x12 || opcode == 0x13 ||
opcode == 0x0B || opcode == 0x17 || opcode == 0x18 || opcode == 0x37 || opcode == 0x38 ||
opcode == 0x60 || opcode == 0x61 || opcode == 0x62 || opcode == 0x66 || opcode == 0x67 ||
opcode == 0x6A ||
(opcode == 0x30 && addr == 0)) {
return CommandPriority::kControl;
}
return CommandPriority::kNormal;
}
bool GatewayRuntime::enqueueCommand(std::vector<uint8_t> command, CommandPriority priority) {
LockGuard guard(command_lock_);
last_enqueue_drop_reason_ = CommandDropReason::kNone;
if (isQueryCommand(command) && hasPendingQueryCommand(command)) {
last_enqueue_drop_reason_ = CommandDropReason::kDuplicate;
return false;
}
if (pendingCommandCountLocked() >= config_.command_queue_capacity) {
last_enqueue_drop_reason_ = CommandDropReason::kQueueFull;
return false;
}
queueForPriorityLocked(priority).push_back(std::move(command));
return true;
}
std::optional<std::vector<uint8_t>> GatewayRuntime::popNextCommand() {
LockGuard guard(command_lock_);
for (const auto priority : {CommandPriority::kControl, CommandPriority::kNormal,
CommandPriority::kMaintenance}) {
auto& queue = queueForPriorityLocked(priority);
if (!queue.empty()) {
current_command_ = std::move(queue.front());
current_command_priority_ = priority;
queue.pop_front();
return current_command_;
}
}
current_command_.reset();
return std::nullopt;
}
void GatewayRuntime::completeCurrentCommand() {
LockGuard guard(command_lock_);
current_command_.reset();
}
bool GatewayRuntime::hasPendingQueryCommand(const std::vector<uint8_t>& command) const {
LockGuard guard(command_lock_);
const auto command_key = queryCommandKey(command);
if (!command_key.has_value()) {
return false;
}
if (current_command_.has_value() && queryCommandKey(*current_command_) == command_key) {
return true;
}
const auto matches = [&](const std::vector<uint8_t>& pending) {
return queryCommandKey(pending) == command_key;
};
return std::any_of(control_commands_.begin(), control_commands_.end(), matches) ||
std::any_of(normal_commands_.begin(), normal_commands_.end(), matches) ||
std::any_of(maintenance_commands_.begin(), maintenance_commands_.end(), matches);
}
bool GatewayRuntime::hasPendingControlCommand(uint8_t gateway_id) const {
LockGuard guard(command_lock_);
return std::any_of(control_commands_.begin(), control_commands_.end(), [gateway_id](const auto& command) {
return command.size() > 2 && command[2] == gateway_id;
});
}
bool GatewayRuntime::shouldYieldMaintenance(uint8_t gateway_id) const {
return hasPendingControlCommand(gateway_id);
}
bool GatewayRuntime::hasActiveCommand(uint8_t gateway_id) const {
LockGuard guard(command_lock_);
return current_command_.has_value() && current_command_->size() > 2 &&
(*current_command_)[2] == gateway_id;
}
bool GatewayRuntime::hasActiveQueryCommand(uint8_t gateway_id) const {
LockGuard guard(command_lock_);
return current_command_.has_value() && isQueryCommand(*current_command_) &&
current_command_->size() > 2 && (*current_command_)[2] == gateway_id;
}
GatewayRuntime::CommandDropReason GatewayRuntime::lastEnqueueDropReason() const {
LockGuard guard(command_lock_);
return last_enqueue_drop_reason_;
}
void GatewayRuntime::setGatewayCount(size_t gateway_count) {
LockGuard guard(command_lock_);
gateway_count_ = gateway_count;
}
void GatewayRuntime::setWirelessInfo(WirelessInfo info) {
bool should_persist_credentials = false;
{
LockGuard guard(command_lock_);
should_persist_credentials = (!info.ssid.empty() || !info.password.empty()) &&
(!wireless_info_.has_value() ||
wireless_info_->ssid != info.ssid ||
wireless_info_->password != info.password);
wireless_info_ = info;
}
if (should_persist_credentials) {
settings_.setWifiCredentials(info.ssid, info.password);
}
}
bool GatewayRuntime::clearWirelessInfo() {
bool had_credentials = false;
{
LockGuard guard(command_lock_);
had_credentials = wireless_info_.has_value() &&
(!wireless_info_->ssid.empty() || !wireless_info_->password.empty());
wireless_info_.reset();
}
if (!had_credentials) {
return true;
}
return settings_.clearWifiCredentials();
}
void GatewayRuntime::setEthernetInfo(EthernetInfo info) {
LockGuard guard(command_lock_);
ethernet_info_ = std::move(info);
}
void GatewayRuntime::clearEthernetInfo() {
LockGuard guard(command_lock_);
ethernet_info_.reset();
}
void GatewayRuntime::clearEthernetIp() {
LockGuard guard(command_lock_);
if (ethernet_info_.has_value()) {
ethernet_info_->ip.clear();
}
}
std::optional<EthernetConfig> GatewayRuntime::ethernetConfig() const {
return settings_.getEthernetConfig();
}
bool GatewayRuntime::setEthernetConfig(const EthernetConfig& config) {
return settings_.setEthernetConfig(config);
}
bool GatewayRuntime::clearEthernetConfig() {
return settings_.clearEthernetConfig();
}
GatewayUsbMode GatewayRuntime::usbMode() const {
LockGuard guard(command_lock_);
return usb_mode_;
}
bool GatewayRuntime::setUsbMode(GatewayUsbMode mode) {
if (mode > GatewayUsbMode::kTridonic) {
return false;
}
{
LockGuard guard(command_lock_);
if (usb_mode_ == mode) {
return true;
}
}
if (!settings_.setUsbMode(mode)) {
return false;
}
LockGuard guard(command_lock_);
usb_mode_ = mode;
return true;
}
uint8_t GatewayRuntime::usbChannelIndex() const {
LockGuard guard(command_lock_);
return usb_channel_index_;
}
bool GatewayRuntime::setUsbChannelIndex(uint8_t channel_index) {
if (channel_index > 15) {
return false;
}
if (dali_domain_ == nullptr) {
return false;
}
const auto channels = dali_domain_->channelInfo();
if (!std::any_of(channels.begin(), channels.end(),
[channel_index](const DaliChannelInfo& channel) {
return channel.channel_index == channel_index;
})) {
return false;
}
{
LockGuard guard(command_lock_);
if (usb_channel_index_ == channel_index) {
return true;
}
}
if (!settings_.setUsbChannelIndex(channel_index)) {
return false;
}
LockGuard guard(command_lock_);
usb_channel_index_ = channel_index;
return true;
}
void GatewayRuntime::setCommandAddressResolver(
std::function<uint8_t(uint8_t gw, uint8_t raw_addr)> resolver) {
LockGuard guard(command_lock_);
if (resolver) {
command_address_resolver_ = std::move(resolver);
return;
}
command_address_resolver_ = [](uint8_t, uint8_t raw_addr) { return raw_addr; };
}
GatewayDeviceInfo GatewayRuntime::deviceInfo() const {
LockGuard guard(command_lock_);
GatewayDeviceInfo info;
info.serial_id = config_.serial_id;
info.type = GatewayCore::RoleToString(profile_.role);
info.project = std::string(config_.project_name);
info.version = std::string(config_.version);
info.dali_gateway_count = gateway_count_;
info.ble_enabled = ble_enabled_;
info.wlan = wireless_info_;
info.eth = ethernet_info_;
return info;
}
bool GatewayRuntime::bleEnabled() const {
LockGuard guard(command_lock_);
return ble_enabled_;
}
bool GatewayRuntime::setBleEnabled(bool enabled) {
{
LockGuard guard(command_lock_);
if (ble_enabled_ == enabled) {
return true;
}
}
if (!settings_.setBleEnabled(enabled)) {
return false;
}
LockGuard guard(command_lock_);
ble_enabled_ = enabled;
return true;
}
bool GatewayRuntime::cacheEnabled() const {
LockGuard guard(command_lock_);
return cache_enabled_;
}
bool GatewayRuntime::setCacheEnabled(bool enabled) {
{
LockGuard guard(command_lock_);
if (cache_enabled_ == enabled) {
return true;
}
}
if (!settings_.setCacheEnabled(enabled)) {
return false;
}
LockGuard guard(command_lock_);
cache_enabled_ = enabled;
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;
}
uint8_t GatewayRuntime::gatewayGroupForChannel(uint8_t channel_index, uint8_t fallback) const {
LockGuard guard(command_lock_);
const auto cached = channel_gateway_groups_.find(channel_index);
if (cached != channel_gateway_groups_.end()) {
return cached->second;
}
const uint8_t gateway_group = settings_.getChannelGatewayGroup(channel_index, fallback);
channel_gateway_groups_[channel_index] = gateway_group;
return gateway_group;
}
bool GatewayRuntime::setGatewayGroupForChannel(uint8_t channel_index, uint8_t gateway_group) {
if (!settings_.setChannelGatewayGroup(channel_index, gateway_group)) {
return false;
}
LockGuard guard(command_lock_);
channel_gateway_groups_[channel_index] = gateway_group;
return true;
}
std::string GatewayRuntime::deviceName() const {
LockGuard guard(command_lock_);
if (device_name_.has_value()) {
return device_name_.value();
}
auto name = settings_.getDeviceName(defaultDeviceName());
if (name.size() > kMaxGatewayNameBytes) {
name.resize(kMaxGatewayNameBytes);
}
device_name_ = name;
return name;
}
bool GatewayRuntime::setDeviceName(std::string_view name) {
std::string normalized(name);
if (normalized.size() > kMaxGatewayNameBytes) {
normalized.resize(kMaxGatewayNameBytes);
}
if (normalized.empty()) {
normalized = defaultDeviceName();
}
if (deviceName() == normalized) {
return true;
}
if (!settings_.setDeviceName(normalized)) {
return false;
}
LockGuard guard(command_lock_);
device_name_ = normalized;
return true;
}
std::string GatewayRuntime::gatewayName(uint8_t gateway_id) const {
LockGuard guard(command_lock_);
const auto cached = gateway_names_.find(gateway_id);
if (cached != gateway_names_.end()) {
return cached->second;
}
auto name = settings_.getGatewayName(gateway_id, defaultGatewayName(gateway_id));
if (name.size() > kMaxGatewayNameBytes) {
name.resize(kMaxGatewayNameBytes);
}
gateway_names_[gateway_id] = name;
return name;
}
bool GatewayRuntime::setGatewayName(uint8_t gateway_id, std::string_view name) {
std::string normalized(name);
if (normalized.size() > kMaxGatewayNameBytes) {
normalized.resize(kMaxGatewayNameBytes);
}
if (normalized.empty()) {
normalized = defaultGatewayName(gateway_id);
}
if (gatewayName(gateway_id) == normalized) {
return true;
}
if (!settings_.setGatewayName(gateway_id, normalized)) {
return false;
}
LockGuard guard(command_lock_);
gateway_names_[gateway_id] = normalized;
return true;
}
std::string GatewayRuntime::gatewaySerialHex(uint8_t gateway_id) const {
const auto bytes = serialBytes();
std::vector<uint8_t> serial(6, 0);
for (size_t i = 0; i < 5; ++i) {
serial[i] = i + 1 < bytes.size() ? bytes[i + 1] : 0;
}
serial[5] = gateway_id;
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 {
return toHex(serialBytes());
}
std::string GatewayRuntime::bleGatewayName(uint8_t gateway_id, std::string_view gateway_name) const {
std::string normalized(deviceName());
if (normalized.size() > kMaxGatewayNameBytes) {
normalized.resize(kMaxGatewayNameBytes);
}
(void)gateway_id;
(void)gateway_name;
if (!normalized.empty() && normalized != defaultDeviceName()) {
return normalized;
}
return defaultBleGatewayName();
}
std::string GatewayRuntime::defaultBleGatewayName() const {
return "DALIGW_" + bleMacHex();
}
bool GatewayRuntime::isQueryCommand(const std::vector<uint8_t>& command) const {
return command.size() >= 6 && isGatewayCommandFrame(command) &&
((command[3] >= 0x14 && command[3] <= 0x16) || command[3] == 0x62);
}
size_t GatewayRuntime::pendingCommandCountLocked() const {
return control_commands_.size() + normal_commands_.size() + maintenance_commands_.size();
}
std::deque<std::vector<uint8_t>>& GatewayRuntime::queueForPriorityLocked(
CommandPriority priority) {
switch (priority) {
case CommandPriority::kControl:
return control_commands_;
case CommandPriority::kMaintenance:
return maintenance_commands_;
case CommandPriority::kNormal:
default:
return normal_commands_;
}
}
const std::deque<std::vector<uint8_t>>& GatewayRuntime::queueForPriorityLocked(
CommandPriority priority) const {
switch (priority) {
case CommandPriority::kControl:
return control_commands_;
case CommandPriority::kMaintenance:
return maintenance_commands_;
case CommandPriority::kNormal:
default:
return normal_commands_;
}
}
std::optional<std::string> GatewayRuntime::queryCommandKey(
const std::vector<uint8_t>& command) const {
if (!isQueryCommand(command)) {
return std::nullopt;
}
const auto gw = command[2];
const auto cmd = command[3];
if (cmd == 0x62) {
if (command.size() < 8) {
return std::nullopt;
}
char key[40] = {0};
std::snprintf(key, sizeof(key), "%u:%u:%u:%u:%u", gw, cmd, command[4], command[5],
command[6]);
return std::string(key);
}
if (cmd == 0x16) {
char key[16] = {0};
std::snprintf(key, sizeof(key), "%u:%u", gw, cmd);
return std::string(key);
}
const auto target_addr = command_address_resolver_(gw, command[4]);
char key[32] = {0};
std::snprintf(key, sizeof(key), "%u:%u:%u:%u", gw, cmd, target_addr, command[5]);
return std::string(key);
}
std::string GatewayRuntime::defaultDeviceName() const {
const std::string serial_hex = bleMacHex();
if (serial_hex.size() <= 6) {
return "DALIGW_" + serial_hex;
}
return "DALIGW_" + serial_hex.substr(serial_hex.size() - 6);
}
std::string GatewayRuntime::defaultGatewayName(uint8_t gateway_id) const {
return "Channel " + std::to_string(gateway_id);
}
std::vector<uint8_t> GatewayRuntime::serialBytes() const {
std::vector<uint8_t> bytes;
const std::string& serial = config_.serial_id;
bytes.reserve(6);
for (size_t i = 0; i + 1 < serial.size() && bytes.size() < 6; i += 2) {
if (!std::isxdigit(static_cast<unsigned char>(serial[i])) ||
!std::isxdigit(static_cast<unsigned char>(serial[i + 1]))) {
break;
}
char pair[3] = {serial[i], serial[i + 1], 0};
bytes.push_back(static_cast<uint8_t>(std::strtoul(pair, nullptr, 16)));
}
while (bytes.size() < 6) {
bytes.push_back(0);
}
return bytes;
}
std::string GatewayRuntime::toHex(const std::vector<uint8_t>& bytes) {
static constexpr char kHex[] = "0123456789ABCDEF";
std::string out;
out.reserve(bytes.size() * 2);
for (const auto byte : bytes) {
out.push_back(kHex[(byte >> 4) & 0x0F]);
out.push_back(kHex[byte & 0x0F]);
}
return out;
}
} // namespace gateway