.. _program_listing_file_src_knx_router_object.cpp: Program Listing for File router_object.cpp ========================================== |exhale_lsh| :ref:`Return to documentation for file ` (``src/knx/router_object.cpp``) .. |exhale_lsh| unicode:: U+021B0 .. UPWARDS ARROW WITH TIP LEFTWARDS .. code-block:: cpp #include "config.h" #include #include "router_object.h" #include "bits.h" #include "memory.h" #include "data_property.h" #include "callback_property.h" #include "function_property.h" // Filter Table Realization Type 3 // The Filter Table Realisation Type 3 shall be organised as a memory mapped bit-field of // 65536 bits and thus 8 192 octets. Each bit shall uniquely correspond to one Group Address. // The full 16 bit KNX GA encoding range shall be supported. // // octet_address = GA_value div 8 // bit_position = GA_value mod 8 static constexpr uint16_t kFilterTableSize = 65536 / 8; // Each group address is represented by one bit enum RouteTableServices { ClearRoutingTable = 0x01, // no info bytes SetRoutingTable = 0x02, // no info bytes ClearGroupAddress = 0x03, // 4 bytes: start address and end address SetGroupAddress = 0x04, // 4 bytes: start address and end address }; RouterObject::RouterObject(Memory& memory, uint32_t staticTableAdr, uint32_t staticTableSize) : TableObject(memory, staticTableAdr, staticTableSize) { } void RouterObject::initialize1x(DptMedium mediumType, uint16_t maxApduSize) { // Object index property is not included for coupler model 1.x, so value is "don't care". initialize(CouplerModel::Model_1x, 200, mediumType, RouterObjectType::Single, maxApduSize); } void RouterObject::initialize20(uint8_t objIndex, DptMedium mediumType, RouterObjectType rtType, uint16_t maxApduSize) { initialize(CouplerModel::Model_20, objIndex, mediumType, rtType, maxApduSize); } void RouterObject::initialize(CouplerModel model, uint8_t objIndex, DptMedium mediumType, RouterObjectType rtType, uint16_t maxApduSize) { bool useHopCount = false; bool useTable = true; _model = model; if (model == CouplerModel::Model_20) { useHopCount = (rtType == RouterObjectType::Primary); useTable = (rtType == RouterObjectType::Secondary); } // These properties are always present Property* fixedProperties[] = { new DataProperty( PID_OBJECT_TYPE, false, PDT_UNSIGNED_INT, 1, ReadLv3 | WriteLv0, (uint16_t) OT_ROUTER ), new DataProperty( PID_MEDIUM_STATUS, false, PDT_GENERIC_01, 1, ReadLv3 | WriteLv0, (uint16_t) 0 ), // 0 means communication is possible, could be set by datalink layer or bau to 1 (comm impossible) new DataProperty( PID_MAX_APDU_LENGTH_ROUTER, false, PDT_UNSIGNED_INT, 1, ReadLv3 | WriteLv0, maxApduSize ), }; uint8_t fixedPropertiesCount = sizeof(fixedProperties) / sizeof(Property*); // Only present if coupler model is 1.x Property* model1xProperties[] = { // default values from Spec, see 03_05_01 4.4.4 and 4.4.5 new DataProperty( PID_MAIN_LCCONFIG, true, PDT_BITSET8, 1, ReadLv3 | WriteLv0, (uint8_t) (LCCONFIG::PHYS_FRAME_ROUT | LCCONFIG::PHYS_REPEAT | LCCONFIG::BROADCAST_REPEAT | LCCONFIG::GROUP_IACK_ROUT | LCCONFIG::PHYS_IACK_NORMAL) ), // Primary: data individual (connless and connorient) + broadcast new DataProperty( PID_SUB_LCCONFIG, true, PDT_BITSET8, 1, ReadLv3 | WriteLv0, (uint8_t) (LCCONFIG::PHYS_FRAME_ROUT | LCCONFIG::PHYS_REPEAT | LCCONFIG::BROADCAST_REPEAT | LCCONFIG::GROUP_IACK_ROUT | LCCONFIG::PHYS_IACK_NORMAL) ), // Secondary: data individual (connless and connorient) + broadcast new DataProperty( PID_MAIN_LCGRPCONFIG, true, PDT_BITSET8, 1, ReadLv3 | WriteLv0, (uint8_t) (LCGRPCONFIG::GROUP_6FFFROUTE | LCGRPCONFIG::GROUP_7000UNLOCK | LCGRPCONFIG::GROUP_REPEAT)), // Primary: data group new DataProperty( PID_SUB_LCGRPCONFIG, true, PDT_BITSET8, 1, ReadLv3 | WriteLv0, (uint8_t) (LCGRPCONFIG::GROUP_6FFFROUTE | LCGRPCONFIG::GROUP_7000UNLOCK | LCGRPCONFIG::GROUP_REPEAT)), // Secondary: data group }; uint8_t model1xPropertiesCount = sizeof(model1xProperties) / sizeof(Property*); // Only present if coupler model is 2.0 // One router object per interface, currently only TP1/RF coupler specified Property* model20Properties[] = { new DataProperty( PID_OBJECT_INDEX, false, PDT_UNSIGNED_CHAR, 1, ReadLv3 | WriteLv0, objIndex ), // Must be set by concrete BAUxxxx! new DataProperty( PID_MEDIUM, false, PDT_ENUM8, 1, ReadLv3 | WriteLv0, (uint8_t) mediumType ), }; uint8_t model20PropertiesCount = sizeof(model20Properties) / sizeof(Property*); Property* tableProperties[] = { new FunctionProperty(this, PID_ROUTETABLE_CONTROL, // Command Callback of PID_ROUTETABLE_CONTROL [](RouterObject * obj, uint8_t* data, uint8_t length, uint8_t* resultData, uint8_t& resultLength) -> void { obj->functionRouteTableControl(true, data, length, resultData, resultLength); }, // State Callback of PID_ROUTETABLE_CONTROL [](RouterObject * obj, uint8_t* data, uint8_t length, uint8_t* resultData, uint8_t& resultLength) -> void { obj->functionRouteTableControl(false, data, length, resultData, resultLength); }) }; Property* tableProperties20[] = { new DataProperty( PID_COUPLER_SERVICES_CONTROL, true, PDT_GENERIC_01, 1, ReadLv3 | WriteLv0, (uint8_t) 0), // written by ETS TODO: implement new DataProperty( PID_FILTER_TABLE_USE, true, PDT_BINARY_INFORMATION, 1, ReadLv3 | WriteLv0, (uint16_t) 0 ) // default: invalid filter table, do not use, written by ETS }; uint8_t tablePropertiesCount = sizeof(tableProperties) / sizeof(Property*); uint8_t tableProperties20Count = sizeof(tableProperties20) / sizeof(Property*); size_t allPropertiesCount = fixedPropertiesCount; allPropertiesCount += (model == CouplerModel::Model_1x) ? model1xPropertiesCount : model20PropertiesCount; allPropertiesCount += useHopCount ? 1 : 0; allPropertiesCount += useTable ? tablePropertiesCount : 0; allPropertiesCount += useTable && (model == CouplerModel::Model_20) ? tableProperties20Count : 0; allPropertiesCount += ((mediumType == DptMedium::KNX_RF) || (mediumType == DptMedium::KNX_IP)) ? 1 : 0; // PID_RF_ENABLE_SBC and PID_IP_ENABLE_SBC Property* allProperties[allPropertiesCount]; memcpy(&allProperties[0], &fixedProperties[0], sizeof(fixedProperties)); uint8_t i = fixedPropertiesCount; if (model == CouplerModel::Model_1x) { memcpy(&allProperties[i], model1xProperties, sizeof(model1xProperties)); i += model1xPropertiesCount; } else { memcpy(&allProperties[i], model20Properties, sizeof(model20Properties)); i += model20PropertiesCount; } if (useHopCount) { // TODO: Primary side: 5 for line coupler, 4 for backbone coupler, only exists if secondary is open medium without hop count // Do we need to set a default value here or is it written by ETS? allProperties[i++] = new DataProperty( PID_HOP_COUNT, true, PDT_UNSIGNED_INT, 1, ReadLv3 | WriteLv0, (uint16_t) 5); } if (useTable) { memcpy(&allProperties[i], tableProperties, sizeof(tableProperties)); i += tablePropertiesCount; if ((model == CouplerModel::Model_20)) { memcpy(&allProperties[i], tableProperties20, sizeof(tableProperties20)); i += tableProperties20Count; } } if (mediumType == DptMedium::KNX_RF) { allProperties[i++] = new FunctionProperty(this, PID_RF_ENABLE_SBC, // Command Callback of PID_RF_ENABLE_SBC [](RouterObject * obj, uint8_t* data, uint8_t length, uint8_t* resultData, uint8_t& resultLength) -> void { obj->functionRfEnableSbc(true, data, length, resultData, resultLength); }, // State Callback of PID_RF_ENABLE_SBC [](RouterObject * obj, uint8_t* data, uint8_t length, uint8_t* resultData, uint8_t& resultLength) -> void { obj->functionRfEnableSbc(false, data, length, resultData, resultLength); }); } else if (mediumType == DptMedium::KNX_IP) { allProperties[i++] = new FunctionProperty(this, PID_IP_ENABLE_SBC, // Command Callback of PID_IP_ENABLE_SBC [](RouterObject * obj, uint8_t* data, uint8_t length, uint8_t* resultData, uint8_t& resultLength) -> void { obj->functionIpEnableSbc(true, data, length, resultData, resultLength); }, // State Callback of PID_IP_ENABLE_SBC [](RouterObject * obj, uint8_t* data, uint8_t length, uint8_t* resultData, uint8_t& resultLength) -> void { obj->functionIpEnableSbc(false, data, length, resultData, resultLength); }); } if (useTable) TableObject::initializeProperties(sizeof(allProperties), allProperties); else InterfaceObject::initializeProperties(sizeof(allProperties), allProperties); } const uint8_t* RouterObject::restore(const uint8_t* buffer) { return TableObject::restore(buffer); } void RouterObject::commandClearSetRoutingTable(bool bitIsSet) { uint8_t fillbyte = bitIsSet ? 0xFF : 0x00; uint32_t relptr = _memory.toRelative(data()); #ifdef KNX_LOG_COUPLER print("RouterObject::commandClearSetRoutingTable "); println(bitIsSet); println(relptr); println((uint32_t)data()); #endif for (uint16_t i = 0; i < kFilterTableSize; i++) { _memory.writeMemory(relptr + i, 1, &fillbyte); } } bool RouterObject::statusClearSetRoutingTable(bool bitIsSet) { #ifdef KNX_LOG_COUPLER print("RouterObject::statusClearSetRoutingTable "); println(bitIsSet); #endif for (uint16_t i = 0; i < kFilterTableSize; i++) { if (data()[i] != (bitIsSet ? 0xFF : 0x00)) return false; } return true; } void RouterObject::commandClearSetGroupAddress(uint16_t startAddress, uint16_t endAddress, bool bitIsSet) { #ifdef KNX_LOG_COUPLER print("RouterObject::commandClearSetGroupAddress "); print(startAddress); print(" "); print(endAddress); print(" "); println(bitIsSet); #endif uint16_t startOctet = startAddress / 8; uint8_t startBitPosition = startAddress % 8; uint16_t endOctet = endAddress / 8; uint8_t endBitPosition = endAddress % 8; if (startOctet == endOctet) { uint32_t relptr = _memory.toRelative(data()) + startOctet; uint8_t octetData = 0; // = data()[startOctet]; _memory.readMemory(relptr, 1, &octetData); for (uint8_t bitPos = startBitPosition; bitPos <= endBitPosition; bitPos++) { if (bitIsSet) octetData |= 1 << bitPos; else octetData &= ~(1 << bitPos); } _memory.writeMemory(relptr, 1, &octetData); return; } for (uint16_t i = startOctet; i <= endOctet; i++) { uint32_t relptr = _memory.toRelative(data()) + i; uint8_t octetData = 0; _memory.readMemory(relptr, 1, &octetData); if (i == startOctet) { for (uint8_t bitPos = startBitPosition; bitPos <= 7; bitPos++) { if (bitIsSet) octetData |= 1 << bitPos; else octetData &= ~(1 << bitPos); } } else if (i == endOctet) { for (uint8_t bitPos = 0; bitPos <= endBitPosition; bitPos++) { if (bitIsSet) octetData |= 1 << bitPos; else octetData &= ~(1 << bitPos); } } else { if (bitIsSet) octetData = 0xFF; else octetData = 0x00; } _memory.writeMemory(relptr, 1, &octetData); } } bool RouterObject::statusClearSetGroupAddress(uint16_t startAddress, uint16_t endAddress, bool bitIsSet) { #ifdef KNX_LOG_COUPLER print("RouterObject::statusClearSetGroupAddress "); print(startAddress); print(" "); print(endAddress); print(" "); println(bitIsSet); #endif uint16_t startOctet = startAddress / 8; uint8_t startBitPosition = startAddress % 8; uint16_t endOctet = endAddress / 8; uint8_t endBitPosition = endAddress % 8; if (startOctet == endOctet) { for (uint8_t bitPos = startBitPosition; bitPos <= endBitPosition; bitPos++) { if (bitIsSet) { if ((data()[startOctet] & (1 << bitPos)) == 0) return false; } else { if ((data()[startOctet] & (1 << bitPos)) != 0) return false; } } return true; } for (uint16_t i = startOctet; i <= endOctet; i++) { if (i == startOctet) { for (uint8_t bitPos = startBitPosition; bitPos <= 7; bitPos++) { if (bitIsSet) { if ((data()[i] & (1 << bitPos)) == 0) return false; } else { if ((data()[i] & (1 << bitPos)) != 0) return false; } } } else if (i == endOctet) { for (uint8_t bitPos = 0; bitPos <= endBitPosition; bitPos++) { if (bitIsSet) { if ((data()[i] & (1 << bitPos)) == 0) return false; } else { if ((data()[i] & (1 << bitPos)) != 0) return false; } } } else { if (data()[i] != (bitIsSet ? 0xFF : 0x00)) return false; } } return true; } void RouterObject::functionRouteTableControl(bool isCommand, uint8_t* data, uint8_t length, uint8_t* resultData, uint8_t& resultLength) { #ifdef KNX_LOG_COUPLER print("RouterObject::functionRouteTableControl "); print(isCommand); print(" "); printHex("", data, length); #endif RouteTableServices srvId = (RouteTableServices) data[1]; if (isCommand) { if (loadState() != LS_LOADING) { println("access violation. filter table can only be modified in LS_LOADING"); resultData[0] = ReturnCodes::AccessReadOnly; resultData[1] = srvId; resultLength = 2; return; } switch (srvId) { case ClearRoutingTable: commandClearSetRoutingTable(false); resultData[0] = ReturnCodes::Success; resultData[1] = srvId; resultLength = 2; return; case SetRoutingTable: commandClearSetRoutingTable(true); resultData[0] = ReturnCodes::Success; resultData[1] = srvId; resultLength = 2; return; case ClearGroupAddress: { uint16_t startAddress; uint16_t endAddress; popWord(startAddress, &data[2]); popWord(endAddress, &data[4]); commandClearSetGroupAddress(startAddress, endAddress, false); resultData[0] = ReturnCodes::Success; resultData[1] = srvId; pushWord(startAddress, &resultData[2]); pushWord(endAddress, &resultData[4]); resultLength = 6; return; } case SetGroupAddress: { uint16_t startAddress; uint16_t endAddress; popWord(startAddress, &data[2]); popWord(endAddress, &data[4]); commandClearSetGroupAddress(startAddress, endAddress, true); resultData[0] = ReturnCodes::Success; resultData[1] = srvId; pushWord(startAddress, &resultData[2]); pushWord(endAddress, &resultData[4]); resultLength = 6; return; } } } else { switch (srvId) { case ClearRoutingTable: resultData[0] = statusClearSetRoutingTable(false) ? ReturnCodes::Success : ReturnCodes::GenericError; resultData[1] = srvId; resultLength = 2; return; case SetRoutingTable: resultData[0] = statusClearSetRoutingTable(true) ? ReturnCodes::Success : ReturnCodes::GenericError; resultData[1] = srvId; resultLength = 2; return; case ClearGroupAddress: { uint16_t startAddress; uint16_t endAddress; popWord(startAddress, &data[2]); popWord(endAddress, &data[4]); resultData[0] = statusClearSetGroupAddress(startAddress, endAddress, false) ? ReturnCodes::Success : ReturnCodes::GenericError; resultData[1] = srvId; pushWord(startAddress, &resultData[2]); pushWord(endAddress, &resultData[4]); resultLength = 6; return; } case SetGroupAddress: { uint16_t startAddress; uint16_t endAddress; popWord(startAddress, &data[2]); popWord(endAddress, &data[4]); resultData[0] = statusClearSetGroupAddress(startAddress, endAddress, true) ? ReturnCodes::Success : ReturnCodes::GenericError; resultData[1] = srvId; pushWord(startAddress, &resultData[2]); pushWord(endAddress, &resultData[4]); resultLength = 6; return; } } } // We should not get here resultData[0] = ReturnCodes::GenericError; resultData[1] = srvId; resultLength = 2; } void RouterObject::functionRfEnableSbc(bool isCommand, uint8_t* data, uint8_t length, uint8_t* resultData, uint8_t& resultLength) { if (isCommand) { _rfSbcRoutingEnabled = (data[0] == 1) ? true : false; } resultData[0] = ReturnCodes::Success; resultData[1] = _rfSbcRoutingEnabled ? 1 : 0; resultLength = 2; } bool RouterObject::isRfSbcRoutingEnabled() { #ifdef KNX_LOG_COUPLER print("RouterObject::isRfSbcRoutingEnabled "); println(_rfSbcRoutingEnabled); #endif return _rfSbcRoutingEnabled; } // TODO: check if IP SBC works the same way, just copied from RF void RouterObject::functionIpEnableSbc(bool isCommand, uint8_t* data, uint8_t length, uint8_t* resultData, uint8_t& resultLength) { #ifdef KNX_LOG_COUPLER print("RouterObject::functionIpEnableSbc "); print(isCommand); printHex(" ", data, length); #endif if (isCommand) { _ipSbcRoutingEnabled = (data[0] == 1) ? true : false; } resultData[0] = ReturnCodes::Success; resultData[1] = _ipSbcRoutingEnabled ? 1 : 0; resultLength = 2; } // TODO: check if IP SBC works the same way, just copied from RF bool RouterObject::isIpSbcRoutingEnabled() { #ifdef KNX_LOG_COUPLER print("RouterObject::isIpSbcRoutingEnabled "); println(_ipSbcRoutingEnabled); #endif return _ipSbcRoutingEnabled; } void RouterObject::beforeStateChange(LoadState& newState) { #ifdef KNX_LOG_COUPLER println("RouterObject::beforeStateChange"); #endif if (newState != LS_LOADED) return; } void RouterObject::masterReset(EraseCode eraseCode, uint8_t channel) { #ifdef KNX_LOG_COUPLER print("RouterObject::masterReset "); print(eraseCode); print(" "); println(channel); #endif if (eraseCode == FactoryReset) { // TODO: handle different erase codes println("Factory reset of router object with filter table requested."); } } bool RouterObject::isGroupAddressInFilterTable(uint16_t groupAddress) { if (loadState() != LS_LOADED) return false; uint8_t filterTableUse = 0x01; Property* propFilterTableUse = property(PID_FILTER_TABLE_USE); if (propFilterTableUse) // check if property PID_FILTER_TABLE_USE exists (only coupler 20), if not, ignore this if (propFilterTableUse->read(filterTableUse) == 0) // check if property PID_FILTER_TABLE_USE is empty, if so, return false return false; if ((filterTableUse & 0x01) == 1) { uint8_t* filterTable = data(); // octet_address = GA_value div 8 // bit_position = GA_value mod 8 uint16_t octetAddress = groupAddress / 8; uint8_t bitPosition = groupAddress % 8; if (filterTable) return (filterTable[octetAddress] & (1 << bitPosition)) == (1 << bitPosition); else { println("RouterObject::isGroupAddressInFilterTable filterTable is NULL"); return false; } } return false; }