Enhance DALI device type discovery and query handling
- Introduce DaliDeviceTypeDiscovery struct for managing device type queries. - Implement discoverDeviceTypes method in DaliBase for improved device type detection. - Update DaliComm to handle query responses more robustly, including handling multiple device replies. - Add setDetectedDeviceTypes method in DaliDevice to manage detected device types and capabilities.
This commit is contained in:
+78
-12
@@ -1,5 +1,7 @@
|
||||
#include "dali_comm.hpp"
|
||||
|
||||
#include "dali_define.hpp"
|
||||
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
|
||||
@@ -8,6 +10,61 @@
|
||||
#include "freertos/task.h"
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr uint32_t kQueryTimeoutMs = 100;
|
||||
|
||||
std::vector<uint8_t> readQueryResponse(const DaliComm::ReadCallback& read_cb, uint32_t timeout_ms) {
|
||||
if (!read_cb) return {};
|
||||
|
||||
const auto deadline = std::chrono::steady_clock::now() + std::chrono::milliseconds(timeout_ms);
|
||||
std::vector<uint8_t> collected;
|
||||
while (std::chrono::steady_clock::now() < deadline && collected.size() < 2) {
|
||||
const auto remaining =
|
||||
std::chrono::duration_cast<std::chrono::milliseconds>(deadline - std::chrono::steady_clock::now())
|
||||
.count();
|
||||
if (remaining <= 0) break;
|
||||
|
||||
const auto chunk = read_cb(2 - collected.size(), static_cast<uint32_t>(remaining));
|
||||
if (chunk.empty()) break;
|
||||
|
||||
const size_t needed = 2 - collected.size();
|
||||
const size_t take = std::min(needed, chunk.size());
|
||||
collected.insert(collected.end(), chunk.begin(), chunk.begin() + static_cast<std::ptrdiff_t>(take));
|
||||
|
||||
// Non-prefixed one-byte replies are complete as soon as they arrive.
|
||||
if (collected.size() == 1 && collected[0] != 0xFF && collected[0] != 0xFE && collected[0] != 0xFD) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return collected;
|
||||
}
|
||||
|
||||
std::optional<uint8_t> parseQueryResponse(const std::vector<uint8_t>& resp, uint8_t addr) {
|
||||
if (resp.empty()) return std::nullopt;
|
||||
|
||||
// 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;
|
||||
}
|
||||
if (resp[0] == 0xFD) {
|
||||
if (addr == DALI_CMD_SPECIAL_COMPARE) {
|
||||
return static_cast<uint8_t>(resp.size() >= 2 ? resp[1] : 0xFF);
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
if (resp.size() == 1) {
|
||||
return resp[0];
|
||||
}
|
||||
if (resp[0] == 0xFF && resp.size() >= 2) {
|
||||
return resp[1];
|
||||
}
|
||||
return resp.back();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
DaliComm::DaliComm(SendCallback send_cb,
|
||||
ReadCallback read_cb,
|
||||
TransactCallback transact_cb,
|
||||
@@ -51,12 +108,15 @@ int DaliComm::checkGatewayType(int gateway) const {
|
||||
const std::vector<uint8_t> newProbe{0x28, 0x01, static_cast<uint8_t>(gateway), 0x11, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0xFF};
|
||||
|
||||
prepareForQuery();
|
||||
const auto usbResp = transact_(usbProbe.data(), usbProbe.size());
|
||||
if (!usbResp.empty() && usbResp[0] > 0) return 1;
|
||||
|
||||
prepareForQuery();
|
||||
const auto legacyResp = transact_(legacyProbe.data(), legacyProbe.size());
|
||||
if (legacyResp.size() >= 2 && legacyResp[0] == gateway) return 2;
|
||||
|
||||
prepareForQuery();
|
||||
const auto newResp = transact_(newProbe.data(), newProbe.size());
|
||||
if (newResp.size() >= 2 && newResp[0] == gateway) return 3;
|
||||
|
||||
@@ -114,6 +174,16 @@ bool DaliComm::writeFrame(const std::vector<uint8_t>& frame) const {
|
||||
return send_(frame.data(), frame.size());
|
||||
}
|
||||
|
||||
void DaliComm::prepareForQuery() const {
|
||||
if (!read_) return;
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
const auto data = read_(64, 1);
|
||||
if (data.empty()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DaliComm::sleepMs(uint32_t ms) const {
|
||||
if (delay_) {
|
||||
delay_(ms);
|
||||
@@ -139,18 +209,14 @@ bool DaliComm::sendExtCmd(uint8_t addr, uint8_t cmd) const {
|
||||
}
|
||||
|
||||
std::optional<uint8_t> DaliComm::queryCmd(uint8_t addr, uint8_t cmd) const {
|
||||
if (!transact_) return std::nullopt;
|
||||
const std::vector<uint8_t> frame{0x12, addr, cmd};
|
||||
const auto resp = transact_(frame.data(), frame.size());
|
||||
if (resp.empty()) return std::nullopt;
|
||||
if (send_ && read_) {
|
||||
prepareForQuery();
|
||||
if (!writeFrame(frame)) return std::nullopt;
|
||||
return parseQueryResponse(readQueryResponse(read_, kQueryTimeoutMs), addr);
|
||||
}
|
||||
|
||||
// Gateway type 1 returns: 0xFF <data> on success; 0xFE no response; 0xFD invalid frame.
|
||||
if (resp.size() == 1) {
|
||||
if (resp[0] == 0xFE || resp[0] == 0xFD) return std::nullopt;
|
||||
return resp[0];
|
||||
}
|
||||
if (resp[0] == 0xFF && resp.size() >= 2) {
|
||||
return resp[1];
|
||||
}
|
||||
return resp.back();
|
||||
prepareForQuery();
|
||||
if (!transact_) return std::nullopt;
|
||||
return parseQueryResponse(transact_(frame.data(), frame.size()), addr);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user