4553ed32e7
Signed-off-by: Tony <tonylu@tony-cloud.com>
372 lines
15 KiB
C++
372 lines
15 KiB
C++
#pragma once
|
|
|
|
#include "bridge.hpp"
|
|
#include "model_value.hpp"
|
|
|
|
#include "esp_err.h"
|
|
#include "freertos/FreeRTOS.h"
|
|
#include "freertos/semphr.h"
|
|
#include "freertos/task.h"
|
|
#include "lwip/sockets.h"
|
|
|
|
#include <atomic>
|
|
#include <array>
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
#include <functional>
|
|
#include <map>
|
|
#include <memory>
|
|
#include <optional>
|
|
#include <set>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
namespace gateway {
|
|
|
|
namespace openknx {
|
|
class EtsDeviceRuntime;
|
|
}
|
|
|
|
constexpr uint16_t kGatewayKnxDefaultUdpPort = 3671;
|
|
constexpr const char* kGatewayKnxDefaultMulticastAddress = "224.0.23.12";
|
|
constexpr uint32_t kGatewayKnxDefaultTpBaudrate = 19200;
|
|
|
|
struct GatewayKnxTpUartConfig {
|
|
int uart_port{1};
|
|
int tx_pin{-1};
|
|
int rx_pin{-1};
|
|
uint32_t baudrate{kGatewayKnxDefaultTpBaudrate};
|
|
size_t rx_buffer_size{1024};
|
|
size_t tx_buffer_size{1024};
|
|
uint32_t read_timeout_ms{20};
|
|
bool nine_bit_mode{true};
|
|
};
|
|
|
|
enum class GatewayKnxMappingMode : uint8_t {
|
|
kFormula = 0,
|
|
kGwReg1Direct = 1,
|
|
kManual = 2,
|
|
kEtsDatabase = 3,
|
|
};
|
|
|
|
struct GatewayKnxEtsAssociation {
|
|
uint16_t group_address{0};
|
|
uint16_t group_object_number{0};
|
|
};
|
|
|
|
struct GatewayKnxConfig {
|
|
bool dali_router_enabled{true};
|
|
bool ip_router_enabled{false};
|
|
bool tunnel_enabled{true};
|
|
bool multicast_enabled{true};
|
|
bool ets_database_enabled{true};
|
|
GatewayKnxMappingMode mapping_mode{GatewayKnxMappingMode::kFormula};
|
|
uint8_t main_group{0};
|
|
uint16_t udp_port{kGatewayKnxDefaultUdpPort};
|
|
std::string multicast_address{kGatewayKnxDefaultMulticastAddress};
|
|
uint16_t ip_interface_individual_address{0xff01};
|
|
uint16_t individual_address{0xfffe};
|
|
int programming_button_gpio{-1};
|
|
bool programming_button_active_low{true};
|
|
int programming_led_gpio{-1};
|
|
bool programming_led_active_high{true};
|
|
std::vector<GatewayKnxEtsAssociation> ets_associations;
|
|
GatewayKnxTpUartConfig tp_uart;
|
|
};
|
|
|
|
enum class GatewayKnxDaliDataType : uint8_t {
|
|
kUnknown = 0,
|
|
kSwitch = 1,
|
|
kBrightness = 2,
|
|
kColorTemperature = 3,
|
|
kRgb = 4,
|
|
};
|
|
|
|
enum class GatewayKnxDaliTargetKind : uint8_t {
|
|
kNone = 0,
|
|
kBroadcast = 1,
|
|
kShortAddress = 2,
|
|
kGroup = 3,
|
|
};
|
|
|
|
struct GatewayKnxDaliTarget {
|
|
GatewayKnxDaliTargetKind kind{GatewayKnxDaliTargetKind::kNone};
|
|
int address{-1};
|
|
};
|
|
|
|
struct GatewayKnxDaliBinding {
|
|
uint16_t group_address{0};
|
|
uint8_t main_group{0};
|
|
uint8_t middle_group{0};
|
|
uint8_t sub_group{0};
|
|
GatewayKnxMappingMode mapping_mode{GatewayKnxMappingMode::kFormula};
|
|
int group_object_number{-1};
|
|
int channel_index{-1};
|
|
std::string address;
|
|
std::string name;
|
|
std::string object_role;
|
|
std::string datapoint_type;
|
|
GatewayKnxDaliDataType data_type{GatewayKnxDaliDataType::kUnknown};
|
|
GatewayKnxDaliTarget target;
|
|
};
|
|
|
|
struct GatewayKnxCommissioningBallast {
|
|
uint8_t high{0};
|
|
uint8_t middle{0};
|
|
uint8_t low{0};
|
|
uint8_t short_address{0xff};
|
|
};
|
|
|
|
std::optional<GatewayKnxConfig> GatewayKnxConfigFromValue(const DaliValue* value);
|
|
DaliValue GatewayKnxConfigToValue(const GatewayKnxConfig& config);
|
|
bool GatewayKnxConfigUsesTpUart(const GatewayKnxConfig& config);
|
|
|
|
const char* GatewayKnxMappingModeToString(GatewayKnxMappingMode mode);
|
|
GatewayKnxMappingMode GatewayKnxMappingModeFromString(const std::string& value);
|
|
const char* GatewayKnxDataTypeToString(GatewayKnxDaliDataType data_type);
|
|
const char* GatewayKnxTargetKindToString(GatewayKnxDaliTargetKind kind);
|
|
std::optional<GatewayKnxDaliDataType> GatewayKnxDaliDataTypeForMiddleGroup(
|
|
uint8_t middle_group);
|
|
std::optional<GatewayKnxDaliTarget> GatewayKnxDaliTargetForSubgroup(uint8_t sub_group);
|
|
uint16_t GatewayKnxGroupAddress(uint8_t main_group, uint8_t middle_group,
|
|
uint8_t sub_group);
|
|
std::string GatewayKnxGroupAddressString(uint16_t group_address);
|
|
|
|
class GatewayKnxBridge {
|
|
public:
|
|
explicit GatewayKnxBridge(DaliBridgeEngine& engine);
|
|
|
|
void setConfig(const GatewayKnxConfig& config);
|
|
const GatewayKnxConfig& config() const;
|
|
size_t etsBindingCount() const;
|
|
|
|
std::vector<GatewayKnxDaliBinding> describeDaliBindings() const;
|
|
bool matchesCemiFrame(const uint8_t* data, size_t len) const;
|
|
bool matchesGroupAddress(uint16_t group_address) const;
|
|
DaliBridgeResult handleCemiFrame(const uint8_t* data, size_t len);
|
|
DaliBridgeResult handleGroupWrite(uint16_t group_address, const uint8_t* data,
|
|
size_t len);
|
|
bool handleFunctionPropertyCommand(uint8_t object_index, uint8_t property_id,
|
|
const uint8_t* data, size_t len,
|
|
std::vector<uint8_t>* response);
|
|
bool handleFunctionPropertyState(uint8_t object_index, uint8_t property_id,
|
|
const uint8_t* data, size_t len,
|
|
std::vector<uint8_t>* response);
|
|
|
|
private:
|
|
DaliBridgeResult executeForDecodedWrite(uint16_t group_address,
|
|
GatewayKnxDaliDataType data_type,
|
|
GatewayKnxDaliTarget target,
|
|
const uint8_t* data, size_t len);
|
|
DaliBridgeResult executeEtsBindings(uint16_t group_address,
|
|
const std::vector<GatewayKnxDaliBinding>& bindings,
|
|
const uint8_t* data, size_t len);
|
|
void rebuildEtsBindings();
|
|
|
|
bool handleReg1TypeCommand(const uint8_t* data, size_t len,
|
|
std::vector<uint8_t>* response);
|
|
bool handleReg1ScanCommand(const uint8_t* data, size_t len,
|
|
std::vector<uint8_t>* response);
|
|
bool handleReg1AssignCommand(const uint8_t* data, size_t len,
|
|
std::vector<uint8_t>* response);
|
|
bool handleReg1EvgWriteCommand(const uint8_t* data, size_t len,
|
|
std::vector<uint8_t>* response);
|
|
bool handleReg1EvgReadCommand(const uint8_t* data, size_t len,
|
|
std::vector<uint8_t>* response);
|
|
bool handleReg1SetSceneCommand(const uint8_t* data, size_t len,
|
|
std::vector<uint8_t>* response);
|
|
bool handleReg1GetSceneCommand(const uint8_t* data, size_t len,
|
|
std::vector<uint8_t>* response);
|
|
bool handleReg1IdentifyCommand(const uint8_t* data, size_t len,
|
|
std::vector<uint8_t>* response);
|
|
bool handleReg1ScanState(const uint8_t* data, size_t len,
|
|
std::vector<uint8_t>* response);
|
|
bool handleReg1AssignState(const uint8_t* data, size_t len,
|
|
std::vector<uint8_t>* response);
|
|
bool handleReg1FoundEvgsState(const uint8_t* data, size_t len,
|
|
std::vector<uint8_t>* response);
|
|
|
|
DaliBridgeEngine& engine_;
|
|
GatewayKnxConfig config_;
|
|
std::map<uint16_t, std::vector<GatewayKnxDaliBinding>> ets_bindings_by_group_address_;
|
|
bool commissioning_scan_done_{true};
|
|
bool commissioning_assign_done_{true};
|
|
std::vector<GatewayKnxCommissioningBallast> commissioning_found_ballasts_;
|
|
};
|
|
|
|
class GatewayKnxTpIpRouter {
|
|
public:
|
|
using CemiFrameHandler = std::function<DaliBridgeResult(const uint8_t* data, size_t len)>;
|
|
using GroupWriteHandler = std::function<DaliBridgeResult(uint16_t group_address,
|
|
const uint8_t* data,
|
|
size_t len)>;
|
|
|
|
GatewayKnxTpIpRouter(GatewayKnxBridge& bridge, CemiFrameHandler handler,
|
|
std::string openknx_namespace = "openknx");
|
|
~GatewayKnxTpIpRouter();
|
|
|
|
void setConfig(const GatewayKnxConfig& config);
|
|
void setCommissioningOnly(bool enabled);
|
|
void setGroupWriteHandler(GroupWriteHandler handler);
|
|
const GatewayKnxConfig& config() const;
|
|
bool tpUartOnline() const;
|
|
bool programmingMode();
|
|
esp_err_t setProgrammingMode(bool enabled);
|
|
esp_err_t toggleProgrammingMode();
|
|
|
|
esp_err_t start(uint32_t task_stack_size, UBaseType_t task_priority);
|
|
esp_err_t stop();
|
|
bool started() const;
|
|
const std::string& lastError() const;
|
|
bool publishDaliStatus(const GatewayKnxDaliTarget& target, uint8_t actual_level);
|
|
|
|
private:
|
|
static constexpr size_t kMaxTunnelClients = 16;
|
|
static constexpr size_t kMaxTcpClients = 4;
|
|
|
|
struct TcpClient {
|
|
int sock{-1};
|
|
::sockaddr_in remote{};
|
|
std::vector<uint8_t> rx_buffer;
|
|
TickType_t last_activity_tick{0};
|
|
};
|
|
|
|
struct TunnelClient {
|
|
bool connected{false};
|
|
uint8_t channel_id{0};
|
|
uint8_t connection_type{0};
|
|
uint8_t received_sequence{255};
|
|
uint8_t send_sequence{0};
|
|
uint16_t individual_address{0};
|
|
int tcp_sock{-1};
|
|
TickType_t last_activity_tick{0};
|
|
::sockaddr_in control_remote{};
|
|
::sockaddr_in data_remote{};
|
|
};
|
|
|
|
static void TaskEntry(void* arg);
|
|
|
|
esp_err_t initializeRuntime();
|
|
void taskLoop();
|
|
void finishTask();
|
|
void closeSockets();
|
|
bool configureSocket();
|
|
void handleTcpAccept();
|
|
void handleTcpClient(TcpClient& client);
|
|
void closeTcpClient(TcpClient& client);
|
|
bool configureTpUart();
|
|
bool initializeTpUart();
|
|
bool configureProgrammingGpio();
|
|
void refreshNetworkInterfaces(bool force_log = false);
|
|
void handleUdpDatagram(const uint8_t* data, size_t len, const ::sockaddr_in& remote);
|
|
void handleSearchRequest(uint16_t service, const uint8_t* body, size_t len,
|
|
const ::sockaddr_in& remote);
|
|
void handleDescriptionRequest(const uint8_t* body, size_t len,
|
|
const ::sockaddr_in& remote);
|
|
void handleRoutingIndication(const uint8_t* body, size_t len);
|
|
void handleTunnellingRequest(const uint8_t* body, size_t len, const ::sockaddr_in& remote);
|
|
void handleDeviceConfigurationRequest(const uint8_t* body, size_t len,
|
|
const ::sockaddr_in& remote);
|
|
void handleConnectRequest(const uint8_t* body, size_t len, const ::sockaddr_in& remote);
|
|
void handleConnectionStateRequest(const uint8_t* body, size_t len,
|
|
const ::sockaddr_in& remote);
|
|
void handleDisconnectRequest(const uint8_t* body, size_t len, const ::sockaddr_in& remote);
|
|
void handleSecureService(uint16_t service, const uint8_t* body, size_t len,
|
|
const ::sockaddr_in& remote);
|
|
void sendTunnellingAck(uint8_t channel_id, uint8_t sequence, uint8_t status,
|
|
const ::sockaddr_in& remote);
|
|
void sendDeviceConfigurationAck(uint8_t channel_id, uint8_t sequence, uint8_t status,
|
|
const ::sockaddr_in& remote);
|
|
void sendConnectionHeaderAck(uint16_t service, uint8_t channel_id, uint8_t sequence,
|
|
uint8_t status, const ::sockaddr_in& remote);
|
|
void sendSecureSessionStatus(uint8_t status, const ::sockaddr_in& remote);
|
|
void sendTunnelIndication(const uint8_t* data, size_t len);
|
|
void sendTunnelIndicationToClient(TunnelClient& client, const uint8_t* data, size_t len);
|
|
void sendConnectionStateResponse(uint8_t channel_id, uint8_t status,
|
|
const ::sockaddr_in& remote);
|
|
void sendDisconnectResponse(uint8_t channel_id, uint8_t status,
|
|
const ::sockaddr_in& remote);
|
|
void sendConnectResponse(uint8_t channel_id, uint8_t status,
|
|
const ::sockaddr_in& remote, uint8_t connection_type,
|
|
uint16_t tunnel_address);
|
|
void sendRoutingIndication(const uint8_t* data, size_t len);
|
|
void sendSearchResponse(uint16_t service, const ::sockaddr_in& remote,
|
|
const std::set<uint8_t>& requested_dibs = {});
|
|
void sendDescriptionResponse(const ::sockaddr_in& remote);
|
|
bool sendPacket(const std::vector<uint8_t>& packet, const ::sockaddr_in& remote) const;
|
|
bool sendPacketToTunnelClient(const TunnelClient& client,
|
|
const std::vector<uint8_t>& packet) const;
|
|
bool currentTransportAllowsTcpHpai() const;
|
|
std::optional<std::array<uint8_t, 8>> localHpaiForRemote(const ::sockaddr_in& remote,
|
|
bool tcp = false) const;
|
|
std::vector<uint8_t> buildDeviceInfoDib(const ::sockaddr_in& remote) const;
|
|
std::vector<uint8_t> buildSupportedServiceDib() const;
|
|
std::vector<uint8_t> buildExtendedDeviceInfoDib() const;
|
|
std::vector<uint8_t> buildIpConfigDib(const ::sockaddr_in& remote, bool current) const;
|
|
std::vector<uint8_t> buildKnxAddressesDib() const;
|
|
std::vector<uint8_t> buildTunnelingInfoDib() const;
|
|
TunnelClient* findTunnelClient(uint8_t channel_id);
|
|
const TunnelClient* findTunnelClient(uint8_t channel_id) const;
|
|
TunnelClient* allocateTunnelClient(const ::sockaddr_in& control_remote,
|
|
const ::sockaddr_in& data_remote,
|
|
uint8_t connection_type);
|
|
void resetTunnelClient(TunnelClient& client);
|
|
uint8_t nextTunnelChannelId() const;
|
|
uint16_t effectiveTunnelAddressForSlot(size_t slot) const;
|
|
void pruneStaleTunnelClients();
|
|
bool handleOpenKnxTunnelFrame(const uint8_t* data, size_t len,
|
|
TunnelClient* response_client);
|
|
bool handleOpenKnxBusFrame(const uint8_t* data, size_t len);
|
|
void selectOpenKnxNetworkInterface(const ::sockaddr_in& remote);
|
|
bool emitOpenKnxGroupValue(uint16_t group_object_number, const uint8_t* data, size_t len);
|
|
bool shouldRouteDaliApplicationFrames() const;
|
|
uint8_t advertisedMedium() const;
|
|
void syncOpenKnxConfigFromDevice();
|
|
uint16_t effectiveIpInterfaceIndividualAddress() const;
|
|
uint16_t effectiveKnxDeviceIndividualAddress() const;
|
|
uint16_t effectiveTunnelAddress() const;
|
|
void pollTpUart();
|
|
void pollProgrammingButton();
|
|
void updateProgrammingLed();
|
|
void setProgrammingLed(bool on);
|
|
void handleTpUartControlByte(uint8_t byte);
|
|
void handleTpTelegram(const uint8_t* data, size_t len);
|
|
void forwardCemiToTp(const uint8_t* data, size_t len);
|
|
|
|
GatewayKnxBridge& bridge_;
|
|
CemiFrameHandler handler_;
|
|
GroupWriteHandler group_write_handler_;
|
|
std::string openknx_namespace_;
|
|
GatewayKnxConfig config_;
|
|
std::unique_ptr<openknx::EtsDeviceRuntime> ets_device_;
|
|
TaskHandle_t task_handle_{nullptr};
|
|
SemaphoreHandle_t openknx_lock_{nullptr};
|
|
SemaphoreHandle_t startup_semaphore_{nullptr};
|
|
esp_err_t startup_result_{ESP_OK};
|
|
std::atomic_bool stop_requested_{false};
|
|
std::atomic_bool started_{false};
|
|
int udp_sock_{-1};
|
|
int tcp_sock_{-1};
|
|
int active_tcp_sock_{-1};
|
|
int tp_uart_port_{-1};
|
|
int tp_uart_tx_pin_{-1};
|
|
int tp_uart_rx_pin_{-1};
|
|
std::vector<uint32_t> multicast_joined_interfaces_;
|
|
TickType_t network_refresh_tick_{0};
|
|
std::array<TcpClient, kMaxTcpClients> tcp_clients_{};
|
|
std::array<TunnelClient, kMaxTunnelClients> tunnel_clients_{};
|
|
uint8_t last_tunnel_channel_id_{0};
|
|
std::vector<uint8_t> tp_rx_frame_;
|
|
std::vector<uint8_t> tp_last_sent_telegram_;
|
|
TickType_t tp_uart_last_byte_tick_{0};
|
|
bool tp_uart_extended_frame_{false};
|
|
bool tp_uart_online_{false};
|
|
bool commissioning_only_{false};
|
|
std::atomic_bool openknx_configured_{false};
|
|
bool programming_button_last_pressed_{false};
|
|
bool programming_led_state_{false};
|
|
TickType_t programming_button_last_toggle_tick_{0};
|
|
std::string last_error_;
|
|
};
|
|
|
|
} // namespace gateway
|