feat: add application controller handling and DALI control device commands

Signed-off-by: Tony <tonylu@tony-cloud.com>
This commit is contained in:
Tony
2026-06-11 18:46:32 +08:00
parent 1342ab6aaf
commit 6ffca719d9
6 changed files with 379 additions and 11 deletions
@@ -144,6 +144,9 @@ class GatewayController {
void publishPayload(uint8_t gateway_id, const std::vector<uint8_t>& payload);
void publishFrame(const std::vector<uint8_t>& frame);
void handleDaliRawFrame(const DaliRawFrame& frame);
bool handleApplicationControllerFrame(const DaliRawFrame& frame);
std::optional<uint8_t> applicationControllerResponse(uint8_t gateway_id, uint8_t first,
uint8_t instance, uint8_t opcode) const;
bool sendRawAndMirror(uint8_t gateway_id, uint8_t raw_addr, uint8_t command);
bool sendExtRawAndMirror(uint8_t gateway_id, uint8_t raw_addr, uint8_t command);
@@ -207,6 +210,14 @@ class GatewayController {
bool ble_enabled_{false};
bool wifi_enabled_{false};
bool ip_router_enabled_{true};
bool application_controller_enabled_{true};
bool application_controller_power_cycle_notification_{true};
bool application_controller_power_cycle_seen_{true};
bool application_controller_reset_state_{false};
uint8_t application_controller_operating_mode_{0};
uint8_t application_controller_dtr0_{0};
uint8_t application_controller_dtr1_{0};
uint8_t application_controller_dtr2_{0};
};
} // namespace gateway
@@ -28,6 +28,12 @@ constexpr uint8_t kBridgeTransportRequestOpcode = 0xB0;
constexpr uint8_t kBridgeTransportResponseOpcode = 0xB1;
constexpr uint8_t kBridgeTransportVersion = 1;
constexpr size_t kBridgeTransportMaxChunkBytes = 120;
constexpr uint8_t kDali103SendOpcode = 0x60;
constexpr uint8_t kDali103SendTwiceOpcode = 0x61;
constexpr uint8_t kDali103QueryOpcode = 0x62;
constexpr uint8_t kDali103QueryResponseOpcode = 0x63;
constexpr uint8_t kDali103NoResponseOpcode = 0x64;
constexpr uint8_t kDali103RawFrameOpcode = 0x65;
constexpr uint8_t kGatewayFeatureCache = 0x40;
constexpr uint8_t kGatewayCacheOpcode = 0x39;
constexpr uint8_t kGatewayCacheProtocolVersion = 1;
@@ -106,6 +112,9 @@ bool IsDaliHostCommandOpcode(uint8_t opcode) {
case 0x32:
case 0x37:
case 0x38:
case kDali103SendOpcode:
case kDali103SendTwiceOpcode:
case kDali103QueryOpcode:
case 0xA0:
case 0xA2:
case kBridgeTransportRequestOpcode:
@@ -933,6 +942,24 @@ void GatewayController::dispatchCommand(const std::vector<uint8_t>& command) {
}
}
break;
case kDali103SendOpcode:
case kDali103SendTwiceOpcode:
if (command.size() >= 8) {
dali_domain_.sendControlDeviceRaw(gateway_id, command[4], command[5], command[6],
opcode == kDali103SendTwiceOpcode);
}
break;
case kDali103QueryOpcode:
if (command.size() >= 8) {
const auto result =
dali_domain_.queryControlDeviceRaw(gateway_id, command[4], command[5], command[6]);
if (result.has_value()) {
publishPayload(gateway_id, {kDali103QueryResponseOpcode, gateway_id, result.value()});
} else {
publishPayload(gateway_id, {kDali103NoResponseOpcode, gateway_id, 0x00});
}
}
break;
case kGatewayCacheOpcode:
handleGatewayCacheCommand(gateway_id, command);
break;
@@ -1036,6 +1063,160 @@ void GatewayController::publishFrame(const std::vector<uint8_t>& frame) {
}
}
std::optional<uint8_t> GatewayController::applicationControllerResponse(
uint8_t gateway_id, uint8_t first, uint8_t instance, uint8_t opcode) const {
const uint8_t gateway_short =
static_cast<uint8_t>(((gateway_id & 0x3F) << 1) | 0x01);
const bool addressed_to_gateway = first == 0xFF || first == gateway_short;
if (!addressed_to_gateway || instance != 0xFE) {
return std::nullopt;
}
switch (opcode) {
case 0x30: {
uint8_t status = 0;
if (application_controller_enabled_) {
status |= 0x08;
}
if (application_controller_power_cycle_seen_) {
status |= 0x20;
}
if (application_controller_reset_state_) {
status |= 0x40;
}
return status;
}
case 0x31:
case 0x32:
case 0x33:
return 0x00;
case 0x34:
return 0x02;
case 0x35:
return 0x00;
case 0x36:
return application_controller_dtr0_;
case 0x37:
return application_controller_dtr1_;
case 0x38:
return application_controller_dtr2_;
case 0x39:
case 0x3A:
case 0x3B:
return 0x00;
case 0x3C:
return 0xFF;
case 0x3D:
return static_cast<uint8_t>(application_controller_enabled_ ? 1 : 0);
case 0x3E:
return application_controller_operating_mode_;
case 0x3F:
case 0x40:
case 0x41:
case 0x42:
case 0x43:
case 0x44:
return 0x00;
case 0x45:
return static_cast<uint8_t>(application_controller_power_cycle_notification_ ? 1 : 0);
case 0x46:
return 0x01;
case 0x47:
return 0x01;
case 0x48:
return static_cast<uint8_t>(application_controller_reset_state_ ? 1 : 0);
default:
return std::nullopt;
}
}
bool GatewayController::handleApplicationControllerFrame(const DaliRawFrame& frame) {
if (frame.data.size() != 3) {
return false;
}
const uint8_t first = frame.data[0];
const uint8_t instance = frame.data[1];
const uint8_t opcode = frame.data[2];
if (first == 0xC1) {
switch (instance) {
case 0x30:
application_controller_dtr0_ = opcode;
break;
case 0x31:
application_controller_dtr1_ = opcode;
break;
case 0x32:
application_controller_dtr2_ = opcode;
break;
default:
break;
}
return false;
}
if ((first & 0x01) == 0) {
return false;
}
const uint8_t gateway_short =
static_cast<uint8_t>(((frame.gateway_id & 0x3F) << 1) | 0x01);
if (first != 0xFF && first != gateway_short) {
return false;
}
if (instance != 0xFE) {
return false;
}
switch (opcode) {
case 0x01:
application_controller_power_cycle_seen_ = false;
application_controller_reset_state_ = false;
break;
case 0x10:
application_controller_enabled_ = true;
application_controller_power_cycle_notification_ = true;
application_controller_reset_state_ = true;
application_controller_operating_mode_ = 0;
application_controller_dtr0_ = 0;
application_controller_dtr1_ = 0;
application_controller_dtr2_ = 0;
break;
case 0x16:
application_controller_enabled_ = true;
application_controller_reset_state_ = false;
break;
case 0x17:
application_controller_enabled_ = false;
application_controller_reset_state_ = false;
break;
case 0x18:
application_controller_operating_mode_ = application_controller_dtr0_;
application_controller_reset_state_ = false;
break;
case 0x1F:
application_controller_power_cycle_notification_ = true;
application_controller_reset_state_ = false;
break;
case 0x20:
application_controller_power_cycle_notification_ = false;
application_controller_reset_state_ = false;
break;
case 0x21:
application_controller_reset_state_ = false;
break;
default:
break;
}
const auto response = applicationControllerResponse(frame.gateway_id, first, instance, opcode);
if (!response.has_value()) {
return false;
}
return dali_domain_.sendBackwardFrame(frame.gateway_id, response.value());
}
void GatewayController::handleBridgeTransportCommand(uint8_t gateway_id,
const std::vector<uint8_t>& command) {
const uint8_t version = command.size() > 4 ? command[4] : kBridgeTransportVersion;
@@ -1119,6 +1300,20 @@ void GatewayController::handleDaliRawFrame(const DaliRawFrame& frame) {
return;
}
if (frame.data.size() == 3 &&
(((frame.data[0] & 0x01) != 0) || frame.data[0] == 0xC1)) {
handleApplicationControllerFrame(frame);
const bool maintenance_activity = maintenance_activity_gateway_.load() == frame.gateway_id;
if (setup_mode_ || dali_domain_.isAllocAddr(frame.gateway_id) || maintenance_activity ||
runtime_.hasActiveQueryCommand(frame.gateway_id)) {
return;
}
publishPayload(frame.gateway_id,
{kDali103RawFrameOpcode, frame.gateway_id, frame.data[0], frame.data[1],
frame.data[2]});
return;
}
uint8_t addr = 0;
uint8_t data = 0;
if (frame.data.size() == 2) {