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 <optional>
|
||||||
#include <vector>
|
#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).
|
// Lightweight communicator for DALI gateway type 1 (USB UART new frame format).
|
||||||
// Frames:
|
// Frames:
|
||||||
// - Send: [0x10, addr, cmd]
|
// - Send: [0x10, addr, cmd]
|
||||||
@@ -43,6 +63,8 @@ class DaliComm {
|
|||||||
bool sendExtRawNew(uint8_t addr, uint8_t cmd) const;
|
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> queryRaw(uint8_t addr, uint8_t cmd) const;
|
||||||
std::optional<uint8_t> queryRawNew(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;
|
bool send(int dec_addr, uint8_t cmd) const;
|
||||||
std::optional<uint8_t> query(int dec_addr, uint8_t cmd) const;
|
std::optional<uint8_t> query(int dec_addr, uint8_t cmd) const;
|
||||||
bool getBusStatus() 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,
|
// 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.
|
// except COMPARE where 0xFD collisions are treated as a positive match.
|
||||||
std::optional<uint8_t> queryCmd(uint8_t addr, uint8_t cmd) const;
|
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).
|
// Helpers to mirror Dart address conversion (DEC short address -> DALI odd/even encoded).
|
||||||
static uint8_t toCmdAddr(int dec_addr); // odd address for commands
|
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() {
|
std::optional<bool> DaliBase::compareAddress() {
|
||||||
const auto ret = queryCmd(DALI_CMD_SPECIAL_COMPARE, DALI_CMD_OFF);
|
const auto ret = comm_.queryCmdResult(DALI_CMD_SPECIAL_COMPARE, DALI_CMD_OFF);
|
||||||
if (!ret.has_value()) return std::nullopt;
|
if (ret.status == DaliQueryStatus::noResponse || ret.status == DaliQueryStatus::timeout) {
|
||||||
return ret.value() >= 0;
|
return false;
|
||||||
|
}
|
||||||
|
if (!ret.hasValue()) return std::nullopt;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<bool> DaliBase::compare(int h, int m, int l) {
|
std::optional<bool> DaliBase::compare(int h, int m, int l) {
|
||||||
|
|||||||
+19
-6
@@ -1,5 +1,7 @@
|
|||||||
#include "bridge.hpp"
|
#include "bridge.hpp"
|
||||||
|
|
||||||
|
#include "dali_define.hpp"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <initializer_list>
|
#include <initializer_list>
|
||||||
@@ -121,6 +123,12 @@ DaliBridgeRequest bridgeRequestFromValue(const DaliValue::Object& object,
|
|||||||
|
|
||||||
bool isShortAddress(int address) { return address >= 0 && address <= 63; }
|
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) {
|
bool isReadOperation(BridgeOperation operation) {
|
||||||
switch (operation) {
|
switch (operation) {
|
||||||
case BridgeOperation::query:
|
case BridgeOperation::query:
|
||||||
@@ -504,7 +512,8 @@ DaliBridgeResult DaliBridgeEngine::executeResolved(const DaliBridgeRequest& requ
|
|||||||
result.error = "invalid addr/cmd";
|
result.error = "invalid addr/cmd";
|
||||||
return result;
|
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";
|
result.error = "query target must be a short address";
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -513,14 +522,18 @@ DaliBridgeResult DaliBridgeEngine::executeResolved(const DaliBridgeRequest& requ
|
|||||||
} else if (operation == BridgeOperation::sendExt) {
|
} else if (operation == BridgeOperation::sendExt) {
|
||||||
result.ok = comm_.sendExtRaw(static_cast<uint8_t>(addr.value()), static_cast<uint8_t>(cmd.value()));
|
result.ok = comm_.sendExtRaw(static_cast<uint8_t>(addr.value()), static_cast<uint8_t>(cmd.value()));
|
||||||
} else {
|
} else {
|
||||||
const auto response =
|
const auto response = comm_.queryRawResult(static_cast<uint8_t>(addr.value()),
|
||||||
comm_.queryRaw(static_cast<uint8_t>(addr.value()), static_cast<uint8_t>(cmd.value()));
|
static_cast<uint8_t>(cmd.value()));
|
||||||
if (!response.has_value()) {
|
result.metadata["queryStatus"] = daliQueryStatusName(response.status);
|
||||||
result.error = "no response";
|
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;
|
return result;
|
||||||
}
|
}
|
||||||
result.ok = true;
|
result.ok = true;
|
||||||
result.data = static_cast<int>(response.value());
|
result.data = static_cast<int>(response.value.value());
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
+103
-12
@@ -1,8 +1,11 @@
|
|||||||
#include "dali_comm.hpp"
|
#include "dali_comm.hpp"
|
||||||
|
|
||||||
#include "dali_define.hpp"
|
#include "dali_define.hpp"
|
||||||
|
#include "log.hpp"
|
||||||
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <string>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
#ifdef ESP_PLATFORM
|
#ifdef ESP_PLATFORM
|
||||||
@@ -14,6 +17,23 @@ namespace {
|
|||||||
|
|
||||||
constexpr uint32_t kQueryTimeoutMs = 100;
|
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) {
|
std::vector<uint8_t> readQueryResponse(const DaliComm::ReadCallback& read_cb, uint32_t timeout_ms) {
|
||||||
if (!read_cb) return {};
|
if (!read_cb) return {};
|
||||||
|
|
||||||
@@ -40,31 +60,75 @@ std::vector<uint8_t> readQueryResponse(const DaliComm::ReadCallback& read_cb, ui
|
|||||||
return collected;
|
return collected;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<uint8_t> parseQueryResponse(const std::vector<uint8_t>& resp, uint8_t addr) {
|
DaliQueryResult parseQueryResponse(const std::vector<uint8_t>& resp, uint8_t addr) {
|
||||||
if (resp.empty()) return std::nullopt;
|
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;
|
// 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.
|
// 0xFD xx on invalid frame. COMPARE is special: collisions still mean a match.
|
||||||
if (resp[0] == 0xFE) {
|
if (resp[0] == 0xFE) {
|
||||||
return std::nullopt;
|
result.status = DaliQueryStatus::noResponse;
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
if (resp[0] == 0xFD) {
|
if (resp[0] == 0xFD) {
|
||||||
if (addr == DALI_CMD_SPECIAL_COMPARE) {
|
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) {
|
if (resp.size() == 1) {
|
||||||
return resp[0];
|
result.status = DaliQueryStatus::success;
|
||||||
|
result.value = resp[0];
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
if (resp[0] == 0xFF && resp.size() >= 2) {
|
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
|
} // 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,
|
DaliComm::DaliComm(SendCallback send_cb,
|
||||||
ReadCallback read_cb,
|
ReadCallback read_cb,
|
||||||
TransactCallback transact_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);
|
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); }
|
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 {
|
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 {
|
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};
|
const std::vector<uint8_t> frame{0x12, addr, cmd};
|
||||||
|
DaliQueryResult result;
|
||||||
if (send_ && read_) {
|
if (send_ && read_) {
|
||||||
prepareForQuery();
|
prepareForQuery();
|
||||||
if (!writeFrame(frame)) return std::nullopt;
|
if (!writeFrame(frame)) {
|
||||||
return parseQueryResponse(readQueryResponse(read_, kQueryTimeoutMs), addr);
|
result.status = DaliQueryStatus::sendFailed;
|
||||||
|
logQueryResult(addr, cmd, result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
result = parseQueryResponse(readQueryResponse(read_, kQueryTimeoutMs), addr);
|
||||||
|
logQueryResult(addr, cmd, result);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
prepareForQuery();
|
prepareForQuery();
|
||||||
if (!transact_) return std::nullopt;
|
if (!transact_) {
|
||||||
return parseQueryResponse(transact_(frame.data(), frame.size()), addr);
|
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