Enhance DALI communication: introduce DaliQueryResult structure, update query handling, and improve logging of query results

Signed-off-by: Tony <tonylu@tony-cloud.com>
This commit is contained in:
Tony
2026-05-21 13:22:15 +08:00
parent b52a95b50a
commit 0c662ecdc3
4 changed files with 151 additions and 21 deletions
+23
View File
@@ -6,6 +6,26 @@
#include <optional>
#include <vector>
enum class DaliQueryStatus {
success = 0,
noResponse,
invalidFrame,
unknownFrame,
sendFailed,
transportUnavailable,
timeout,
};
const char* daliQueryStatusName(DaliQueryStatus status);
struct DaliQueryResult {
DaliQueryStatus status{DaliQueryStatus::transportUnavailable};
std::optional<uint8_t> value;
std::vector<uint8_t> raw;
bool hasValue() const { return status == DaliQueryStatus::success && value.has_value(); }
};
// Lightweight communicator for DALI gateway type 1 (USB UART new frame format).
// Frames:
// - Send: [0x10, addr, cmd]
@@ -43,6 +63,8 @@ class DaliComm {
bool sendExtRawNew(uint8_t addr, uint8_t cmd) const;
std::optional<uint8_t> queryRaw(uint8_t addr, uint8_t cmd) const;
std::optional<uint8_t> queryRawNew(uint8_t addr, uint8_t cmd) const;
DaliQueryResult queryRawResult(uint8_t addr, uint8_t cmd) const;
DaliQueryResult queryRawNewResult(uint8_t addr, uint8_t cmd) const;
bool send(int dec_addr, uint8_t cmd) const;
std::optional<uint8_t> query(int dec_addr, uint8_t cmd) const;
bool getBusStatus() const;
@@ -54,6 +76,7 @@ class DaliComm {
// Send query frame (0x12) and parse single-byte response. Returns nullopt on no/invalid response,
// except COMPARE where 0xFD collisions are treated as a positive match.
std::optional<uint8_t> queryCmd(uint8_t addr, uint8_t cmd) const;
DaliQueryResult queryCmdResult(uint8_t addr, uint8_t cmd) const;
// Helpers to mirror Dart address conversion (DEC short address -> DALI odd/even encoded).
static uint8_t toCmdAddr(int dec_addr); // odd address for commands
+6 -3
View File
@@ -471,9 +471,12 @@ std::optional<bool> DaliBase::verifyShortAddr(int a) {
}
std::optional<bool> DaliBase::compareAddress() {
const auto ret = queryCmd(DALI_CMD_SPECIAL_COMPARE, DALI_CMD_OFF);
if (!ret.has_value()) return std::nullopt;
return ret.value() >= 0;
const auto ret = comm_.queryCmdResult(DALI_CMD_SPECIAL_COMPARE, DALI_CMD_OFF);
if (ret.status == DaliQueryStatus::noResponse || ret.status == DaliQueryStatus::timeout) {
return false;
}
if (!ret.hasValue()) return std::nullopt;
return true;
}
std::optional<bool> DaliBase::compare(int h, int m, int l) {
+19 -6
View File
@@ -1,5 +1,7 @@
#include "bridge.hpp"
#include "dali_define.hpp"
#include <algorithm>
#include <cmath>
#include <initializer_list>
@@ -121,6 +123,12 @@ DaliBridgeRequest bridgeRequestFromValue(const DaliValue::Object& object,
bool isShortAddress(int address) { return address >= 0 && address <= 63; }
bool isSpecialQueryRawAddress(int address) {
return address == DALI_CMD_SPECIAL_COMPARE ||
address == DALI_CMD_SPECIAL_VERIFY_SHORT_ADDRESS ||
address == DALI_CMD_SPECIAL_QUERY_SHORT_ADDRESS;
}
bool isReadOperation(BridgeOperation operation) {
switch (operation) {
case BridgeOperation::query:
@@ -504,7 +512,8 @@ DaliBridgeResult DaliBridgeEngine::executeResolved(const DaliBridgeRequest& requ
result.error = "invalid addr/cmd";
return result;
}
if (operation == BridgeOperation::query && addr.value() >= 0x80) {
if (operation == BridgeOperation::query && addr.value() >= 0x80 &&
!isSpecialQueryRawAddress(addr.value())) {
result.error = "query target must be a short address";
return result;
}
@@ -513,14 +522,18 @@ DaliBridgeResult DaliBridgeEngine::executeResolved(const DaliBridgeRequest& requ
} else if (operation == BridgeOperation::sendExt) {
result.ok = comm_.sendExtRaw(static_cast<uint8_t>(addr.value()), static_cast<uint8_t>(cmd.value()));
} else {
const auto response =
comm_.queryRaw(static_cast<uint8_t>(addr.value()), static_cast<uint8_t>(cmd.value()));
if (!response.has_value()) {
result.error = "no response";
const auto response = comm_.queryRawResult(static_cast<uint8_t>(addr.value()),
static_cast<uint8_t>(cmd.value()));
result.metadata["queryStatus"] = daliQueryStatusName(response.status);
result.metadata["queryRawLength"] = static_cast<int>(response.raw.size());
if (!response.raw.empty()) result.metadata["queryRaw0"] = response.raw[0];
if (response.raw.size() > 1) result.metadata["queryRaw1"] = response.raw[1];
if (!response.hasValue()) {
result.error = daliQueryStatusName(response.status);
return result;
}
result.ok = true;
result.data = static_cast<int>(response.value());
result.data = static_cast<int>(response.value.value());
}
break;
}
+103 -12
View File
@@ -1,8 +1,11 @@
#include "dali_comm.hpp"
#include "dali_define.hpp"
#include "log.hpp"
#include <chrono>
#include <cstdio>
#include <string>
#include <thread>
#ifdef ESP_PLATFORM
@@ -14,6 +17,23 @@ namespace {
constexpr uint32_t kQueryTimeoutMs = 100;
std::string hexByte(uint8_t value) {
char buffer[5]{};
std::snprintf(buffer, sizeof(buffer), "0x%02X", static_cast<unsigned>(value));
return buffer;
}
std::string formatBytes(const std::vector<uint8_t>& bytes) {
if (bytes.empty()) return "[]";
std::string out = "[";
for (size_t i = 0; i < bytes.size(); ++i) {
if (i > 0) out += ' ';
out += hexByte(bytes[i]);
}
out += ']';
return out;
}
std::vector<uint8_t> readQueryResponse(const DaliComm::ReadCallback& read_cb, uint32_t timeout_ms) {
if (!read_cb) return {};
@@ -40,31 +60,75 @@ std::vector<uint8_t> readQueryResponse(const DaliComm::ReadCallback& read_cb, ui
return collected;
}
std::optional<uint8_t> parseQueryResponse(const std::vector<uint8_t>& resp, uint8_t addr) {
if (resp.empty()) return std::nullopt;
DaliQueryResult parseQueryResponse(const std::vector<uint8_t>& resp, uint8_t addr) {
DaliQueryResult result;
result.raw = resp;
if (resp.empty()) {
result.status = DaliQueryStatus::timeout;
return result;
}
// Gateway type 1 returns: 0xFF <data> on success; 0xFE 0x00 on no response;
// 0xFD xx on invalid frame. COMPARE is special: collisions still mean a match.
if (resp[0] == 0xFE) {
return std::nullopt;
result.status = DaliQueryStatus::noResponse;
return result;
}
if (resp[0] == 0xFD) {
if (addr == DALI_CMD_SPECIAL_COMPARE) {
return static_cast<uint8_t>(resp.size() >= 2 ? resp[1] : 0xFF);
result.status = DaliQueryStatus::success;
result.value = static_cast<uint8_t>(resp.size() >= 2 ? resp[1] : 0xFF);
return result;
}
return std::nullopt;
result.status = DaliQueryStatus::invalidFrame;
return result;
}
if (resp.size() == 1) {
return resp[0];
result.status = DaliQueryStatus::success;
result.value = resp[0];
return result;
}
if (resp[0] == 0xFF && resp.size() >= 2) {
return resp[1];
result.status = DaliQueryStatus::success;
result.value = resp[1];
return result;
}
return resp.back();
result.status = DaliQueryStatus::unknownFrame;
return result;
}
void logQueryResult(uint8_t addr, uint8_t cmd, const DaliQueryResult& result) {
std::string message = "dali:query addr=" + hexByte(addr) + " cmd=" + hexByte(cmd) +
" status=" + daliQueryStatusName(result.status);
if (result.value.has_value()) {
message += " value=" + hexByte(result.value.value());
}
message += " raw=" + formatBytes(result.raw);
DaliLog::instance().debugLog(message);
}
} // namespace
const char* daliQueryStatusName(DaliQueryStatus status) {
switch (status) {
case DaliQueryStatus::success:
return "success";
case DaliQueryStatus::noResponse:
return "noResponse";
case DaliQueryStatus::invalidFrame:
return "invalidFrame";
case DaliQueryStatus::unknownFrame:
return "unknownFrame";
case DaliQueryStatus::sendFailed:
return "sendFailed";
case DaliQueryStatus::transportUnavailable:
return "transportUnavailable";
case DaliQueryStatus::timeout:
return "timeout";
}
return "unknown";
}
DaliComm::DaliComm(SendCallback send_cb,
ReadCallback read_cb,
TransactCallback transact_cb,
@@ -157,6 +221,14 @@ std::optional<uint8_t> DaliComm::queryRawNew(uint8_t addr, uint8_t cmd) const {
return queryCmd(addr, cmd);
}
DaliQueryResult DaliComm::queryRawResult(uint8_t addr, uint8_t cmd) const {
return queryCmdResult(addr, cmd);
}
DaliQueryResult DaliComm::queryRawNewResult(uint8_t addr, uint8_t cmd) const {
return queryCmdResult(addr, cmd);
}
bool DaliComm::send(int dec_addr, uint8_t cmd) const { return sendCmd(toCmdAddr(dec_addr), cmd); }
std::optional<uint8_t> DaliComm::query(int dec_addr, uint8_t cmd) const {
@@ -210,14 +282,33 @@ bool DaliComm::sendExtCmd(uint8_t addr, uint8_t cmd) const {
}
std::optional<uint8_t> DaliComm::queryCmd(uint8_t addr, uint8_t cmd) const {
const auto result = queryCmdResult(addr, cmd);
if (!result.hasValue()) return std::nullopt;
return result.value;
}
DaliQueryResult DaliComm::queryCmdResult(uint8_t addr, uint8_t cmd) const {
const std::vector<uint8_t> frame{0x12, addr, cmd};
DaliQueryResult result;
if (send_ && read_) {
prepareForQuery();
if (!writeFrame(frame)) return std::nullopt;
return parseQueryResponse(readQueryResponse(read_, kQueryTimeoutMs), addr);
if (!writeFrame(frame)) {
result.status = DaliQueryStatus::sendFailed;
logQueryResult(addr, cmd, result);
return result;
}
result = parseQueryResponse(readQueryResponse(read_, kQueryTimeoutMs), addr);
logQueryResult(addr, cmd, result);
return result;
}
prepareForQuery();
if (!transact_) return std::nullopt;
return parseQueryResponse(transact_(frame.data(), frame.size()), addr);
if (!transact_) {
result.status = DaliQueryStatus::transportUnavailable;
logQueryResult(addr, cmd, result);
return result;
}
result = parseQueryResponse(transact_(frame.data(), frame.size()), addr);
logQueryResult(addr, cmd, result);
return result;
}