#include "dali_gateway.hpp" #include #include #include #include int main() { DaliGatewayCache cache; std::map, std::string> persisted; int commits = 0; cache.setPersistenceCallbacks({ [&](uint8_t channel, uint8_t address) -> std::optional { const auto it = persisted.find({channel, address}); return it == persisted.end() ? std::nullopt : std::optional(it->second); }, [&](uint8_t channel, uint8_t address, const std::optional& payload) { if (payload.has_value()) { persisted[{channel, address}] = *payload; } else { persisted.erase({channel, address}); } return true; }, [&]() { ++commits; return true; }, }); int status_updates = 0; int observer_updates = 0; DaliGatewayStatusUpdate last_update; cache.setStatusUpdateCallback([&](const DaliGatewayStatusUpdate& update) { ++status_updates; last_update = update; }); cache.addStatusUpdateCallback( [&](const DaliGatewayStatusUpdate&) { ++observer_updates; }); assert(cache.setGroupMask(7, 4, 0x0001)); assert(cache.setCapabilities(7, 4, 0x0180, 0x07)); assert(cache.mirrorForwardFrame(7, 0x80, 42)); assert(status_updates == 1); assert(observer_updates == status_updates); 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{4}); assert(cache.groupStatus(7, 0).actualLevel == 42); assert(cache.addressState(7, 4).status.actualLevel == 42); // Replacing the owning callback must not remove additive adapter observers. cache.setStatusUpdateCallback({}); assert(cache.setActualLevel(7, 4, 43)); assert(status_updates == 1); assert(observer_updates == 2); // 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 { const auto it = persisted.find({channel, address}); return it == persisted.end() ? std::nullopt : std::optional(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); assert(restored_state.daliDeviceTypeMask == 0x0180); assert(restored_state.dt8ColorTypeFeatures == 0x07); // Version-one records remain readable and retain their original fields. persisted[{8, 2}] = "1,129,9,2,111,0,0,0,4,254,0,0,0"; assert(restored.preloadChannel(8)); const auto migrated_state = restored.addressState(8, 2); assert(migrated_state.groupMaskKnown); assert(migrated_state.groupMask == 2); assert(migrated_state.settings.minLevel == 4); assert(!migrated_state.daliDeviceTypeMask.has_value()); 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); }