diff --git a/include/dali_comm.hpp b/include/dali_comm.hpp index e1c5274..b55a1ad 100644 --- a/include/dali_comm.hpp +++ b/include/dali_comm.hpp @@ -6,6 +6,26 @@ #include #include +enum class DaliQueryStatus { + success = 0, + noResponse, + invalidFrame, + unknownFrame, + sendFailed, + transportUnavailable, + timeout, +}; + +const char* daliQueryStatusName(DaliQueryStatus status); + +struct DaliQueryResult { + DaliQueryStatus status{DaliQueryStatus::transportUnavailable}; + std::optional value; + std::vector 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 queryRaw(uint8_t addr, uint8_t cmd) const; std::optional 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 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 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 diff --git a/src/base.cpp b/src/base.cpp index 37960e3..d569a96 100644 --- a/src/base.cpp +++ b/src/base.cpp @@ -471,9 +471,12 @@ std::optional DaliBase::verifyShortAddr(int a) { } std::optional 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 DaliBase::compare(int h, int m, int l) { diff --git a/src/bridge.cpp b/src/bridge.cpp index 807ac45..37ba89e 100644 --- a/src/bridge.cpp +++ b/src/bridge.cpp @@ -1,5 +1,7 @@ #include "bridge.hpp" +#include "dali_define.hpp" + #include #include #include @@ -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(addr.value()), static_cast(cmd.value())); } else { - const auto response = - comm_.queryRaw(static_cast(addr.value()), static_cast(cmd.value())); - if (!response.has_value()) { - result.error = "no response"; + const auto response = comm_.queryRawResult(static_cast(addr.value()), + static_cast(cmd.value())); + result.metadata["queryStatus"] = daliQueryStatusName(response.status); + result.metadata["queryRawLength"] = static_cast(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(response.value()); + result.data = static_cast(response.value.value()); } break; } diff --git a/src/dali_comm.cpp b/src/dali_comm.cpp index fe8382f..f2f5b30 100644 --- a/src/dali_comm.cpp +++ b/src/dali_comm.cpp @@ -1,8 +1,11 @@ #include "dali_comm.hpp" #include "dali_define.hpp" +#include "log.hpp" #include +#include +#include #include #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(value)); + return buffer; +} + +std::string formatBytes(const std::vector& 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 readQueryResponse(const DaliComm::ReadCallback& read_cb, uint32_t timeout_ms) { if (!read_cb) return {}; @@ -40,31 +60,75 @@ std::vector readQueryResponse(const DaliComm::ReadCallback& read_cb, ui return collected; } -std::optional parseQueryResponse(const std::vector& resp, uint8_t addr) { - if (resp.empty()) return std::nullopt; +DaliQueryResult parseQueryResponse(const std::vector& resp, uint8_t addr) { + DaliQueryResult result; + result.raw = resp; + if (resp.empty()) { + result.status = DaliQueryStatus::timeout; + return result; + } // Gateway type 1 returns: 0xFF 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(resp.size() >= 2 ? resp[1] : 0xFF); + result.status = DaliQueryStatus::success; + result.value = static_cast(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 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 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 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 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; }