diff --git a/examples/knx-linux/CMakeLists.txt b/examples/knx-linux/CMakeLists.txt index 1b400f0..a71fe6b 100644 --- a/examples/knx-linux/CMakeLists.txt +++ b/examples/knx-linux/CMakeLists.txt @@ -103,13 +103,16 @@ add_executable(knx-linux ../../src/knx/rf_medium_object.h ../../src/knx/rf_physical_layer.cpp ../../src/knx/rf_physical_layer.h + ../../src/knx/router_object.cpp + ../../src/knx/router_object.h + ../../src/knx/router_object_filtertable.cpp + ../../src/knx/router_object_filtertable.h ../../src/knx/secure_application_layer.cpp ../../src/knx/secure_application_layer.h ../../src/knx/security_interface_object.cpp ../../src/knx/security_interface_object.h ../../src/knx/simple_functional.h ../../src/knx/simple_map.h - ../../src/knx/table_object.cpp ../../src/knx/save_restore.h ../../src/knx/table_object.cpp ../../src/knx/table_object.h diff --git a/src/knx/property.h b/src/knx/property.h index 17104c9..77e271f 100644 --- a/src/knx/property.h +++ b/src/knx/property.h @@ -174,6 +174,16 @@ enum PropertyID PID_GO_SECURITY_FLAGS = 61, // Defines the required security requirements for each group object PID_ROLE_TABLE = 62, // Role table PID_TOOL_SEQUENCE_NUMBER_SENDING = 250, // Sequence Number used for the next outgoing secure communication (Tool Access only, non-standardized!) + + /** Router Object */ + PID_MEDIUM_STATUS = 51, + PID_ROUTETABLE_CONTROL = 56, + PID_COUPLER_SERVICES_CONTROL = 57, + PID_MAX_APDU_LENGTH_ROUTER = 58, + PID_HOP_COUNT = 61, + PID_MEDIUM = 63, + PID_FILTER_TABLE_USE = 67, + PID_RF_ENABLE_SBC = 112, // Exists only if medium for this router object is RF }; enum LoadState diff --git a/src/knx/router_object.cpp b/src/knx/router_object.cpp new file mode 100644 index 0000000..b7b1412 --- /dev/null +++ b/src/knx/router_object.cpp @@ -0,0 +1,61 @@ +#include "config.h" + +#include +#include "router_object.h" +#include "bits.h" +#include "data_property.h" + +RouterObject::RouterObject() +{ + initializeProperties(0, nullptr); +} + +void RouterObject::initializeProperties(size_t propertiesSize, Property** properties) +{ + Property* ownProperties[] = + { + new DataProperty( PID_OBJECT_TYPE, false, PDT_UNSIGNED_INT, 1, ReadLv3 | WriteLv0, (uint16_t) OT_ROUTER ), + new DataProperty( PID_OBJECT_INDEX, false, PDT_UNSIGNED_CHAR, 1, ReadLv3 | WriteLv0, (uint16_t) 0 ), // TODO + new DataProperty( PID_MEDIUM_STATUS, false, PDT_GENERIC_01, 1, ReadLv3 | WriteLv0, (uint16_t) 0 ), // TODO + new DataProperty( PID_MAX_APDU_LENGTH_ROUTER, false, PDT_UNSIGNED_INT, 1, ReadLv3 | WriteLv0, (uint16_t) 254 ), + new DataProperty( PID_HOP_COUNT, false, PDT_UNSIGNED_INT, 1, ReadLv3 | WriteLv0, (uint16_t) 5), + new DataProperty( PID_MEDIUM, false, PDT_ENUM8, 1, ReadLv3 | WriteLv0, (uint16_t) 0) , // TODO + }; + + uint8_t ownPropertiesCount = sizeof(ownProperties) / sizeof(Property*); + + uint8_t propertyCount = propertiesSize / sizeof(Property*); + uint8_t allPropertiesCount = propertyCount + ownPropertiesCount; + + Property* allProperties[allPropertiesCount]; + if (properties) + memcpy(allProperties, properties, propertiesSize); + memcpy(allProperties + propertyCount, ownProperties, sizeof(ownProperties)); + + InterfaceObject::initializeProperties(sizeof(allProperties), allProperties); +} + +void RouterObject::masterReset(EraseCode eraseCode, uint8_t channel) +{ + if (eraseCode == FactoryReset) + { + // TODO handle different erase codes + println("Factory reset of router object requested."); + } +} + +uint16_t RouterObject::getNumberOfElements(PropertyID propId) +{ + // Get number of entries for this property + uint16_t numElements = 0; + + uint8_t data[sizeof(uint16_t)]; // is sizeof(_currentElements) which is uint16_t + uint8_t count = property(propId)->read(0, 1, data); + + if (count > 0) + { + popWord(numElements, data); + } + + return numElements; +} diff --git a/src/knx/router_object.h b/src/knx/router_object.h new file mode 100644 index 0000000..dd09ed7 --- /dev/null +++ b/src/knx/router_object.h @@ -0,0 +1,20 @@ +#pragma once + +#include "config.h" + +#include "interface_object.h" +#include "knx_types.h" + +class RouterObject: public InterfaceObject +{ +public: + RouterObject(); + + virtual void masterReset(EraseCode eraseCode, uint8_t channel) override; + +protected: + void initializeProperties(size_t propertiesSize, Property** properties) override; + +private: + uint16_t getNumberOfElements(PropertyID propId); +}; diff --git a/src/knx/router_object_filtertable.cpp b/src/knx/router_object_filtertable.cpp new file mode 100644 index 0000000..60039d8 --- /dev/null +++ b/src/knx/router_object_filtertable.cpp @@ -0,0 +1,349 @@ +#include "config.h" + +#include +#include "router_object_filtertable.h" +#include "bits.h" +#include "memory.h" +#include "data_property.h" +#include "callback_property.h" +#include "function_property.h" + +RouterObjectFilterTable::RouterObjectFilterTable(Memory& memory) + : _memory(memory) +{ + Property* properties[] = + { + new CallbackProperty(this, PID_LOAD_STATE_CONTROL, true, PDT_CONTROL, 1, ReadLv3 | WriteLv3, + // ReadCallback of PID_LOAD_STATE_CONTROL + [](RouterObjectFilterTable* obj, uint16_t start, uint8_t count, uint8_t* data) -> uint8_t { + if (start == 0) + return 1; + + data[0] = obj->_state; + return 1; + }, + // WriteCallback of PID_LOAD_STATE_CONTROL + [](RouterObjectFilterTable* obj, uint16_t start, uint8_t count, const uint8_t* data) -> uint8_t { + obj->loadEvent(data); + return 1; + }), + new CallbackProperty(this, PID_TABLE_REFERENCE, false, PDT_UNSIGNED_LONG, 1, ReadLv3 | WriteLv0, + [](RouterObjectFilterTable* obj, uint16_t start, uint8_t count, uint8_t* data) -> uint8_t { + if(start == 0) + { + uint16_t currentNoOfElements = 1; + pushWord(currentNoOfElements, data); + return 1; + } + + if (obj->_state == LS_UNLOADED) + pushInt(0, data); + else + pushInt(obj->tableReference(), data); + return 1; + }), + + new DataProperty( PID_MCB_TABLE, false, PDT_GENERIC_08, 1, ReadLv3 | WriteLv0, (uint16_t) 0 ), // TODO + + new FunctionProperty(this, PID_ROUTETABLE_CONTROL, + // Command Callback of PID_ROUTETABLE_CONTROL + [](RouterObjectFilterTable* obj, uint8_t* data, uint8_t length, uint8_t* resultData, uint8_t& resultLength) -> void { + if (length != 3) + { + resultData[0] = ReturnCodes::DataVoid; + resultLength = 1; + return; + } + uint8_t id = data[1]; + uint8_t info = data[2]; + if (id == 0 && info == 0) + { + //obj->clearFailureLog(); + resultData[0] = ReturnCodes::Success; + resultData[1] = id; + resultLength = 2; + return; + } + resultData[0] = ReturnCodes::GenericError; + resultLength = 1; + }, + // State Callback of PID_ROUTETABLE_CONTROL + [](RouterObjectFilterTable* obj, uint8_t* data, uint8_t length, uint8_t* resultData, uint8_t& resultLength) -> void { + if (length != 3) + { + resultData[0] = ReturnCodes::DataVoid; + resultLength = 1; + return; + } + uint8_t id = data[1]; + uint8_t info = data[2]; + + // failure counters + if (id == 0 && info == 0) + { + resultData[0] = ReturnCodes::Success; + resultData[1] = id; + resultData[2] = info; + //obj->getFailureCounters(&resultData[3]); // Put 8 bytes in the buffer + resultLength = 3 + 8; + return; + } + // query latest failure by index + else if(id == 1) + { + uint8_t maxBufferSize = resultLength; // Remember the maximum buffer size of the buffer that is provided to us + uint8_t index = info; + uint8_t numBytes = 0;//obj->getFromFailureLogByIndex(index, &resultData[2], maxBufferSize); + if ( numBytes > 0) + { + resultData[0] = ReturnCodes::Success; + resultData[1] = id; + resultData[2] = index; + resultLength += numBytes; + resultLength = 3 + numBytes; + return; + } + resultData[0] = ReturnCodes::DataVoid; + resultData[1] = id; + resultLength = 2; + return; + } + resultData[0] = ReturnCodes::GenericError; + resultLength = 1; + }), + + new DataProperty( PID_FILTER_TABLE_USE, false, PDT_BINARY_INFORMATION, 1, ReadLv3 | WriteLv0, (uint16_t) 0 ), // TODO +/* + new FunctionProperty(this, PID_RF_ENABLE_SBC, + // Command Callback of PID_RF_ENABLE_SBC + [](RouterObjectFilterTable* obj, uint8_t* data, uint8_t length, uint8_t* resultData, uint8_t& resultLength) -> void { + uint8_t serviceId = data[1] & 0xff; + if (serviceId != 0) + { + resultData[0] = ReturnCodes::InvalidCommand; + resultLength = 1; + return; + } + if (length == 3) + { + uint8_t mode = data[2]; + if (mode > 1) + { + resultData[0] = ReturnCodes::DataVoid; + resultLength = 1; + return; + } + //obj->setSecurityMode(mode == 1); + resultData[0] = ReturnCodes::Success; + resultData[1] = serviceId; + resultLength = 2; + return; + } + resultData[0] = ReturnCodes::GenericError; + resultLength = 1; + }, + // State Callback of PID_RF_ENABLE_SBC + [](RouterObjectFilterTable* obj, uint8_t* data, uint8_t length, uint8_t* resultData, uint8_t& resultLength) -> void { + uint8_t serviceId = data[1] & 0xff; + if (serviceId != 0) + { + resultData[0] = ReturnCodes::InvalidCommand; + resultLength = 1; + return; + } + if (length == 2) + { + resultData[0] = ReturnCodes::Success; + resultData[1] = serviceId; + resultData[2] = 0;//obj->isSecurityModeEnabled() ? 1 : 0; + resultLength = 3; + return; + } + resultData[0] = ReturnCodes::GenericError; + resultLength = 1; + }), +*/ + }; + + RouterObject::initializeProperties(sizeof(properties), properties); +} + +uint8_t* RouterObjectFilterTable::save(uint8_t* buffer) +{ + buffer = pushByte(_state, buffer); + + if (_data) + buffer = pushInt(_memory.toRelative(_data), buffer); + else + buffer = pushInt(0, buffer); + + return RouterObject::save(buffer); +} + +const uint8_t* RouterObjectFilterTable::restore(const uint8_t* buffer) +{ + uint8_t state = 0; + buffer = popByte(state, buffer); + _state = (LoadState)state; + + uint32_t relativeAddress = 0; + buffer = popInt(relativeAddress, buffer); + + if (relativeAddress != 0) + _data = _memory.toAbsolute(relativeAddress); + else + _data = 0; + + return RouterObject::restore(buffer); +} + +uint16_t RouterObjectFilterTable::saveSize() +{ + return 1 + 4 + RouterObject::saveSize(); +} + +uint32_t RouterObjectFilterTable::tableReference() +{ + return (uint32_t)_memory.toRelative(_data); +} + +bool RouterObjectFilterTable::isLoaded() +{ + return _state == LS_LOADED; +} + +LoadState RouterObjectFilterTable::loadState() +{ + return _state; +} + +void RouterObjectFilterTable::loadEvent(const uint8_t* data) +{ + switch (_state) + { + case LS_UNLOADED: + loadEventUnloaded(data); + break; + case LS_LOADING: + loadEventLoading(data); + break; + case LS_LOADED: + loadEventLoaded(data); + break; + case LS_ERROR: + loadEventError(data); + break; + default: + /* do nothing */ + break; + } +} + +void RouterObjectFilterTable::loadEventUnloaded(const uint8_t* data) +{ + uint8_t event = data[0]; + switch (event) + { + case LE_NOOP: + case LE_LOAD_COMPLETED: + case LE_ADDITIONAL_LOAD_CONTROLS: + case LE_UNLOAD: + break; + case LE_START_LOADING: + loadState(LS_LOADING); + break; + default: + loadState(LS_ERROR); + errorCode(E_GOT_UNDEF_LOAD_CMD); + } +} + +void RouterObjectFilterTable::loadEventLoading(const uint8_t* data) +{ + uint8_t event = data[0]; + switch (event) + { + case LE_NOOP: + case LE_START_LOADING: + break; + case LE_LOAD_COMPLETED: + loadState(LS_LOADED); + break; + case LE_UNLOAD: + loadState(LS_UNLOADED); + break; + case LE_ADDITIONAL_LOAD_CONTROLS: // Not supported here + default: + loadState(LS_ERROR); + errorCode(E_GOT_UNDEF_LOAD_CMD); + } +} + +void RouterObjectFilterTable::loadEventLoaded(const uint8_t* data) +{ + uint8_t event = data[0]; + switch (event) + { + case LE_NOOP: + case LE_LOAD_COMPLETED: + break; + case LE_START_LOADING: + loadState(LS_LOADING); + break; + case LE_UNLOAD: + loadState(LS_UNLOADED); + break; + case LE_ADDITIONAL_LOAD_CONTROLS: + loadState(LS_ERROR); + errorCode(E_INVALID_OPCODE); + break; + default: + loadState(LS_ERROR); + errorCode(E_GOT_UNDEF_LOAD_CMD); + } +} + +void RouterObjectFilterTable::loadEventError(const uint8_t* data) +{ + uint8_t event = data[0]; + switch (event) + { + case LE_NOOP: + case LE_LOAD_COMPLETED: + case LE_ADDITIONAL_LOAD_CONTROLS: + case LE_START_LOADING: + break; + case LE_UNLOAD: + loadState(LS_UNLOADED); + break; + default: + loadState(LS_ERROR); + errorCode(E_GOT_UNDEF_LOAD_CMD); + } +} + +void RouterObjectFilterTable::loadState(LoadState newState) +{ + if (newState == _state) + return; + //beforeStateChange(newState); + _state = newState; +} + +void RouterObjectFilterTable::errorCode(ErrorCode errorCode) +{ + uint8_t data = errorCode; + Property* prop = property(PID_ERROR_CODE); + prop->write(data); +} + +void RouterObjectFilterTable::masterReset(EraseCode eraseCode, uint8_t channel) +{ + RouterObject::masterReset(eraseCode, channel); + + if (eraseCode == FactoryReset) + { + // TODO handle different erase codes + println("Factory reset of router object with filter table requested."); + } +} diff --git a/src/knx/router_object_filtertable.h b/src/knx/router_object_filtertable.h new file mode 100644 index 0000000..15c3e72 --- /dev/null +++ b/src/knx/router_object_filtertable.h @@ -0,0 +1,39 @@ +#pragma once + +#include "config.h" + +#include "router_object.h" +#include "knx_types.h" + +class Memory; + +class RouterObjectFilterTable: public RouterObject +{ +public: + RouterObjectFilterTable(Memory& memory); + + virtual void masterReset(EraseCode eraseCode, uint8_t channel) override; + + bool isLoaded(); + + LoadState loadState(); + uint8_t* save(uint8_t* buffer) override; + const uint8_t* restore(const uint8_t* buffer) override; + uint16_t saveSize() override; + +private: + uint32_t tableReference(); + void errorCode(ErrorCode errorCode); + + void loadEvent(const uint8_t* data); + void loadEventUnloaded(const uint8_t* data); + void loadEventLoading(const uint8_t* data); + void loadEventLoaded(const uint8_t* data); + void loadEventError(const uint8_t* data); + + void loadState(LoadState newState); + LoadState _state = LS_UNLOADED; + + Memory& _memory; + uint8_t *_data = 0; +};