449a3a801a
- Introduced KnxDaliModule class for handling DALI message queuing, commissioning, and KNX group-object dispatch. - Implemented Message and MessageQueue classes for managing message operations. - Removed obsolete OpenKNX IDF component files and CMake configurations. - Updated submodule reference for KNX. Signed-off-by: Tony <tonylu@tony-cloud.com>
324 lines
11 KiB
C++
324 lines
11 KiB
C++
#include "dali_gateway_bridge.h"
|
|
|
|
#include "dali_define.hpp"
|
|
#include "dali_comm.hpp"
|
|
|
|
#include <algorithm>
|
|
#include <cmath>
|
|
|
|
namespace gateway {
|
|
namespace knx_dali_gw {
|
|
|
|
namespace {
|
|
|
|
constexpr double kArcMax = 254.0;
|
|
constexpr double kLogFactor = 3.0;
|
|
|
|
// DALI address encoding helpers (mirroring lib/dali/comm.dart).
|
|
// Short address 0-63 → (addr << 1) | 0x01 for commands.
|
|
constexpr uint8_t kShortAddrCmdBase = 0x01;
|
|
constexpr uint8_t kShortAddrArcBase = 0x00;
|
|
constexpr uint8_t kGroupAddrBase = 0x80;
|
|
constexpr uint8_t kBroadcastCmd = 0xFE;
|
|
constexpr uint8_t kBroadcastArc = 0xFF;
|
|
|
|
uint8_t makeShortCmdAddr(int addr) {
|
|
return static_cast<uint8_t>((addr << 1) | kShortAddrCmdBase);
|
|
}
|
|
uint8_t makeShortArcAddr(int addr) {
|
|
return static_cast<uint8_t>((addr << 1) | kShortAddrArcBase);
|
|
}
|
|
uint8_t makeGroupCmdAddr(int group) {
|
|
return static_cast<uint8_t>(kGroupAddrBase | (group << 1) | 0x01);
|
|
}
|
|
uint8_t makeBroadcastCmdAddr() { return kBroadcastCmd; }
|
|
uint8_t makeBroadcastArcAddr() { return kBroadcastArc; }
|
|
|
|
bool isBroadcastAddr(uint8_t raw) { return raw == kBroadcastCmd || raw == kBroadcastArc; }
|
|
bool isGroupAddr(uint8_t raw) { return (raw & 0x80) != 0 && !isBroadcastAddr(raw); }
|
|
int extractGroupAddr(uint8_t raw) { return (raw >> 1) & 0x0F; }
|
|
int extractShortAddr(uint8_t raw) { return (raw >> 1) & 0x3F; }
|
|
|
|
uint8_t encodeDaliRawAddr(DaliTarget target) {
|
|
switch (target.kind) {
|
|
case DaliTargetKind::kShortAddress:
|
|
return makeShortCmdAddr(target.address);
|
|
case DaliTargetKind::kGroup:
|
|
return makeGroupCmdAddr(target.address);
|
|
case DaliTargetKind::kBroadcast:
|
|
default:
|
|
return makeBroadcastCmdAddr();
|
|
}
|
|
}
|
|
|
|
} // namespace
|
|
|
|
// =============================================================================
|
|
// DaliTarget ↔ raw address encoding (public API)
|
|
// =============================================================================
|
|
|
|
uint8_t EncodeDaliRawAddr(DaliTarget target) {
|
|
return encodeDaliRawAddr(target);
|
|
}
|
|
|
|
DaliTarget DecodeDaliRawAddr(uint8_t raw_addr, int default_short_address) {
|
|
if (isBroadcastAddr(raw_addr)) {
|
|
return {DaliTargetKind::kBroadcast, 0};
|
|
}
|
|
if (isGroupAddr(raw_addr)) {
|
|
return {DaliTargetKind::kGroup, extractGroupAddr(raw_addr)};
|
|
}
|
|
int sa = extractShortAddr(raw_addr);
|
|
if (sa < 0 && default_short_address >= 0) {
|
|
sa = default_short_address;
|
|
}
|
|
return {DaliTargetKind::kShortAddress, sa};
|
|
}
|
|
|
|
// =============================================================================
|
|
// Arc power ↔ percentage conversion
|
|
// =============================================================================
|
|
|
|
double ArcToPercent(uint8_t arc) {
|
|
if (arc == 0) return 0.0;
|
|
return 100.0 * std::pow(static_cast<double>(arc) / kArcMax, kLogFactor);
|
|
}
|
|
|
|
uint8_t PercentToArc(double percent) {
|
|
if (percent <= 0.0) return 0;
|
|
if (percent >= 100.0) return 254;
|
|
return static_cast<uint8_t>(
|
|
std::round(kArcMax * std::pow(percent / 100.0, 1.0 / kLogFactor)));
|
|
}
|
|
|
|
// =============================================================================
|
|
// DaliGatewayBridge
|
|
// =============================================================================
|
|
|
|
DaliGatewayBridge::DaliGatewayBridge(DaliDomainService& dali, uint8_t gateway_id)
|
|
: dali_(dali), gateway_id_(gateway_id) {}
|
|
|
|
bool DaliGatewayBridge::sendRaw(DaliTarget target, uint8_t command) const {
|
|
return dali_.sendRaw(gateway_id_, encodeDaliRawAddr(target), command);
|
|
}
|
|
|
|
bool DaliGatewayBridge::sendExtRaw(DaliTarget target, uint8_t command) const {
|
|
return dali_.sendExtRaw(gateway_id_, encodeDaliRawAddr(target), command);
|
|
}
|
|
|
|
std::optional<uint8_t> DaliGatewayBridge::queryRaw(DaliTarget target, uint8_t command) const {
|
|
return dali_.queryRaw(gateway_id_, encodeDaliRawAddr(target), command);
|
|
}
|
|
|
|
bool DaliGatewayBridge::setArc(DaliTarget target, uint8_t arc) const {
|
|
return sendRaw(target, arc);
|
|
}
|
|
|
|
std::optional<uint8_t> DaliGatewayBridge::queryActualLevel(int short_address) const {
|
|
return dali_.queryRaw(gateway_id_, makeShortCmdAddr(short_address),
|
|
DALI_CMD_QUERY_ACTUAL_LEVEL);
|
|
}
|
|
|
|
bool DaliGatewayBridge::on(DaliTarget target) const {
|
|
return sendRaw(target, DALI_CMD_RECALL_MAX);
|
|
}
|
|
|
|
bool DaliGatewayBridge::off(DaliTarget target) const {
|
|
return sendRaw(target, DALI_CMD_OFF);
|
|
}
|
|
|
|
bool DaliGatewayBridge::stepUp(DaliTarget target) const {
|
|
return sendRaw(target, DALI_CMD_RECALL_MAX);
|
|
}
|
|
|
|
bool DaliGatewayBridge::stepDown(DaliTarget target) const {
|
|
return sendRaw(target, DALI_CMD_OFF);
|
|
}
|
|
|
|
bool DaliGatewayBridge::recallMax(DaliTarget target) const {
|
|
return sendRaw(target, DALI_CMD_RECALL_MAX);
|
|
}
|
|
|
|
bool DaliGatewayBridge::recallMin(DaliTarget target) const {
|
|
return sendRaw(target, DALI_CMD_RECALL_MIN);
|
|
}
|
|
|
|
bool DaliGatewayBridge::goToScene(DaliTarget target, uint8_t scene) const {
|
|
return sendRaw(target, DALI_CMD_GO_TO_SCENE(scene & 0x0F));
|
|
}
|
|
|
|
std::optional<uint8_t> DaliGatewayBridge::queryStatus(int short_address) const {
|
|
return dali_.queryRaw(gateway_id_, makeShortCmdAddr(short_address),
|
|
DALI_CMD_QUERY_STATUS);
|
|
}
|
|
|
|
std::optional<uint8_t> DaliGatewayBridge::queryDeviceType(int short_address) const {
|
|
return dali_.queryRaw(gateway_id_, makeShortCmdAddr(short_address),
|
|
DALI_CMD_QUERY_DEVICE_TYPE);
|
|
}
|
|
|
|
std::optional<uint8_t> DaliGatewayBridge::queryMinLevel(int short_address) const {
|
|
return dali_.queryRaw(gateway_id_, makeShortCmdAddr(short_address),
|
|
DALI_CMD_QUERY_MIN_LEVEL);
|
|
}
|
|
|
|
std::optional<uint8_t> DaliGatewayBridge::queryMaxLevel(int short_address) const {
|
|
return dali_.queryRaw(gateway_id_, makeShortCmdAddr(short_address),
|
|
DALI_CMD_QUERY_MAX_LEVEL);
|
|
}
|
|
|
|
std::optional<uint8_t> DaliGatewayBridge::queryPowerOnLevel(int short_address) const {
|
|
return dali_.queryRaw(gateway_id_, makeShortCmdAddr(short_address),
|
|
DALI_CMD_QUERY_POWER_ON_LEVEL);
|
|
}
|
|
|
|
std::optional<uint8_t> DaliGatewayBridge::querySystemFailureLevel(int short_address) const {
|
|
return dali_.queryRaw(gateway_id_, makeShortCmdAddr(short_address),
|
|
DALI_CMD_QUERY_SYSTEM_FAILURE_LEVEL);
|
|
}
|
|
|
|
std::optional<uint8_t> DaliGatewayBridge::queryFadeTimeRate(int short_address) const {
|
|
return dali_.queryRaw(gateway_id_, makeShortCmdAddr(short_address),
|
|
DALI_CMD_QUERY_FADE_TIME_FADE_RATE);
|
|
}
|
|
|
|
std::optional<uint8_t> DaliGatewayBridge::queryFadeTime(int short_address) const {
|
|
return dali_.queryRaw(gateway_id_, makeShortCmdAddr(short_address),
|
|
DALI_CMD_QUERY_EXTENDED_FADE_TIME);
|
|
}
|
|
|
|
std::optional<uint16_t> DaliGatewayBridge::queryGroups(int short_address) const {
|
|
return dali_.queryGroupMask(gateway_id_, short_address);
|
|
}
|
|
|
|
std::optional<uint8_t> DaliGatewayBridge::querySceneLevel(int short_address,
|
|
uint8_t scene) const {
|
|
return dali_.querySceneLevel(gateway_id_, short_address, scene & 0x0F);
|
|
}
|
|
|
|
// ---- DT8 ----
|
|
|
|
bool DaliGatewayBridge::setColourTemperature(int short_address, int kelvin) const {
|
|
return dali_.setColTemp(gateway_id_, short_address, kelvin);
|
|
}
|
|
|
|
bool DaliGatewayBridge::setColourRGB(int short_address, uint8_t r, uint8_t g,
|
|
uint8_t b) const {
|
|
return dali_.setColourRGB(gateway_id_, short_address, r, g, b);
|
|
}
|
|
|
|
std::optional<DaliDomainSnapshot> DaliGatewayBridge::dt8StatusSnapshot(
|
|
int short_address) const {
|
|
return dali_.dt8StatusSnapshot(gateway_id_, short_address);
|
|
}
|
|
|
|
std::optional<DaliDomainSnapshot> DaliGatewayBridge::dt8SceneColorReport(
|
|
int short_address, int scene) const {
|
|
return dali_.dt8SceneColorReport(gateway_id_, short_address, scene);
|
|
}
|
|
|
|
// ---- Scenes & groups ----
|
|
|
|
bool DaliGatewayBridge::setDtr(uint8_t value) const {
|
|
return dali_.sendRaw(gateway_id_, makeBroadcastCmdAddr(),
|
|
DALI_CMD_SPECIAL_SET_DTR0) &&
|
|
dali_.sendRaw(gateway_id_, makeBroadcastCmdAddr(), value);
|
|
}
|
|
|
|
bool DaliGatewayBridge::setDtrAsScene(DaliTarget target, uint8_t scene) const {
|
|
return sendRaw(target, DALI_CMD_SET_SCENE(scene & 0x0F));
|
|
}
|
|
|
|
bool DaliGatewayBridge::addToGroup(DaliTarget target, uint8_t group) const {
|
|
return sendRaw(target, DALI_CMD_ADD_TO_GROUP(group & 0x0F));
|
|
}
|
|
|
|
bool DaliGatewayBridge::removeFromGroup(DaliTarget target, uint8_t group) const {
|
|
return sendRaw(target, DALI_CMD_REMOVE_FROM_GROUP(group & 0x0F));
|
|
}
|
|
|
|
bool DaliGatewayBridge::removeFromScene(DaliTarget target, uint8_t scene) const {
|
|
return sendRaw(target, DALI_CMD_REMOVE_SCENE(scene & 0x0F));
|
|
}
|
|
|
|
bool DaliGatewayBridge::setSceneLevel(DaliTarget target, uint8_t scene,
|
|
uint8_t level) const {
|
|
return setDtr(level) && setDtrAsScene(target, scene);
|
|
}
|
|
|
|
// ---- Commissioning ----
|
|
|
|
bool DaliGatewayBridge::initialise(DaliTarget target) const {
|
|
return sendRaw(target, DALI_CMD_SPECIAL_INITIALIZE);
|
|
}
|
|
|
|
bool DaliGatewayBridge::randomise() const {
|
|
return sendRaw({DaliTargetKind::kBroadcast, 0}, DALI_CMD_SPECIAL_RANDOMIZE);
|
|
}
|
|
|
|
bool DaliGatewayBridge::searchAddrH(uint8_t high) const {
|
|
return dali_.sendRaw(gateway_id_, makeBroadcastCmdAddr(),
|
|
DALI_CMD_SPECIAL_SEARCHADDRH) &&
|
|
dali_.sendRaw(gateway_id_, makeBroadcastCmdAddr(), high);
|
|
}
|
|
|
|
bool DaliGatewayBridge::searchAddrM(uint8_t middle) const {
|
|
return dali_.sendRaw(gateway_id_, makeBroadcastCmdAddr(),
|
|
DALI_CMD_SPECIAL_SEARCHADDRM) &&
|
|
dali_.sendRaw(gateway_id_, makeBroadcastCmdAddr(), middle);
|
|
}
|
|
|
|
bool DaliGatewayBridge::searchAddrL(uint8_t low) const {
|
|
return dali_.sendRaw(gateway_id_, makeBroadcastCmdAddr(),
|
|
DALI_CMD_SPECIAL_SEARCHADDRL) &&
|
|
dali_.sendRaw(gateway_id_, makeBroadcastCmdAddr(), low);
|
|
}
|
|
|
|
bool DaliGatewayBridge::compare() const {
|
|
return sendRaw({DaliTargetKind::kBroadcast, 0}, DALI_CMD_SPECIAL_COMPARE);
|
|
}
|
|
|
|
bool DaliGatewayBridge::withdraw() const {
|
|
return sendRaw({DaliTargetKind::kBroadcast, 0}, DALI_CMD_SPECIAL_WITHDRAW);
|
|
}
|
|
|
|
bool DaliGatewayBridge::terminate() const {
|
|
return sendRaw({DaliTargetKind::kBroadcast, 0}, DALI_CMD_SPECIAL_TERMINATE);
|
|
}
|
|
|
|
bool DaliGatewayBridge::programShort(DaliTarget target, uint8_t short_address) const {
|
|
const uint8_t raw = (short_address << 1) | 0x01;
|
|
return dali_.sendRaw(gateway_id_, encodeDaliRawAddr(target),
|
|
DALI_CMD_SPECIAL_PROGRAM_SHORT_ADDRESS) &&
|
|
dali_.sendRaw(gateway_id_, encodeDaliRawAddr(target), raw);
|
|
}
|
|
|
|
bool DaliGatewayBridge::verifyShort(DaliTarget target) const {
|
|
return sendRaw(target, DALI_CMD_SPECIAL_VERIFY_SHORT_ADDRESS);
|
|
}
|
|
|
|
std::optional<uint8_t> DaliGatewayBridge::queryShort(DaliTarget target) const {
|
|
return dali_.queryRaw(gateway_id_, encodeDaliRawAddr(target),
|
|
DALI_CMD_SPECIAL_QUERY_SHORT_ADDRESS);
|
|
}
|
|
|
|
bool DaliGatewayBridge::allocateAllAddr(int start_address) const {
|
|
return dali_.allocateAllAddr(gateway_id_, start_address);
|
|
}
|
|
|
|
void DaliGatewayBridge::stopAllocAddr() const {
|
|
dali_.stopAllocAddr(gateway_id_);
|
|
}
|
|
|
|
bool DaliGatewayBridge::resetAndAllocAddr(int start_address, bool remove_addr_first,
|
|
bool close_light) const {
|
|
return dali_.resetAndAllocAddr(gateway_id_, start_address, remove_addr_first, close_light);
|
|
}
|
|
|
|
bool DaliGatewayBridge::resetBus() const {
|
|
return dali_.resetBus(gateway_id_);
|
|
}
|
|
|
|
} // namespace knx_dali_gw
|
|
} // namespace gateway
|