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:
@@ -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
@@ -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
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user