Enhance DaliDecode: add support for device type decoding and context management

- Introduced new structures and methods for handling device type decoding, including DaliDecodeContext and DeviceTypeDecodeTable.
- Implemented decoding functions for device types DT1, DT4, DT5, DT6, and DT8, with specific command and query handling.
- Updated existing decode methods to incorporate gateway and source parameters for better context tracking.
- Added detailed formatting for device type commands and queries, enhancing the decoding process for various device types.
- Refactored the decode logic to utilize the new context management for maintaining state across different device types.

Signed-off-by: Tony <tonylu@tony-cloud.com>
This commit is contained in:
Tony
2026-06-13 14:25:27 +08:00
parent e11a0f646c
commit 211a9a9c41
11 changed files with 527 additions and 74 deletions
+6
View File
@@ -7,6 +7,12 @@ idf_component_register(
"src/bridge_model.cpp"
"src/bridge_provisioning.cpp"
"src/decode.cpp"
"src/decode_device_type.cpp"
"src/decode_dt1.cpp"
"src/decode_dt4.cpp"
"src/decode_dt5.cpp"
"src/decode_dt6.cpp"
"src/decode_dt8.cpp"
"src/device.cpp"
"src/dt1.cpp"
"src/dt4.cpp"
+17 -6
View File
@@ -4,15 +4,20 @@
#include <cstdint>
#include <functional>
#include <string>
#include <vector>
enum class BusDir { front, back };
enum class BusFrameSource { local, external };
struct BusFrame {
BusDir dir = BusDir::front;
int proto = 0x10;
int b1 = 0;
int b2 = 0;
int b3 = -1;
int gateway = -1;
BusFrameSource source = BusFrameSource::local;
};
class BusMonitor {
@@ -27,22 +32,24 @@ class BusMonitor {
void setRawSink(std::function<void(const BusFrame&)> sink) { rawSink_ = std::move(sink); }
void setDecodedSink(std::function<void(const DecodedRecord&)> sink) { decodedSink_ = std::move(sink); }
void emitFront(int proto, int addr, int cmd) {
BusFrame f{BusDir::front, proto, addr & 0xFF, cmd & 0xFF};
void emitFront(int proto, int addr, int cmd, int gateway = -1,
BusFrameSource source = BusFrameSource::local) {
BusFrame f{BusDir::front, proto, addr & 0xFF, cmd & 0xFF, -1, gateway, source};
rawFrames.push_back(f);
if (rawSink_) rawSink_(f);
auto rec = decoder.decode(addr & 0xFF, cmd & 0xFF, proto);
auto rec = decoder.decode(addr & 0xFF, cmd & 0xFF, proto, gateway, sourceName(source));
records.push_back(rec);
if (decodedSink_) decodedSink_(rec);
}
void emitBack(int value, int prefix = 0xFF) {
BusFrame f{BusDir::back, 0xFF, prefix & 0xFF, value & 0xFF};
void emitBack(int value, int prefix = 0xFF, int gateway = -1,
BusFrameSource source = BusFrameSource::local) {
BusFrame f{BusDir::back, 0xFF, prefix & 0xFF, value & 0xFF, -1, gateway, source};
rawFrames.push_back(f);
if (rawSink_) rawSink_(f);
auto rec = decoder.decodeCmdResponse(value & 0xFF, prefix & 0xFF);
auto rec = decoder.decodeCmdResponse(value & 0xFF, prefix & 0xFF, gateway, sourceName(source));
records.push_back(rec);
if (decodedSink_) decodedSink_(rec);
}
@@ -66,6 +73,10 @@ class BusMonitor {
private:
BusMonitor() = default;
static std::string sourceName(BusFrameSource source) {
return source == BusFrameSource::external ? "external" : "local";
}
std::function<void(const BusFrame&)> rawSink_;
std::function<void(const DecodedRecord&)> decodedSink_;
};
+23 -12
View File
@@ -5,12 +5,16 @@
#include <string>
#include <vector>
#include "decode_device_type.hpp"
struct DecodedRecord {
std::string text;
std::string type;
int addr = -1;
int cmd = -1;
int proto = -1;
int gateway = -1;
std::string source;
};
class DaliDecode {
@@ -26,24 +30,26 @@ class DaliDecode {
int isQueryCmd(int cmd) const;
DecodedRecord decodeBright(int addr, int level);
DecodedRecord decodeScene(int addr, int sceneCmd);
DecodedRecord decodeCmd(int addr, int c);
DecodedRecord decodeSpCmd(int addr, int c);
DecodedRecord querySpCMD(int cmdByte, int dataByte);
DecodedRecord decodeQuery(int addr, int c);
DecodedRecord decodeCmdResponse(int value, int gwPrefix = 0xFF);
DecodedRecord decode(int addr, int c, int proto = 0x10);
DecodedRecord decodeBright(int addr, int level, int gateway = -1, const std::string& source = "");
DecodedRecord decodeScene(int addr, int sceneCmd, int gateway = -1,
const std::string& source = "");
DecodedRecord decodeCmd(int addr, int c, int gateway = -1, const std::string& source = "");
DecodedRecord decodeSpCmd(int addr, int c, int gateway = -1, const std::string& source = "");
DecodedRecord querySpCMD(int cmdByte, int dataByte, int gateway = -1,
const std::string& source = "");
DecodedRecord decodeQuery(int addr, int c, int gateway = -1, const std::string& source = "",
int deviceType = -1);
DecodedRecord decodeCmdResponse(int value, int gwPrefix = 0xFF, int gateway = -1,
const std::string& source = "");
DecodedRecord decode(int addr, int c, int proto = 0x10, int gateway = -1,
const std::string& source = "");
private:
std::map<int, std::string> cmd_;
std::map<int, std::string> sCMD_;
std::vector<int> queryCmd_;
int lastQueryCmd_ = 0;
int64_t lastQueryAtMs_ = 0;
int pendingColourType_ = -1;
int64_t lastColourQueryAtMs_ = 0;
std::map<int, DaliDecodeContext> contexts_;
static int64_t nowMs();
static std::string hex(int v);
@@ -56,5 +62,10 @@ class DaliDecode {
static std::string booleanQueryLabel(int c);
static std::string fadeTimeLabel(int code);
static std::string fadeRateLabel(int code);
DaliDecodeContext& contextFor(int gateway);
void syncDefaultDtrFromContext();
void syncDefaultDtrToContext();
DecodedRecord decodeDeviceTypeCommand(int addr, int c, const DtCommandSpec& spec, int proto,
int gateway, const std::string& source);
DecodedRecord withRaw(const DecodedRecord& r) const;
};
+55
View File
@@ -0,0 +1,55 @@
#pragma once
#include <cstdint>
#include <functional>
#include <map>
#include <string>
struct DaliDecodeContext {
int lastQueryCmd = 0;
int lastQueryDeviceType = -1;
int64_t lastQueryAtMs = 0;
int dtr0 = 0;
int dtr1 = 0;
int dtr2 = 0;
int pendingColourType = -1;
int64_t lastColourQueryAtMs = 0;
int pendingDeviceType = -1;
};
struct DtCommandSpec {
std::string name;
std::string type = "cmd";
bool isQuery = false;
std::function<std::string(const DaliDecodeContext&)> detail;
std::string format(const DaliDecodeContext& context) const;
};
struct DeviceTypeDecodeTable {
int deviceType = -1;
std::map<int, DtCommandSpec> commands;
std::map<int, DtCommandSpec> queries;
const DtCommandSpec* command(int code) const;
const DtCommandSpec* query(int code) const;
};
const DeviceTypeDecodeTable* deviceTypeDecodeTable(int deviceType);
std::string dtr0Detail(const DaliDecodeContext& context);
std::string dtr1Detail(const DaliDecodeContext& context);
std::string dtr2Detail(const DaliDecodeContext& context);
std::string dtr10WordDetail(const DaliDecodeContext& context, const std::string& label);
std::string dt8CoordinateDetail(const DaliDecodeContext& context, const std::string& label);
std::string dt8MirekDetail(const DaliDecodeContext& context);
std::string dt8RgbDetail(const DaliDecodeContext& context);
std::string dt8WafDetail(const DaliDecodeContext& context);
std::string dt8PrimaryDetail(const DaliDecodeContext& context);
std::string dt8ControlDetail(const DaliDecodeContext& context);
const DeviceTypeDecodeTable& dt1DecodeTable();
const DeviceTypeDecodeTable& dt4DecodeTable();
const DeviceTypeDecodeTable& dt5DecodeTable();
const DeviceTypeDecodeTable& dt6DecodeTable();
const DeviceTypeDecodeTable& dt8DecodeTable();
+135 -56
View File
@@ -50,15 +50,6 @@ DaliDecode::DaliDecode() {
{DALI_CMD_QUERY_RANDOM_ADDRESS_M, "QUERY_RANDOM_ADDRESS_(M)"},
{DALI_CMD_QUERY_RANDOM_ADDRESS_L, "QUERY_RANDOM_ADDRESS_(L)"},
{DALI_CMD_READ_MEMORY_LOCATION, "READ_MEMORY_LOCATION"},
{DALI_CMD_DT8_STORE_DTR_AS_COLORX, "STORE_DTR_AS_COLORX"},
{DALI_CMD_DT8_STORE_DTR_AS_COLORY, "STORE_DTR_AS_COLORY"},
{DALI_CMD_DT8_ACTIVATE, "ACTIVATE"},
{DALI_CMD_DT8_SET_COLOR_TEMPERATURE, "SET_COLOR_TEMPERATURE"},
{DALI_CMD_DT8_STEP_UP_COLOR_TEMPERATURE, "STEP_UP_COLOR_TEMPERATURE"},
{DALI_CMD_DT8_STEP_DOWN_COLOR_TEMPERATURE, "STEP_DOWN_COLOR_TEMPERATURE"},
{DALI_CMD_QUERY_COLOR_STATUS, "QUERY_COLOR_STATUS"},
{DALI_CMD_QUERY_COLOR_TYPE, "QUERY_COLOR_TYPE"},
{DALI_CMD_QUERY_COLOR_VALUE, "QUERY_COLOR_VALUE"},
};
sCMD_ = {
@@ -111,12 +102,7 @@ DaliDecode::DaliDecode() {
DALI_CMD_QUERY_RANDOM_ADDRESS_H,
DALI_CMD_QUERY_RANDOM_ADDRESS_M,
DALI_CMD_QUERY_RANDOM_ADDRESS_L,
DALI_CMD_READ_MEMORY_LOCATION,
DALI_CMD_DT8_ACTIVATE,
DALI_CMD_DT8_SET_COLOR_TEMPERATURE,
DALI_CMD_DT8_STEP_UP_COLOR_TEMPERATURE,
DALI_CMD_DT8_STEP_DOWN_COLOR_TEMPERATURE,
DALI_CMD_QUERY_COLOR_VALUE};
DALI_CMD_READ_MEMORY_LOCATION};
for (int c = DALI_CMD_QUERY_SCENE_LEVEL_MIN; c <= DALI_CMD_QUERY_SCENE_LEVEL_MAX; c++) {
queryCmd_.push_back(c);
}
@@ -126,17 +112,39 @@ int DaliDecode::isQueryCmd(int cmd) const {
return std::find(queryCmd_.begin(), queryCmd_.end(), cmd) != queryCmd_.end() ? 1 : 0;
}
DecodedRecord DaliDecode::decodeBright(int addr, int level) {
DaliDecodeContext& DaliDecode::contextFor(int gateway) {
return contexts_[gateway];
}
void DaliDecode::syncDefaultDtrFromContext() {
const auto it = contexts_.find(-1);
if (it == contexts_.end()) return;
dtr0 = it->second.dtr0;
dtr1 = it->second.dtr1;
dtr2 = it->second.dtr2;
}
void DaliDecode::syncDefaultDtrToContext() {
auto& context = contexts_[-1];
context.dtr0 = dtr0 & 0xFF;
context.dtr1 = dtr1 & 0xFF;
context.dtr2 = dtr2 & 0xFF;
}
DecodedRecord DaliDecode::decodeBright(int addr, int level, int gateway, const std::string& source) {
DecodedRecord r;
r.text = whoLabel(addr, true) + " DAPC level=" + std::to_string(level);
r.type = "brightness";
r.addr = addr;
r.cmd = level;
r.proto = 0x10;
r.gateway = gateway;
r.source = source;
return withRaw(r);
}
DecodedRecord DaliDecode::decodeScene(int addr, int sceneCmd) {
DecodedRecord DaliDecode::decodeScene(int addr, int sceneCmd, int gateway,
const std::string& source) {
const int sc = sceneCmd - DALI_CMD_GO_TO_SCENE_MIN;
DecodedRecord r;
r.text = whoLabel(addr, false) + " GO_TO_SCENE " + std::to_string(sc);
@@ -144,10 +152,12 @@ DecodedRecord DaliDecode::decodeScene(int addr, int sceneCmd) {
r.addr = addr;
r.cmd = sceneCmd;
r.proto = 0x10;
r.gateway = gateway;
r.source = source;
return withRaw(r);
}
DecodedRecord DaliDecode::decodeCmd(int addr, int c) {
DecodedRecord DaliDecode::decodeCmd(int addr, int c, int gateway, const std::string& source) {
std::string name = "CMD 0x" + hex(c);
const auto it = cmd_.find(c);
if (it != cmd_.end()) name = it->second;
@@ -166,16 +176,21 @@ DecodedRecord DaliDecode::decodeCmd(int addr, int c) {
r.addr = addr;
r.cmd = c;
r.proto = 0x10;
r.gateway = gateway;
r.source = source;
return withRaw(r);
}
DecodedRecord DaliDecode::decodeSpCmd(int addr, int c) {
DecodedRecord DaliDecode::decodeSpCmd(int addr, int c, int gateway, const std::string& source) {
auto& context = contextFor(gateway);
if (gateway == -1) syncDefaultDtrToContext();
const int cmdByte = addr & 0xFF;
const int dataByte = c & 0xFF;
if (cmdByte == DALI_CMD_SPECIAL_COMPARE || cmdByte == DALI_CMD_SPECIAL_VERIFY_SHORT_ADDRESS ||
cmdByte == DALI_CMD_SPECIAL_QUERY_SHORT_ADDRESS) {
return querySpCMD(cmdByte, dataByte);
context.pendingDeviceType = -1;
return querySpCMD(cmdByte, dataByte, gateway, source);
}
std::string name = "SPECIAL_CMD 0x" + hex(cmdByte);
@@ -183,12 +198,20 @@ DecodedRecord DaliDecode::decodeSpCmd(int addr, int c) {
if (it != sCMD_.end()) name = it->second;
if (cmdByte == DALI_CMD_SPECIAL_SET_DTR0) {
dtr0 = dataByte;
context.pendingDeviceType = -1;
context.dtr0 = dataByte;
} else if (cmdByte == DALI_CMD_SPECIAL_SET_DTR_1) {
dtr1 = dataByte;
context.pendingDeviceType = -1;
context.dtr1 = dataByte;
} else if (cmdByte == DALI_CMD_SPECIAL_SET_DTR_2) {
dtr2 = dataByte;
context.pendingDeviceType = -1;
context.dtr2 = dataByte;
} else if (cmdByte == DALI_CMD_SPECIAL_DT_SELECT) {
context.pendingDeviceType = dataByte;
} else {
context.pendingDeviceType = -1;
}
if (gateway == -1) syncDefaultDtrFromContext();
DecodedRecord r;
r.text = name + " data=0x" + hex(dataByte) + " (" + std::to_string(dataByte) + ")";
@@ -196,12 +219,17 @@ DecodedRecord DaliDecode::decodeSpCmd(int addr, int c) {
r.addr = addr;
r.cmd = c;
r.proto = 0x10;
r.gateway = gateway;
r.source = source;
return withRaw(r);
}
DecodedRecord DaliDecode::querySpCMD(int cmdByte, int dataByte) {
lastQueryCmd_ = cmdByte;
lastQueryAtMs_ = nowMs();
DecodedRecord DaliDecode::querySpCMD(int cmdByte, int dataByte, int gateway,
const std::string& source) {
auto& context = contextFor(gateway);
context.lastQueryCmd = cmdByte;
context.lastQueryDeviceType = -1;
context.lastQueryAtMs = nowMs();
std::string name = "SPECIAL_CMD 0x" + hex(cmdByte);
const auto it = sCMD_.find(cmdByte);
@@ -213,30 +241,41 @@ DecodedRecord DaliDecode::querySpCMD(int cmdByte, int dataByte) {
r.addr = cmdByte;
r.cmd = dataByte;
r.proto = 0x10;
r.gateway = gateway;
r.source = source;
return withRaw(r);
}
DecodedRecord DaliDecode::decodeQuery(int addr, int c) {
lastQueryCmd_ = c;
lastQueryAtMs_ = nowMs();
DecodedRecord DaliDecode::decodeQuery(int addr, int c, int gateway, const std::string& source,
int deviceType) {
auto& context = contextFor(gateway);
if (gateway == -1) syncDefaultDtrToContext();
context.lastQueryCmd = c;
context.lastQueryDeviceType = deviceType;
context.lastQueryAtMs = nowMs();
std::string name = "QUERY 0x" + hex(c);
const auto it = cmd_.find(c);
if (it != cmd_.end()) name = it->second;
const auto* table = deviceTypeDecodeTable(deviceType);
if (table != nullptr) {
const auto* spec = table->query(c);
if (spec != nullptr) name = spec->format(context);
}
if (c >= DALI_CMD_QUERY_SCENE_LEVEL_MIN && c <= DALI_CMD_QUERY_SCENE_LEVEL_MAX) {
name = "QUERY_SCENE_LEVEL " + std::to_string(c - DALI_CMD_QUERY_SCENE_LEVEL_MIN);
}
if (c == DALI_CMD_QUERY_COLOR_VALUE) {
lastColourQueryAtMs_ = nowMs();
int t = dtr0;
context.lastColourQueryAtMs = nowMs();
int t = context.dtr0;
if (t == 128) t = 0;
if (t == 130) t = 1;
pendingColourType_ = (t == 0 || t == 1 || t == 2) ? t : -1;
if (pendingColourType_ == 0) name = "QUERY_COLOUR_VALUE(x)";
if (pendingColourType_ == 1) name = "QUERY_COLOUR_VALUE(y)";
if (pendingColourType_ == 2) name = "QUERY_COLOUR_VALUE(ct)";
context.pendingColourType = (t == 0 || t == 1 || t == 2) ? t : -1;
if (context.pendingColourType == 0) name = "QUERY_COLOUR_VALUE(x)";
if (context.pendingColourType == 1) name = "QUERY_COLOUR_VALUE(y)";
if (context.pendingColourType == 2) name = "QUERY_COLOUR_VALUE(ct)";
}
DecodedRecord r;
@@ -245,24 +284,33 @@ DecodedRecord DaliDecode::decodeQuery(int addr, int c) {
r.addr = addr;
r.cmd = c;
r.proto = 0x12;
r.gateway = gateway;
r.source = source;
return withRaw(r);
}
DecodedRecord DaliDecode::decodeCmdResponse(int value, int /*gwPrefix*/) {
DecodedRecord DaliDecode::decodeCmdResponse(int value, int /*gwPrefix*/, int gateway,
const std::string& source) {
auto& context = contextFor(gateway);
if (gateway == -1) syncDefaultDtrToContext();
const int64_t now = nowMs();
if ((now - lastQueryAtMs_) > responseWindowMs || lastQueryCmd_ == 0) {
if ((now - context.lastQueryAtMs) > responseWindowMs || context.lastQueryCmd == 0) {
DecodedRecord r{"unknown back 0x" + hex(value) + " / " + std::to_string(value), "unknown", -1,
-1, 0xFF};
r.gateway = gateway;
r.source = source;
return withRaw(r);
}
const int c = lastQueryCmd_;
lastQueryCmd_ = 0;
lastQueryAtMs_ = 0;
const int c = context.lastQueryCmd;
context.lastQueryCmd = 0;
context.lastQueryDeviceType = -1;
context.lastQueryAtMs = 0;
if (c == DALI_CMD_QUERY_CONTENT_DTR) dtr0 = value & 0xFF;
if (c == DALI_CMD_QUERY_CONTENT_DTR1) dtr1 = value & 0xFF;
if (c == DALI_CMD_QUERY_CONTENT_DTR2) dtr2 = value & 0xFF;
if (c == DALI_CMD_QUERY_CONTENT_DTR) context.dtr0 = value & 0xFF;
if (c == DALI_CMD_QUERY_CONTENT_DTR1) context.dtr1 = value & 0xFF;
if (c == DALI_CMD_QUERY_CONTENT_DTR2) context.dtr2 = value & 0xFF;
if (gateway == -1) syncDefaultDtrFromContext();
if (c == DALI_CMD_QUERY_STATUS) {
std::ostringstream oss;
@@ -407,14 +455,14 @@ DecodedRecord DaliDecode::decodeCmdResponse(int value, int /*gwPrefix*/) {
"response", -1, c, 0xFF});
}
if (pendingColourType_ >= 0 && (now - lastColourQueryAtMs_) <= 1000) {
const int combined = dtr1 * 256 + dtr0;
if (context.pendingColourType >= 0 && (now - context.lastColourQueryAtMs) <= 1000) {
const int combined = context.dtr1 * 256 + context.dtr0;
std::string pretty;
if (pendingColourType_ == 2) {
if (context.pendingColourType == 2) {
const int kelvin = combined == 0 ? 0 : (1000000 / combined);
pretty = "ct mirek=" + std::to_string(combined) + " kelvin=" + std::to_string(kelvin);
} else {
const char key = pendingColourType_ == 0 ? 'x' : 'y';
const char key = context.pendingColourType == 0 ? 'x' : 'y';
std::ostringstream oss;
oss << key << "=" << std::fixed << std::setprecision(4)
<< (static_cast<double>(combined) / 65535.0) << " (" << combined << ")";
@@ -427,30 +475,61 @@ DecodedRecord DaliDecode::decodeCmdResponse(int value, int /*gwPrefix*/) {
-1, c, 0xFF});
}
DecodedRecord DaliDecode::decode(int addr, int c, int proto) {
DecodedRecord DaliDecode::decode(int addr, int c, int proto, int gateway, const std::string& source) {
auto& context = contextFor(gateway);
if (gateway == -1) syncDefaultDtrToContext();
if (proto == 0x12) {
return decodeQuery(addr, c);
const int selectedDeviceType = context.pendingDeviceType;
context.pendingDeviceType = -1;
return decodeQuery(addr, c, gateway, source, selectedDeviceType);
}
if (addr >= DALI_CMD_SPECIAL_RANGE_MIN && addr <= DALI_CMD_SPECIAL_RANGE_MAX) {
return decodeSpCmd(addr, c);
return decodeSpCmd(addr, c, gateway, source);
}
const int selectedDeviceType = context.pendingDeviceType;
context.pendingDeviceType = -1;
const auto* table = deviceTypeDecodeTable(selectedDeviceType);
if (table != nullptr) {
const auto* querySpec = table->query(c);
if (querySpec != nullptr) {
return decodeQuery(addr, c, gateway, source, selectedDeviceType);
}
const auto* commandSpec = table->command(c);
if (commandSpec != nullptr) {
return decodeDeviceTypeCommand(addr, c, *commandSpec, proto, gateway, source);
}
}
if (isQueryCmd(c) == 1) {
return decodeQuery(addr, c);
return decodeQuery(addr, c, gateway, source);
}
if ((addr & 1) == 0) {
return decodeBright(addr, c);
return decodeBright(addr, c, gateway, source);
}
if (c >= DALI_CMD_GO_TO_SCENE_MIN && c <= DALI_CMD_GO_TO_SCENE_MAX) {
return decodeScene(addr, c);
return decodeScene(addr, c, gateway, source);
}
if (proto == 0x11) {
if (c == DALI_CMD_QUERY_COLOR_VALUE) return decodeQuery(addr, c);
std::string name = "EXT 0x" + hex(c);
const auto it = cmd_.find(c);
if (it != cmd_.end()) name = it->second;
return withRaw({whoLabel(addr, false) + " " + name, "ext", addr, c, proto});
return withRaw({whoLabel(addr, false) + " " + name, "ext", addr, c, proto, gateway, source});
}
return decodeCmd(addr, c);
return decodeCmd(addr, c, gateway, source);
}
DecodedRecord DaliDecode::decodeDeviceTypeCommand(int addr, int c, const DtCommandSpec& spec,
int proto, int gateway,
const std::string& source) {
auto& context = contextFor(gateway);
DecodedRecord r;
r.text = whoLabel(addr, false) + " " + spec.format(context);
r.type = proto == 0x11 ? "ext" : spec.type;
r.addr = addr;
r.cmd = c;
r.proto = proto;
r.gateway = gateway;
r.source = source;
return withRaw(r);
}
int64_t DaliDecode::nowMs() {
+107
View File
@@ -0,0 +1,107 @@
#include "decode_device_type.hpp"
#include <iomanip>
#include <sstream>
namespace {
std::string byteHex(int value) {
std::ostringstream oss;
oss << std::hex << std::uppercase << std::setfill('0') << std::setw(2) << (value & 0xFF);
return oss.str();
}
std::string wordHex(int value) {
std::ostringstream oss;
oss << std::hex << std::uppercase << std::setfill('0') << std::setw(4) << (value & 0xFFFF);
return oss.str();
}
} // namespace
std::string DtCommandSpec::format(const DaliDecodeContext& context) const {
if (!detail) return name;
const auto extra = detail(context);
if (extra.empty()) return name;
return name + " (" + extra + ")";
}
const DtCommandSpec* DeviceTypeDecodeTable::command(int code) const {
const auto it = commands.find(code & 0xFF);
return it == commands.end() ? nullptr : &it->second;
}
const DtCommandSpec* DeviceTypeDecodeTable::query(int code) const {
const auto it = queries.find(code & 0xFF);
return it == queries.end() ? nullptr : &it->second;
}
const DeviceTypeDecodeTable* deviceTypeDecodeTable(int deviceType) {
switch (deviceType) {
case 1:
return &dt1DecodeTable();
case 4:
return &dt4DecodeTable();
case 5:
return &dt5DecodeTable();
case 6:
return &dt6DecodeTable();
case 8:
return &dt8DecodeTable();
default:
return nullptr;
}
}
std::string dtr0Detail(const DaliDecodeContext& context) {
return "DTR0=0x" + byteHex(context.dtr0) + " / " + std::to_string(context.dtr0);
}
std::string dtr1Detail(const DaliDecodeContext& context) {
return "DTR1=0x" + byteHex(context.dtr1) + " / " + std::to_string(context.dtr1);
}
std::string dtr2Detail(const DaliDecodeContext& context) {
return "DTR2=0x" + byteHex(context.dtr2) + " / " + std::to_string(context.dtr2);
}
std::string dtr10WordDetail(const DaliDecodeContext& context, const std::string& label) {
const int value = ((context.dtr1 & 0xFF) << 8) | (context.dtr0 & 0xFF);
return label + "=0x" + wordHex(value) + " / " + std::to_string(value) + ", DTR1=0x" +
byteHex(context.dtr1) + ", DTR0=0x" + byteHex(context.dtr0);
}
std::string dt8CoordinateDetail(const DaliDecodeContext& context, const std::string& label) {
const int value = ((context.dtr1 & 0xFF) << 8) | (context.dtr0 & 0xFF);
std::ostringstream oss;
oss << label << "=0x" << wordHex(value) << " / " << value << ", normalized=" << std::fixed
<< std::setprecision(4) << (static_cast<double>(value) / 65535.0) << ", DTR1=0x"
<< byteHex(context.dtr1) << ", DTR0=0x" << byteHex(context.dtr0);
return oss.str();
}
std::string dt8MirekDetail(const DaliDecodeContext& context) {
const int mirek = ((context.dtr1 & 0xFF) << 8) | (context.dtr0 & 0xFF);
const int kelvin = mirek > 0 ? 1000000 / mirek : 0;
return "mirek=0x" + wordHex(mirek) + " / " + std::to_string(mirek) +
", kelvin=" + std::to_string(kelvin) + ", DTR1=0x" + byteHex(context.dtr1) +
", DTR0=0x" + byteHex(context.dtr0);
}
std::string dt8RgbDetail(const DaliDecodeContext& context) {
return "R=" + std::to_string(context.dtr0) + ", G=" + std::to_string(context.dtr1) +
", B=" + std::to_string(context.dtr2);
}
std::string dt8WafDetail(const DaliDecodeContext& context) {
return "W=" + std::to_string(context.dtr0) + ", A=" + std::to_string(context.dtr1) +
", F=" + std::to_string(context.dtr2);
}
std::string dt8PrimaryDetail(const DaliDecodeContext& context) {
return dtr10WordDetail(context, "value") + ", primary=" + std::to_string(context.dtr2 & 0xFF);
}
std::string dt8ControlDetail(const DaliDecodeContext& context) {
return "control=0x" + byteHex(context.dtr0) + " / " + std::to_string(context.dtr0);
}
+43
View File
@@ -0,0 +1,43 @@
#include "decode_device_type.hpp"
const DeviceTypeDecodeTable& dt1DecodeTable() {
static const DeviceTypeDecodeTable table = {
1,
{
{0xE0, {"REST"}},
{0xE1, {"INHIBIT"}},
{0xE2, {"RE_LIGHT_OR_RESET_INHIBIT"}},
{0xE3, {"START_FUNCTION_TEST"}},
{0xE4, {"START_DURATION_TEST"}},
{0xE5, {"STOP_TEST"}},
{0xE6, {"RESET_FUNCTION_TEST_DONE_FLAG"}},
{0xE7, {"RESET_DURATION_TEST_DONE_FLAG"}},
{0xE8, {"RESET_LAMP_TIME"}},
{0xE9, {"STORE_EMERGENCY_LEVEL", "cmd", false, dtr0Detail}},
{0xEA, {"STORE_TEST_DELAY_TIME_HIGH_BYTE", "cmd", false, dtr0Detail}},
{0xEB, {"STORE_TEST_DELAY_TIME_LOW_BYTE", "cmd", false, dtr0Detail}},
{0xEC, {"STORE_FUNCTION_TEST_INTERVAL", "cmd", false, dtr0Detail}},
{0xED, {"STORE_DURATION_TEST_INTERVAL", "cmd", false, dtr0Detail}},
{0xEE, {"STORE_TEST_EXECUTION_TIMEOUT", "cmd", false, dtr0Detail}},
{0xEF, {"STORE_PROLONG_TIME", "cmd", false, dtr0Detail}},
{0xF0, {"START_IDENTIFICATION"}},
{0xFE, {"PERFORM_DTR_SELECTED_FUNCTION", "cmd", false, dtr0Detail}},
},
{
{0xF1, {"QUERY_BATTERY_CHARGE_LEVEL", "query", true}},
{0xF2, {"QUERY_TEST_TIMING", "query", true, dtr0Detail}},
{0xF3, {"QUERY_DURATION_TEST_RESULT", "query", true}},
{0xF4, {"QUERY_LAMP_EMERGENCY_TIME", "query", true}},
{0xF5, {"QUERY_LAMP_TOTAL_OPERATION_TIME", "query", true}},
{0xF6, {"QUERY_EMERGENCY_LEVEL", "query", true}},
{0xF7, {"QUERY_EMERGENCY_MIN_LEVEL", "query", true}},
{0xF8, {"QUERY_EMERGENCY_MAX_LEVEL", "query", true}},
{0xF9, {"QUERY_RATED_DURATION", "query", true}},
{0xFA, {"QUERY_EMERGENCY_MODE", "query", true}},
{0xFB, {"QUERY_FEATURES", "query", true}},
{0xFC, {"QUERY_FAILURE_STATUS", "query", true}},
{0xFD, {"QUERY_EMERGENCY_STATUS", "query", true}},
{0xFF, {"QUERY_EXTENDED_VERSION_NUMBER", "query", true}},
}};
return table;
}
+27
View File
@@ -0,0 +1,27 @@
#include "decode_device_type.hpp"
const DeviceTypeDecodeTable& dt4DecodeTable() {
static const DeviceTypeDecodeTable table = {
4,
{
{0xE0, {"REFERENCE_SYSTEM_POWER"}},
{0xE1, {"SELECT_DIMMING_CURVE", "cmd", false, dtr0Detail}},
},
{
{0xEE, {"QUERY_DIMMING_CURVE", "query", true}},
{0xEF, {"QUERY_RATED_MINIMUM_LEVEL", "query", true}},
{0xF0, {"QUERY_RATED_MAXIMUM_LEVEL", "query", true}},
{0xF1, {"QUERY_SYSTEM_POWER", "query", true}},
{0xF2, {"QUERY_DIMMER_TEMPERATURE", "query", true}},
{0xF3, {"QUERY_RMS_SUPPLY_VOLTAGE", "query", true}},
{0xF4, {"QUERY_SUPPLY_FREQUENCY", "query", true}},
{0xF5, {"QUERY_RMS_LOAD_VOLTAGE", "query", true}},
{0xF6, {"QUERY_RMS_LOAD_CURRENT", "query", true}},
{0xF7, {"QUERY_REAL_LOAD_POWER", "query", true}},
{0xF8, {"QUERY_LOAD_RATING", "query", true}},
{0xF9, {"QUERY_REFERENCE_RUNNING", "query", true}},
{0xFA, {"QUERY_REFERENCE_MEASUREMENT_FAILED", "query", true}},
{0xFF, {"QUERY_EXTENDED_VERSION_NUMBER", "query", true}},
}};
return table;
}
+24
View File
@@ -0,0 +1,24 @@
#include "decode_device_type.hpp"
const DeviceTypeDecodeTable& dt5DecodeTable() {
static const DeviceTypeDecodeTable table = {
5,
{
{0xE0, {"SET_OUTPUT_RANGE_1_TO_10V"}},
{0xE1, {"SET_OUTPUT_RANGE_0_TO_10V"}},
{0xE2, {"SWITCH_ON_INTERNAL_PULL_UP"}},
{0xE3, {"SWITCH_OFF_INTERNAL_PULL_UP"}},
{0xE4, {"STORE_DTR_AS_PHYSICAL_MINIMUM_LEVEL", "cmd", false, dtr0Detail}},
{0xE5, {"SELECT_DIMMING_CURVE", "cmd", false, dtr0Detail}},
{0xE6, {"RESET_CONVERTER_SETTINGS"}},
},
{
{0xEE, {"QUERY_DIMMING_CURVE", "query", true}},
{0xEF, {"QUERY_OUTPUT_LEVEL", "query", true}},
{0xF0, {"QUERY_CONVERTER_STATUS", "query", true}},
{0xF1, {"QUERY_OUTPUT_RANGE", "query", true}},
{0xF2, {"QUERY_FEATURES", "query", true}},
{0xFF, {"QUERY_EXTENDED_VERSION_NUMBER", "query", true}},
}};
return table;
}
+35
View File
@@ -0,0 +1,35 @@
#include "decode_device_type.hpp"
const DeviceTypeDecodeTable& dt6DecodeTable() {
static const DeviceTypeDecodeTable table = {
6,
{
{0xE0, {"REFERENCE_SYSTEM_POWER"}},
{0xE1, {"ENABLE_CURRENT_PROTECTOR"}},
{0xE2, {"DISABLE_CURRENT_PROTECTOR"}},
{0xE3, {"SELECT_DIMMING_CURVE", "cmd", false, dtr0Detail}},
{0xE4, {"STORE_DTR_AS_FAST_FADE_TIME", "cmd", false, dtr0Detail}},
},
{
{0xED, {"QUERY_GEAR_TYPE", "query", true}},
{0xEE, {"QUERY_DIMMING_CURVE", "query", true}},
{0xEF, {"QUERY_POSSIBLE_OPERATING_MODES", "query", true}},
{0xF0, {"QUERY_FEATURES", "query", true}},
{0xF1, {"QUERY_FAILURE_STATUS", "query", true}},
{0xF2, {"QUERY_SHORT_CIRCUIT", "query", true}},
{0xF3, {"QUERY_OPEN_CIRCUIT", "query", true}},
{0xF4, {"QUERY_LOAD_DECREASE", "query", true}},
{0xF5, {"QUERY_LOAD_INCREASE", "query", true}},
{0xF6, {"QUERY_CURRENT_PROTECTOR_ACTIVE", "query", true}},
{0xF7, {"QUERY_THERMAL_SHUTDOWN", "query", true}},
{0xF8, {"QUERY_THERMAL_OVERLOAD", "query", true}},
{0xF9, {"QUERY_REFERENCE_RUNNING", "query", true}},
{0xFA, {"QUERY_REFERENCE_MEASUREMENT_FAILED", "query", true}},
{0xFB, {"QUERY_CURRENT_PROTECTOR_ENABLED", "query", true}},
{0xFC, {"QUERY_OPERATING_MODE", "query", true}},
{0xFD, {"QUERY_FAST_FADE_TIME", "query", true}},
{0xFE, {"QUERY_MIN_FAST_FADE_TIME", "query", true}},
{0xFF, {"QUERY_EXTENDED_VERSION_NUMBER", "query", true}},
}};
return table;
}
+55
View File
@@ -0,0 +1,55 @@
#include "decode_device_type.hpp"
namespace {
std::string colorXDetail(const DaliDecodeContext& context) {
return dt8CoordinateDetail(context, "x");
}
std::string colorYDetail(const DaliDecodeContext& context) {
return dt8CoordinateDetail(context, "y");
}
std::string mirekLimitDetail(const DaliDecodeContext& context) {
return dt8MirekDetail(context) + ", limit=" + std::to_string(context.dtr2 & 0xFF);
}
} // namespace
const DeviceTypeDecodeTable& dt8DecodeTable() {
static const DeviceTypeDecodeTable table = {
8,
{
{0xE0, {"STORE_DTR_AS_COLORX", "cmd", false, colorXDetail}},
{0xE1, {"STORE_DTR_AS_COLORY", "cmd", false, colorYDetail}},
{0xE2, {"ACTIVATE"}},
{0xE3, {"STEP_UP_COLORX"}},
{0xE4, {"STEP_DOWN_COLORX"}},
{0xE5, {"STEP_UP_COLORY"}},
{0xE6, {"STEP_DOWN_COLORY"}},
{0xE7, {"SET_COLOR_TEMPERATURE", "cmd", false, dt8MirekDetail}},
{0xE8, {"STEP_UP_COLOR_TEMPERATURE"}},
{0xE9, {"STEP_DOWN_COLOR_TEMPERATURE"}},
{0xEA, {"SET_TEMPORARY_PRIMARY_N_DIM_LEVEL", "cmd", false, dt8PrimaryDetail}},
{0xEB, {"SET_TEMPORARY_RGB_DIM_LEVELS", "cmd", false, dt8RgbDetail}},
{0xEC, {"SET_TEMPORARY_WAF_DIM_LEVELS", "cmd", false, dt8WafDetail}},
{0xED, {"SET_TEMPORARY_RGBWAF_CONTROL", "cmd", false, dt8ControlDetail}},
{0xEE, {"COPY_REPORT_TO_TEMPORARY"}},
{0xF0, {"STORE_XY_PRIMARY_N", "cmd", false, dt8PrimaryDetail}},
{0xF1, {"STORE_TEMPORARY_XY_AS_PRIMARY_N", "cmd", false, dtr2Detail}},
{0xF2, {"STORE_COLOR_TEMPERATURE_LIMIT", "cmd", false, mirekLimitDetail}},
{0xF3, {"STORE_GEAR_FEATURES_STATUS", "cmd", false, dtr0Detail}},
{0xF5, {"ASSIGN_COLOR_TO_LINKED_CHANNELS", "cmd", false, dtr0Detail}},
{0xF6, {"START_AUTO_CALIBRATION"}},
},
{
{0xF7, {"QUERY_GEAR_FEATURES_STATUS", "query", true}},
{0xF8, {"QUERY_COLOR_STATUS", "query", true}},
{0xF9, {"QUERY_COLOR_TYPE_FEATURES", "query", true}},
{0xFA, {"QUERY_COLOUR_VALUE", "query", true, dtr0Detail}},
{0xFB, {"QUERY_RGBWAF_CONTROL", "query", true}},
{0xFC, {"QUERY_ASSIGNED_COLOR_FOR_CHANNEL", "query", true, dtr0Detail}},
{0xFF, {"QUERY_EXTENDED_VERSION_NUMBER", "query", true}},
}};
return table;
}