c5a079e779
- Added DaliGatewayCache class for managing DALI device states, including address states, presence, and group statuses. - Implemented methods for configuring cache, enabling/disabling features, and setting status update callbacks. - Introduced state encoding/decoding for persistence and added support for CSV parsing. - Created mutation handling for runtime status updates, group masks, scene levels, and settings. - Developed reconciliation logic to handle incoming frames and classify mutations. - Added tests to validate cache functionality, persistence, and DALI protocol handling. Signed-off-by: Tony <tonylu@tony-cloud.com>
120 lines
4.7 KiB
C++
120 lines
4.7 KiB
C++
#include "dali_gateway.hpp"
|
|
|
|
#include <array>
|
|
#include <cassert>
|
|
#include <map>
|
|
#include <utility>
|
|
|
|
int main() {
|
|
DaliGatewayCache cache;
|
|
std::map<std::pair<uint8_t, uint8_t>, std::string> persisted;
|
|
int commits = 0;
|
|
cache.setPersistenceCallbacks({
|
|
[&](uint8_t channel, uint8_t address) -> std::optional<std::string> {
|
|
const auto it = persisted.find({channel, address});
|
|
return it == persisted.end() ? std::nullopt
|
|
: std::optional<std::string>(it->second);
|
|
},
|
|
[&](uint8_t channel, uint8_t address, const std::optional<std::string>& payload) {
|
|
if (payload.has_value()) {
|
|
persisted[{channel, address}] = *payload;
|
|
} else {
|
|
persisted.erase({channel, address});
|
|
}
|
|
return true;
|
|
},
|
|
[&]() {
|
|
++commits;
|
|
return true;
|
|
},
|
|
});
|
|
|
|
int status_updates = 0;
|
|
DaliGatewayStatusUpdate last_update;
|
|
cache.setStatusUpdateCallback([&](const DaliGatewayStatusUpdate& update) {
|
|
++status_updates;
|
|
last_update = update;
|
|
});
|
|
|
|
assert(cache.setGroupMask(7, 4, 0x0001));
|
|
assert(cache.mirrorForwardFrame(7, 0x80, 42));
|
|
assert(status_updates == 1);
|
|
assert(last_update.channel == 7);
|
|
assert(last_update.target.kind == DaliGatewayTargetKind::group);
|
|
assert(last_update.target.value == 0);
|
|
assert(last_update.status.actualLevel == 42);
|
|
assert(last_update.affectedShortAddresses == std::vector<uint8_t>{4});
|
|
assert(cache.groupStatus(7, 0).actualLevel == 42);
|
|
assert(cache.addressState(7, 4).status.actualLevel == 42);
|
|
|
|
// The command-state cache tracks DTR-dependent settings and applies a
|
|
// broadcast command to every state known by the adapter.
|
|
cache.mirrorForwardFrame(7, 0xA3, 12);
|
|
assert(cache.mirrorForwardFrame(7, 0x09, 0x2B));
|
|
assert(cache.addressState(7, 4).settings.minLevel == 12);
|
|
assert(cache.mirrorForwardFrame(7, 0xFF, 0x06));
|
|
assert(cache.addressState(7, 4).status.actualLevel == 12);
|
|
|
|
assert(cache.observeForwardFrame(7, 0x09, 0x60, DaliGatewayFrameOrigin::outsideBus));
|
|
assert(cache.pendingChannelFlags(7).needUpdateGroup);
|
|
assert(cache.flush());
|
|
assert(commits == 1);
|
|
assert(!persisted.empty());
|
|
|
|
DaliGatewayCache restored;
|
|
restored.setPersistenceCallbacks({
|
|
[&](uint8_t channel, uint8_t address) -> std::optional<std::string> {
|
|
const auto it = persisted.find({channel, address});
|
|
return it == persisted.end() ? std::nullopt
|
|
: std::optional<std::string>(it->second);
|
|
},
|
|
{},
|
|
{},
|
|
});
|
|
assert(restored.preloadChannel(7));
|
|
const auto restored_state = restored.addressState(7, 4);
|
|
assert(restored_state.groupMaskKnown);
|
|
assert(restored_state.groupMask == 0x0001);
|
|
assert(restored_state.settings.minLevel == 12);
|
|
assert(restored_state.status.stale);
|
|
|
|
DaliApplicationControllerConfig config;
|
|
config.shortAddress = 3;
|
|
config.randomAddress = 0x123456;
|
|
DaliApplicationController controller(config);
|
|
|
|
const auto capabilities = controller.handleForwardFrame({0x07, 0xFE, 0x46});
|
|
assert(capabilities.backwardFrame == 0x01);
|
|
const auto status = controller.handleForwardFrame({0x07, 0xFE, 0x30});
|
|
assert(status.backwardFrame == 0x28);
|
|
|
|
// Commission short address 5 by selecting the controller's random address.
|
|
controller.handleForwardFrame({0xC1, 0x01, 0xFF}, true);
|
|
controller.handleForwardFrame({0xC1, 0x05, 0x12});
|
|
controller.handleForwardFrame({0xC1, 0x06, 0x34});
|
|
controller.handleForwardFrame({0xC1, 0x07, 0x56});
|
|
const auto compared = controller.handleForwardFrame({0xC1, 0x03, 0x00});
|
|
assert(compared.backwardFrame == 0xFF);
|
|
const auto programmed = controller.handleForwardFrame({0xC1, 0x08, 0x0B});
|
|
assert(programmed.shortAddressChanged);
|
|
assert(programmed.shortAddress == 5);
|
|
const auto verified = controller.handleForwardFrame({0xC1, 0x09, 0x0B});
|
|
assert(verified.backwardFrame == 0xFF);
|
|
controller.handleForwardFrame({0xC1, 0x30, 0x5A});
|
|
const auto dtr0 = controller.handleForwardFrame({0x0B, 0xFE, 0x36});
|
|
assert(dtr0.backwardFrame == 0x5A);
|
|
|
|
const auto before_repeat = controller.handleForwardFrame({0x0B, 0xFE, 0x00});
|
|
assert(!before_repeat.identifyRequested);
|
|
const auto identity = controller.handleForwardFrame({0x0B, 0xFE, 0x00}, true);
|
|
assert(identity.identifyRequested);
|
|
|
|
DaliApplicationControllerConfig unaddressed_config;
|
|
unaddressed_config.shortAddress.reset();
|
|
DaliApplicationController unaddressed(unaddressed_config);
|
|
const auto missing_short_address = unaddressed.handleForwardFrame({0xFF, 0xFE, 0x33});
|
|
assert(missing_short_address.backwardFrame == 0xFF);
|
|
const auto unaddressed_status = unaddressed.handleForwardFrame({0xFF, 0xFE, 0x30});
|
|
assert(unaddressed_status.backwardFrame == 0x2C);
|
|
}
|