#pragma once #include #include #include #include #include #include #include #include #include #include "gateway_cache.hpp" #include "esp_err.h" #include "freertos/FreeRTOS.h" #include "freertos/semphr.h" #include "freertos/task.h" namespace gateway { class DaliDomainService; struct DaliRawFrame; class GatewayBridgeService; class GatewayRuntime; struct GatewayControllerConfig { uint32_t task_stack_size{12288}; UBaseType_t task_priority{5}; int color_temperature_min{2000}; int color_temperature_max{6500}; bool setup_supported{true}; bool ble_supported{false}; bool wifi_supported{true}; bool ip_router_supported{true}; bool internal_scene_supported{true}; bool internal_group_supported{true}; bool cache_supported{true}; uint32_t cache_refresh_interval_ms{120000}; uint32_t cache_refresh_idle_ms{100}; uint32_t cache_host_snooze_ms{5000}; uint32_t cache_host_echo_ms{250}; }; struct GatewayChannelSnapshot { uint8_t channel_index{0}; uint8_t gateway_id{0}; std::string name; std::string phy; uint8_t scene_mask_low{0}; uint8_t scene_mask_high{0}; uint8_t group_mask_low{0}; uint8_t group_mask_high{0}; bool allocating{false}; int last_alloc_addr{0}; bool operation_active{false}; uint8_t operation_request_id{0}; uint16_t operation_id{0}; uint8_t operation_status{0}; uint8_t operation_progress{0}; uint8_t operation_target{0}; uint8_t operation_count{0}; }; struct GatewayControllerSnapshot { bool setup_mode{false}; bool wireless_setup_mode{false}; bool ble_enabled{false}; bool wifi_enabled{false}; bool ip_router_enabled{false}; bool internal_scene_supported{false}; bool internal_group_supported{false}; std::vector channels; }; enum class GatewayCommandTransactionStatus : uint8_t { kOk = 0, kInvalidFrame = 1, kQueueRejected = 2, kTimeout = 3, kNoResponse = 4, }; struct GatewayCommandTransactionResult { GatewayCommandTransactionStatus status{GatewayCommandTransactionStatus::kNoResponse}; std::vector frames; }; class GatewayController { public: using NotificationSink = std::function& frame)>; using BleStateSink = std::function; using WifiStateSink = std::function; using GatewayNameSink = std::function; GatewayController(GatewayRuntime& runtime, DaliDomainService& dali_domain, GatewayCache& cache, GatewayControllerConfig config = {}); ~GatewayController(); esp_err_t start(); bool enqueueCommandFrame(const std::vector& frame); GatewayCommandTransactionResult transactCommandFrame( const std::vector& frame, uint32_t timeout_ms = 700, uint32_t idle_ms = 30, size_t max_response_bytes = 2048); void addNotificationSink(NotificationSink sink); void addBleStateSink(BleStateSink sink); void addWifiStateSink(WifiStateSink sink); void addGatewayNameSink(GatewayNameSink sink); void setBridgeService(GatewayBridgeService* bridge_service); bool setupMode() const; bool wirelessSetupMode() const; void setWirelessSetupMode(bool enabled); bool bleEnabled() const; bool wifiEnabled() const; bool ipRouterEnabled() const; bool rawReportingEnabled(uint8_t gateway_id) const; GatewayControllerSnapshot snapshot(); struct ParsedTlv { uint8_t type{0}; std::vector value; }; using ParsedTlvMap = std::map; private: struct ReconciliationJob { enum class Phase : uint8_t { kReloadFlags = 0, kGroups = 1, kScenes = 2, kSettings = 3, }; GatewayCacheChannelFlags flags{}; Phase phase{Phase::kReloadFlags}; std::optional target; std::vector addresses; size_t address_index{0}; uint8_t scene_id{0}; }; struct BridgeTransportRequestState { uint8_t version{0}; uint16_t payload_length{0}; uint8_t total_chunks{0}; std::map> chunks; }; struct CacheRefreshJob { TickType_t next_due_tick{0}; uint8_t short_address{0}; }; struct GatewayOperationRuntimeState { bool active{false}; bool cancel_requested{false}; uint8_t request_id{0}; uint16_t operation_id{0}; uint8_t status{0}; uint8_t progress{0}; uint8_t target{0}; uint8_t count{0}; }; struct GatewayRawReportLease { bool enabled{false}; TickType_t expires_at{0}; }; struct GatewayOperationTaskContext { GatewayController* controller{nullptr}; uint8_t gateway_id{0}; uint8_t request_id{0}; uint16_t operation_id{0}; ParsedTlvMap fields; }; struct TransactionWaiter { uint8_t gateway_id{0}; uint8_t opcode{0}; SemaphoreHandle_t signal{nullptr}; std::vector frames; size_t max_response_bytes{0}; bool overflow{false}; }; static void TaskEntry(void* arg); static void OperationTaskEntry(void* arg); void taskLoop(); void runOperationTask(GatewayOperationTaskContext* context); void dispatchCommand(const std::vector& command); void scheduleReconciliation(uint8_t gateway_id, std::optional target = std::nullopt); bool hasPendingReconciliation() const; bool cacheRefreshEnabled() const; bool cacheMaintenanceSnoozed(uint8_t gateway_id) const; bool runMaintenanceStep(); bool runReconciliationStep(uint8_t gateway_id, ReconciliationJob& job); bool runCacheRefreshStep(); void reconcileGroupStep(uint8_t gateway_id, uint8_t short_address); void reconcileSceneStep(uint8_t gateway_id, uint8_t short_address, uint8_t scene_id); void reconcileSettingsStep(uint8_t gateway_id, uint8_t short_address); bool hasGateway(uint8_t gateway_id) const; std::vector gatewayIds() const; std::optional gatewayIdForChannelNumber(uint8_t channel_number) const; std::string gatewayName(uint8_t gateway_id) const; void refreshRuntimeGatewayNames(); void publishPayload(uint8_t gateway_id, const std::vector& payload); void publishFrame(const std::vector& frame); void publishRawReportLeaseResponse(uint8_t gateway_id, uint8_t status); void publishOperationEvent(uint8_t gateway_id, uint8_t request_id, uint16_t operation_id, uint8_t event, uint8_t status, uint8_t progress, uint8_t target, uint8_t count); void publishOperationResultChunks(uint8_t gateway_id, uint8_t request_id, uint16_t operation_id, const std::vector& tlv_payload); bool transactionFrameMatches(const TransactionWaiter& waiter, const std::vector& frame) const; void captureTransactionFrame(const std::vector& frame); void handleDaliRawFrame(const DaliRawFrame& frame); bool handleApplicationControllerFrame(const DaliRawFrame& frame); std::optional applicationControllerResponse(uint8_t gateway_id, uint8_t first, uint8_t instance, uint8_t opcode) const; bool sendRawAndMirror(uint8_t gateway_id, uint8_t raw_addr, uint8_t command); bool sendExtRawAndMirror(uint8_t gateway_id, uint8_t raw_addr, uint8_t command); bool setBrightAndMirror(uint8_t gateway_id, int dec_address, uint8_t level); bool offAndMirror(uint8_t gateway_id, int dec_address); bool onAndMirror(uint8_t gateway_id, int dec_address); uint8_t resolveInternalGroupRawAddress(uint8_t gateway_id, uint8_t raw_addr); static uint8_t normalizeGroupTargetType(uint8_t target_type); static uint8_t normalizeGroupTargetValue(uint8_t target_type, uint8_t target_value); static uint8_t internalGroupRawTargetAddress(uint8_t target_type, uint8_t target_value, uint8_t raw_addr); static int internalGroupDecTargetAddress(uint8_t target_type, uint8_t target_value); static uint8_t rawArcAddressFromDec(int dec_address); static uint8_t rawCommandAddressFromDec(int dec_address); static int shortAddressFromRaw(uint8_t raw_addr); static int reverseInRange(int value, int min_value, int max_value); bool setSceneEnabled(uint8_t gateway_id, uint8_t scene_id, bool enabled); bool setSceneDetail(uint8_t gateway_id, uint8_t scene_id, uint8_t brightness, uint8_t color_mode, uint8_t data1, uint8_t data2, uint8_t data3); bool setSceneName(uint8_t gateway_id, uint8_t scene_id, std::string_view name); bool deleteScene(uint8_t gateway_id, uint8_t scene_id); std::pair sceneMask(uint8_t gateway_id); bool executeScene(uint8_t gateway_id, int short_address, uint8_t scene_id); bool setGroupEnabled(uint8_t gateway_id, uint8_t group_id, bool enabled); bool setGroupDetail(uint8_t gateway_id, uint8_t group_id, uint8_t target_type, uint8_t target_value); bool setGroupName(uint8_t gateway_id, uint8_t group_id, std::string_view name); bool deleteGroup(uint8_t gateway_id, uint8_t group_id); std::pair groupMask(uint8_t gateway_id); bool executeGroup(uint8_t gateway_id, uint8_t group_id); void handleGatewayNameCommand(uint8_t gateway_id, const std::vector& command); void handleGatewaySerialCommand(uint8_t channel_number, const std::vector& command); void publishGatewaySerialReport(); void publishGatewaySerialResponse(uint8_t status, uint8_t op, const std::vector& data); bool gatewaySerialMatches(const std::vector& command) const; void handleGatewayIdentityCommand(uint8_t gateway_id, uint8_t op); void handleAllocationCommand(uint8_t gateway_id, const std::vector& command); void handleRawReportLeaseCommand(uint8_t gateway_id, const std::vector& command); void handleGatewayOperationCommand(uint8_t gateway_id, const std::vector& command); bool operationActive(uint8_t gateway_id) const; bool operationCancelRequested(uint8_t gateway_id, uint8_t request_id) const; bool startOperation(uint8_t gateway_id, uint8_t request_id, uint16_t operation_id, ParsedTlvMap fields); void abortOperation(uint8_t gateway_id, uint8_t request_id); void finishOperation(uint8_t gateway_id, uint8_t request_id, uint8_t status, uint8_t progress, uint8_t target, uint8_t count); ParsedTlvMap parseOperationTlvs(const uint8_t* data, size_t len, bool* ok) const; void handleInternalSceneCommand(uint8_t gateway_id, const std::vector& command); void handleInternalGroupCommand(uint8_t gateway_id, const std::vector& command); void handleGatewayCacheCommand(uint8_t gateway_id, const std::vector& command); void handleBridgeTransportCommand(uint8_t gateway_id, const std::vector& command); void publishBridgeTransportResponse(uint8_t gateway_id, uint8_t version, uint8_t sequence, std::string_view response); GatewayRuntime& runtime_; DaliDomainService& dali_domain_; GatewayCache& cache_; GatewayBridgeService* bridge_service_{nullptr}; GatewayControllerConfig config_; TaskHandle_t task_handle_{nullptr}; SemaphoreHandle_t maintenance_lock_{nullptr}; SemaphoreHandle_t transaction_lock_{nullptr}; SemaphoreHandle_t operation_lock_{nullptr}; std::vector notification_sinks_; std::vector ble_state_sinks_; std::vector wifi_state_sinks_; std::vector gateway_name_sinks_; std::map bridge_transport_requests_; std::vector transaction_waiters_; std::map reconciliation_jobs_; std::map cache_refresh_jobs_; std::map operation_states_; std::map raw_report_leases_; std::atomic maintenance_activity_gateway_{-1}; bool setup_mode_{false}; bool wireless_setup_mode_{false}; bool ble_enabled_{false}; bool wifi_enabled_{false}; bool ip_router_enabled_{true}; bool application_controller_enabled_{true}; bool application_controller_power_cycle_notification_{true}; bool application_controller_power_cycle_seen_{true}; bool application_controller_reset_state_{false}; uint8_t application_controller_operating_mode_{0}; uint8_t application_controller_dtr0_{0}; uint8_t application_controller_dtr1_{0}; uint8_t application_controller_dtr2_{0}; }; } // namespace gateway