diff --git a/examples/knx-demo-smal-go/platformio.ini b/examples/knx-demo-smal-go/platformio.ini index a919602..d028f0a 100644 --- a/examples/knx-demo-smal-go/platformio.ini +++ b/examples/knx-demo-smal-go/platformio.ini @@ -14,7 +14,6 @@ libdeps_dir = /tmp/libdeps src_dir = . ;--- SAMD -------------------------------------------------- -; SMALL_GROUPOBJECT just tested with TP on SAMD, but should work also in other environments [env:zeroUSB] platform = atmelsam board = zeroUSB @@ -25,7 +24,6 @@ lib_extra_dirs = ../../../ lib_deps = SPI - https://github.com/thelsing/FlashStorage.git knx build_flags = @@ -43,7 +41,6 @@ build_flags = ; lib_deps = ; SPI -; https://github.com/thelsing/FlashStorage.git ; knx ; build_flags = diff --git a/examples/knx-linux/main.cpp b/examples/knx-linux/main.cpp index 6d855dd..1a1ee4e 100644 --- a/examples/knx-linux/main.cpp +++ b/examples/knx-linux/main.cpp @@ -113,7 +113,7 @@ void setup() GO_MAX.dataPointType(Dpt(9, 1)); GO_MAX.valueNoSend(-273.0); GO_RESET.dataPointType(Dpt(1, 15)); - GO_RESET.callback(resetCallback); + //GO_RESET.callback(resetCallback); LOGGER.info("Timeout: %d", knx.paramWord(0)); LOGGER.info("Zykl. senden: %d", knx.paramByte(2)); LOGGER.info("Min/Max senden: %d", knx.paramByte(3)); diff --git a/examples/knxPython/CMakeLists.txt b/examples/knxPython/CMakeLists.txt index 7425021..141b028 100644 --- a/examples/knxPython/CMakeLists.txt +++ b/examples/knxPython/CMakeLists.txt @@ -1,16 +1,16 @@ cmake_minimum_required(VERSION 3.16) -project(knx VERSION 1.5) +project(knxPython VERSION 1.5) add_subdirectory(pybind11) - pybind11_add_module(knxPython knxmodule.cpp ) include_directories(src pybind11/include) - +SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -O0") #set(outdir ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}) #set_target_properties(knx PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${outdir}) set_target_properties(knxPython PROPERTIES OUTPUT_NAME knx) set_property(TARGET knxPython PROPERTY CXX_STANDARD 11) #target_compile_definitions(knxPython PUBLIC -DMASK_VERSION=0x57B0) -target_link_libraries(knxPython PRIVATE knx) +target_link_libraries(knxPython PUBLIC knx) +install(TARGETS knxPython LIBRARY DESTINATION .) \ No newline at end of file diff --git a/examples/knxPython/Manifest.in b/examples/knxPython/Manifest.in deleted file mode 100644 index a1f2447..0000000 --- a/examples/knxPython/Manifest.in +++ /dev/null @@ -1,14 +0,0 @@ - - -recursive-include external * -include ./*.py -include ./VERSION -include ./*.cmake -include ./*.cpp -include ./CMakeLists.txt -include README.md - -global-exclude .git - -graft pybind11/include -graft src diff --git a/examples/knxPython/VERSION b/examples/knxPython/VERSION deleted file mode 100644 index c946ee6..0000000 --- a/examples/knxPython/VERSION +++ /dev/null @@ -1 +0,0 @@ -0.1.6 diff --git a/examples/knxPython/knxmodule.cpp b/examples/knxPython/knxmodule.cpp index 5e305ec..773e5f3 100644 --- a/examples/knxPython/knxmodule.cpp +++ b/examples/knxPython/knxmodule.cpp @@ -2,7 +2,7 @@ #include #include #include - + namespace py = pybind11; #include @@ -18,6 +18,10 @@ namespace py = pybind11; #include "knx/platform/linux_platform.h" #include "knx/ip/bau57B0.h" #include "knx/interface_object/group_object_table_object.h" +#include "knx/util/logger.h" + +#define LOGGER Logger::logger("knxmodule") + using namespace Knx; LinuxPlatform* platform = 0; @@ -40,22 +44,33 @@ static std::vector argv; struct StdStringCStrFunctor { - const char* operator() (const std::string& str) { return str.c_str(); } + const char* operator() (const std::string& str) + { + return str.c_str(); + } }; -static void Prepare(std::vector args) +static void init() { - // copy args so we control the livetime of the char* - argsVector = args; - for(int i = 0; i < args.size(); i++) - printf("%s\n", args[i].c_str()); + Logger::logLevel("knxmodule", Logger::Info); + Logger::logLevel("ApplicationLayer", Logger::Info); + Logger::logLevel("BauSystemBDevice", Logger::Info); + Logger::logLevel("GroupObject", Logger::Info); - argv = std::vector(argsVector.size()); - std::transform(argsVector.begin(), argsVector.end(), argv.begin(), StdStringCStrFunctor()); + /* + // copy args so we control the livetime of the char* + argsVector = args; + for (int i = 0; i < args.size(); i++) + printf("%s\n", args[i].c_str()); + + argv = std::vector(argsVector.size()); + std::transform(argsVector.begin(), argsVector.end(), argv.begin(), StdStringCStrFunctor()); + */ platform = new LinuxPlatform(); platform->cmdLineArgs(argv.size(), const_cast(argv.data())); bau = new Bau57B0(*platform); + } static void Destroy() @@ -69,7 +84,7 @@ static void Destroy() static void ReadMemory() { if (!bau) - return; + init(); bau->readMemory(); } @@ -78,12 +93,12 @@ static void Start() { if (running) return; - + if (!bau) - return; + init(); running = true; - + bau->enabled(true); workerThread = std::thread(loop); @@ -94,19 +109,20 @@ static void Stop() { if (!running) return; - + running = false; bau->writeMemory(); bau->enabled(false); - + workerThread.join(); } static bool ProgramMode(bool value) { if (!bau) - return false; + init(); + LOGGER.info("ProgramMode %d", value); bau->deviceObject().progMode(value); return bau->deviceObject().progMode(); } @@ -114,7 +130,7 @@ static bool ProgramMode(bool value) static bool ProgramMode() { if (!bau) - return false; + init(); return bau->deviceObject().progMode(); } @@ -122,71 +138,75 @@ static bool ProgramMode() static bool Configured() { if (!bau) - return false; + init(); return bau->configured(); } -PYBIND11_MODULE(knx, m) +PYBIND11_MODULE(knx, m) { m.doc() = "wrapper for knx device lib"; // optional module docstring m.def("Start", &Start, "Start knx handling thread."); m.def("Stop", &Stop, "Stop knx handling thread."); - m.def("Prepare", &Prepare, "Allocated needed objects."); - m.def("Destroy", &Destroy, "Free object allocated by Prepare."); + m.def("Destroy", &Destroy, "Free object allocated objects."); m.def("ProgramMode", (bool(*)())&ProgramMode, "get programing mode active."); m.def("ProgramMode", (bool(*)(bool))&ProgramMode, "Activate / deactivate programing mode."); - m.def("Configured", (bool(*)())&Configured, "get configured status."); + m.def("Configured", (bool(*)())&Configured, "get configured status."); m.def("ReadMemory", &ReadMemory, "read memory from flash file"); - m.def("FlashFilePath", []() - { - if(!platform) - return std::string(""); + m.def("FlashFilePath", []() + { + if (!platform) + init(); - return platform->flashFilePath(); - }); - m.def("FlashFilePath", [](std::string path) - { - if(!platform) - return; + return platform->flashFilePath(); + }); + m.def("FlashFilePath", [](std::string path) + { + if (!platform) + init(); - platform->flashFilePath(path); - }); - m.def("GetGroupObject", [](uint16_t goNr) - { - if(!bau || goNr > bau->groupObjectTable().entryCount()) - return (GroupObject*)nullptr; + platform->flashFilePath(path); + }); + m.def("GetGroupObject", [](uint16_t goNr) + { + LOGGER.info("GetGroupObject arg %d", goNr); + LOGGER.info("GetGroupObject entrycount %d", bau->groupObjectTable().entryCount()); + + if (!bau) + init(); + + if (goNr > bau->groupObjectTable().entryCount()) + return (GroupObject*)nullptr; + + return &bau->groupObjectTable().get(goNr); + }, py::return_value_policy::reference); + m.def("Callback", [](GroupObjectUpdatedHandler handler) + { + GroupObject::classCallback(handler); + }); - return &bau->groupObjectTable().get(goNr); - }, py::return_value_policy::reference); - py::class_(m, "GroupObject", py::dynamic_attr()) - .def(py::init()) - .def("asap", &GroupObject::asap) - .def("size", &GroupObject::valueSize) - .def_property("value", - [](GroupObject& go) { return py::bytes((const char*)go.valueRef(), go.valueSize()); }, - [](GroupObject& go, py::bytes bytesValue) - { - const auto value = static_cast(bytesValue); - if (value.length() != go.valueSize()) - throw std::length_error("bytesValue"); - - auto valueRef = go.valueRef(); - memcpy(valueRef, value.c_str(), go.valueSize()); - go.objectWritten(); - }) - .def_property("callback", - [](GroupObject& go) - { - return go.callback(); - }, - [](GroupObject& go, GroupObjectUpdatedHandler handler) - { - go.callback(handler); - } - ) - .def("callBack", (void(GroupObject::*)(GroupObjectUpdatedHandler))&GroupObject::callback); + .def(py::init()) + .def("asap", &GroupObject::asap) + .def("size", &GroupObject::valueSize) + .def_property("value", + [](GroupObject & go) + { + + return py::bytes((const char*)go.valueRef(), go.valueSize()); + }, + [](GroupObject & go, py::bytes bytesValue) + { + const auto value = static_cast(bytesValue); + + if (value.length() != go.valueSize()) + throw std::length_error("bytesValue"); + + auto valueRef = go.valueRef(); + memcpy(valueRef, value.c_str(), go.valueSize()); + go.objectWritten(); + }); + } diff --git a/examples/knxPython/setup.py b/examples/knxPython/setup.py deleted file mode 100644 index 114d1ff..0000000 --- a/examples/knxPython/setup.py +++ /dev/null @@ -1,81 +0,0 @@ -import os -import re -import sys -import platform -import subprocess - -from setuptools import setup, Extension, find_packages -from setuptools.command.build_ext import build_ext -from distutils.version import LooseVersion - -from write_version_info import get_version_info - -class CMakeExtension(Extension): - def __init__(self, name, sourcedir=''): - Extension.__init__(self, name, sources=[]) - self.sourcedir = os.path.abspath(sourcedir) - - -class CMakeBuild(build_ext): - def run(self): - try: - out = subprocess.check_output(['cmake', '--version']) - except OSError: - raise RuntimeError("CMake must be installed to build the following extensions: " + - ", ".join(e.name for e in self.extensions)) - - cmake_version = LooseVersion(re.search(r'version\s*([\d.]+)', out.decode()).group(1)) - if cmake_version < LooseVersion('3.5.0'): - raise RuntimeError("CMake >= 3.5.0 is required") - - for ext in self.extensions: - self.build_extension(ext) - - def build_extension(self, ext): - extdir = os.path.abspath(os.path.dirname(self.get_ext_fullpath(ext.name))) - cmake_args = ['-DCMAKE_LIBRARY_OUTPUT_DIRECTORY=' + extdir, - '-DPYTHON_EXECUTABLE=' + sys.executable] - - build_type = os.environ.get("BUILD_TYPE", "Release") - build_args = ['--config', build_type] - - # Pile all .so in one place and use $ORIGIN as RPATH - cmake_args += ["-DCMAKE_BUILD_WITH_INSTALL_RPATH=TRUE"] - cmake_args += ["-DCMAKE_INSTALL_RPATH={}".format("$ORIGIN")] - - if platform.system() == "Windows": - cmake_args += ['-DCMAKE_LIBRARY_OUTPUT_DIRECTORY_{}={}'.format(build_type.upper(), extdir)] - if sys.maxsize > 2**32: - cmake_args += ['-A', 'x64'] - build_args += ['--', '/m'] - else: - cmake_args += ['-DCMAKE_BUILD_TYPE=' + build_type] - build_args += ['--', '-j4'] - - env = os.environ.copy() - env['CXXFLAGS'] = '{} -DVERSION_INFO=\\"{}\\"'.format(env.get('CXXFLAGS', ''), - self.distribution.get_version()) - if not os.path.exists(self.build_temp): - os.makedirs(self.build_temp) - subprocess.check_call(['cmake', ext.sourcedir] + cmake_args, cwd=self.build_temp, env=env) - subprocess.check_call(['cmake', - '--build', '.', - '--target', ext.name - ] + build_args, - cwd=self.build_temp) - -setup( - name='knxPython', - version=get_version_info()[3], - author='Thomas Kunze', - author_email='thomas.kunze@gmx.com', - description='Lib to implement knx-devices', - long_description_content_type="text/markdown", - long_description=open("../../README.md").read(), - ext_modules=[CMakeExtension('knx')], - packages=find_packages(), - license="GPL3", - cmdclass=dict(build_ext=CMakeBuild), - url="https://github.com/thelsing/knx", - zip_safe=False -) diff --git a/examples/knxPython/write_version_info.py b/examples/knxPython/write_version_info.py deleted file mode 100644 index cd8e254..0000000 --- a/examples/knxPython/write_version_info.py +++ /dev/null @@ -1,49 +0,0 @@ -import subprocess -import time -import sys -import socket - -def get_version_info(): - - try: - git_revision = subprocess.check_output(["git", "rev-parse", "HEAD"]).decode("utf-8") .split("\n")[0] - git_branch = subprocess.check_output(["git", "rev-parse","--abbrev-ref", "HEAD"]).decode("utf-8").split("\n")[0] - except (subprocess.CalledProcessError, OSError): - git_revision = "" - git_branch = "non-git" - - def read_version(): - with open("VERSION") as f: - return f.readline().strip() - - build_datetime = time.strftime("%a, %d %b %Y %H:%M:%S +0000", time.gmtime()) - version_number = read_version() - - hostname = socket.gethostname() - - return git_revision, git_branch, build_datetime, version_number, hostname - -def print_version_number(): - sys.stdout.write(get_version_info()[3]) - -if __name__ =="__main__": - - output_file = sys.argv[1] - with open(output_file, "w") as fout: - fout.write("""#pragma once - -namespace knx{{ -namespace version{{ - -auto constexpr git_revision = u8"{0}"; -auto constexpr git_branch = u8"{1}"; -auto constexpr build_datetime = u8"{2}"; -auto constexpr version_number = u8"{3}"; -auto constexpr build_hostname = u8"{4}"; - - -}} -}} - -""".format(*get_version_info())) - diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..7c0bb39 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,7 @@ +[build-system] +requires = ["scikit-build-core", "pybind11"] +build-backend = "scikit_build_core.build" + +[project] +name = "knxPython" +version = "0.1.5" \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 461e283..605e593 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -187,8 +187,10 @@ set(SOURCES set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -Wall -Wno-unknown-pragmas -g -O0") set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Wall -Wno-unknown-pragmas -g -O0") +SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC -g -O0") -add_library(knx SHARED ${SOURCES}) +add_library(knx ${SOURCES}) target_include_directories(knx PUBLIC .) -set_property(TARGET knx PROPERTY CXX_STANDARD 11) +set_property(TARGET knx PROPERTY CXX_STANDARD 17) target_compile_definitions(knx PUBLIC) +install(TARGETS knx DESTINATION lib) \ No newline at end of file diff --git a/src/knx/bau/bau_systemB.cpp b/src/knx/bau/bau_systemB.cpp index d16c2e4..8b903f5 100644 --- a/src/knx/bau/bau_systemB.cpp +++ b/src/knx/bau/bau_systemB.cpp @@ -237,6 +237,8 @@ namespace Knx // Flush the EEPROM before resetting _memory.writeMemory(); + // Disable progmode (should be disabled after reboot, but some plaforms don't really restart) + _deviceObj.progMode(false); _platform.restart(); } diff --git a/src/knx/bau/bau_systemB_device.cpp b/src/knx/bau/bau_systemB_device.cpp index a1b9840..abcc071 100644 --- a/src/knx/bau/bau_systemB_device.cpp +++ b/src/knx/bau/bau_systemB_device.cpp @@ -1,10 +1,13 @@ #include "bau_systemB_device.h" #include "../bits.h" +#include "../util/logger.h" #include #include +#define LOGGER Logger::logger("BauSystemBDevice") + namespace Knx { @@ -77,15 +80,7 @@ namespace Knx if (flag == WriteRequest) { -#ifdef SMALL_GROUPOBJECT GroupObject::processClassCallback(go); -#else - GroupObjectUpdatedHandler handler = go.callback(); - - if (handler) - handler(go); - -#endif } if (!go.communicationEnable()) @@ -126,6 +121,8 @@ namespace Knx void BauSystemBDevice::updateGroupObject(GroupObject& go, uint8_t* data, uint8_t length) { + LOGGER.info("updateGroupObject %d flag %d %B", go.asap(), go.commFlag(), data, length); + uint8_t* goData = go.valueRef(); if (length != go.valueSize()) @@ -139,15 +136,7 @@ namespace Knx if (go.commFlag() != WriteRequest) { go.commFlag(Updated); -#ifdef SMALL_GROUPOBJECT GroupObject::processClassCallback(go); -#else - GroupObjectUpdatedHandler handler = go.callback(); - - if (handler) - handler(go); - -#endif } else { diff --git a/src/knx/cemi_server/cemi_server_object.h b/src/knx/cemi_server/cemi_server_object.h index e65836d..8bf6f4f 100644 --- a/src/knx/cemi_server/cemi_server_object.h +++ b/src/knx/cemi_server/cemi_server_object.h @@ -11,5 +11,9 @@ namespace Knx void setMediumTypeAsSupported(DptMedium dptMedium); void clearSupportedMediaTypes(); + const char* name() override + { + return "CemiServerObject"; + } }; } \ No newline at end of file diff --git a/src/knx/coupler/router_object.h b/src/knx/coupler/router_object.h index ba2beff..deb0a8e 100644 --- a/src/knx/coupler/router_object.h +++ b/src/knx/coupler/router_object.h @@ -38,7 +38,10 @@ namespace Knx void masterReset(EraseCode eraseCode, uint8_t channel) override; const uint8_t* restore(const uint8_t* buffer) override; - + const char* name() override + { + return "RouterObject"; + } protected: void beforeStateChange(LoadState& newState) override; diff --git a/src/knx/data_secure/security_interface_object.h b/src/knx/data_secure/security_interface_object.h index 82b1c2a..2895654 100644 --- a/src/knx/data_secure/security_interface_object.h +++ b/src/knx/data_secure/security_interface_object.h @@ -35,7 +35,10 @@ namespace Knx uint8_t* save(uint8_t* buffer) override; const uint8_t* restore(const uint8_t* buffer) override; uint16_t saveSize() override; - + const char* name() override + { + return "SecurityObject"; + } private: void setSecurityMode(bool enabled); diff --git a/src/knx/group_object/group_object.cpp b/src/knx/group_object/group_object.cpp index 35254c5..6a9f131 100644 --- a/src/knx/group_object/group_object.cpp +++ b/src/knx/group_object/group_object.cpp @@ -3,347 +3,331 @@ #include "datapoint_types.h" #include "../interface_object/group_object_table_object.h" #include "../bits.h" +#include "../util/logger.h" #include +#define LOGGER Logger::logger("GroupObject") + namespace Knx { -#ifdef SMALL_GROUPOBJECT GroupObjectUpdatedHandler GroupObject::_updateHandlerStatic = 0; -#endif -GroupObjectTableObject* GroupObject::_table = 0; + GroupObjectTableObject* GroupObject::_table = 0; -GroupObject::GroupObject() -{ - _data = 0; - _uninitialized = true; - _commFlag = Uninitialized; - _dataLength = 0; -#ifndef SMALL_GROUPOBJECT - _updateHandler = 0; -#endif -} - -GroupObject::~GroupObject() -{ - if (_data) - delete[] _data; -} - -bool GroupObject::responseUpdateEnable() -{ - if (!_table) - return false; - - return bitRead(ntohs(_table->_tableData[_asap]), 15) > 0; -} - -bool GroupObject::transmitEnable() -{ - if (!_table) - return false; - - return bitRead(ntohs(_table->_tableData[_asap]), 14) > 0 ; -} - -bool GroupObject::valueReadOnInit() -{ - if (!_table) - return false; - - return bitRead(ntohs(_table->_tableData[_asap]), 13) > 0; -} - -bool GroupObject::writeEnable() -{ - if (!_table) - return false; - - return bitRead(ntohs(_table->_tableData[_asap]), 12) > 0 ; -} - -bool GroupObject::readEnable() -{ - if (!_table) - return false; - - // we forbid reading of new (uninitialized) go - if (_uninitialized) - return false; - - return bitRead(ntohs(_table->_tableData[_asap]), 11) > 0; -} - -bool GroupObject::communicationEnable() -{ - if (!_table) - return false; - - return bitRead(ntohs(_table->_tableData[_asap]), 10) > 0; -} - - -Priority GroupObject::priority() -{ - if (!_table) - return LowPriority; - - return (Priority)((ntohs(_table->_tableData[_asap]) >> 6) & (3 << 2)) ; -} - -uint8_t* GroupObject::valueRef() -{ - return _data; -} - -uint16_t GroupObject::asap() -{ - return _asap; -} - -size_t GroupObject::goSize() -{ - size_t size = sizeInTelegram(); - - if (size == 0) - return 1; - - return size; -} - -// see knxspec 3.5.1 p. 178 -size_t GroupObject::asapValueSize(uint8_t code) const -{ - if (code < 7) - return 0; - - if (code < 8) - return 1; - - if (code < 11 || (code > 20 && code < 255)) - return code - 6; - - switch (code) + GroupObject::GroupObject() { - case 11: - return 6; - - case 12: - return 8; - - case 13: - return 10; - - case 14: - return 14; - - case 15: - return 5; - - case 16: - return 7; - - case 17: - return 9; - - case 18: - return 11; - - case 19: - return 12; - - case 20: - return 13; - - case 255: - return 252; + _data = 0; + _uninitialized = true; + _commFlag = Uninitialized; + _dataLength = 0; } - return -1; -} - - -ComFlag GroupObject::commFlag() -{ - return _commFlag; -} - -void GroupObject::commFlag(ComFlag value) -{ - _commFlag = value; - - if (value == WriteRequest || value == Updated || value == Ok) - _uninitialized = false; -} - -bool GroupObject::initialized() -{ - return !_uninitialized; -} - -void GroupObject::requestObjectRead() -{ - commFlag(ReadRequest); -} - -void GroupObject::objectWritten() -{ - commFlag(WriteRequest); -} - -size_t GroupObject::valueSize() -{ - return _dataLength; -} - -size_t GroupObject::sizeInTelegram() -{ - uint8_t code = lowByte(ntohs(_table->_tableData[_asap])); - return asapValueSize(code); -} - -size_t GroupObject::sizeInMemory() const -{ - uint8_t code = lowByte(ntohs(_table->_tableData[_asap])); - size_t result = asapValueSize(code); - - if (code == 0) - return 1; - - if (code == 14) - return 14 + 1; - - return result; -} - -#ifdef SMALL_GROUPOBJECT -GroupObjectUpdatedHandler GroupObject::classCallback() -{ - return _updateHandlerStatic; -} - -void GroupObject::classCallback(GroupObjectUpdatedHandler handler) -{ - _updateHandlerStatic = handler; -} - -void GroupObject::processClassCallback(GroupObject& ko) -{ - if (_updateHandlerStatic != 0) - _updateHandlerStatic(ko); -} - -#else -void GroupObject::callback(GroupObjectUpdatedHandler handler) -{ - _updateHandler = handler; -} - - -GroupObjectUpdatedHandler GroupObject::callback() -{ - return _updateHandler; -} -#endif - -void GroupObject::value(const KNXValue& value, const Dpt& type) -{ - valueNoSend(value, type); - objectWritten(); -} - - -KNXValue GroupObject::value(const Dpt& type) -{ - KNXValue value = ""; - KNX_Decode_Value(_data, _dataLength, type, value); - return value; -} - -bool GroupObject::tryValue(KNXValue& value, const Dpt& type) -{ - return KNX_Decode_Value(_data, _dataLength, type, value); -} - -#ifndef SMALL_GROUPOBJECT -void GroupObject::dataPointType(Dpt value) -{ - _datapointType = value; -} - - -Dpt GroupObject::dataPointType() -{ - return _datapointType; -} - - -bool GroupObject::tryValue(KNXValue& value) -{ - return tryValue(value, _datapointType); -} - - -void GroupObject::value(const KNXValue& value) -{ - this->value(value, _datapointType); -} - - -KNXValue GroupObject::value() -{ - return value(_datapointType); -} - - -void GroupObject::valueNoSend(const KNXValue& value) -{ - valueNoSend(value, _datapointType); -} -#endif - -void GroupObject::valueNoSend(const KNXValue& value, const Dpt& type) -{ - if (_uninitialized) - commFlag(Ok); - - KNX_Encode_Value(value, _data, _dataLength, type); -} - -bool GroupObject::valueNoSendCompare(const KNXValue& value, const Dpt& type) -{ - if (_uninitialized) + GroupObject::~GroupObject() { - // always set first value - this->valueNoSend(value, type); - return true; + if (_data) + delete[] _data; } - else + + bool GroupObject::responseUpdateEnable() { - // convert new value to given dtp - uint8_t newData[_dataLength]; - memset(newData, 0, _dataLength); - KNX_Encode_Value(value, newData, _dataLength, type); + if (!_table) + return false; - // check for change in converted value / update value on change only - const bool dataChanged = memcmp(_data, newData, _dataLength); - - if (dataChanged) - memcpy(_data, newData, _dataLength); - - return dataChanged; + return bitRead(ntohs(_table->_tableData[_asap]), 15) > 0; } -} -bool GroupObject::valueCompare(const KNXValue& value, const Dpt& type) -{ - if (valueNoSendCompare(value, type)) + bool GroupObject::transmitEnable() { + if (!_table) + return false; + + return bitRead(ntohs(_table->_tableData[_asap]), 14) > 0 ; + } + + bool GroupObject::valueReadOnInit() + { + if (!_table) + return false; + + return bitRead(ntohs(_table->_tableData[_asap]), 13) > 0; + } + + bool GroupObject::writeEnable() + { + if (!_table) + return false; + + return bitRead(ntohs(_table->_tableData[_asap]), 12) > 0 ; + } + + bool GroupObject::readEnable() + { + if (!_table) + return false; + + // we forbid reading of new (uninitialized) go + if (_uninitialized) + return false; + + return bitRead(ntohs(_table->_tableData[_asap]), 11) > 0; + } + + bool GroupObject::communicationEnable() + { + if (!_table) + return false; + + return bitRead(ntohs(_table->_tableData[_asap]), 10) > 0; + } + + + Priority GroupObject::priority() + { + if (!_table) + return LowPriority; + + return (Priority)((ntohs(_table->_tableData[_asap]) >> 6) & (3 << 2)) ; + } + + uint8_t* GroupObject::valueRef() + { + return _data; + } + + uint16_t GroupObject::asap() + { + return _asap; + } + + size_t GroupObject::goSize() + { + size_t size = sizeInTelegram(); + + if (size == 0) + return 1; + + return size; + } + + // see knxspec 3.5.1 p. 178 + size_t GroupObject::asapValueSize(uint8_t code) const + { + if (code < 7) + return 0; + + if (code < 8) + return 1; + + if (code < 11 || (code > 20 && code < 255)) + return code - 6; + + switch (code) + { + case 11: + return 6; + + case 12: + return 8; + + case 13: + return 10; + + case 14: + return 14; + + case 15: + return 5; + + case 16: + return 7; + + case 17: + return 9; + + case 18: + return 11; + + case 19: + return 12; + + case 20: + return 13; + + case 255: + return 252; + } + + return -1; + } + + + ComFlag GroupObject::commFlag() + { + return _commFlag; + } + + void GroupObject::commFlag(ComFlag value) + { + _commFlag = value; + + if (value == WriteRequest || value == Updated || value == Ok) + _uninitialized = false; + } + + bool GroupObject::initialized() + { + return !_uninitialized; + } + + void GroupObject::requestObjectRead() + { + commFlag(ReadRequest); + } + + void GroupObject::objectWritten() + { + commFlag(WriteRequest); + } + + size_t GroupObject::valueSize() + { + return _dataLength; + } + + size_t GroupObject::sizeInTelegram() + { + uint8_t code = lowByte(ntohs(_table->_tableData[_asap])); + return asapValueSize(code); + } + + size_t GroupObject::sizeInMemory() const + { + uint8_t code = lowByte(ntohs(_table->_tableData[_asap])); + size_t result = asapValueSize(code); + + if (code == 0) + return 1; + + if (code == 14) + return 14 + 1; + + return result; + } + + GroupObjectUpdatedHandler GroupObject::classCallback() + { + return _updateHandlerStatic; + } + + void GroupObject::classCallback(GroupObjectUpdatedHandler handler) + { + _updateHandlerStatic = handler; + } + + void GroupObject::processClassCallback(GroupObject& go) + { + LOGGER.info("processClassCallback for go %d, handlerset:%d", go.asap(), _updateHandlerStatic != 0); + if (_updateHandlerStatic != 0) + _updateHandlerStatic(go); + } + + void GroupObject::value(const KNXValue& value, const Dpt& type) + { + valueNoSend(value, type); objectWritten(); - return true; } - return false; -} + + KNXValue GroupObject::value(const Dpt& type) + { + KNXValue value = ""; + KNX_Decode_Value(_data, _dataLength, type, value); + return value; + } + + bool GroupObject::tryValue(KNXValue& value, const Dpt& type) + { + return KNX_Decode_Value(_data, _dataLength, type, value); + } + +#ifndef SMALL_GROUPOBJECT + void GroupObject::dataPointType(Dpt value) + { + _datapointType = value; + } + + + Dpt GroupObject::dataPointType() + { + return _datapointType; + } + + bool GroupObject::tryValue(KNXValue& value) + { + return tryValue(value, _datapointType); + } + + + void GroupObject::value(const KNXValue& value) + { + this->value(value, _datapointType); + } + + + KNXValue GroupObject::value() + { + return value(_datapointType); + } + + + void GroupObject::valueNoSend(const KNXValue& value) + { + valueNoSend(value, _datapointType); + } +#endif + + void GroupObject::valueNoSend(const KNXValue& value, const Dpt& type) + { + if (_uninitialized) + commFlag(Ok); + + KNX_Encode_Value(value, _data, _dataLength, type); + } + + bool GroupObject::valueNoSendCompare(const KNXValue& value, const Dpt& type) + { + if (_uninitialized) + { + // always set first value + this->valueNoSend(value, type); + return true; + } + else + { + // convert new value to given dtp + uint8_t newData[_dataLength]; + memset(newData, 0, _dataLength); + KNX_Encode_Value(value, newData, _dataLength, type); + + // check for change in converted value / update value on change only + const bool dataChanged = memcmp(_data, newData, _dataLength); + + if (dataChanged) + memcpy(_data, newData, _dataLength); + + return dataChanged; + } + } + + bool GroupObject::valueCompare(const KNXValue& value, const Dpt& type) + { + if (valueNoSendCompare(value, type)) + { + objectWritten(); + return true; + } + + return false; + } } \ No newline at end of file diff --git a/src/knx/group_object/group_object.h b/src/knx/group_object/group_object.h index 81bcde5..46938cd 100644 --- a/src/knx/group_object/group_object.h +++ b/src/knx/group_object/group_object.h @@ -144,16 +144,6 @@ namespace Knx */ uint16_t asap(); -#ifndef SMALL_GROUPOBJECT - /** - * register a callback for this group object. The registered callback will be called if the group object was changed from the bus. - */ - void callback(GroupObjectUpdatedHandler handler); - /** - * returns the registered callback - */ - GroupObjectUpdatedHandler callback(); -#endif /** * return the current value of the group object. * @param type the datapoint type used for the conversion. If this doesn't fit to the group object the returned value is invalid. @@ -250,23 +240,20 @@ namespace Knx * sets the datapoint type of the group object. */ void dataPointType(Dpt value); -#else +#endif /** - * Alternative callback processing: register one global callback for all group object. + * Callback processing: register one global callback for all group object. * The registered callback will be called if any group object was changed from the bus. * The callback method has to dispatch to the correct handler for this group object. */ static GroupObjectUpdatedHandler classCallback(); static void classCallback(GroupObjectUpdatedHandler handler); static void processClassCallback(GroupObject& ko); -#endif private: // class members static GroupObjectTableObject* _table; -#ifdef SMALL_GROUPOBJECT static GroupObjectUpdatedHandler _updateHandlerStatic; -#endif size_t asapValueSize(uint8_t code) const; size_t goSize(); @@ -276,7 +263,6 @@ namespace Knx uint8_t* _data = 0; uint8_t _dataLength = 0; #ifndef SMALL_GROUPOBJECT - GroupObjectUpdatedHandler _updateHandler; Dpt _datapointType; #endif }; diff --git a/src/knx/interface_object/address_table_object.h b/src/knx/interface_object/address_table_object.h index 6e33ffe..d4fbee9 100644 --- a/src/knx/interface_object/address_table_object.h +++ b/src/knx/interface_object/address_table_object.h @@ -51,7 +51,10 @@ namespace Knx * @return true if the address table contains the group address, false otherwise */ bool contains(uint16_t groupAddress); - + const char* name() override + { + return "AddressTable"; + } protected: void beforeStateChange(LoadState& newState) override; diff --git a/src/knx/interface_object/application_program_object.h b/src/knx/interface_object/application_program_object.h index 6da9117..5fe8e14 100644 --- a/src/knx/interface_object/application_program_object.h +++ b/src/knx/interface_object/application_program_object.h @@ -22,7 +22,10 @@ namespace Knx uint16_t getWord(uint32_t addr); uint32_t getInt(uint32_t addr); double getFloat(uint32_t addr, ParameterFloatEncodings encoding); - + const char* name() override + { + return "ApplicationProgram"; + } protected: void beforeStateChange(LoadState& newState) override; }; diff --git a/src/knx/interface_object/association_table_object.h b/src/knx/interface_object/association_table_object.h index da803b9..8d5a97a 100644 --- a/src/knx/interface_object/association_table_object.h +++ b/src/knx/interface_object/association_table_object.h @@ -13,7 +13,10 @@ namespace Knx int32_t translateAsap(uint16_t asap); int32_t nextAsap(uint16_t tsap, uint16_t& startIdx); - + const char* name() override + { + return "AssociationTable"; + } protected: void beforeStateChange(LoadState& newState) override; diff --git a/src/knx/interface_object/device_object.h b/src/knx/interface_object/device_object.h index fffc737..eeb68c5 100644 --- a/src/knx/interface_object/device_object.h +++ b/src/knx/interface_object/device_object.h @@ -43,6 +43,10 @@ namespace Knx const uint8_t* rfDomainAddress(); void rfDomainAddress(uint8_t* value); uint8_t defaultHopCount(); + const char* name() override + { + return "DeviceObject"; + } private: uint8_t _prgMode = 0; #if MASK_VERSION == 0x091A || MASK_VERSION == 0x2920 diff --git a/src/knx/interface_object/group_object_table_object.cpp b/src/knx/interface_object/group_object_table_object.cpp index a52bed6..50766c1 100644 --- a/src/knx/interface_object/group_object_table_object.cpp +++ b/src/knx/interface_object/group_object_table_object.cpp @@ -35,6 +35,9 @@ namespace Knx GroupObject& GroupObjectTableObject::get(uint16_t asap) { + if(asap == 0 || asap > entryCount()) + LOGGER.warning("get: %d is no valid GroupObject. Asap must be > 0 and <= %d", asap, entryCount()); + return _groupObjects[asap - 1]; } @@ -100,10 +103,13 @@ namespace Knx bool GroupObjectTableObject::initGroupObjects() { if (!_tableData) + { + LOGGER.info("initGroupObjects: no table data"); return false; + } freeGroupObjects(); - + LOGGER.info("initGroupObjects %B", _tableData, 40); uint16_t goCount = ntohs(_tableData[0]); _groupObjects = new GroupObject[goCount]; diff --git a/src/knx/interface_object/group_object_table_object.h b/src/knx/interface_object/group_object_table_object.h index a4ead13..7753064 100644 --- a/src/knx/interface_object/group_object_table_object.h +++ b/src/knx/interface_object/group_object_table_object.h @@ -18,7 +18,10 @@ namespace Knx void groupObjects(GroupObject* objs, uint16_t size); const uint8_t* restore(const uint8_t* buffer) override; - + const char* name() override + { + return "GroupObjectTable"; + } protected: void beforeStateChange(LoadState& newState) override; diff --git a/src/knx/interface_object/property.h b/src/knx/interface_object/property.h index ff6723e..9e255f2 100644 --- a/src/knx/interface_object/property.h +++ b/src/knx/interface_object/property.h @@ -292,6 +292,10 @@ namespace Knx uint8_t write(uint16_t position, uint16_t value); uint8_t write(uint32_t value); uint8_t write(const uint8_t* value); + const char* name() override + { + return ""; + } protected: PropertyID _id; bool _writeEnable; diff --git a/src/knx/interface_object/table_object.cpp b/src/knx/interface_object/table_object.cpp index 7a6ef69..ec8466d 100644 --- a/src/knx/interface_object/table_object.cpp +++ b/src/knx/interface_object/table_object.cpp @@ -1,6 +1,9 @@ #include "table_object.h" #include "../bits.h" #include "../util/memory.h" +#include "../util/logger.h" + +#define LOGGER Logger::logger("TableObject") #include @@ -81,24 +84,23 @@ namespace Knx const uint8_t* TableObject::restore(const uint8_t* buffer) { - //println("TableObject::restore"); - uint8_t state = 0; buffer = popByte(state, buffer); _state = (LoadState)state; + buffer = popInt(_size, buffer); uint32_t relativeAddress = 0; buffer = popInt(relativeAddress, buffer); - //println(relativeAddress); + LOGGER.info("restore: state %s, size %d, relAdr %d", enum_name(_state), _size, relativeAddress); if (relativeAddress != 0) _data = _memory.toAbsolute(relativeAddress); else _data = 0; - //println((uint32_t)_data); + LOGGER.info("restore: content %B", _data, _size); return InterfaceObject::restore(buffer); } diff --git a/src/knx/ip/ip_data_link_layer.cpp b/src/knx/ip/ip_data_link_layer.cpp index 75d9c4f..b8c6055 100644 --- a/src/knx/ip/ip_data_link_layer.cpp +++ b/src/knx/ip/ip_data_link_layer.cpp @@ -1146,7 +1146,7 @@ namespace Knx if (!_enabled) return false; - LOGGER.info("sendUnicast to %d.%d.%d.%d:%d %s %s", addr & 0xFF000000 >> 24, addr & 0xFF0000 >> 16, addr & 0xFF00 >> 8, addr & 0xFF + LOGGER.info("sendUnicast to %d.%d.%d.%d:%d %s %s", (addr & 0xFF000000) >> 24, (addr & 0xFF0000) >> 16, (addr & 0xFF00) >> 8, addr & 0xFF , port, enum_name(ipFrame.protocolVersion()), enum_name(ipFrame.serviceTypeIdentifier())); return _platform.sendBytesMultiCast(ipFrame.data(), ipFrame.totalLength()); diff --git a/src/knx/ip/ip_parameter_object.h b/src/knx/ip/ip_parameter_object.h index cb5df31..ed7ae66 100644 --- a/src/knx/ip/ip_parameter_object.h +++ b/src/knx/ip/ip_parameter_object.h @@ -12,6 +12,10 @@ namespace Knx public: IpParameterObject(DeviceObject& deviceObject, Platform& platform); uint16_t* additionalIndivualAddresses(uint8_t& numAddresses); + const char* name() override + { + return "IpParameterObject"; + } private: DeviceObject& _deviceObject; Platform& _platform; diff --git a/src/knx/knx_facade.h b/src/knx/knx_facade.h index 607af3a..ab23bea 100644 --- a/src/knx/knx_facade.h +++ b/src/knx/knx_facade.h @@ -429,6 +429,11 @@ namespace Knx return buffer; } + const char* name() override + { + return "KnxFacade"; + } + uint16_t saveSize() { return _saveSize; diff --git a/src/knx/platform/linux_platform.cpp b/src/knx/platform/linux_platform.cpp index 4ff4ee9..f671509 100644 --- a/src/knx/platform/linux_platform.cpp +++ b/src/knx/platform/linux_platform.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include // Needed for SPI port #include // Needed for SPI port @@ -38,6 +39,8 @@ #include "../ip/ip_host_protocol_address_information.h" #include "../util/logger.h" +namespace fs = std::filesystem; + #define LOGGER Logger::logger("LinuxPlatform") #define MAX_MEM 4096 @@ -141,7 +144,8 @@ namespace Knx void LinuxPlatform::restart() { - execv(_args[0], _args); + if (_args != nullptr) + execv(_args[0], _args); } void LinuxPlatform::fatalError() @@ -154,21 +158,13 @@ namespace Knx void LinuxPlatform::setupMultiCast(uint32_t addr, uint16_t port) { + LOGGER.info("setupMultiCast %d.%d.%d.%d:%d", (addr & 0xFF000000) >> 24, (addr & 0xFF0000) >> 16, (addr & 0xFF00) >> 8, addr & 0xFF, port); + if (_multicastSocketFd >= 0) closeMultiCast(); _multicastAddr = addr; _multicastPort = port; - - struct ip_mreq command; - uint32_t loop = 1; - - struct sockaddr_in sin; - memset(&sin, 0, sizeof(sin)); - sin.sin_family = AF_INET; - sin.sin_addr.s_addr = htonl(INADDR_ANY); - sin.sin_port = htons(port); - _multicastSocketFd = socket(AF_INET, SOCK_DGRAM, 0); if (_multicastSocketFd == -1) @@ -178,14 +174,23 @@ namespace Knx } /* Mehr Prozessen erlauben, denselben Port zu nutzen */ - loop = 1; + int reuse = 1; - if (setsockopt(_multicastSocketFd, SOL_SOCKET, SO_REUSEADDR, &loop, sizeof(loop)) < 0) + if (setsockopt(_multicastSocketFd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0) { LOGGER.critical("setsockopt:SO_REUSEADDR %s", strerror(errno)); fatalError(); } + + struct sockaddr_in sin = {0}; + + sin.sin_family = AF_INET; + + sin.sin_addr.s_addr = htonl(INADDR_ANY); + + sin.sin_port = htons(port); + if (bind(_multicastSocketFd, (struct sockaddr*)&sin, sizeof(sin)) < 0) { LOGGER.critical("bind %s", strerror(errno)); @@ -193,7 +198,7 @@ namespace Knx } /* loopback */ - loop = 0; + uint32_t loop = 0; if (setsockopt(_multicastSocketFd, IPPROTO_IP, IP_MULTICAST_LOOP, &loop, sizeof(loop)) < 0) { @@ -202,6 +207,7 @@ namespace Knx } /* Join the broadcast group: */ + struct ip_mreq command = {0}; command.imr_multiaddr.s_addr = htonl(addr); command.imr_interface.s_addr = htonl(INADDR_ANY); @@ -290,6 +296,14 @@ namespace Knx #define FLASHSIZE 0x10000 void LinuxPlatform::doMemoryMapping() { + fs::path filePath = _flashFilePath; + fs::path dir = filePath.parent_path(); + + if (!dir.empty() && !fs::exists(dir)) + { + fs::create_directory(dir); + } + _fd = open(_flashFilePath.c_str(), O_RDWR | O_CREAT, S_IRWXU | S_IRGRP | S_IROTH); if (_fd < 0) @@ -408,6 +422,7 @@ namespace Knx void LinuxPlatform::flashFilePath(const std::string path) { _flashFilePath = path; + } std::string LinuxPlatform::flashFilePath() diff --git a/src/knx/rf/rf_medium_object.h b/src/knx/rf/rf_medium_object.h index 9a478fb..a5b53ff 100644 --- a/src/knx/rf/rf_medium_object.h +++ b/src/knx/rf/rf_medium_object.h @@ -10,7 +10,10 @@ namespace Knx RfMediumObject(); const uint8_t* rfDomainAddress(); void rfDomainAddress(const uint8_t* value); - + const char* name() override + { + return "RfMediumObject"; + } private: uint8_t _rfDiagSourceAddressFilterTable[24] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,}; uint8_t _rfDiagLinkBudgetTable[24] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,}; diff --git a/src/knx/util/logger.cpp b/src/knx/util/logger.cpp index 817502d..a66bf32 100644 --- a/src/knx/util/logger.cpp +++ b/src/knx/util/logger.cpp @@ -71,22 +71,22 @@ namespace Knx bool Logger::log(LogType type) { #ifndef KNX_NO_PRINT - /*LogType* level = _loggers.get(_name); + LogType* level = _loggers.get(_name); if (level == nullptr) { print("Logger "); print(_name); - print(" is disabled. Use Logger::logLevel(\""); + print(" is set to Warning. Use Logger::logLevel(\""); print(_name); - println("\", Logger::Info) to enable."); - _loggers.insertOrAssign(_name, Info); - return false; + println("\", Logger::Info) to show more logging."); + _loggers.insertOrAssign(_name, Warning); + level = _loggers.get(_name); } if (*level > type) return false; - */ + print(millis()); print(" "); print(_name); @@ -131,7 +131,9 @@ namespace Knx } else if (*format == 'B') { - printHex("", va_arg(args, uint8_t*), va_arg(args, size_t), false); + uint8_t* data = va_arg(args, uint8_t*); + size_t length = va_arg(args, int); + printHex("", data, length, false); } } else diff --git a/src/knx/util/memory.cpp b/src/knx/util/memory.cpp index 734da1c..e3948a8 100644 --- a/src/knx/util/memory.cpp +++ b/src/knx/util/memory.cpp @@ -94,7 +94,7 @@ namespace Knx for (int i = 0; i < _saveCount; i++) { - LOGGER.info("Offset %d", buffer - flashStart); + LOGGER.info("%s Offset %d", _saveRestores[i]->name(), buffer - flashStart); buffer = _saveRestores[i]->restore(buffer); } @@ -111,10 +111,11 @@ namespace Knx for (int i = 0; i < _tableObjCount; i++) { ptrdiff_t offset = (buffer - flashStart); + LOGGER.info("%s Offset %d", _tableObjects[i]->name(), offset); buffer = _tableObjects[i]->restore(buffer); uint16_t memorySize = 0; buffer = popWord(memorySize, buffer); - LOGGER.info("Offset %d, Size %d", offset, memorySize); + LOGGER.info("%s Size %d", _tableObjects[i]->name(), memorySize); if (memorySize == 0) continue; diff --git a/src/knx/util/save_restore.h b/src/knx/util/save_restore.h index 7482d08..3070189 100644 --- a/src/knx/util/save_restore.h +++ b/src/knx/util/save_restore.h @@ -43,5 +43,7 @@ namespace Knx { return 0; } + + virtual const char* name() = 0; }; } \ No newline at end of file