diff --git a/src/knx/application_layer.cpp b/src/knx/application_layer.cpp index 74f99d8..cbf5afa 100644 --- a/src/knx/application_layer.cpp +++ b/src/knx/application_layer.cpp @@ -494,6 +494,25 @@ void ApplicationLayer::propertyValueWriteRequest(AckType ack, Priority priority, startIndex, data, length); } +void ApplicationLayer::functionPropertyStateResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, + uint8_t objectIndex, uint8_t propertyId, uint8_t* resultData, uint8_t resultLength) +{ + CemiFrame frame(3 + resultLength + 1); + APDU& apdu = frame.apdu(); + apdu.type(FunctionPropertyStateResponse); + uint8_t* data = apdu.data() + 1; + + data[0] = objectIndex; + data[1] = propertyId; + if (resultLength > 0) + memcpy(&data[2], resultData, resultLength); + + if (asap == _connectedTsap) + dataConnectedRequest(asap, priority, apdu); + else + dataIndividualRequest(ack, hopType, priority, asap, apdu); +} + void ApplicationLayer::propertyDescriptionReadRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t objectIndex, uint8_t propertyId, uint8_t propertyIndex) { @@ -751,6 +770,12 @@ void ApplicationLayer::individualIndication(HopCountType hopType, Priority prior startIndex, data + 5, apdu.length() - 5); break; } + case FunctionPropertyCommand: + _bau.functionPropertyCommandIndication(priority, hopType, tsap, data[1], data[2], &data[3], apdu.length() - 4); //TODO: check length + break; + case FunctionPropertyState: + _bau.functionPropertyStateIndication(priority, hopType, tsap, data[1], data[2], &data[3], apdu.length() - 4); //TODO: check length + break; case PropertyDescriptionRead: _bau.propertyDescriptionReadIndication(priority, hopType, tsap, data[1], data[2], data[3]); break; @@ -919,4 +944,4 @@ void ApplicationLayer::individualSend(AckType ack, HopCountType hopType, Priorit bool ApplicationLayer::isConnected() { return (_connectedTsap >= 0); -} \ No newline at end of file +} diff --git a/src/knx/application_layer.h b/src/knx/application_layer.h index 401a653..c2de79c 100644 --- a/src/knx/application_layer.h +++ b/src/knx/application_layer.h @@ -105,6 +105,8 @@ class ApplicationLayer uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex, uint8_t* data, uint8_t length); void propertyValueWriteRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t objectIndex, uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex, uint8_t* data, uint8_t length); + void functionPropertyStateResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, + uint8_t objectIndex, uint8_t propertyId, uint8_t *resultData, uint8_t resultLength); void propertyDescriptionReadRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t objectIndex, uint8_t propertyId, uint8_t propertyIndex); void propertyDescriptionReadResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, diff --git a/src/knx/bau.cpp b/src/knx/bau.cpp index e2b1a9d..f28c94f 100644 --- a/src/knx/bau.cpp +++ b/src/knx/bau.cpp @@ -106,6 +106,14 @@ void BusAccessUnit::propertyValueReadIndication(Priority priority, HopCountType { } +void BusAccessUnit::functionPropertyCommandIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t objectIndex, uint8_t propertyId, uint8_t* data, uint8_t length) +{ +} + +void BusAccessUnit::functionPropertyStateIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t objectIndex, uint8_t propertyId, uint8_t* data, uint8_t length) +{ +} + void BusAccessUnit::propertyValueReadResponseConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t objectIndex, uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex, uint8_t* data, uint8_t length, bool status) { } diff --git a/src/knx/bau.h b/src/knx/bau.h index a0e567c..444c61f 100644 --- a/src/knx/bau.h +++ b/src/knx/bau.h @@ -48,6 +48,10 @@ class BusAccessUnit uint8_t objectIndex, uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex, bool status); virtual void propertyValueReadIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t objectIndex, uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex); + virtual void functionPropertyCommandIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t objectIndex, + uint8_t propertyId, uint8_t* data, uint8_t length); + virtual void functionPropertyStateIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t objectIndex, + uint8_t propertyId, uint8_t* data, uint8_t length); virtual void propertyValueReadResponseConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t objectIndex, uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex, uint8_t* data, uint8_t length, bool status); virtual void propertyValueReadAppLayerConfirm(Priority priority, HopCountType hopType, uint16_t asap, uint8_t objectIndex, diff --git a/src/knx/bau_systemB.cpp b/src/knx/bau_systemB.cpp index 8f44ef1..f3cc234 100644 --- a/src/knx/bau_systemB.cpp +++ b/src/knx/bau_systemB.cpp @@ -240,6 +240,32 @@ void BauSystemB::propertyValueReadIndication(Priority priority, HopCountType hop startIndex, data, size); } +void BauSystemB::functionPropertyCommandIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t objectIndex, + uint8_t propertyId, uint8_t* data, uint8_t length) +{ + uint8_t resultLength = 0; + uint8_t resultData[32]; + + InterfaceObject* obj = getInterfaceObject(objectIndex); + if(obj) + obj->command((PropertyID)propertyId, data, length, resultData, resultLength); + + _appLayer.functionPropertyStateResponse(AckRequested, priority, hopType, asap, objectIndex, propertyId, resultData, resultLength); +} + +void BauSystemB::functionPropertyStateIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t objectIndex, + uint8_t propertyId, uint8_t* data, uint8_t length) +{ + uint8_t resultLength = 0; + uint8_t resultData[32]; + + InterfaceObject* obj = getInterfaceObject(objectIndex); + if(obj) + obj->state((PropertyID)propertyId, data, length, resultData, resultLength); + + _appLayer.functionPropertyStateResponse(AckRequested, priority, hopType, asap, objectIndex, propertyId, resultData, resultLength); +} + void BauSystemB::individualAddressReadIndication(HopCountType hopType) { if (_deviceObj.progMode()) @@ -433,4 +459,4 @@ void BauSystemB::propertyValueWrite(ObjectType objectType, uint8_t objectInstanc Memory& BauSystemB::memory() { return _memory; -} \ No newline at end of file +} diff --git a/src/knx/bau_systemB.h b/src/knx/bau_systemB.h index b651150..2dce5e7 100644 --- a/src/knx/bau_systemB.h +++ b/src/knx/bau_systemB.h @@ -56,6 +56,10 @@ class BauSystemB : protected BusAccessUnit uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex, uint8_t* data, uint8_t length) override; void propertyValueReadIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t objectIndex, uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex) override; + void functionPropertyCommandIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t objectIndex, + uint8_t propertyId, uint8_t* data, uint8_t length); + void functionPropertyStateIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t objectIndex, + uint8_t propertyId, uint8_t* data, uint8_t length); void individualAddressReadIndication(HopCountType hopType) override; void individualAddressWriteIndication(HopCountType hopType, uint16_t newaddress) override; void groupValueWriteLocalConfirm(AckType ack, uint16_t asap, Priority priority, HopCountType hopType, @@ -97,4 +101,4 @@ class BauSystemB : protected BusAccessUnit bool _configured = true; RestartState _restartState = Idle; uint32_t _restartDelay = 0; -}; \ No newline at end of file +}; diff --git a/src/knx/function_property.h b/src/knx/function_property.h new file mode 100644 index 0000000..b43df9f --- /dev/null +++ b/src/knx/function_property.h @@ -0,0 +1,51 @@ +#pragma once + +#include "property.h" + +class InterfaceObject; + +template class FunctionProperty : public Property +{ + public: + FunctionProperty(T* io, PropertyID id, uint8_t access, + void (*commandCallback)(T*, uint8_t*, uint8_t, uint8_t*, uint8_t&), + void (*stateCallback)(T*, uint8_t*, uint8_t, uint8_t*, uint8_t&)) + : Property(id, false, PDT_FUNCTION, 1, access), _interfaceObject(io), _commandCallback(commandCallback), _stateCallback(stateCallback) + /* max_elements is set to 1, see 3.3.7 Application Layer p.68 */ + {} + + virtual uint8_t read(uint16_t start, uint8_t count, uint8_t* data) const override + { + return 0; + } + + virtual uint8_t write(uint16_t start, uint8_t count, const uint8_t* data) override + { + return 0; + } + + virtual void command(uint8_t* data, uint8_t length, uint8_t* resultData, uint8_t& resultLength) override + { + if (length == 0 || _commandCallback == nullptr ) + { + resultLength = 0; + return; + } + _commandCallback(_interfaceObject, data, length, resultData, resultLength); + } + + virtual void state(uint8_t* data, uint8_t length, uint8_t* resultData, uint8_t& resultLength) override + { + if (length == 0 || _stateCallback == nullptr ) + { + resultLength = 0; + return; + } + _stateCallback(_interfaceObject, data, length, resultData, resultLength); + } + + private: + T* _interfaceObject = nullptr; + void (*_commandCallback)(T*, uint8_t*, uint8_t, uint8_t*, uint8_t&) = nullptr; + void (*_stateCallback)(T*, uint8_t*, uint8_t, uint8_t*, uint8_t&) = nullptr; +}; diff --git a/src/knx/interface_object.cpp b/src/knx/interface_object.cpp index 20276f6..19396ac 100644 --- a/src/knx/interface_object.cpp +++ b/src/knx/interface_object.cpp @@ -137,6 +137,30 @@ uint8_t InterfaceObject::propertySize(PropertyID id) return prop->ElementSize(); } +void InterfaceObject::command(PropertyID id, uint8_t* data, uint8_t length, uint8_t* resultData, uint8_t& resultLength) +{ + Property* prop = property(id); + if (prop == nullptr) + { + resultLength = 0; + return;; + } + + prop->command(data, length, resultData, resultLength); +} + +void InterfaceObject::state(PropertyID id, uint8_t* data, uint8_t length, uint8_t* resultData, uint8_t resultLength) +{ + Property* prop = property(id); + if (prop == nullptr) + { + resultLength = 0; + return;; + } + + prop->state(data, length, resultData, resultLength); +} + uint8_t InterfaceObject::propertyDescriptionCount() { return 0; diff --git a/src/knx/interface_object.h b/src/knx/interface_object.h index 03f93e9..0b92633 100644 --- a/src/knx/interface_object.h +++ b/src/knx/interface_object.h @@ -98,6 +98,34 @@ class InterfaceObject : public SaveRestore * @returns the size in byte or 0 if the interface object does not have the property */ virtual uint8_t propertySize(PropertyID id); + /** + * Call command of a function property of the interface object. Property type must be PDT_FUNCTION + * + * @param id id of the property to call + * + * @param[in] length The size of the data buffer + * + * @param[in] data The argument data for the function + * + * @param[out] resultLength The size of the result data buffer + * + * @param[out] resultData The result data for the function + */ + virtual void command(PropertyID id, uint8_t* data, uint8_t length, uint8_t* resultData, uint8_t &resultLength); + /** + * Get state of a function property of the interface object. Property type must be PDT_FUNCTION + * + * @param id id of the property to call + * + * @param[in] length The size of the data buffer + * + * @param[in] data The argument data for the function + * + * @param[out] resultLength The size of the result data buffer + * + * @param[out] resultData The result data for the function + */ + virtual void state(PropertyID id, uint8_t* data, uint8_t length, uint8_t* resultData, uint8_t resultLength); /** * Read the Description of a property of the interface object. The output parameters are only valid if nuberOfElements is not zero. * @@ -170,4 +198,4 @@ class InterfaceObject : public SaveRestore Property** _properties = nullptr; uint8_t _propertyCount = 0; -}; \ No newline at end of file +}; diff --git a/src/knx/knx_types.h b/src/knx/knx_types.h index 046a4b3..a84b224 100644 --- a/src/knx/knx_types.h +++ b/src/knx/knx_types.h @@ -138,6 +138,9 @@ enum ApduType UserMemoryWrite = 0x2C2, UserManufacturerInfoRead = 0x2C5, UserManufacturerInfoResponse = 0x2C6, + FunctionPropertyCommand = 0x2C7, + FunctionPropertyState = 0x2C8, + FunctionPropertyStateResponse = 0x2C9, DeviceDescriptorRead = 0x300, DeviceDescriptorResponse = 0x340, Restart = 0x380, diff --git a/src/knx/property.cpp b/src/knx/property.cpp index a5077dd..a42989f 100644 --- a/src/knx/property.cpp +++ b/src/knx/property.cpp @@ -203,3 +203,19 @@ uint8_t Property::write(uint16_t position, uint16_t value) pushWord(value, data); return write(position, 1, data); } + +void Property::command(uint8_t* data, uint8_t length, uint8_t* resultData, uint8_t& resultLength) +{ + (void)data; + (void)length; + (void)resultData; + resultLength = 0; +} + +void Property::state(uint8_t* data, uint8_t length, uint8_t* resultData, uint8_t &resultLength) +{ + (void)data; + (void)length; + (void)resultData; + resultLength = 0; +} diff --git a/src/knx/property.h b/src/knx/property.h index 11ac4ef..7c3ea93 100644 --- a/src/knx/property.h +++ b/src/knx/property.h @@ -238,6 +238,8 @@ class Property : public SaveRestore uint8_t ElementSize() const; virtual uint8_t read(uint16_t start, uint8_t count, uint8_t* data) const = 0; virtual uint8_t write(uint16_t start, uint8_t count, const uint8_t* data) = 0; + virtual void command(uint8_t* data, uint8_t length, uint8_t* resultData, uint8_t& resultLength); + virtual void state(uint8_t* data, uint8_t length, uint8_t* resultData, uint8_t& resultLength); uint8_t read(uint8_t& value) const; uint8_t read(uint16_t& value) const; uint8_t read(uint32_t& value) const; @@ -253,4 +255,4 @@ class Property : public SaveRestore PropertyDataType _type; uint16_t _maxElements; uint8_t _access; -}; \ No newline at end of file +};