From 21c8d6dabbc0ce57c06939df854504b2a05ca600 Mon Sep 17 00:00:00 2001 From: "Dr. Sathya Laufer" Date: Wed, 27 Feb 2019 14:08:58 +0100 Subject: [PATCH 01/47] Make rate limiter of presence-light configurable --- presence-light/PresenceLight.cpp | 9 ++++++--- presence-light/PresenceLight.h | 1 + presence-light/locales/en-US/presence-light | 12 +++++++----- presence-light/presence-light.hni | 8 +++++++- 4 files changed, 21 insertions(+), 9 deletions(-) diff --git a/presence-light/PresenceLight.cpp b/presence-light/PresenceLight.cpp index f8d4539c..440cb506 100644 --- a/presence-light/PresenceLight.cpp +++ b/presence-light/PresenceLight.cpp @@ -63,6 +63,9 @@ bool PresenceLight::init(Flows::PNodeInfo info) settingsIterator = info->info->structValue->find("keep-on"); if(settingsIterator != info->info->structValue->end()) _keepOn = settingsIterator->second->booleanValue; + settingsIterator = info->info->structValue->find("refraction-time"); + if(settingsIterator != info->info->structValue->end()) _refractionTime = Flows::Math::getUnsignedNumber(settingsIterator->second->stringValue); + return true; } catch(const std::exception& ex) @@ -297,7 +300,7 @@ void PresenceLight::timer() } } - std::this_thread::sleep_for(std::chrono::milliseconds(1000)); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); } catch(const std::exception& ex) { @@ -349,9 +352,9 @@ void PresenceLight::input(const Flows::PNodeInfo info, uint32_t index, const Flo { { //Rate limiter auto time = BaseLib::HelperFunctions::getTime(); - if(time - _lastInput < 1000) + if(time - _lastInput < _refractionTime) { - int64_t timeToSleep = 1000 - (time - _lastInput); + int64_t timeToSleep = _refractionTime - (time - _lastInput); std::this_thread::sleep_for(std::chrono::milliseconds(timeToSleep)); } _lastInput = BaseLib::HelperFunctions::getTime(); diff --git a/presence-light/PresenceLight.h b/presence-light/PresenceLight.h index 87ea0001..d1068d73 100644 --- a/presence-light/PresenceLight.h +++ b/presence-light/PresenceLight.h @@ -56,6 +56,7 @@ class PresenceLight: public Flows::INode int64_t _lastInput = -1; bool _switchOffOnInFalse = false; bool _keepOn = false; + uint32_t _refractionTime = 1000; //}}} std::atomic _lastLightEvent{-1}; diff --git a/presence-light/locales/en-US/presence-light b/presence-light/locales/en-US/presence-light index 4cb51d25..fa0da9fa 100644 --- a/presence-light/locales/en-US/presence-light +++ b/presence-light/locales/en-US/presence-light @@ -4,16 +4,18 @@ "label": { "name": "Name", "on-time": "On time", - "on-time2": "Time in seconds", + "on-time2": "Duration in seconds", "always-on-time": "Always on time", - "always-on-time2": "Time in seconds", + "always-on-time2": "Duration in seconds", "always-off-time": "Always off time", - "always-off-time2": "Time in seconds", + "always-off-time2": "Duration in seconds", "process-false": "Switch light off immediately, when \"IN\" is set to \"false\".", - "keep-on": "Keep light on when \"EN\" is set to \"false\"." + "keep-on": "Keep light on when \"EN\" is set to \"false\".", + "refraction-time": "Rate limiter delay", + "refraction-time2": "Duration in milliseconds" }, "paletteHelp": "

This node automatically switches lights on or off depending on presence.

", - "help": "

This node automatically switches lights on or off depending on presence. A presence signal (true) on IN sets S to SVAL (true of a profile number greater 0). S is reset to false or 0 after On time. An input of true on ON sets S to SVAL regardless of other inputs. ON is automatically reset after Always on time. When this happens, RES is set to true and S is set depending on IN and On time. An input of true on OFF sets S to false or 0 regardless of other inputs. OFF is automatically reset after Always off time. When this happens, RES is set to true and S is set depending on IN and On time. Setting Always on time or Always off time to 0, disables the timer so the lights will stay on or off until ON or OFF are set to false. Setting EN to false disables processing of IN for example during daytime. IN2 also sets S to true but an input of false to IN2 immediately sets S to false or 0 and resets the On time timer. TG toggles s setting it to SVAL and starting the timer when it is false and immediately setting it to false resetting the timer when it is true or greater 0. TG also works with profile (scene) numbers. 0 is recognized as the on/off profile toggling the last input scene number greater than 0. Any other number switches to that profile and also sets SVAL.

Note: The inputs are rate limited to one input per seconds to avoid too fast outputs.

On time, Always on time and Always off time can also be set dynamically by setting $message['onTime'], $message['alwaysOnTime'] or $message['alwaysOffTime']

", + "help": "

This node automatically switches lights on or off depending on presence. A presence signal (true) on IN sets S to SVAL (true of a profile number greater 0). S is reset to false or 0 after On time. An input of true on ON sets S to SVAL regardless of other inputs. ON is automatically reset after Always on time. When this happens, RES is set to true and S is set depending on IN and On time. An input of true on OFF sets S to false or 0 regardless of other inputs. OFF is automatically reset after Always off time. When this happens, RES is set to true and S is set depending on IN and On time. Setting Always on time or Always off time to 0, disables the timer so the lights will stay on or off until ON or OFF are set to false. Setting EN to false disables processing of IN for example during daytime. IN2 also sets S to true but an input of false to IN2 immediately sets S to false or 0 and resets the On time timer. TG toggles s setting it to SVAL and starting the timer when it is false and immediately setting it to false resetting the timer when it is true or greater 0. TG also works with profile (scene) numbers. 0 is recognized as the on/off profile toggling the last input scene number greater than 0. Any other number switches to that profile and also sets SVAL.

Note: The inputs are rate limited to one input per second by default (settable through Rate limiter delay) to avoid too fast outputs.

On time, Always on time and Always off time can also be set dynamically by setting $message['onTime'], $message['alwaysOnTime'] or $message['alwaysOffTime']

", "input1Description": "Enables or disables processing of IN.", "input2Description": "Enables \"always on mode\".", "input3Description": "Enables \"always off mode\".", diff --git a/presence-light/presence-light.hni b/presence-light/presence-light.hni index 919f4fa1..ef939c21 100644 --- a/presence-light/presence-light.hni +++ b/presence-light/presence-light.hni @@ -36,6 +36,11 @@ +
+ + + +
+ + + + diff --git a/presence-light/PresenceLight.cpp b/presence-light/PresenceLight.cpp index 455300b1..1ea734b7 100644 --- a/presence-light/PresenceLight.cpp +++ b/presence-light/PresenceLight.cpp @@ -540,6 +540,8 @@ void PresenceLight::input(const Flows::PNodeInfo info, uint32_t index, const Flo return; } + if(booleanStateValue && !inputValue) return; + if(!getLightState() || lastStateValue == 0 || (!booleanStateValue && input->integerValue64 > 0 && ((lastStateValue != -1 && lastStateValue != input->integerValue64) || _toggleProfile0Only))) { _stateValue.store(_lastNonNullStateValue.load(std::memory_order_acquire), std::memory_order_release); diff --git a/variable/toggle/MyNode.cpp b/variable/toggle/MyNode.cpp index 7fb43cc0..c3e73285 100644 --- a/variable/toggle/MyNode.cpp +++ b/variable/toggle/MyNode.cpp @@ -48,7 +48,8 @@ bool MyNode::init(Flows::PNodeInfo info) auto settingsIterator = info->info->structValue->find("variabletype"); if(settingsIterator != info->info->structValue->end()) variableType = settingsIterator->second->stringValue; - if(variableType == "device") _variableType = VariableType::device; + if(variableType == "self") _variableType = VariableType::self; + else if(variableType == "device") _variableType = VariableType::device; else if(variableType == "metadata") _variableType = VariableType::metadata; else if(variableType == "system") _variableType = VariableType::system; else if(variableType == "flow") _variableType = VariableType::flow; @@ -66,8 +67,11 @@ bool MyNode::init(Flows::PNodeInfo info) if(settingsIterator != info->info->structValue->end()) _channel = Flows::Math::getNumber(settingsIterator->second->stringValue); } - settingsIterator = info->info->structValue->find("variable"); - if(settingsIterator != info->info->structValue->end()) _variable = settingsIterator->second->stringValue; + if(_variableType != VariableType::self) + { + settingsIterator = info->info->structValue->find("variable"); + if(settingsIterator != info->info->structValue->end()) _variable = settingsIterator->second->stringValue; + } return true; } @@ -86,7 +90,17 @@ void MyNode::input(const Flows::PNodeInfo info, uint32_t index, const Flows::PVa { try { - if(_variableType == VariableType::flow) + if(_variableType == VariableType::self) + { + bool newValue = !((bool)(*getNodeData("value"))); + setNodeData("value", std::make_shared(newValue)); + + Flows::PVariable message = std::make_shared(Flows::VariableType::tStruct); + message->structValue->emplace("payload", std::make_shared(newValue)); + + output(0, message); + } + else if(_variableType == VariableType::flow) { bool newValue = !((bool)(*getFlowData(_variable))); setFlowData(_variable, std::make_shared(newValue)); diff --git a/variable/toggle/MyNode.h b/variable/toggle/MyNode.h index b9c005af..3b2529d7 100644 --- a/variable/toggle/MyNode.h +++ b/variable/toggle/MyNode.h @@ -45,6 +45,7 @@ class MyNode: public Flows::INode private: enum class VariableType { + self, device, metadata, system, diff --git a/variable/toggle/locales/en-US/toggle b/variable/toggle/locales/en-US/toggle index 2025a32d..8b41cb69 100644 --- a/variable/toggle/locales/en-US/toggle +++ b/variable/toggle/locales/en-US/toggle @@ -5,6 +5,7 @@ "variabletype": "Variable type", "devicevariable": "Device variable", "metadata": "Metadata", + "novariable": "None", "systemvariable": "System variable", "flowvariable": "Flow variable", "globalvariable": "Global variable", diff --git a/variable/toggle/toggle.hni b/variable/toggle/toggle.hni index 94b8b144..2c4c63ef 100644 --- a/variable/toggle/toggle.hni +++ b/variable/toggle/toggle.hni @@ -14,6 +14,7 @@
- +
+ + diff --git a/variable/variable-out/locales/en-US/variable-out b/variable/variable-out/locales/en-US/variable-out index 616993fc..dd1efe9c 100644 --- a/variable/variable-out/locales/en-US/variable-out +++ b/variable/variable-out/locales/en-US/variable-out @@ -20,7 +20,7 @@ }, "tip": "Tip: See help tab for usage information.", "paletteHelp": "

Sets a variable.

", - "help": "Use this node to set a variable in Homegear. When the variable was successfully set, the node outputs true. On error it outputs false.", + "help": "Use this node to set a variable in Homegear. When the variable was successfully set, the node outputs true. On error it outputs false. Please beware that not all errors are handled and this doesn't work for all device families. So make sure, you test all relevant error conditions.", "input1Description": "The value to set the referenced variable to.", "output1Description": "Outputs true on success and false on error." } From 6d9e278928b5fc78fa74a6de7292e63b33723033 Mon Sep 17 00:00:00 2001 From: "Dr. Sathya Laufer" Date: Mon, 13 May 2019 18:42:21 +0200 Subject: [PATCH 26/47] Fixed IN2 of presence-light --- presence-light/PresenceLight.cpp | 16 ++++------------ presence-light/PresenceLight.h | 1 - 2 files changed, 4 insertions(+), 13 deletions(-) diff --git a/presence-light/PresenceLight.cpp b/presence-light/PresenceLight.cpp index 1ea734b7..42877e77 100644 --- a/presence-light/PresenceLight.cpp +++ b/presence-light/PresenceLight.cpp @@ -148,8 +148,6 @@ void PresenceLight::startUpComplete() { try { - _lastLightEvent.store(BaseLib::HelperFunctions::getTime(), std::memory_order_release); - stateOutput(getLightStateVariable()); } catch(const std::exception& ex) @@ -224,8 +222,6 @@ void PresenceLight::timer() auto alwaysOnTo = _alwaysOnTo.load(std::memory_order_acquire); if(alwaysOnTo == -1 || (alwaysOnTo != 0 && (time >= alwaysOnTo))) { - _lastLightEvent.store(BaseLib::HelperFunctions::getTime(), std::memory_order_release); - Flows::PVariable state; if(_booleanStateValue.load(std::memory_order_acquire)) state = std::make_shared(false); else state = std::make_shared(0); @@ -259,8 +255,6 @@ void PresenceLight::timer() { if(alwaysOnTo <= time) { - _lastLightEvent.store(BaseLib::HelperFunctions::getTime(), std::memory_order_release); - bool lightState = onTo > time && (_enabled.load(std::memory_order_acquire) || _manuallyEnabled.load(std::memory_order_acquire)) && !_manuallyDisabled.load(std::memory_order_acquire); stateOutput(std::make_shared(_booleanStateValue.load(std::memory_order_acquire) ? lightState : (lightState ? _stateValue.load(std::memory_order_acquire) : 0))); @@ -288,8 +282,6 @@ void PresenceLight::timer() { if(alwaysOffTo <= time) { - _lastLightEvent.store(BaseLib::HelperFunctions::getTime(), std::memory_order_release); - bool lightState = onTo > time && (_enabled.load(std::memory_order_acquire) || _manuallyEnabled.load(std::memory_order_acquire)) && !_manuallyDisabled.load(std::memory_order_acquire); stateOutput(std::make_shared(_booleanStateValue.load(std::memory_order_acquire) ? lightState : (lightState ? _stateValue.load(std::memory_order_acquire) : 0))); @@ -471,8 +463,6 @@ void PresenceLight::input(const Flows::PNodeInfo info, uint32_t index, const Flo } else if(index == 4) //Light input (IN2) { - auto lastLightEvent = _lastLightEvent.load(std::memory_order_acquire); - if(BaseLib::HelperFunctions::getTime() - lastLightEvent < 10000) return; _manuallyEnabled.store(false, std::memory_order_release); _manuallyDisabled.store(false, std::memory_order_release); if(inputValue) @@ -484,6 +474,8 @@ void PresenceLight::input(const Flows::PNodeInfo info, uint32_t index, const Flo _onTo.store(BaseLib::HelperFunctions::getTime() + onTime, std::memory_order_release); setNodeData("onTo", std::make_shared(_onTo.load(std::memory_order_acquire))); + + if(getLightState()) return; } else { @@ -492,6 +484,8 @@ void PresenceLight::input(const Flows::PNodeInfo info, uint32_t index, const Flo _onTo.store(-1, std::memory_order_release); setNodeData("onTo", std::make_shared(-1)); + + if(!getLightState()) return; } } else if(index == 5) //State value @@ -568,8 +562,6 @@ void PresenceLight::input(const Flows::PNodeInfo info, uint32_t index, const Flo //}}} } - _lastLightEvent.store(BaseLib::HelperFunctions::getTime(), std::memory_order_release); - stateOutput(getLightStateVariable()); } catch(const std::exception& ex) diff --git a/presence-light/PresenceLight.h b/presence-light/PresenceLight.h index e16b7948..0ee9a220 100644 --- a/presence-light/PresenceLight.h +++ b/presence-light/PresenceLight.h @@ -60,7 +60,6 @@ class PresenceLight: public Flows::INode bool _outputChangesOnly = false; //}}} - std::atomic _lastLightEvent{-1}; std::atomic_bool _stopThread{true}; std::atomic_bool _stopped{true}; std::mutex _timerThreadMutex; From 3302279fcfde170560059a10b0aaff2f05842868 Mon Sep 17 00:00:00 2001 From: "Dr. Sathya Laufer" Date: Mon, 13 May 2019 19:39:08 +0200 Subject: [PATCH 27/47] Small change to presence-light --- presence-light/PresenceLight.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/presence-light/PresenceLight.cpp b/presence-light/PresenceLight.cpp index 42877e77..c371d610 100644 --- a/presence-light/PresenceLight.cpp +++ b/presence-light/PresenceLight.cpp @@ -471,11 +471,12 @@ void PresenceLight::input(const Flows::PNodeInfo info, uint32_t index, const Flo auto payloadIterator = message->structValue->find("onTime"); if(payloadIterator != message->structValue->end()) onTime = payloadIterator->second->integerValue64; + auto onTo = _onTo.load(std::memory_order_acquire); _onTo.store(BaseLib::HelperFunctions::getTime() + onTime, std::memory_order_release); setNodeData("onTo", std::make_shared(_onTo.load(std::memory_order_acquire))); - if(getLightState()) return; + if(getLightState() && onTo - BaseLib::HelperFunctions::getTime() > 1000) return; } else { From 9251dc661ec42c408c5b5e5280d6c3b9ecffd271 Mon Sep 17 00:00:00 2001 From: "Dr. Sathya Laufer" Date: Tue, 14 May 2019 18:24:26 +0200 Subject: [PATCH 28/47] Fixed true/false evaluation in switch node and added change node --- CMakeLists.txt | 4 + basic-logic/Makefile.am | 10 +- basic-logic/change/Factory.cpp | 41 +++ basic-logic/change/Factory.h | 44 ++++ basic-logic/change/MyNode.cpp | 332 ++++++++++++++++++++++++ basic-logic/change/MyNode.h | 91 +++++++ basic-logic/change/change.hni | 234 +++++++++++++++++ basic-logic/change/locales/en-US/change | 35 +++ basic-logic/switch/MyNode.cpp | 14 +- 9 files changed, 799 insertions(+), 6 deletions(-) create mode 100644 basic-logic/change/Factory.cpp create mode 100644 basic-logic/change/Factory.h create mode 100644 basic-logic/change/MyNode.cpp create mode 100644 basic-logic/change/MyNode.h create mode 100644 basic-logic/change/change.hni create mode 100644 basic-logic/change/locales/en-US/change diff --git a/CMakeLists.txt b/CMakeLists.txt index 02dcafa7..f610cd76 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,6 +12,10 @@ set(SOURCE_FILES basic-logic/and/Factory.h basic-logic/and/MyNode.cpp basic-logic/and/MyNode.h + basic-logic/change/Factory.cpp + basic-logic/change/Factory.h + basic-logic/change/MyNode.cpp + basic-logic/change/MyNode.h basic-logic/fallingedge/Factory.cpp basic-logic/fallingedge/Factory.h basic-logic/fallingedge/MyNode.cpp diff --git a/basic-logic/Makefile.am b/basic-logic/Makefile.am index 95a7093e..f32862ca 100644 --- a/basic-logic/Makefile.am +++ b/basic-logic/Makefile.am @@ -6,11 +6,14 @@ LIBS += -Wl,-Bdynamic -lhomegear-node libdir = $(localstatedir)/lib/homegear/node-blue/nodes/basic-logic -lib_LTLIBRARIES = and.la fallingedge.la not.la or.la risingedge.la srflipflop.la switch.la variable-switch.la +lib_LTLIBRARIES = and.la change.la fallingedge.la not.la or.la risingedge.la srflipflop.la switch.la variable-switch.la and_la_SOURCES = and/Factory.cpp and/MyNode.cpp and_la_LDFLAGS =-module -avoid-version -shared +change_la_SOURCES = change/Factory.cpp change/MyNode.cpp +change_la_LDFLAGS =-module -avoid-version -shared + fallingedge_la_SOURCES = fallingedge/Factory.cpp fallingedge/MyNode.cpp fallingedge_la_LDFLAGS =-module -avoid-version -shared @@ -33,12 +36,13 @@ variable_switch_la_SOURCES = variable-switch/Factory.cpp variable-switch/MyNode. variable_switch_la_LDFLAGS =-module -avoid-version -shared logic_ladir = $(libdir) -logic_la_DATA = and/and.hni fallingedge/fallingedge.hni not/not.hni or/or.hni risingedge/risingedge.hni srflipflop/srflipflop.hni switch/switch.hni variable-switch/variable-switch.hni +logic_la_DATA = and/and.hni change/change.hni fallingedge/fallingedge.hni not/not.hni or/or.hni risingedge/risingedge.hni srflipflop/srflipflop.hni switch/switch.hni variable-switch/variable-switch.hni locale_en_usdir = $(libdir)/locales/en-US -locale_en_us_DATA = and/locales/en-US/and fallingedge/locales/en-US/fallingedge not/locales/en-US/not or/locales/en-US/or risingedge/locales/en-US/risingedge srflipflop/locales/en-US/srflipflop switch/locales/en-US/switch variable-switch/locales/en-US/variable-switch +locale_en_us_DATA = and/locales/en-US/and change/locales/en-US/change fallingedge/locales/en-US/fallingedge not/locales/en-US/not or/locales/en-US/or risingedge/locales/en-US/risingedge srflipflop/locales/en-US/srflipflop switch/locales/en-US/switch variable-switch/locales/en-US/variable-switch install-exec-hook: rm -f $(DESTDIR)$(libdir)/and.la + rm -f $(DESTDIR)$(libdir)/change.la rm -f $(DESTDIR)$(libdir)/fallingedge.la rm -f $(DESTDIR)$(libdir)/not.la rm -f $(DESTDIR)$(libdir)/or.la diff --git a/basic-logic/change/Factory.cpp b/basic-logic/change/Factory.cpp new file mode 100644 index 00000000..47cc94ec --- /dev/null +++ b/basic-logic/change/Factory.cpp @@ -0,0 +1,41 @@ +/* Copyright 2013-2019 Homegear GmbH + * + * Homegear is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Homegear is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Homegear. If not, see . + * + * In addition, as a special exception, the copyright holders give + * permission to link the code of portions of this program with the + * OpenSSL library under certain conditions as described in each + * individual source file, and distribute linked combinations + * including the two. + * You must obey the GNU General Public License in all respects + * for all of the code used other than OpenSSL. If you modify + * file(s) with this exception, you may extend this exception to your + * version of the file(s), but you are not obligated to do so. If you + * do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source + * files in the program, then also delete it here. + */ + +#include "Factory.h" +#include "MyNode.h" + +Flows::INode* MyFactory::createNode(std::string path, std::string nodeNamespace, std::string type, const std::atomic_bool* frontendConnected) +{ + return new MyNode::MyNode(path, nodeNamespace, type, frontendConnected); +} + +Flows::NodeFactory* getFactory() +{ + return (Flows::NodeFactory*) (new MyFactory); +} diff --git a/basic-logic/change/Factory.h b/basic-logic/change/Factory.h new file mode 100644 index 00000000..6fa76bfb --- /dev/null +++ b/basic-logic/change/Factory.h @@ -0,0 +1,44 @@ +/* Copyright 2013-2019 Homegear GmbH + * + * Homegear is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Homegear is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Homegear. If not, see . + * + * In addition, as a special exception, the copyright holders give + * permission to link the code of portions of this program with the + * OpenSSL library under certain conditions as described in each + * individual source file, and distribute linked combinations + * including the two. + * You must obey the GNU General Public License in all respects + * for all of the code used other than OpenSSL. If you modify + * file(s) with this exception, you may extend this exception to your + * version of the file(s), but you are not obligated to do so. If you + * do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source + * files in the program, then also delete it here. + */ + +#ifndef FACTORY_H +#define FACTORY_H + +#include +#include "MyNode.h" + +class MyFactory : Flows::NodeFactory +{ +public: + virtual Flows::INode* createNode(std::string path, std::string nodeNamespace, std::string type, const std::atomic_bool* frontendConnected); +}; + +extern "C" Flows::NodeFactory* getFactory(); + +#endif diff --git a/basic-logic/change/MyNode.cpp b/basic-logic/change/MyNode.cpp new file mode 100644 index 00000000..e1c690f4 --- /dev/null +++ b/basic-logic/change/MyNode.cpp @@ -0,0 +1,332 @@ +/* Copyright 2013-2019 Homegear GmbH + * + * Homegear is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Homegear is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Homegear. If not, see . + * + * In addition, as a special exception, the copyright holders give + * permission to link the code of portions of this program with the + * OpenSSL library under certain conditions as described in each + * individual source file, and distribute linked combinations + * including the two. + * You must obey the GNU General Public License in all respects + * for all of the code used other than OpenSSL. If you modify + * file(s) with this exception, you may extend this exception to your + * version of the file(s), but you are not obligated to do so. If you + * do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source + * files in the program, then also delete it here. + */ + +#include +#include "MyNode.h" +#include "../switch/MyNode.h" + +namespace MyNode +{ + +MyNode::MyNode(std::string path, std::string nodeNamespace, std::string type, const std::atomic_bool* frontendConnected) : Flows::INode(path, nodeNamespace, type, frontendConnected) +{ +} + +MyNode::~MyNode() +{ +} + +std::string& MyNode::stringReplace(std::string& haystack, const std::string& search, const std::string& replace) +{ + if(search.empty()) return haystack; + int32_t pos = 0; + while(true) + { + pos = haystack.find(search, pos); + if (pos == (signed)std::string::npos) break; + haystack.replace(pos, search.size(), replace); + pos += replace.size(); + } + return haystack; +} + +MyNode::RuleType MyNode::getRuleTypeFromString(std::string& t) +{ + MyNode::RuleType ruleType = MyNode::RuleType::tSet; + if(t == "set") ruleType = MyNode::RuleType::tSet; + else if(t == "change") ruleType = MyNode::RuleType::tChange; + else if(t == "move") ruleType = MyNode::RuleType::tMove; + else if(t == "delete") ruleType = MyNode::RuleType::tDelete; + return ruleType; +} + +Flows::VariableType MyNode::getValueTypeFromString(std::string& vt) +{ + Flows::VariableType variableType = Flows::VariableType::tVoid; + if(vt == "bool") variableType = Flows::VariableType::tBoolean; + else if(vt == "int") variableType = Flows::VariableType::tInteger64; + else if(vt == "float") variableType = Flows::VariableType::tFloat; + else if(vt == "string") variableType = Flows::VariableType::tString; + else if(vt == "array") variableType = Flows::VariableType::tArray; + else if(vt == "struct") variableType = Flows::VariableType::tStruct; + return variableType; +} + +void MyNode::convertType(Flows::PVariable& value, Flows::VariableType vt) +{ + if(vt == Flows::VariableType::tBoolean) + { + if(value->type == Flows::VariableType::tString) + { + value->setType(Flows::VariableType::tBoolean); + value->booleanValue = (value->stringValue == "true"); + } + } + else if(vt == Flows::VariableType::tInteger) + { + value->setType(Flows::VariableType::tInteger64); + value->integerValue = Flows::Math::getNumber(value->stringValue); + value->integerValue64 = value->integerValue; + } + else if(vt == Flows::VariableType::tInteger64) + { + value->setType(vt); + value->integerValue64 = Flows::Math::getNumber64(value->stringValue); + } + else if(vt == Flows::VariableType::tFloat) + { + value->setType(vt); + value->floatValue = Flows::Math::getDouble(value->stringValue); + } + else if(vt == Flows::VariableType::tArray || vt == Flows::VariableType::tStruct) + { + Flows::JsonDecoder jsonDecoder; + value = jsonDecoder.decode(value->stringValue); + } +} + +bool MyNode::init(Flows::PNodeInfo info) +{ + try + { + auto flowIdIterator = info->info->structValue->find("z"); + if(flowIdIterator != info->info->structValue->end()) _flowId = flowIdIterator->second->stringValue; + else _flowId = "g"; + + Flows::PArray rules; + auto settingsIterator = info->info->structValue->find("rules"); + if(settingsIterator != info->info->structValue->end()) rules = settingsIterator->second->arrayValue; + + _rules.clear(); + if(rules) + { + _rules.reserve(rules->size()); + uint32_t index = 0; + for(auto& ruleStruct : *rules) + { + Rule rule; + + auto typeIterator = ruleStruct->structValue->find("t"); + auto valueIterator = ruleStruct->structValue->find("p"); + auto valueTypeIterator = ruleStruct->structValue->find("pt"); + if(typeIterator == ruleStruct->structValue->end()) continue; + rule.t = getRuleTypeFromString(typeIterator->second->stringValue); + if(valueIterator != ruleStruct->structValue->end() && valueTypeIterator != ruleStruct->structValue->end()) + { + if(valueTypeIterator->second->stringValue == "message") rule.messageProperty = valueIterator->second->stringValue; + else if(valueTypeIterator->second->stringValue == "flow") rule.flowVariable = valueIterator->second->stringValue; + else if(valueTypeIterator->second->stringValue == "global") rule.globalVariable = valueIterator->second->stringValue; + } + + + valueIterator = ruleStruct->structValue->find("from"); + valueTypeIterator = ruleStruct->structValue->find("fromt"); + if(valueIterator != ruleStruct->structValue->end() && valueTypeIterator != ruleStruct->structValue->end()) + { + rule.from = valueIterator->second; + rule.fromt = getValueTypeFromString(valueTypeIterator->second->stringValue); + if(valueTypeIterator->second->stringValue == "message") rule.messagePropertyFrom = rule.from->stringValue; + else if(valueTypeIterator->second->stringValue == "flow") rule.flowVariableFrom = rule.from->stringValue; + else if(valueTypeIterator->second->stringValue == "global") rule.globalVariableFrom = rule.from->stringValue; + convertType(rule.from, rule.fromt); + } + else rule.from = std::make_shared(); + if(valueTypeIterator->second->stringValue == "regex") + { + rule.fromRegexSet = true; + rule.fromRegex = std::regex(rule.from->stringValue, std::regex::ECMAScript); + } + + + valueIterator = ruleStruct->structValue->find("to"); + valueTypeIterator = ruleStruct->structValue->find("tot"); + if(valueIterator != ruleStruct->structValue->end() && valueTypeIterator != ruleStruct->structValue->end()) + { + rule.to = valueIterator->second; + rule.tot = getValueTypeFromString(valueTypeIterator->second->stringValue); + if(valueTypeIterator->second->stringValue == "message") rule.messagePropertyTo = rule.to->stringValue; + else if(valueTypeIterator->second->stringValue == "flow") rule.flowVariableTo = rule.to->stringValue; + else if(valueTypeIterator->second->stringValue == "global") rule.globalVariableTo = rule.to->stringValue; + convertType(rule.to, rule.tot); + } + else rule.to = std::make_shared(); + + _rules.push_back(rule); + index++; + } + } + + return true; + } + catch(const std::exception& ex) + { + _out->printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__, ex.what()); + } + return false; +} + +void MyNode::applyRule(Rule& rule, Flows::PVariable& value) +{ + try + { + if(!rule.messageProperty.empty()) + { + if(rule.t == RuleType::tDelete) + { + if(!rule.flowVariable.empty()) setFlowData(rule.flowVariable, std::make_shared()); + else if(!rule.globalVariable.empty()) setFlowData(rule.globalVariable, std::make_shared()); + else if(!rule.messageProperty.empty()) value->structValue->erase(rule.messageProperty); + } + else if(rule.t == RuleType::tSet) + { + if(!rule.flowVariableTo.empty()) rule.to = getFlowData(rule.flowVariableTo); + else if(!rule.globalVariableTo.empty()) rule.to = getGlobalData(rule.globalVariableTo); + else if(!rule.messagePropertyTo.empty()) + { + auto propertyIterator = value->structValue->find(rule.messagePropertyTo); + if(propertyIterator != value->structValue->end()) rule.to = propertyIterator->second; + else rule.to = std::make_shared(); + } + + if(!rule.flowVariable.empty()) setFlowData(rule.flowVariable, rule.to); + else if(!rule.globalVariable.empty()) setFlowData(rule.globalVariable, rule.to); + else if(!rule.messageProperty.empty()) (*value->structValue)[rule.messageProperty] = rule.to; + } + else if(rule.t == RuleType::tMove) + { + Flows::PVariable currentValue; + + //{{{ Set + if(!rule.flowVariable.empty()) currentValue = getFlowData(rule.flowVariable); + else if(!rule.globalVariable.empty()) currentValue = getGlobalData(rule.globalVariable); + else if(!rule.messageProperty.empty()) + { + auto propertyIterator = value->structValue->find(rule.messageProperty); + if(propertyIterator != value->structValue->end()) currentValue = propertyIterator->second; + else currentValue = std::make_shared(); + } + else currentValue = std::make_shared(); + + if(!rule.flowVariableTo.empty()) setFlowData(rule.flowVariableTo, currentValue); + else if(!rule.globalVariableTo.empty()) setFlowData(rule.globalVariableTo, currentValue); + else if(!rule.messagePropertyTo.empty()) (*value->structValue)[rule.messagePropertyTo] = currentValue; + //}}} + + //{{{ Delete + if(!rule.flowVariable.empty()) setFlowData(rule.flowVariable, std::make_shared()); + else if(!rule.globalVariable.empty()) setFlowData(rule.globalVariable, std::make_shared()); + else if(!rule.messageProperty.empty()) value->structValue->erase(rule.messageProperty); + //}}} + } + else if(rule.t == RuleType::tChange) + { + Flows::PVariable currentValue; + + if(!rule.flowVariable.empty()) currentValue = getFlowData(rule.flowVariable); + else if(!rule.globalVariable.empty()) currentValue = getGlobalData(rule.globalVariable); + else if(!rule.messageProperty.empty()) + { + auto propertyIterator = value->structValue->find(rule.messageProperty); + if(propertyIterator != value->structValue->end()) currentValue = propertyIterator->second; + else currentValue = std::make_shared(); + } + else currentValue = std::make_shared(); + + if(rule.fromRegexSet) + { + if(currentValue->type != Flows::VariableType::tString) + { + currentValue->stringValue = currentValue->toString(); + currentValue->type = Flows::VariableType::tString; + } + + if(rule.tot != Flows::VariableType::tString) + { + if(!std::regex_match(currentValue->stringValue, rule.fromRegex)) return; + currentValue = rule.to; + } + else currentValue->stringValue = std::regex_replace(currentValue->stringValue, rule.fromRegex, rule.to->toString()); + } + else + { + if(rule.fromt == Flows::VariableType::tString) + { + if(currentValue->type != Flows::VariableType::tString) + { + currentValue->stringValue = currentValue->toString(); + currentValue->type = Flows::VariableType::tString; + } + + if(currentValue->stringValue.find(rule.from->stringValue) == std::string::npos) return; + + if(rule.tot != Flows::VariableType::tString) currentValue = rule.to; + else stringReplace(currentValue->stringValue, rule.from->stringValue, rule.to->toString()); + } + else + { + if(currentValue->type != rule.from->type) return; + + if(currentValue->type == Flows::VariableType::tBoolean && currentValue->booleanValue == rule.from->booleanValue) currentValue = rule.to; + else if(currentValue->type == Flows::VariableType::tInteger64 && currentValue->integerValue64 == rule.from->integerValue64) currentValue = rule.to; + else if(currentValue->type == Flows::VariableType::tFloat && currentValue->floatValue == rule.from->floatValue) currentValue = rule.to; + } + } + + if(!rule.flowVariable.empty()) setFlowData(rule.flowVariable, currentValue); + else if(!rule.globalVariable.empty()) setFlowData(rule.globalVariable, currentValue); + else if(!rule.messageProperty.empty()) (*value->structValue)[rule.messageProperty] = currentValue; + } + } + } + catch(const std::exception& ex) + { + _out->printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__, ex.what()); + } +} + +void MyNode::input(const Flows::PNodeInfo info, uint32_t index, const Flows::PVariable message) +{ + try + { + Flows::PVariable myMessage = std::make_shared(); + *myMessage = *message; + + for(auto& rule : _rules) + { + applyRule(rule, myMessage); + output(0, myMessage); + } + } + catch(const std::exception& ex) + { + _out->printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__, ex.what()); + } +} + +} diff --git a/basic-logic/change/MyNode.h b/basic-logic/change/MyNode.h new file mode 100644 index 00000000..83c0f6d2 --- /dev/null +++ b/basic-logic/change/MyNode.h @@ -0,0 +1,91 @@ +/* Copyright 2013-2019 Homegear GmbH + * + * Homegear is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Homegear is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Homegear. If not, see . + * + * In addition, as a special exception, the copyright holders give + * permission to link the code of portions of this program with the + * OpenSSL library under certain conditions as described in each + * individual source file, and distribute linked combinations + * including the two. + * You must obey the GNU General Public License in all respects + * for all of the code used other than OpenSSL. If you modify + * file(s) with this exception, you may extend this exception to your + * version of the file(s), but you are not obligated to do so. If you + * do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source + * files in the program, then also delete it here. + */ + +#ifndef MYNODE_H_ +#define MYNODE_H_ + +#include +#include +#include +#include + +namespace MyNode +{ + +class MyNode: public Flows::INode +{ +public: + enum class RuleType + { + tSet, + tChange, + tMove, + tDelete + }; + + MyNode(std::string path, std::string nodeNamespace, std::string type, const std::atomic_bool* frontendConnected); + virtual ~MyNode(); + + virtual bool init(Flows::PNodeInfo info); +private: + struct Rule + { + RuleType t; + std::string messageProperty; + std::string flowVariable; + std::string globalVariable; + Flows::PVariable from; + Flows::VariableType fromt; + std::string messagePropertyFrom; + std::string flowVariableFrom; + std::string globalVariableFrom; + bool fromRegexSet = false; + std::regex fromRegex; + Flows::PVariable to; + Flows::VariableType tot; + std::string messagePropertyTo; + std::string flowVariableTo; + std::string globalVariableTo; + }; + + typedef std::string Operator; + + std::vector _rules; + + std::string& stringReplace(std::string& haystack, const std::string& search, const std::string& replace); + RuleType getRuleTypeFromString(std::string& t); + Flows::VariableType getValueTypeFromString(std::string& vt); + void convertType(Flows::PVariable& value, Flows::VariableType vt); + void applyRule(Rule& rule, Flows::PVariable& value); + virtual void input(const Flows::PNodeInfo info, uint32_t index, const Flows::PVariable message); +}; + +} + +#endif diff --git a/basic-logic/change/change.hni b/basic-logic/change/change.hni new file mode 100644 index 00000000..4663a0aa --- /dev/null +++ b/basic-logic/change/change.hni @@ -0,0 +1,234 @@ + + + + + diff --git a/basic-logic/change/locales/en-US/change b/basic-logic/change/locales/en-US/change new file mode 100644 index 00000000..0267e5fc --- /dev/null +++ b/basic-logic/change/locales/en-US/change @@ -0,0 +1,35 @@ +{ + "basic-logic/change.hni": { + "change": { + "label": { + "rules": "Rules", + "rule": "rule", + "set": "set __property__", + "change": "change __property__", + "delete": "delete __property__", + "move": "move __property__", + "changeCount": "change: __count__ rules", + "regex": "Use regular expressions" + }, + "action": { + "set": "Set", + "change": "Change", + "delete": "Delete", + "move": "Move", + "to": "to", + "search": "Search for", + "replace": "Replace with" + }, + "errors": { + "invalid-from": "Invalid 'from' property: __error__", + "invalid-json": "Invalid 'to' JSON property", + "invalid-expr": "Invalid JSONata expression: __error__" + }, + "message": "message.", + "flow": "flow.", + "global": "global.", + "paletteHelp": "

Set, change, delete or move properties of a message, flow context or global context.

", + "help": "

Set, change, delete or move properties of a message, flow context or global context.

The node can specify multiple rules that will be applied in the order they are defined.

Details

The available operations are:

Set
set a property. The value can be a variety of different types, or can be taken from an existing message or context property.
Change
search & replace parts of the property. If regular expressions are enabled, the \"replace with\" property can include capture groups, for example $1. Replace will only change the type if there is a complete match.
Delete
delete a property.
Move
move or rename a property.
" + } + } +} \ No newline at end of file diff --git a/basic-logic/switch/MyNode.cpp b/basic-logic/switch/MyNode.cpp index c902101f..95710c0c 100644 --- a/basic-logic/switch/MyNode.cpp +++ b/basic-logic/switch/MyNode.cpp @@ -75,7 +75,15 @@ Flows::VariableType MyNode::getValueTypeFromString(std::string& vt) void MyNode::convertType(Flows::PVariable& value, Flows::VariableType vt) { - if(vt == Flows::VariableType::tInteger) + if(vt == Flows::VariableType::tBoolean) + { + if(value->type == Flows::VariableType::tString) + { + value->setType(Flows::VariableType::tBoolean); + value->booleanValue = (value->stringValue == "true"); + } + } + else if(vt == Flows::VariableType::tInteger) { value->setType(Flows::VariableType::tInteger64); value->integerValue = Flows::Math::getNumber(value->stringValue); @@ -291,10 +299,10 @@ bool MyNode::match(Rule& rule, Flows::PVariable& value) switch(rule.t) { case RuleType::tEq: - if(value->type == rule.vt && *value == *rule.v) return true; + if(*value == *rule.v) return true; return false; case RuleType::tNeq: - if(value->type == rule.vt && *value == *rule.v) return false; + if(*value == *rule.v) return false; return true; case RuleType::tLt: if(value->type == rule.vt && *value < *rule.v) return true; From fd6860fa3f1938e93dc325421cfd2a4e5c081c95 Mon Sep 17 00:00:00 2001 From: "Dr. Sathya Laufer" Date: Fri, 17 May 2019 19:15:53 +0200 Subject: [PATCH 29/47] Added option to presence-light to be able to restore profile after setting TG to 0 --- .../variable-switch/variable-switch.hni | 1 + presence-light/PresenceLight.cpp | 23 ++++++++++++++++--- presence-light/PresenceLight.h | 1 + presence-light/locales/en-US/presence-light | 1 + presence-light/presence-light.hni | 20 ++++++++++------ variable/variable-in/variable-in.hni | 3 ++- variable/variable-out/variable-out.hni | 3 ++- 7 files changed, 40 insertions(+), 12 deletions(-) diff --git a/basic-logic/variable-switch/variable-switch.hni b/basic-logic/variable-switch/variable-switch.hni index 54a45414..aad121d0 100644 --- a/basic-logic/variable-switch/variable-switch.hni +++ b/basic-logic/variable-switch/variable-switch.hni @@ -26,6 +26,7 @@
+
diff --git a/presence-light/PresenceLight.cpp b/presence-light/PresenceLight.cpp index c371d610..e1952bf6 100644 --- a/presence-light/PresenceLight.cpp +++ b/presence-light/PresenceLight.cpp @@ -67,6 +67,9 @@ bool PresenceLight::init(Flows::PNodeInfo info) settingsIterator = info->info->structValue->find("toggle-profile-0-only"); if(settingsIterator != info->info->structValue->end()) _toggleProfile0Only = settingsIterator->second->booleanValue; + settingsIterator = info->info->structValue->find("restore-profile"); + if(settingsIterator != info->info->structValue->end()) _restoreProfile = settingsIterator->second->booleanValue; + settingsIterator = info->info->structValue->find("refraction-time"); if(settingsIterator != info->info->structValue->end()) _refractionTime = Flows::Math::getUnsignedNumber(settingsIterator->second->stringValue); @@ -235,9 +238,23 @@ void PresenceLight::timer() _onTo.store(-1, std::memory_order_release); setNodeData("onTo", std::make_shared(-1)); - _manuallyEnabled.store(false, std::memory_order_release); - _manuallyDisabled.store(false, std::memory_order_release); - setNodeData("manuallyEnabled", std::make_shared(false)); + if(_manuallyEnabled.load(std::memory_order_acquire)) + { + _manuallyEnabled.store(false, std::memory_order_release); + setNodeData("manuallyEnabled", std::make_shared(false)); + } + if(_manuallyDisabled.load(std::memory_order_acquire)) + { + _manuallyDisabled.store(false, std::memory_order_release); + setNodeData("manuallyDisabled", std::make_shared(false)); + + if(_restoreProfile.load(std::memory_order_acquire) && !_booleanStateValue.load(std::memory_order_acquire)) + { + auto lastNonNullStateValue = _lastNonNullStateValue.load(std::memory_order_acquire); + _stateValue.store(lastNonNullStateValue, std::memory_order_release); + setNodeData("stateValue", std::make_shared(lastNonNullStateValue)); + } + } } else if(getLightState()) { diff --git a/presence-light/PresenceLight.h b/presence-light/PresenceLight.h index 0ee9a220..49027f06 100644 --- a/presence-light/PresenceLight.h +++ b/presence-light/PresenceLight.h @@ -65,6 +65,7 @@ class PresenceLight: public Flows::INode std::mutex _timerThreadMutex; std::thread _timerThread; std::atomic_bool _toggleProfile0Only{false}; + std::atomic_bool _restoreProfile{false}; std::atomic_bool _booleanStateValue{true}; std::atomic _stateValue{1}; std::atomic _lastNonNullStateValue{1}; diff --git a/presence-light/locales/en-US/presence-light b/presence-light/locales/en-US/presence-light index 49c1ec20..61cdf7c0 100644 --- a/presence-light/locales/en-US/presence-light +++ b/presence-light/locales/en-US/presence-light @@ -12,6 +12,7 @@ "process-false": "Switch light off immediately, when \"IN\" is set to \"false\".", "keep-on": "Keep light on when \"EN\" is set to \"false\".", "toggle-profile-0-only": "\"TG\" only toggles on an input of \"0\", other inputs set \"SVAL\", but don't switch the light off.", + "restore-profile": "When the light is switched off (in profile mode) using \"TG\", restore the last profile after no \"IN\" for \"On time\".", "changes-only": "Output changes only", "refraction-time": "Rate limiter delay", "refraction-time2": "Duration in milliseconds" diff --git a/presence-light/presence-light.hni b/presence-light/presence-light.hni index 0eb7625c..a0994a3c 100644 --- a/presence-light/presence-light.hni +++ b/presence-light/presence-light.hni @@ -41,6 +41,11 @@
+
+ + + +
@@ -59,13 +64,14 @@ color: "#A6BBCF", defaults: { name: {value:""}, - "on-time": {value: "300",required:true,validate:RED.validators.number()}, - "always-on-time": {value: "21600",required:true,validate:RED.validators.number()}, - "always-off-time": {value: "21600",required:true,validate:RED.validators.number()}, - "process-false": {value: false,required:false}, - "keep-on": {value: false,required:false}, - "toggle-profile-0-only": {value: false,required:false}, - "refraction-time": {value: "1000",required:true,validate:RED.validators.number()}, + "on-time": {value:"300",required:true,validate:RED.validators.number()}, + "always-on-time": {value:"21600",required:true,validate:RED.validators.number()}, + "always-off-time": {value:"21600",required:true,validate:RED.validators.number()}, + "process-false": {value:false,required:false}, + "keep-on": {value:false,required:false}, + "toggle-profile-0-only": {value:false,required:false}, + "restore-profile": {value:false,required:false}, + "refraction-time": {value:"1000",required:true,validate:RED.validators.number()}, "changes-only": {value:false,required:false} }, inputs:7, diff --git a/variable/variable-in/variable-in.hni b/variable/variable-in/variable-in.hni index 04b1de8d..f2bf25ee 100644 --- a/variable/variable-in/variable-in.hni +++ b/variable/variable-in/variable-in.hni @@ -100,6 +100,7 @@ inputs:1, inputInfo: [ { + label: "TRG", types: ["any"] } ], @@ -117,7 +118,7 @@ else if(this.variabletype == "flow" && this.variable) return "flow." + this.variable; else if(this.variabletype == "global" && this.variable) return "global." + this.variable; else if(this.variable) return this.variable; - return "variable"; + return "variable-in"; }, oneditprepare: function() { var that = this; diff --git a/variable/variable-out/variable-out.hni b/variable/variable-out/variable-out.hni index 97a139ec..0a8277f5 100644 --- a/variable/variable-out/variable-out.hni +++ b/variable/variable-out/variable-out.hni @@ -74,6 +74,7 @@ outputs: 1, outputInfo: [ { + label: "RSLT", types: ["any"] } ], @@ -86,7 +87,7 @@ else if(this.variabletype == "flow" && this.variable) return "flow." + this.variable; else if(this.variabletype == "global" && this.variable) return "global." + this.variable; else if(this.variable) return this.variable; - return "variable"; + return "variable-out"; }, oneditprepare: function() { var that = this; From eb65a1029d65956d7e16800d21b155dc9e887a2f Mon Sep 17 00:00:00 2001 From: "Dr. Sathya Laufer" Date: Wed, 22 May 2019 21:21:12 +0200 Subject: [PATCH 30/47] Small change to debug node --- debug/MyNode.cpp | 4 ++-- debug/MyNode.h | 2 +- function/function.php | 6 ++++++ variable/constant/MyNode.cpp | 2 +- variable/constant/MyNode.h | 2 +- 5 files changed, 11 insertions(+), 5 deletions(-) diff --git a/debug/MyNode.cpp b/debug/MyNode.cpp index 92c2cc3d..0e18c23f 100644 --- a/debug/MyNode.cpp +++ b/debug/MyNode.cpp @@ -69,7 +69,7 @@ bool MyNode::init(Flows::PNodeInfo info) return false; } -void MyNode::setNodeVariable(std::string variable, Flows::PVariable value) +void MyNode::setNodeVariable(const std::string& variable, Flows::PVariable value) { try { @@ -91,7 +91,7 @@ std::string MyNode::stripNonPrintable(const std::string& s) strippedString.reserve(s.size()); for(std::string::const_iterator i = s.begin(); i != s.end(); ++i) { - if(std::isprint(*i)) strippedString.push_back(*i); + if(std::isprint(*i, std::locale("en_US.UTF-8"))) strippedString.push_back(*i); } return strippedString; } diff --git a/debug/MyNode.h b/debug/MyNode.h index 26c1a16d..2a105167 100644 --- a/debug/MyNode.h +++ b/debug/MyNode.h @@ -42,7 +42,7 @@ class MyNode: public Flows::INode virtual ~MyNode(); virtual bool init(Flows::PNodeInfo info); - virtual void setNodeVariable(std::string variable, Flows::PVariable value); + virtual void setNodeVariable(const std::string& variable, Flows::PVariable value); private: bool _active = true; bool _hg = false; diff --git a/function/function.php b/function/function.php index bf3cc927..3b96c30e 100644 --- a/function/function.php +++ b/function/function.php @@ -45,6 +45,12 @@ function output(int $outputIndex, array $message) return $nodeObject->output($outputIndex, $message); } +function eventLog(string $message) +{ + global $nodeObject; + return $nodeObject->frontendEventLog($message); +} + class HomegearNode extends HomegearNodeBase { diff --git a/variable/constant/MyNode.cpp b/variable/constant/MyNode.cpp index 5cc595d5..2af3ef9a 100644 --- a/variable/constant/MyNode.cpp +++ b/variable/constant/MyNode.cpp @@ -121,7 +121,7 @@ void MyNode::startUpComplete() } } -void MyNode::setNodeVariable(std::string variable, Flows::PVariable value) +void MyNode::setNodeVariable(const std::string& variable, Flows::PVariable value) { try { diff --git a/variable/constant/MyNode.h b/variable/constant/MyNode.h index c5b3dba9..7bbe4779 100644 --- a/variable/constant/MyNode.h +++ b/variable/constant/MyNode.h @@ -44,7 +44,7 @@ class MyNode: public Flows::INode virtual bool init(Flows::PNodeInfo info); virtual void startUpComplete(); - virtual void setNodeVariable(std::string variable, Flows::PVariable value); + virtual void setNodeVariable(const std::string& variable, Flows::PVariable value); private: bool _outputOnStartup = true; std::string _payloadType; From 4b82b7625c8a2a25b9daa25935f2dbf03ce968eb Mon Sep 17 00:00:00 2001 From: "Dr. Sathya Laufer" Date: Thu, 23 May 2019 01:12:04 +0200 Subject: [PATCH 31/47] Added exec node --- CMakeLists.txt | 8 +- Makefile.am | 2 +- configure.ac | 2 +- exec/Exec.cpp | 405 +++++++++++++++++++++++++++ runscript/RunScript.h => exec/Exec.h | 34 ++- {runscript => exec}/Factory.cpp | 4 +- {runscript => exec}/Factory.h | 0 exec/Makefile.am | 16 ++ exec/exec.hni | 82 ++++++ exec/locales/en-US/exec | 21 ++ function/locales/en-US/function | 2 +- runscript/Makefile.am | 16 -- runscript/RunScript.cpp | 82 ------ runscript/locales/en-US/runscript | 16 -- runscript/runscript.hni | 51 ---- 15 files changed, 560 insertions(+), 181 deletions(-) create mode 100644 exec/Exec.cpp rename runscript/RunScript.h => exec/Exec.h (65%) rename {runscript => exec}/Factory.cpp (94%) rename {runscript => exec}/Factory.h (100%) create mode 100644 exec/Makefile.am create mode 100644 exec/exec.hni create mode 100644 exec/locales/en-US/exec delete mode 100644 runscript/Makefile.am delete mode 100644 runscript/RunScript.cpp delete mode 100644 runscript/locales/en-US/runscript delete mode 100644 runscript/runscript.hni diff --git a/CMakeLists.txt b/CMakeLists.txt index f610cd76..82c0bca8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -48,6 +48,10 @@ set(SOURCE_FILES debug/Factory.h debug/MyNode.cpp debug/MyNode.h + exec/Factory.cpp + exec/Factory.h + exec/Exec.cpp + exec/Exec.h http/http-in/Factory.cpp http/http-in/Factory.h http/http-in/MyNode.cpp @@ -136,10 +140,6 @@ set(SOURCE_FILES pulsecounter/Factory.h pulsecounter/MyNode.cpp pulsecounter/MyNode.h - runscript/Factory.cpp - runscript/Factory.h - runscript/RunScript.cpp - runscript/RunScript.h serial/serial-port/Factory.cpp serial/serial-port/Factory.h serial/serial-port/MyNode.cpp diff --git a/Makefile.am b/Makefile.am index d103778f..1ffa9c6e 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,3 +1,3 @@ AUTOMAKE_OPTIONS = foreign ACLOCAL_AMFLAGS = -I m4 -I cfg -SUBDIRS = average basic-logic comment debug function gpio http influxdb light link modbus mqtt notification parsers passthrough ping pulsecounter presence-light press-pattern runscript serial synchronous storage template timers tls-config tls-server-config variable +SUBDIRS = average basic-logic comment debug exec function gpio http influxdb light link modbus mqtt notification parsers passthrough ping pulsecounter presence-light press-pattern serial synchronous storage template timers tls-config tls-server-config variable diff --git a/configure.ac b/configure.ac index ef08f59a..ba0c5587 100644 --- a/configure.ac +++ b/configure.ac @@ -62,4 +62,4 @@ esac #AC_ARG_ENABLE(debug, AS_HELP_STRING([--enable-debug], [enable debugging, default: no]), [case "${enableval}" in yes) debug=true ;; no) debug=false ;; *) AC_MSG_ERROR([bad value ${enableval} for --enable-debug]) ;; esac], [debug=false]) #AM_CONDITIONAL(DEBUG, test x"$debug" = x"true") -AC_OUTPUT(Makefile average/Makefile basic-logic/Makefile comment/Makefile debug/Makefile function/Makefile gpio/Makefile http/Makefile influxdb/Makefile light/Makefile link/Makefile modbus/Makefile mqtt/Makefile notification/Makefile parsers/Makefile passthrough/Makefile ping/Makefile presence-light/Makefile press-pattern/Makefile pulsecounter/Makefile runscript/Makefile serial/Makefile synchronous/Makefile storage/Makefile template/Makefile timers/Makefile tls-config/Makefile tls-server-config/Makefile variable/Makefile) +AC_OUTPUT(Makefile average/Makefile basic-logic/Makefile comment/Makefile debug/Makefile exec/Makefile function/Makefile gpio/Makefile http/Makefile influxdb/Makefile light/Makefile link/Makefile modbus/Makefile mqtt/Makefile notification/Makefile parsers/Makefile passthrough/Makefile ping/Makefile presence-light/Makefile press-pattern/Makefile pulsecounter/Makefile serial/Makefile synchronous/Makefile storage/Makefile template/Makefile timers/Makefile tls-config/Makefile tls-server-config/Makefile variable/Makefile) diff --git a/exec/Exec.cpp b/exec/Exec.cpp new file mode 100644 index 00000000..fe467a3f --- /dev/null +++ b/exec/Exec.cpp @@ -0,0 +1,405 @@ +/* Copyright 2013-2019 Homegear GmbH + * + * Homegear is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Homegear is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Homegear. If not, see . + * + * In addition, as a special exception, the copyright holders give + * permission to link the code of portions of this program with the + * OpenSSL library under certain conditions as described in each + * individual source file, and distribute linked combinations + * including the two. + * You must obey the GNU General Public License in all respects + * for all of the code used other than OpenSSL. If you modify + * file(s) with this exception, you may extend this exception to your + * version of the file(s), but you are not obligated to do so. If you + * do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source + * files in the program, then also delete it here. + */ + +#include "Exec.h" +#include "homegear-base/Managers/ProcessManager.h" +#include + +namespace Exec +{ + +Exec::Exec(std::string path, std::string nodeNamespace, std::string type, const std::atomic_bool* frontendConnected) : Flows::INode(path, nodeNamespace, type, frontendConnected) +{ +} + +Exec::~Exec() +{ +} + +bool Exec::init(Flows::PNodeInfo info) +{ + try + { + auto settingsIterator = info->info->structValue->find("filename"); + if(settingsIterator != info->info->structValue->end()) _filename = settingsIterator->second->stringValue; + + settingsIterator = info->info->structValue->find("arguments"); + if(settingsIterator != info->info->structValue->end()) _arguments = settingsIterator->second->stringValue; + + settingsIterator = info->info->structValue->find("autostart"); + if(settingsIterator != info->info->structValue->end()) _autostart = settingsIterator->second->booleanValue; + + settingsIterator = info->info->structValue->find("collect-output"); + if(settingsIterator != info->info->structValue->end()) _collectOutput = settingsIterator->second->booleanValue; + + return true; + } + catch(const std::exception& ex) + { + _out->printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__, ex.what()); + } + return false; +} + +bool Exec::start() +{ + try + { + _callbackHandlerId = BaseLib::ProcessManager::registerCallbackHandler(std::function(std::bind(&Exec::sigchildHandler, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4))); + + if(_autostart) startProgram(); + + return true; + } + catch(const std::exception& ex) + { + _out->printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__, ex.what()); + } + return false; +} + +void Exec::stop() +{ + try + { + _autostart = false; + if(_pid != -1) kill(_pid, 15); + } + catch(const std::exception& ex) + { + _out->printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__, ex.what()); + } +} + +void Exec::waitForStop() +{ + if(_pid != -1) kill(_pid, 15); + for(int32_t i = 0; i < 600; i++) + { + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + if(_pid == -1) break; + } + if(_pid != -1) + { + _out->printError("Error: Process did not finish within 60 seconds. Killing it."); + kill(_pid, 9); + close(_stdIn); + close(_stdOut); + close(_stdErr); + _stdIn = -1; + _stdOut = -1; + _stdErr = -1; + } + if(_execThread.joinable()) _execThread.join(); + if(_errorThread.joinable()) _errorThread.join(); + BaseLib::ProcessManager::unregisterCallbackHandler(_callbackHandlerId); +} + +void Exec::input(const Flows::PNodeInfo info, uint32_t index, const Flows::PVariable message) +{ + try + { + if(index == 0) + { + if(_pid == -1) startProgram(); + } + else if(index == 1) + { + if(_pid != -1) kill(_pid, message->structValue->at("payload")->integerValue64); + } + else if(index == 2) + { + if(_stdIn != -1) + { + size_t totalBytesWritten = 0; + while(totalBytesWritten < message->structValue->at("payload")->stringValue.size()) + { + int bytesWritten = -1; + do + { + bytesWritten = write(_stdIn, message->structValue->at("payload")->stringValue.data(), message->structValue->at("payload")->stringValue.size()); + } while(bytesWritten == -1 && (errno == EAGAIN || errno == EINTR)); + if(bytesWritten <= 0) + { + _out->printWarning("Warning: Could not write to STDIN: " + std::string(strerror(errno))); + break; + } + totalBytesWritten += bytesWritten; + } + } + } + } + catch(const std::exception& ex) + { + _out->printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__, ex.what()); + } +} + +void Exec::startProgram() +{ + try + { + if(_filename.empty()) + { + _out->printError("Error: filename is not set."); + return; + } + + { + std::lock_guard bufferGuard(_bufferMutex); + _bufferOut.clear(); + _bufferErr.clear(); + } + + if(_execThread.joinable()) _execThread.join(); + if(_errorThread.joinable()) _errorThread.join(); + _execThread = std::thread(&Exec::execThread, this); + } + catch(const std::exception& ex) + { + _out->printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__, ex.what()); + } +} + +int32_t Exec::getMaxFd() +{ + struct rlimit limits{}; + if(getrlimit(RLIMIT_NOFILE, &limits) == -1 || limits.rlim_cur >= INT32_MAX) + { + return 1024; + } + return limits.rlim_cur; +} + +void Exec::execThread() +{ + try + { + do + { + if(_stdErr != -1) + { + close(_stdErr); + _stdErr = -1; + } + if(_errorThread.joinable()) _errorThread.join(); + + int stdIn = -1; + int stdOut = -1; + int stdErr = -1; + _pid = BaseLib::ProcessManager::systemp(_filename, BaseLib::ProcessManager::splitArguments(_arguments), getMaxFd(), stdIn, stdOut, stdErr); + _stdIn = stdIn; + _stdOut = stdOut; + _stdErr = stdErr; + + _errorThread = std::thread(&Exec::errorThread, this); + + std::array buffer{}; + std::string bufferOut; + while(_stdOut != -1) + { + if(!_collectOutput) bufferOut.clear(); + auto bytesRead = 0; + do + { + bytesRead = read(_stdOut, buffer.data(), buffer.size()); + if(bytesRead > 0) + { + if(_collectOutput) + { + std::lock_guard bufferGuard(_bufferMutex); + _bufferOut.insert(_bufferOut.end(), buffer.begin(), buffer.begin() + bytesRead); + } + else bufferOut.insert(bufferOut.end(), buffer.begin(), buffer.begin() + bytesRead); + } + } while(bytesRead > 0); + + if(!_collectOutput && !bufferOut.empty()) + { + Flows::PVariable message = std::make_shared(Flows::VariableType::tStruct); + message->structValue->emplace("payload", std::make_shared(bufferOut)); + output(1, message); + } + } + + if(_collectOutput) + { + std::lock_guard bufferGuard(_bufferMutex); + Flows::PVariable message = std::make_shared(Flows::VariableType::tStruct); + message->structValue->emplace("payload", std::make_shared(_bufferOut)); + output(1, message); + } + + if(_autostart) std::this_thread::sleep_for(std::chrono::seconds(1)); + } + while(_autostart); + } + catch(const std::exception& ex) + { + _out->printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__, ex.what()); + } +} + +void Exec::errorThread() +{ + try + { + std::array buffer{}; + std::string bufferErr; + while(_stdErr != -1) + { + int32_t bytesRead = 0; + if(!_collectOutput) bufferErr.clear(); + do + { + bytesRead = read(_stdErr, buffer.data(), buffer.size()); + if(bytesRead > 0) + { + if(_collectOutput) + { + std::lock_guard bufferGuard(_bufferMutex); + _bufferErr.insert(_bufferErr.end(), buffer.begin(), buffer.begin() + bytesRead); + } + else bufferErr.insert(bufferErr.end(), buffer.begin(), buffer.begin() + bytesRead); + } + } while(bytesRead > 0); + + if(!_collectOutput && !bufferErr.empty()) + { + Flows::PVariable message = std::make_shared(Flows::VariableType::tStruct); + message->structValue->emplace("payload", std::make_shared(bufferErr)); + output(2, message); + } + } + + if(_collectOutput) + { + std::lock_guard bufferGuard(_bufferMutex); + if(!_bufferErr.empty()) + { + Flows::PVariable message = std::make_shared(Flows::VariableType::tStruct); + message->structValue->emplace("payload", std::make_shared(_bufferErr)); + output(2, message); + } + } + } + catch(const std::exception& ex) + { + _out->printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__, ex.what()); + } +} + +void Exec::sigchildHandler(pid_t pid, int exitCode, int signal, bool coreDumped) +{ + try + { + if(pid == _pid) + { + close(_stdIn); + close(_stdOut); + close(_stdErr); + _stdIn = -1; + _stdOut = -1; + _stdErr = -1; + _pid = -1; + Flows::PVariable message = std::make_shared(Flows::VariableType::tStruct); + message->structValue->emplace("coreDumped", std::make_shared(coreDumped)); + message->structValue->emplace("signal", std::make_shared(signal)); + message->structValue->emplace("payload", std::make_shared(exitCode)); + if(_collectOutput) + { + std::lock_guard bufferGuard(_bufferMutex); + message->structValue->emplace("stdout", std::make_shared(_bufferOut)); + if(!_bufferErr.empty()) message->structValue->emplace("stderr", std::make_shared(_bufferErr)); + } + output(1, message); + } + } + catch(const std::exception& ex) + { + _out->printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__, ex.what()); + } +} + +int32_t Exec::read(std::atomic_int& fd, uint8_t* buffer, int32_t bufferSize) +{ + if(fd == -1) return 0; + + timeval timeout{}; + timeout.tv_sec = 0; + timeout.tv_usec = 100000; + fd_set readFileDescriptor{}; + FD_ZERO(&readFileDescriptor); + int32_t nfds = fd + 1; + if(nfds <= 0) + { + close(fd); + fd = -1; + return -1; + } + FD_SET(fd, &readFileDescriptor); + auto bytesRead = select(nfds, &readFileDescriptor, nullptr, nullptr, &timeout); + if(bytesRead == 0) + { + return 0; + } + if(bytesRead != 1) + { + close(fd); + fd = -1; + return -1; + } + do + { + bytesRead = ::read(fd, buffer, bufferSize); + } while(bytesRead < 0 && (errno == EAGAIN || errno == EINTR)); + if(bytesRead <= 0) + { + if(bytesRead == -1) + { + if(errno == ETIMEDOUT) return 0; + else + { + close(fd); + fd = -1; + return -1; + } + } + else + { + close(fd); + fd = -1; + return -1; + } + } + if(bytesRead > bufferSize) bytesRead = bufferSize; + return bytesRead; +} + +} diff --git a/runscript/RunScript.h b/exec/Exec.h similarity index 65% rename from runscript/RunScript.h rename to exec/Exec.h index ed39964f..06734786 100644 --- a/runscript/RunScript.h +++ b/exec/Exec.h @@ -34,22 +34,42 @@ #include #include -namespace RunScript +namespace Exec { -class RunScript: public Flows::INode +class Exec : public Flows::INode { public: - RunScript(std::string path, std::string nodeNamespace, std::string type, const std::atomic_bool* frontendConnected); - virtual ~RunScript(); + Exec(std::string path, std::string nodeNamespace, std::string type, const std::atomic_bool* frontendConnected); + virtual ~Exec(); virtual bool init(Flows::PNodeInfo info); + virtual bool start(); + virtual void stop(); + virtual void waitForStop(); private: - bool _onBoolean = false; - Flows::PVariable _input1; - bool _input2 = false; + int32_t _callbackHandlerId = -1; + std::string _filename; + std::string _arguments; + std::atomic_bool _autostart{false}; + bool _collectOutput = false; + std::thread _execThread; + std::thread _errorThread; + std::mutex _bufferMutex; + std::string _bufferOut; + std::string _bufferErr; + std::atomic_int _pid{-1}; + std::atomic_int _stdIn{-1}; + std::atomic_int _stdOut{-1}; + std::atomic_int _stdErr{-1}; virtual void input(const Flows::PNodeInfo info, uint32_t index, const Flows::PVariable message); + void startProgram(); + int32_t getMaxFd(); + void sigchildHandler(pid_t pid, int exitCode, int signal, bool coreDumped); + void execThread(); + void errorThread(); + int32_t read(std::atomic_int& fd, uint8_t* buffer, int32_t bufferSize); }; } diff --git a/runscript/Factory.cpp b/exec/Factory.cpp similarity index 94% rename from runscript/Factory.cpp rename to exec/Factory.cpp index bdced55d..b1012835 100644 --- a/runscript/Factory.cpp +++ b/exec/Factory.cpp @@ -28,12 +28,12 @@ */ #include "Factory.h" -#include "RunScript.h" +#include "Exec.h" #include "../config.h" Flows::INode* MyFactory::createNode(std::string path, std::string nodeNamespace, std::string type, const std::atomic_bool* frontendConnected) { - return new RunScript::RunScript(path, nodeNamespace, type, frontendConnected); + return new Exec::Exec(path, nodeNamespace, type, frontendConnected); } Flows::NodeFactory* getFactory() diff --git a/runscript/Factory.h b/exec/Factory.h similarity index 100% rename from runscript/Factory.h rename to exec/Factory.h diff --git a/exec/Makefile.am b/exec/Makefile.am new file mode 100644 index 00000000..c62d84ca --- /dev/null +++ b/exec/Makefile.am @@ -0,0 +1,16 @@ +AUTOMAKE_OPTIONS = subdir-objects + +AM_CPPFLAGS = -Wall -std=c++11 -DFORTIFY_SOURCE=2 -DGCRYPT_NO_DEPRECATED +AM_LDFLAGS = -Wl,-rpath=/lib/homegear -Wl,-rpath=/usr/lib/homegear -Wl,-rpath=/usr/local/lib/homegear +LIBS += -Wl,-Bdynamic -lhomegear-node -lhomegear-base + +libdir = $(localstatedir)/lib/homegear/node-blue/nodes/exec +lib_LTLIBRARIES = exec.la +exec_la_SOURCES = Factory.cpp Exec.cpp +exec_la_LDFLAGS =-module -avoid-version -shared +exec_ladir = $(libdir) +exec_la_DATA = exec.hni +locale_en_usdir = $(libdir)/locales/en-US +locale_en_us_DATA = locales/en-US/exec +install-exec-hook: + rm -f $(DESTDIR)$(libdir)/exec.la diff --git a/exec/exec.hni b/exec/exec.hni new file mode 100644 index 00000000..9e38a568 --- /dev/null +++ b/exec/exec.hni @@ -0,0 +1,82 @@ + + + diff --git a/exec/locales/en-US/exec b/exec/locales/en-US/exec new file mode 100644 index 00000000..a7982167 --- /dev/null +++ b/exec/locales/en-US/exec @@ -0,0 +1,21 @@ +{ + "exec/exec.hni": { + "exec": { + "label": { + "filename": "Filename", + "arguments": "Arguments", + "autostart": "Start program on startup and restart it if it exits.", + "collect-output": "Wait to send output until process exits.", + "name": "Name" + }, + "paletteHelp": "

This node executes a program.

", + "help": "

This node executes a program. EXEC or the autostart option starts the program. The program is allowed to run indefinitely. When the autostart option is enabled, the program is restarted 1 second after it finishes. You can send input to the program throuth IN and the program's output is sent to OUT or ERR. When the program finishes, the exit code is output on CODE. You can also send signals to the program through SIG.

", + "input1Description": "Execute the program. If it is already running, the input is ignored.", + "input2Description": "This signal is passed to the running program.", + "input3Description": "This input is sent to stdin of the program.", + "output1Description": "Outputs the exit code and some additional information when the process finishes.", + "output2Description": "stdout of the program.", + "output3Description": "stderr of the program." + } + } +} \ No newline at end of file diff --git a/function/locales/en-US/function b/function/locales/en-US/function index a90d29af..ec1a3a03 100644 --- a/function/locales/en-US/function +++ b/function/locales/en-US/function @@ -9,7 +9,7 @@ }, "tip": "Tip: See the Info tab for help writing functions.", "paletteHelp": "

A function block where you can write PHP code to do more interesting things.

", - "help": "

A function block where you can write PHP code to do more interesting things.

By convention it will have a $message['payload'] property containing the body of the message.

The message is passed in as an associative array called $message. Information about the node can be found in the associative array $nodeInfo. The input index is passed in $inputIndex

The Homegear object is predefined as $hg. So you can directly execute RPC methods. E. g. $hg->setValue(...).

Data storage

Data can be stored by calling setNodeData($key, $value), setFlowData($key, $value) or setGlobalData($key, $value) and retrieved with getNodeData($key), getFlowData($key) or getGlobalData($key).

Sending messages

The function can either return the messages it wants to pass on to the next nodes in the flow, or can call output($outputIndex, $message).

It can return:

  • a single message object - passed to nodes connected to the first output
  • an array of message objects - passed to nodes connected to the corresponding outputs

If any element of the array is itself an array of messages, multiple messages are sent to the corresponding output.

If NULL is returned, either by itself or as an element of the array, no message is passed on.

See the online reference for more help.

" + "help": "

A function block where you can write PHP code to do more interesting things.

By convention it will have a $message['payload'] property containing the body of the message.

The message is passed in as an associative array called $message. Information about the node can be found in the associative array $nodeInfo. The input index is passed in $inputIndex

The Homegear object is predefined as $hg. So you can directly execute RPC methods. E. g. $hg->setValue(...).

Data storage

Data can be stored by calling setNodeData($key, $value), setFlowData($key, $value) or setGlobalData($key, $value) and retrieved with getNodeData($key), getFlowData($key) or getGlobalData($key).

Event log

You can send messages to the frontend event log by exedutiong eventLog($message).

Sending messages

The function can either return the messages it wants to pass on to the next nodes in the flow, or can call output($outputIndex, $message).

It can return:

  • a single message object - passed to nodes connected to the first output
  • an array of message objects - passed to nodes connected to the corresponding outputs

If any element of the array is itself an array of messages, multiple messages are sent to the corresponding output.

If NULL is returned, either by itself or as an element of the array, no message is passed on.

See the online reference for more help.

" } } } \ No newline at end of file diff --git a/runscript/Makefile.am b/runscript/Makefile.am deleted file mode 100644 index 00887b0f..00000000 --- a/runscript/Makefile.am +++ /dev/null @@ -1,16 +0,0 @@ -AUTOMAKE_OPTIONS = subdir-objects - -AM_CPPFLAGS = -Wall -std=c++11 -DFORTIFY_SOURCE=2 -DGCRYPT_NO_DEPRECATED -AM_LDFLAGS = -Wl,-rpath=/lib/homegear -Wl,-rpath=/usr/lib/homegear -Wl,-rpath=/usr/local/lib/homegear -LIBS += -Wl,-Bdynamic -lhomegear-node -lhomegear-base - -libdir = $(localstatedir)/lib/homegear/node-blue/nodes/runscript -lib_LTLIBRARIES = runscript.la -runscript_la_SOURCES = Factory.cpp RunScript.cpp -runscript_la_LDFLAGS =-module -avoid-version -shared -runscript_ladir = $(libdir) -runscript_la_DATA = runscript.hni -locale_en_usdir = $(libdir)/locales/en-US -locale_en_us_DATA = locales/en-US/runscript -install-exec-hook: - rm -f $(DESTDIR)$(libdir)/runscript.la diff --git a/runscript/RunScript.cpp b/runscript/RunScript.cpp deleted file mode 100644 index cbedc867..00000000 --- a/runscript/RunScript.cpp +++ /dev/null @@ -1,82 +0,0 @@ -/* Copyright 2013-2019 Homegear GmbH - * - * Homegear is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Homegear is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Homegear. If not, see . - * - * In addition, as a special exception, the copyright holders give - * permission to link the code of portions of this program with the - * OpenSSL library under certain conditions as described in each - * individual source file, and distribute linked combinations - * including the two. - * You must obey the GNU General Public License in all respects - * for all of the code used other than OpenSSL. If you modify - * file(s) with this exception, you may extend this exception to your - * version of the file(s), but you are not obligated to do so. If you - * do not wish to do so, delete this exception statement from your - * version. If you delete this exception statement from all source - * files in the program, then also delete it here. - */ - -#include "RunScript.h" - -namespace RunScript -{ - -RunScript::RunScript(std::string path, std::string nodeNamespace, std::string type, const std::atomic_bool* frontendConnected) : Flows::INode(path, nodeNamespace, type, frontendConnected) -{ -} - -RunScript::~RunScript() -{ -} - -bool RunScript::init(Flows::PNodeInfo info) -{ - try - { - auto settingsIterator = info->info->structValue->find("onboolean"); - if(settingsIterator != info->info->structValue->end()) _onBoolean = settingsIterator->second->booleanValue; - - _input1 = getNodeData("input1"); - _input2 = getNodeData("input2")->booleanValue; - - return true; - } - catch(const std::exception& ex) - { - _out->printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__, ex.what()); - } - catch(...) - { - _out->printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__); - } - return false; -} - -void RunScript::input(const Flows::PNodeInfo info, uint32_t index, const Flows::PVariable message) -{ - try - { - - } - catch(const std::exception& ex) - { - _out->printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__, ex.what()); - } - catch(...) - { - _out->printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__); - } -} - -} diff --git a/runscript/locales/en-US/runscript b/runscript/locales/en-US/runscript deleted file mode 100644 index 90b34158..00000000 --- a/runscript/locales/en-US/runscript +++ /dev/null @@ -1,16 +0,0 @@ -{ - "runscript/runscript.hni": { - "runscript": { - "label": { - "filename": "Filename", - "browse": "Browse", - "name": "Name" - }, - "paletteHelp": "

This node passes a value to the output depending on a boolean input.

", - "help": "

This node checks the boolean state of input EN. If the state is true the value at input v is passed to the output.

", - "input1Description": "The value to pass through.", - "input2Description": "Set to true to pass the value at input v through.", - "output1Description": "Outputs the value at input v unchanged." - } - } -} \ No newline at end of file diff --git a/runscript/runscript.hni b/runscript/runscript.hni deleted file mode 100644 index 670163cb..00000000 --- a/runscript/runscript.hni +++ /dev/null @@ -1,51 +0,0 @@ - - - From b5ec7a4cd3d607a0918767563d65adb05fdfde33 Mon Sep 17 00:00:00 2001 From: "Dr. Sathya Laufer" Date: Thu, 23 May 2019 01:14:40 +0200 Subject: [PATCH 32/47] Delete runscript node after installation --- debian/postinst | 1 + 1 file changed, 1 insertion(+) diff --git a/debian/postinst b/debian/postinst index 02e705c7..648e075e 100644 --- a/debian/postinst +++ b/debian/postinst @@ -21,6 +21,7 @@ case $1 in rm -Rf /var/lib/homegear/node-blue/nodes/basic-logic/locales/en-US/greater-than rm -Rf /var/lib/homegear/node-blue/nodes/basic-logic/locales/en-US/less-or-equal rm -Rf /var/lib/homegear/node-blue/nodes/basic-logic/locales/en-US/less-than + rm -Rf /var/lib/homegear/node-blue/nodes/runscript rm -Rf /var/lib/homegear/node-blue/nodes/sequence chown -R homegear:homegear /var/lib/homegear/node-blue/nodes chmod -R 550 /var/lib/homegear/node-blue/nodes From f09950d7bb7d211810487ab34da36a8dcceb72bf Mon Sep 17 00:00:00 2001 From: "Dr. Sathya Laufer" Date: Thu, 23 May 2019 01:28:50 +0200 Subject: [PATCH 33/47] Small changes to exec node --- exec/Exec.cpp | 76 +++++++++++++++++++++++++++++++++++++---- exec/exec.hni | 4 +-- exec/locales/en-US/exec | 2 +- 3 files changed, 72 insertions(+), 10 deletions(-) diff --git a/exec/Exec.cpp b/exec/Exec.cpp index fe467a3f..b47ca163 100644 --- a/exec/Exec.cpp +++ b/exec/Exec.cpp @@ -242,8 +242,17 @@ void Exec::execThread() if(!_collectOutput && !bufferOut.empty()) { + auto outputVector = BaseLib::HelperFunctions::splitAll(bufferOut, '\n'); + Flows::PVariable message = std::make_shared(Flows::VariableType::tStruct); - message->structValue->emplace("payload", std::make_shared(bufferOut)); + Flows::PVariable outputArray = std::make_shared(Flows::VariableType::tArray); + outputArray->arrayValue->reserve(outputVector.size()); + for(int32_t i = 0; i < outputVector.size(); i++) + { + if(i == outputVector.size() - 1 && outputVector[i].empty()) continue; + outputArray->arrayValue->emplace_back(std::make_shared(std::move(outputVector[i]))); + } + message->structValue->emplace("payload", outputArray); output(1, message); } } @@ -251,8 +260,17 @@ void Exec::execThread() if(_collectOutput) { std::lock_guard bufferGuard(_bufferMutex); + auto outputVector = BaseLib::HelperFunctions::splitAll(_bufferOut, '\n'); + Flows::PVariable message = std::make_shared(Flows::VariableType::tStruct); - message->structValue->emplace("payload", std::make_shared(_bufferOut)); + Flows::PVariable outputArray = std::make_shared(Flows::VariableType::tArray); + outputArray->arrayValue->reserve(outputVector.size()); + for(int32_t i = 0; i < outputVector.size(); i++) + { + if(i == outputVector.size() - 1 && outputVector[i].empty()) continue; + outputArray->arrayValue->emplace_back(std::make_shared(std::move(outputVector[i]))); + } + message->structValue->emplace("payload", outputArray); output(1, message); } @@ -292,8 +310,17 @@ void Exec::errorThread() if(!_collectOutput && !bufferErr.empty()) { + auto outputVector = BaseLib::HelperFunctions::splitAll(bufferErr, '\n'); + Flows::PVariable message = std::make_shared(Flows::VariableType::tStruct); - message->structValue->emplace("payload", std::make_shared(bufferErr)); + Flows::PVariable outputArray = std::make_shared(Flows::VariableType::tArray); + outputArray->arrayValue->reserve(outputVector.size()); + for(int32_t i = 0; i < outputVector.size(); i++) + { + if(i == outputVector.size() - 1 && outputVector[i].empty()) continue; + outputArray->arrayValue->emplace_back(std::make_shared(std::move(outputVector[i]))); + } + message->structValue->emplace("payload", outputArray); output(2, message); } } @@ -303,9 +330,17 @@ void Exec::errorThread() std::lock_guard bufferGuard(_bufferMutex); if(!_bufferErr.empty()) { + auto outputVector = BaseLib::HelperFunctions::splitAll(_bufferErr, '\n'); + Flows::PVariable message = std::make_shared(Flows::VariableType::tStruct); - message->structValue->emplace("payload", std::make_shared(_bufferErr)); - output(2, message); + Flows::PVariable outputArray = std::make_shared(Flows::VariableType::tArray); + outputArray->arrayValue->reserve(outputVector.size()); + for(int32_t i = 0; i < outputVector.size(); i++) + { + if(i == outputVector.size() - 1 && outputVector[i].empty()) continue; + outputArray->arrayValue->emplace_back(std::make_shared(std::move(outputVector[i]))); + } + message->structValue->emplace("payload", outputArray); } } } @@ -335,8 +370,35 @@ void Exec::sigchildHandler(pid_t pid, int exitCode, int signal, bool coreDumped) if(_collectOutput) { std::lock_guard bufferGuard(_bufferMutex); - message->structValue->emplace("stdout", std::make_shared(_bufferOut)); - if(!_bufferErr.empty()) message->structValue->emplace("stderr", std::make_shared(_bufferErr)); + + { + auto outputVector = BaseLib::HelperFunctions::splitAll(_bufferOut, '\n'); + + Flows::PVariable message = std::make_shared(Flows::VariableType::tStruct); + Flows::PVariable outputArray = std::make_shared(Flows::VariableType::tArray); + outputArray->arrayValue->reserve(outputVector.size()); + for(int32_t i = 0; i < outputVector.size(); i++) + { + if(i == outputVector.size() - 1 && outputVector[i].empty()) continue; + outputArray->arrayValue->emplace_back(std::make_shared(std::move(outputVector[i]))); + } + message->structValue->emplace("stdout", outputArray); + } + + if(!_bufferErr.empty()) + { + auto outputVector = BaseLib::HelperFunctions::splitAll(_bufferErr, '\n'); + + Flows::PVariable message = std::make_shared(Flows::VariableType::tStruct); + Flows::PVariable outputArray = std::make_shared(Flows::VariableType::tArray); + outputArray->arrayValue->reserve(outputVector.size()); + for(int32_t i = 0; i < outputVector.size(); i++) + { + if(i == outputVector.size() - 1 && outputVector[i].empty()) continue; + outputArray->arrayValue->emplace_back(std::make_shared(std::move(outputVector[i]))); + } + message->structValue->emplace("stderr", outputArray); + } } output(1, message); } diff --git a/exec/exec.hni b/exec/exec.hni index 9e38a568..d75e4d01 100644 --- a/exec/exec.hni +++ b/exec/exec.hni @@ -65,11 +65,11 @@ }, { label: "OUT", - types: ["string"] + types: ["array[string]"] }, { label: "ERR", - types: ["string"] + types: ["array[string]"] } ], icon: "function.png", diff --git a/exec/locales/en-US/exec b/exec/locales/en-US/exec index a7982167..9d6341d1 100644 --- a/exec/locales/en-US/exec +++ b/exec/locales/en-US/exec @@ -9,7 +9,7 @@ "name": "Name" }, "paletteHelp": "

This node executes a program.

", - "help": "

This node executes a program. EXEC or the autostart option starts the program. The program is allowed to run indefinitely. When the autostart option is enabled, the program is restarted 1 second after it finishes. You can send input to the program throuth IN and the program's output is sent to OUT or ERR. When the program finishes, the exit code is output on CODE. You can also send signals to the program through SIG.

", + "help": "

This node executes a program. EXEC or the autostart option starts the program. The program is allowed to run indefinitely. When the autostart option is enabled, the program is restarted 1 second after it finishes. You can send input to the program throuth IN and the program's output is sent to OUT or ERR as an array split on new line characters. When the program finishes, the exit code is output on CODE. You can also send signals to the program through SIG.

", "input1Description": "Execute the program. If it is already running, the input is ignored.", "input2Description": "This signal is passed to the running program.", "input3Description": "This input is sent to stdin of the program.", From f79de2dfb4bba95d5e86ffabc9e89effd7573e4b Mon Sep 17 00:00:00 2001 From: "Dr. Sathya Laufer" Date: Thu, 23 May 2019 09:41:59 +0200 Subject: [PATCH 34/47] Added "changes only" option to variable-in --- variable/variable-in/MyNode.cpp | 12 ++++++++++++ variable/variable-in/MyNode.h | 2 ++ variable/variable-in/locales/en-US/variable-in | 1 + variable/variable-in/variable-in.hni | 6 ++++++ 4 files changed, 21 insertions(+) diff --git a/variable/variable-in/MyNode.cpp b/variable/variable-in/MyNode.cpp index cfdb08c3..650e31b4 100644 --- a/variable/variable-in/MyNode.cpp +++ b/variable/variable-in/MyNode.cpp @@ -90,6 +90,9 @@ bool MyNode::init(Flows::PNodeInfo info) settingsIterator = info->info->structValue->find("outputonstartup"); if(settingsIterator != info->info->structValue->end()) _outputOnStartup = settingsIterator->second->booleanValue; + settingsIterator = info->info->structValue->find("changes-only"); + if(settingsIterator != info->info->structValue->end()) _outputChangesOnly = settingsIterator->second->booleanValue; + settingsIterator = info->info->structValue->find("loopprevention"); if(settingsIterator != info->info->structValue->end()) _loopPrevention = settingsIterator->second->booleanValue; @@ -230,6 +233,9 @@ void MyNode::variableEvent(std::string source, uint64_t peerId, int32_t channel, if(Flows::HelperFunctions::getTime() - _lastInput < _refractionPeriod) return; _lastInput = Flows::HelperFunctions::getTime(); + if(_outputChangesOnly && _lastValue && (*_lastValue) == (*value)) return; + _lastValue = value; + Flows::PVariable message = std::make_shared(Flows::VariableType::tStruct); message->structValue->emplace("eventSource", std::make_shared(source)); message->structValue->emplace("peerId", std::make_shared(peerId)); @@ -267,6 +273,9 @@ void MyNode::flowVariableEvent(std::string flowId, std::string variable, Flows:: if(Flows::HelperFunctions::getTime() - _lastInput < _refractionPeriod) return; _lastInput = Flows::HelperFunctions::getTime(); + if(_outputChangesOnly && _lastValue && (*_lastValue) == (*value)) return; + _lastValue = value; + Flows::PVariable message = std::make_shared(Flows::VariableType::tStruct); message->structValue->emplace("variable", std::make_shared(variable)); message->structValue->emplace("payload", value); @@ -300,6 +309,9 @@ void MyNode::globalVariableEvent(std::string variable, Flows::PVariable value) if(Flows::HelperFunctions::getTime() - _lastInput < _refractionPeriod) return; _lastInput = Flows::HelperFunctions::getTime(); + if(_outputChangesOnly && _lastValue && (*_lastValue) == (*value)) return; + _lastValue = value; + Flows::PVariable message = std::make_shared(Flows::VariableType::tStruct); message->structValue->emplace("variable", std::make_shared(variable)); message->structValue->emplace("payload", value); diff --git a/variable/variable-in/MyNode.h b/variable/variable-in/MyNode.h index cfa060b7..cb010e76 100644 --- a/variable/variable-in/MyNode.h +++ b/variable/variable-in/MyNode.h @@ -64,6 +64,8 @@ class MyNode: public Flows::INode VariableType _variableType = VariableType::device; int64_t _lastInput = 0; uint32_t _refractionPeriod = 0; + Flows::PVariable _lastValue; + bool _outputChangesOnly = false; bool _outputOnStartup = false; uint64_t _peerId = 0; int32_t _channel = -1; diff --git a/variable/variable-in/locales/en-US/variable-in b/variable/variable-in/locales/en-US/variable-in index e8a34921..0b3521d6 100644 --- a/variable/variable-in/locales/en-US/variable-in +++ b/variable/variable-in/locales/en-US/variable-in @@ -18,6 +18,7 @@ "homegear": "Homegear", "refractoryperiod": "Refractory period", "outputonstartup": "Output value on startup", + "changes-only": "Output changes only", "name": "Name", "loopprevention": "Enable feedback loop prevention", "looppreventiongroup": "Loop group", diff --git a/variable/variable-in/variable-in.hni b/variable/variable-in/variable-in.hni index f2bf25ee..5851d3f7 100644 --- a/variable/variable-in/variable-in.hni +++ b/variable/variable-in/variable-in.hni @@ -67,6 +67,11 @@
+
+ + + +
@@ -93,6 +98,7 @@ eventsource: {value:"all",validate: function(v) { return v != ""; }}, refractoryperiod: {value: "0",validate:RED.validators.number()}, outputonstartup: {value: false}, + "changes-only": {value: false, required: false}, loopprevention: {value: false}, looppreventiongroup: {type:"variable-loop-prevention-group", required: false}, name: {value:""} From f356c511e755316b0919e83a7946a431aa6acbc3 Mon Sep 17 00:00:00 2001 From: "Dr. Sathya Laufer" Date: Fri, 24 May 2019 09:07:05 +0200 Subject: [PATCH 35/47] Small fix to exec node --- exec/Exec.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/exec/Exec.cpp b/exec/Exec.cpp index b47ca163..e6bc0ac6 100644 --- a/exec/Exec.cpp +++ b/exec/Exec.cpp @@ -341,6 +341,7 @@ void Exec::errorThread() outputArray->arrayValue->emplace_back(std::make_shared(std::move(outputVector[i]))); } message->structValue->emplace("payload", outputArray); + output(2, message); } } } @@ -400,7 +401,7 @@ void Exec::sigchildHandler(pid_t pid, int exitCode, int signal, bool coreDumped) message->structValue->emplace("stderr", outputArray); } } - output(1, message); + output(0, message); } } catch(const std::exception& ex) From 3c0ba598d5c82933da17becf1060ff7b9428828a Mon Sep 17 00:00:00 2001 From: "Dr. Sathya Laufer" Date: Sat, 25 May 2019 22:51:29 +0200 Subject: [PATCH 36/47] Small syntax changes --- exec/Exec.cpp | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/exec/Exec.cpp b/exec/Exec.cpp index e6bc0ac6..c6dc7003 100644 --- a/exec/Exec.cpp +++ b/exec/Exec.cpp @@ -247,9 +247,9 @@ void Exec::execThread() Flows::PVariable message = std::make_shared(Flows::VariableType::tStruct); Flows::PVariable outputArray = std::make_shared(Flows::VariableType::tArray); outputArray->arrayValue->reserve(outputVector.size()); - for(int32_t i = 0; i < outputVector.size(); i++) + for(int32_t i = 0; i < (signed)outputVector.size(); i++) { - if(i == outputVector.size() - 1 && outputVector[i].empty()) continue; + if(i == (signed)outputVector.size() - 1 && outputVector[i].empty()) continue; outputArray->arrayValue->emplace_back(std::make_shared(std::move(outputVector[i]))); } message->structValue->emplace("payload", outputArray); @@ -265,9 +265,9 @@ void Exec::execThread() Flows::PVariable message = std::make_shared(Flows::VariableType::tStruct); Flows::PVariable outputArray = std::make_shared(Flows::VariableType::tArray); outputArray->arrayValue->reserve(outputVector.size()); - for(int32_t i = 0; i < outputVector.size(); i++) + for(int32_t i = 0; i < (signed)outputVector.size(); i++) { - if(i == outputVector.size() - 1 && outputVector[i].empty()) continue; + if(i == (signed)outputVector.size() - 1 && outputVector[i].empty()) continue; outputArray->arrayValue->emplace_back(std::make_shared(std::move(outputVector[i]))); } message->structValue->emplace("payload", outputArray); @@ -315,9 +315,9 @@ void Exec::errorThread() Flows::PVariable message = std::make_shared(Flows::VariableType::tStruct); Flows::PVariable outputArray = std::make_shared(Flows::VariableType::tArray); outputArray->arrayValue->reserve(outputVector.size()); - for(int32_t i = 0; i < outputVector.size(); i++) + for(int32_t i = 0; i < (signed)outputVector.size(); i++) { - if(i == outputVector.size() - 1 && outputVector[i].empty()) continue; + if(i == (signed)outputVector.size() - 1 && outputVector[i].empty()) continue; outputArray->arrayValue->emplace_back(std::make_shared(std::move(outputVector[i]))); } message->structValue->emplace("payload", outputArray); @@ -335,9 +335,9 @@ void Exec::errorThread() Flows::PVariable message = std::make_shared(Flows::VariableType::tStruct); Flows::PVariable outputArray = std::make_shared(Flows::VariableType::tArray); outputArray->arrayValue->reserve(outputVector.size()); - for(int32_t i = 0; i < outputVector.size(); i++) + for(int32_t i = 0; i < (signed)outputVector.size(); i++) { - if(i == outputVector.size() - 1 && outputVector[i].empty()) continue; + if(i == (signed)outputVector.size() - 1 && outputVector[i].empty()) continue; outputArray->arrayValue->emplace_back(std::make_shared(std::move(outputVector[i]))); } message->structValue->emplace("payload", outputArray); @@ -378,9 +378,9 @@ void Exec::sigchildHandler(pid_t pid, int exitCode, int signal, bool coreDumped) Flows::PVariable message = std::make_shared(Flows::VariableType::tStruct); Flows::PVariable outputArray = std::make_shared(Flows::VariableType::tArray); outputArray->arrayValue->reserve(outputVector.size()); - for(int32_t i = 0; i < outputVector.size(); i++) + for(int32_t i = 0; i < (signed)outputVector.size(); i++) { - if(i == outputVector.size() - 1 && outputVector[i].empty()) continue; + if(i == (signed)outputVector.size() - 1 && outputVector[i].empty()) continue; outputArray->arrayValue->emplace_back(std::make_shared(std::move(outputVector[i]))); } message->structValue->emplace("stdout", outputArray); @@ -393,9 +393,9 @@ void Exec::sigchildHandler(pid_t pid, int exitCode, int signal, bool coreDumped) Flows::PVariable message = std::make_shared(Flows::VariableType::tStruct); Flows::PVariable outputArray = std::make_shared(Flows::VariableType::tArray); outputArray->arrayValue->reserve(outputVector.size()); - for(int32_t i = 0; i < outputVector.size(); i++) + for(int32_t i = 0; i < (signed)outputVector.size(); i++) { - if(i == outputVector.size() - 1 && outputVector[i].empty()) continue; + if(i == (signed)outputVector.size() - 1 && outputVector[i].empty()) continue; outputArray->arrayValue->emplace_back(std::make_shared(std::move(outputVector[i]))); } message->structValue->emplace("stderr", outputArray); From 7e8add4176b48bb36122b67a71d9acee4a85f0c7 Mon Sep 17 00:00:00 2001 From: "Dr. Sathya Laufer" Date: Sat, 25 May 2019 22:52:47 +0200 Subject: [PATCH 37/47] Made nodes core nodes --- average/average.hni | 3 ++- basic-logic/and/and.hni | 3 ++- basic-logic/change/change.hni | 3 ++- basic-logic/fallingedge/fallingedge.hni | 3 ++- basic-logic/not/not.hni | 3 ++- basic-logic/or/or.hni | 3 ++- basic-logic/risingedge/risingedge.hni | 3 ++- basic-logic/srflipflop/srflipflop.hni | 3 ++- basic-logic/switch/switch.hni | 3 ++- basic-logic/variable-switch/variable-switch.hni | 3 ++- comment/comment.hni | 3 ++- debug/debug.hni | 3 ++- exec/exec.hni | 3 ++- function/function.hni | 3 ++- gpio/gpio-in/gpio-in.hni | 3 ++- gpio/gpio-out/gpio-out.hni | 3 ++- http/http-in/http-in.hni | 3 ++- http/http-request/http-request.hni | 3 ++- http/http-response/http-response.hni | 3 ++- http/http-server/http-server.hni | 3 ++- influxdb/influxdb.hni | 3 ++- light/light.hni | 3 ++- link/link-in/link-in.hni | 3 ++- link/link-out/link-out.hni | 3 ++- modbus/modbus-host/modbus-host.hni | 3 ++- modbus/modbus-in/modbus-in.hni | 3 ++- modbus/modbus-out/modbus-out.hni | 3 ++- mqtt/mqtt-broker/mqtt-broker.hni | 3 ++- mqtt/mqtt-in/mqtt-in.hni | 3 ++- mqtt/mqtt-out/mqtt-out.hni | 3 ++- notification/xmpp-server.hni | 3 ++- notification/xmpp.hni | 3 ++- parsers/json/json.hni | 3 ++- parsers/strip/strip.hni | 3 ++- parsers/xml/xml.hni | 3 ++- passthrough/passthrough.hni | 3 ++- php-test/php-test.hni | 3 ++- ping/ping.hni | 3 ++- presence-light/presence-light.hni | 3 ++- press-pattern/press-pattern.hni | 3 ++- pulsecounter/pulsecounter.hni | 3 ++- serial/serial-in/serial-in.hni | 3 ++- serial/serial-out/serial-out.hni | 3 ++- serial/serial-port/serial-port.hni | 3 ++- storage/file/file.hni | 3 ++- synchronous/synchronous.hni | 3 ++- template/template.hni | 3 ++- timers/clock/clock.hni | 3 ++- timers/delay/delay.hni | 3 ++- timers/impulse/impulse.hni | 3 ++- timers/interval/interval.hni | 3 ++- timers/off-delay/off-delay.hni | 3 ++- timers/on-delay/on-delay.hni | 3 ++- timers/rate-limiter/rate-limiter.hni | 3 ++- timers/slow-pwm/slow-pwm.hni | 3 ++- timers/sun-position/sun-position.hni | 3 ++- timers/timer/timer.hni | 3 ++- tls-config/tls-config.hni | 3 ++- tls-server-config/tls-server-config.hni | 3 ++- variable/constant/constant.hni | 3 ++- variable/toggle/toggle.hni | 3 ++- variable/variable-in/variable-in.hni | 3 ++- .../variable-loop-prevention-group.hni | 3 ++- variable/variable-out/variable-out.hni | 3 ++- 64 files changed, 128 insertions(+), 64 deletions(-) diff --git a/average/average.hni b/average/average.hni index 65638519..a7ae1275 100644 --- a/average/average.hni +++ b/average/average.hni @@ -2,7 +2,8 @@ { "name": "average", "readableName": "average", - "version": "0.0.1", + "version": "1.0.0", + "coreNode": true, "maxThreadCount": 1 } diff --git a/basic-logic/and/and.hni b/basic-logic/and/and.hni index 3006759c..c7edd8c6 100644 --- a/basic-logic/and/and.hni +++ b/basic-logic/and/and.hni @@ -2,7 +2,8 @@ { "name": "and", "readableName": "And", - "version": "0.0.1", + "version": "1.0.0", + "coreNode": true, "maxThreadCount": 0 } diff --git a/basic-logic/change/change.hni b/basic-logic/change/change.hni index 4663a0aa..bd9439f6 100644 --- a/basic-logic/change/change.hni +++ b/basic-logic/change/change.hni @@ -2,7 +2,8 @@ { "name": "change", "readableName": "Change", - "version": "0.0.1", + "version": "1.0.0", + "coreNode": true, "maxThreadCount": 0 } diff --git a/basic-logic/fallingedge/fallingedge.hni b/basic-logic/fallingedge/fallingedge.hni index 7a3d25cd..1830e5bb 100644 --- a/basic-logic/fallingedge/fallingedge.hni +++ b/basic-logic/fallingedge/fallingedge.hni @@ -2,7 +2,8 @@ { "name": "fallingedge", "readableName": "FallingEdge", - "version": "0.0.1", + "version": "1.0.0", + "coreNode": true, "maxThreadCount": 0 } diff --git a/basic-logic/not/not.hni b/basic-logic/not/not.hni index c8239f2a..1a1b4ab7 100644 --- a/basic-logic/not/not.hni +++ b/basic-logic/not/not.hni @@ -2,7 +2,8 @@ { "name": "not", "readableName": "Not", - "version": "0.0.1", + "version": "1.0.0", + "coreNode": true, "maxThreadCount": 0 } diff --git a/basic-logic/or/or.hni b/basic-logic/or/or.hni index 02cfc997..c5237869 100644 --- a/basic-logic/or/or.hni +++ b/basic-logic/or/or.hni @@ -2,7 +2,8 @@ { "name": "or", "readableName": "Or", - "version": "0.0.1", + "version": "1.0.0", + "coreNode": true, "maxThreadCount": 0 } diff --git a/basic-logic/risingedge/risingedge.hni b/basic-logic/risingedge/risingedge.hni index dfaed1e9..e53c5d6d 100644 --- a/basic-logic/risingedge/risingedge.hni +++ b/basic-logic/risingedge/risingedge.hni @@ -2,7 +2,8 @@ { "name": "risingedge", "readableName": "RisingEdge", - "version": "0.0.1", + "version": "1.0.0", + "coreNode": true, "maxThreadCount": 0 } diff --git a/basic-logic/srflipflop/srflipflop.hni b/basic-logic/srflipflop/srflipflop.hni index 39054776..cf944609 100644 --- a/basic-logic/srflipflop/srflipflop.hni +++ b/basic-logic/srflipflop/srflipflop.hni @@ -2,7 +2,8 @@ { "name": "srflipflop", "readableName": "Set-Reset-Flip-Flop", - "version": "0.0.1", + "version": "1.0.0", + "coreNode": true, "maxThreadCount": 0 } diff --git a/basic-logic/switch/switch.hni b/basic-logic/switch/switch.hni index cac32465..353a0d53 100644 --- a/basic-logic/switch/switch.hni +++ b/basic-logic/switch/switch.hni @@ -2,7 +2,8 @@ { "name": "switch", "readableName": "Switch", - "version": "0.0.1", + "version": "1.0.0", + "coreNode": true, "maxThreadCount": 0 } diff --git a/basic-logic/variable-switch/variable-switch.hni b/basic-logic/variable-switch/variable-switch.hni index aad121d0..d23bc86e 100644 --- a/basic-logic/variable-switch/variable-switch.hni +++ b/basic-logic/variable-switch/variable-switch.hni @@ -2,7 +2,8 @@ { "name": "variable-switch", "readableName": "Variable switch", - "version": "0.0.1", + "version": "1.0.0", + "coreNode": true, "maxThreadCount": 0 } diff --git a/comment/comment.hni b/comment/comment.hni index 0c3ef07b..65c10c85 100644 --- a/comment/comment.hni +++ b/comment/comment.hni @@ -2,7 +2,8 @@ { "name": "comment", "readableName": "Comment", - "version": "0.0.1", + "version": "1.0.0", + "coreNode": true, "maxThreadCount": 0 } diff --git a/debug/debug.hni b/debug/debug.hni index e8e46510..36e165c3 100644 --- a/debug/debug.hni +++ b/debug/debug.hni @@ -2,7 +2,8 @@ { "name": "debug", "readableName": "Debug", - "version": "0.0.1", + "version": "1.0.0", + "coreNode": true, "maxThreadCount": 0 } diff --git a/exec/exec.hni b/exec/exec.hni index d75e4d01..d9dbb763 100644 --- a/exec/exec.hni +++ b/exec/exec.hni @@ -2,7 +2,8 @@ { "name": "exec", "readableName": "exec", - "version": "0.0.1", + "version": "1.0.0", + "coreNode": true, "maxThreadCount": 2 } diff --git a/function/function.hni b/function/function.hni index 4a24c746..efcd5f22 100644 --- a/function/function.hni +++ b/function/function.hni @@ -2,7 +2,8 @@ { "name": "function", "readableName": "function", - "version": "0.0.1", + "version": "1.0.0", + "coreNode": true, "maxThreadCount": 0 } diff --git a/gpio/gpio-in/gpio-in.hni b/gpio/gpio-in/gpio-in.hni index 9f81d451..11a5f39f 100644 --- a/gpio/gpio-in/gpio-in.hni +++ b/gpio/gpio-in/gpio-in.hni @@ -2,7 +2,8 @@ { "name": "gpio-in", "readableName": "GPIO input", - "version": "0.0.1", + "version": "1.0.0", + "coreNode": true, "maxThreadCount": 1 } diff --git a/gpio/gpio-out/gpio-out.hni b/gpio/gpio-out/gpio-out.hni index b28200d4..7d6da6d6 100644 --- a/gpio/gpio-out/gpio-out.hni +++ b/gpio/gpio-out/gpio-out.hni @@ -2,7 +2,8 @@ { "name": "gpio-out", "readableName": "GPIO output", - "version": "0.0.1", + "version": "1.0.0", + "coreNode": true, "maxThreadCount": 0 } diff --git a/http/http-in/http-in.hni b/http/http-in/http-in.hni index 1fd919ac..47f2069a 100644 --- a/http/http-in/http-in.hni +++ b/http/http-in/http-in.hni @@ -2,7 +2,8 @@ { "name": "http-in", "readableName": "HTTP input", - "version": "0.0.1", + "version": "1.0.0", + "coreNode": true, "maxThreadCount": 0 } diff --git a/http/http-request/http-request.hni b/http/http-request/http-request.hni index ab3135be..9d9d6006 100644 --- a/http/http-request/http-request.hni +++ b/http/http-request/http-request.hni @@ -2,7 +2,8 @@ { "name": "http-request", "readableName": "HTTP request", - "version": "0.0.1", + "version": "1.0.0", + "coreNode": true, "maxThreadCount": 0 } diff --git a/http/http-response/http-response.hni b/http/http-response/http-response.hni index 6276284c..4f25335d 100644 --- a/http/http-response/http-response.hni +++ b/http/http-response/http-response.hni @@ -2,7 +2,8 @@ { "name": "http-response", "readableName": "HTTP response", - "version": "0.0.1", + "version": "1.0.0", + "coreNode": true, "maxThreadCount": 0 } diff --git a/http/http-server/http-server.hni b/http/http-server/http-server.hni index 551d330e..a5a960d5 100644 --- a/http/http-server/http-server.hni +++ b/http/http-server/http-server.hni @@ -2,7 +2,8 @@ { "name": "http-server", "readableName": "HTTP server", - "version": "0.0.1", + "version": "1.0.0", + "coreNode": true, "maxThreadCount": 1 } diff --git a/influxdb/influxdb.hni b/influxdb/influxdb.hni index 950757af..c523da60 100644 --- a/influxdb/influxdb.hni +++ b/influxdb/influxdb.hni @@ -2,7 +2,8 @@ { "name": "influxdb", "readableName": "InfluxDB", - "version": "0.0.1", + "version": "1.0.0", + "coreNode": true, "maxThreadCount": 0 } diff --git a/light/light.hni b/light/light.hni index a8bfbfbe..1d088b41 100644 --- a/light/light.hni +++ b/light/light.hni @@ -2,7 +2,8 @@ { "name": "light", "readableName": "Light", - "version": "0.0.1", + "version": "1.0.0", + "coreNode": true, "maxThreadCount": 1 } diff --git a/link/link-in/link-in.hni b/link/link-in/link-in.hni index ee29ea44..2e89446b 100644 --- a/link/link-in/link-in.hni +++ b/link/link-in/link-in.hni @@ -2,7 +2,8 @@ { "name": "link-in", "readableName": "Link in", - "version": "0.0.1", + "version": "1.0.0", + "coreNode": true, "maxThreadCount": 0 } diff --git a/link/link-out/link-out.hni b/link/link-out/link-out.hni index 944f073c..480b8a6d 100644 --- a/link/link-out/link-out.hni +++ b/link/link-out/link-out.hni @@ -2,7 +2,8 @@ { "name": "link-out", "readableName": "Link out", - "version": "0.0.1", + "version": "1.0.0", + "coreNode": true, "maxThreadCount": 1 } diff --git a/modbus/modbus-host/modbus-host.hni b/modbus/modbus-host/modbus-host.hni index 80f1d6ad..3dba016d 100644 --- a/modbus/modbus-host/modbus-host.hni +++ b/modbus/modbus-host/modbus-host.hni @@ -2,7 +2,8 @@ { "name": "modbus-host", "readableName": "Modbus host", - "version": "0.0.1", + "version": "1.0.0", + "coreNode": true, "maxThreadCount": 1 } diff --git a/modbus/modbus-in/modbus-in.hni b/modbus/modbus-in/modbus-in.hni index 710ce92e..02e3199b 100644 --- a/modbus/modbus-in/modbus-in.hni +++ b/modbus/modbus-in/modbus-in.hni @@ -2,7 +2,8 @@ { "name": "modbus-in", "readableName": "Modbus input", - "version": "0.0.1", + "version": "1.0.0", + "coreNode": true, "maxThreadCount": 0 } diff --git a/modbus/modbus-out/modbus-out.hni b/modbus/modbus-out/modbus-out.hni index 8797f7a3..a51cf71a 100644 --- a/modbus/modbus-out/modbus-out.hni +++ b/modbus/modbus-out/modbus-out.hni @@ -2,7 +2,8 @@ { "name": "modbus-out", "readableName": "Modbus output", - "version": "0.0.1", + "version": "1.0.0", + "coreNode": true, "maxThreadCount": 0 } diff --git a/mqtt/mqtt-broker/mqtt-broker.hni b/mqtt/mqtt-broker/mqtt-broker.hni index c8616491..3304dc9e 100644 --- a/mqtt/mqtt-broker/mqtt-broker.hni +++ b/mqtt/mqtt-broker/mqtt-broker.hni @@ -2,7 +2,8 @@ { "name": "mqtt-broker", "readableName": "MQTT broker", - "version": "0.0.1", + "version": "1.0.0", + "coreNode": true, "maxThreadCount": 6 } diff --git a/mqtt/mqtt-in/mqtt-in.hni b/mqtt/mqtt-in/mqtt-in.hni index 2ce484d4..fc9f44fc 100644 --- a/mqtt/mqtt-in/mqtt-in.hni +++ b/mqtt/mqtt-in/mqtt-in.hni @@ -2,7 +2,8 @@ { "name": "mqtt-in", "readableName": "MQTT input", - "version": "0.0.1", + "version": "1.0.0", + "coreNode": true, "maxThreadCount": 0 } diff --git a/mqtt/mqtt-out/mqtt-out.hni b/mqtt/mqtt-out/mqtt-out.hni index 5f4412c5..d0400236 100644 --- a/mqtt/mqtt-out/mqtt-out.hni +++ b/mqtt/mqtt-out/mqtt-out.hni @@ -2,7 +2,8 @@ { "name": "mqtt-out", "readableName": "MQTT output", - "version": "0.0.1", + "version": "1.0.0", + "coreNode": true, "maxThreadCount": 0 } diff --git a/notification/xmpp-server.hni b/notification/xmpp-server.hni index ea9649a6..5d3b2a89 100755 --- a/notification/xmpp-server.hni +++ b/notification/xmpp-server.hni @@ -2,7 +2,8 @@ { "name": "xmpp-server", "readableName": "XMPP Server", - "version": "0.0.1", + "version": "1.0.0", + "coreNode": true, "maxThreadCount": 6 } diff --git a/notification/xmpp.hni b/notification/xmpp.hni index 099603e0..87f4de01 100755 --- a/notification/xmpp.hni +++ b/notification/xmpp.hni @@ -2,7 +2,8 @@ { "name": "xmpp", "readableName": "XMPP", - "version": "0.0.1", + "version": "1.0.0", + "coreNode": true, "maxThreadCount": 0 } diff --git a/parsers/json/json.hni b/parsers/json/json.hni index 53824149..3c8926ba 100644 --- a/parsers/json/json.hni +++ b/parsers/json/json.hni @@ -2,7 +2,8 @@ { "name": "json", "readableName": "JSON", - "version": "0.0.1", + "version": "1.0.0", + "coreNode": true, "maxThreadCount": 0 } diff --git a/parsers/strip/strip.hni b/parsers/strip/strip.hni index 2edce96e..98c81b0b 100644 --- a/parsers/strip/strip.hni +++ b/parsers/strip/strip.hni @@ -2,7 +2,8 @@ { "name": "strip", "readableName": "Strip", - "version": "0.0.1", + "version": "1.0.0", + "coreNode": true, "maxThreadCount": 0 } diff --git a/parsers/xml/xml.hni b/parsers/xml/xml.hni index 771d4643..1d395609 100644 --- a/parsers/xml/xml.hni +++ b/parsers/xml/xml.hni @@ -2,7 +2,8 @@ { "name": "xml", "readableName": "XML", - "version": "0.0.1", + "version": "1.0.0", + "coreNode": true, "maxThreadCount": 0 } diff --git a/passthrough/passthrough.hni b/passthrough/passthrough.hni index 304e6310..2fb5d953 100644 --- a/passthrough/passthrough.hni +++ b/passthrough/passthrough.hni @@ -2,7 +2,8 @@ { "name": "passthrough", "readableName": "Passthrough", - "version": "0.0.1", + "version": "1.0.0", + "coreNode": true, "maxThreadCount": 0 } diff --git a/php-test/php-test.hni b/php-test/php-test.hni index 6d22cef4..a1e43182 100644 --- a/php-test/php-test.hni +++ b/php-test/php-test.hni @@ -2,7 +2,8 @@ { "name": "php-test", "readableName": "php-test", - "version": "0.0.1", + "version": "1.0.0", + "coreNode": true, "maxThreadCount": 1 } diff --git a/ping/ping.hni b/ping/ping.hni index c3328498..698fcc31 100644 --- a/ping/ping.hni +++ b/ping/ping.hni @@ -2,7 +2,8 @@ { "name": "ping", "readableName": "ping", - "version": "0.0.1", + "version": "1.0.0", + "coreNode": true, "maxThreadCount": 1 } diff --git a/presence-light/presence-light.hni b/presence-light/presence-light.hni index a0994a3c..b88bb01c 100644 --- a/presence-light/presence-light.hni +++ b/presence-light/presence-light.hni @@ -2,7 +2,8 @@ { "name": "presence-light", "readableName": "Presence light", - "version": "0.0.1", + "version": "1.0.0", + "coreNode": true, "maxThreadCount": 1 } diff --git a/press-pattern/press-pattern.hni b/press-pattern/press-pattern.hni index c1430d01..b0191774 100644 --- a/press-pattern/press-pattern.hni +++ b/press-pattern/press-pattern.hni @@ -2,7 +2,8 @@ { "name": "press-pattern", "readableName": "Press pattern", - "version": "0.0.1", + "version": "1.0.0", + "coreNode": true, "maxThreadCount": 1 } diff --git a/pulsecounter/pulsecounter.hni b/pulsecounter/pulsecounter.hni index c49d9a61..3a1beb42 100644 --- a/pulsecounter/pulsecounter.hni +++ b/pulsecounter/pulsecounter.hni @@ -2,7 +2,8 @@ { "name": "pulsecounter", "readableName": "pulsecounter", - "version": "0.0.1", + "version": "1.0.0", + "coreNode": true, "maxThreadCount": 1 } diff --git a/serial/serial-in/serial-in.hni b/serial/serial-in/serial-in.hni index c18aec74..460bb7a3 100644 --- a/serial/serial-in/serial-in.hni +++ b/serial/serial-in/serial-in.hni @@ -2,7 +2,8 @@ { "name": "serial-in", "readableName": "Serial input", - "version": "0.0.1", + "version": "1.0.0", + "coreNode": true, "maxThreadCount": 0 } diff --git a/serial/serial-out/serial-out.hni b/serial/serial-out/serial-out.hni index 1a1d6d9e..4797a3de 100644 --- a/serial/serial-out/serial-out.hni +++ b/serial/serial-out/serial-out.hni @@ -2,7 +2,8 @@ { "name": "serial-out", "readableName": "Serial output", - "version": "0.0.1", + "version": "1.0.0", + "coreNode": true, "maxThreadCount": 0 } diff --git a/serial/serial-port/serial-port.hni b/serial/serial-port/serial-port.hni index 4a101c81..58b805c5 100644 --- a/serial/serial-port/serial-port.hni +++ b/serial/serial-port/serial-port.hni @@ -2,7 +2,8 @@ { "name": "serial-port", "readableName": "Serial port", - "version": "0.0.1", + "version": "1.0.0", + "coreNode": true, "maxThreadCount": 1 } diff --git a/storage/file/file.hni b/storage/file/file.hni index 226179bf..fdb5d6ba 100644 --- a/storage/file/file.hni +++ b/storage/file/file.hni @@ -2,7 +2,8 @@ { "name": "file", "readableName": "File", - "version": "0.0.1", + "version": "1.0.0", + "coreNode": true, "maxThreadCount": 0 } diff --git a/synchronous/synchronous.hni b/synchronous/synchronous.hni index a926f168..299d6fe6 100644 --- a/synchronous/synchronous.hni +++ b/synchronous/synchronous.hni @@ -2,7 +2,8 @@ { "name": "synchronous", "readableName": "Synchronous", - "version": "0.0.1", + "version": "1.0.0", + "coreNode": true, "maxThreadCount": 0 } diff --git a/template/template.hni b/template/template.hni index 03bf315a..d319125b 100644 --- a/template/template.hni +++ b/template/template.hni @@ -2,7 +2,8 @@ { "name": "template", "readableName": "Template", - "version": "0.0.1", + "version": "1.0.0", + "coreNode": true, "maxThreadCount": 0 } diff --git a/timers/clock/clock.hni b/timers/clock/clock.hni index 3eb9cb7b..775d6987 100644 --- a/timers/clock/clock.hni +++ b/timers/clock/clock.hni @@ -2,7 +2,8 @@ { "name": "clock", "readableName": "Clock", - "version": "0.0.1", + "version": "1.0.0", + "coreNode": true, "maxThreadCount": 1 } diff --git a/timers/delay/delay.hni b/timers/delay/delay.hni index 52c10c83..feaf4e3d 100644 --- a/timers/delay/delay.hni +++ b/timers/delay/delay.hni @@ -2,7 +2,8 @@ { "name": "delay", "readableName": "Delay", - "version": "0.0.1", + "version": "1.0.0", + "coreNode": true, "maxThreadCount": 10 } diff --git a/timers/impulse/impulse.hni b/timers/impulse/impulse.hni index b0a55a33..17f7481e 100644 --- a/timers/impulse/impulse.hni +++ b/timers/impulse/impulse.hni @@ -2,7 +2,8 @@ { "name": "impulse", "readableName": "Impulse", - "version": "0.0.1", + "version": "1.0.0", + "coreNode": true, "maxThreadCount": 1 } diff --git a/timers/interval/interval.hni b/timers/interval/interval.hni index e92101a9..3673ef5f 100644 --- a/timers/interval/interval.hni +++ b/timers/interval/interval.hni @@ -2,7 +2,8 @@ { "name": "interval", "readableName": "Interval", - "version": "0.0.1", + "version": "1.0.0", + "coreNode": true, "maxThreadCount": 1 } diff --git a/timers/off-delay/off-delay.hni b/timers/off-delay/off-delay.hni index 4d9c56a5..91da631d 100644 --- a/timers/off-delay/off-delay.hni +++ b/timers/off-delay/off-delay.hni @@ -2,7 +2,8 @@ { "name": "off-delay", "readableName": "Off-delay", - "version": "0.0.1", + "version": "1.0.0", + "coreNode": true, "maxThreadCount": 1 } diff --git a/timers/on-delay/on-delay.hni b/timers/on-delay/on-delay.hni index 526b3bcf..cda96b3e 100644 --- a/timers/on-delay/on-delay.hni +++ b/timers/on-delay/on-delay.hni @@ -2,7 +2,8 @@ { "name": "on-delay", "readableName": "on-delay", - "version": "0.0.1", + "version": "1.0.0", + "coreNode": true, "maxThreadCount": 1 } diff --git a/timers/rate-limiter/rate-limiter.hni b/timers/rate-limiter/rate-limiter.hni index 524cfdac..3eac97d3 100644 --- a/timers/rate-limiter/rate-limiter.hni +++ b/timers/rate-limiter/rate-limiter.hni @@ -2,7 +2,8 @@ { "name": "rate-limiter", "readableName": "Rate limiter", - "version": "0.0.1", + "version": "1.0.0", + "coreNode": true, "maxThreadCount": 1 } diff --git a/timers/slow-pwm/slow-pwm.hni b/timers/slow-pwm/slow-pwm.hni index f4eba8cf..6fd84649 100644 --- a/timers/slow-pwm/slow-pwm.hni +++ b/timers/slow-pwm/slow-pwm.hni @@ -2,7 +2,8 @@ { "name": "slow-pwm", "readableName": "Slow PWM", - "version": "0.0.1", + "version": "1.0.0", + "coreNode": true, "maxThreadCount": 1 } diff --git a/timers/sun-position/sun-position.hni b/timers/sun-position/sun-position.hni index 900cd254..a2cb9b6e 100644 --- a/timers/sun-position/sun-position.hni +++ b/timers/sun-position/sun-position.hni @@ -2,7 +2,8 @@ { "name": "sun-position", "readableName": "Sun Position", - "version": "0.0.1", + "version": "1.0.0", + "coreNode": true, "maxThreadCount": 1 } diff --git a/timers/timer/timer.hni b/timers/timer/timer.hni index 92893e7c..f22a4f34 100644 --- a/timers/timer/timer.hni +++ b/timers/timer/timer.hni @@ -2,7 +2,8 @@ { "name": "timer", "readableName": "Timer", - "version": "0.0.1", + "version": "1.0.0", + "coreNode": true, "maxThreadCount": 1 } diff --git a/tls-config/tls-config.hni b/tls-config/tls-config.hni index 98c48bcf..cca20eba 100644 --- a/tls-config/tls-config.hni +++ b/tls-config/tls-config.hni @@ -2,7 +2,8 @@ { "name": "tls-config", "readableName": "TLS configuration", - "version": "0.0.1", + "version": "1.0.0", + "coreNode": true, "maxThreadCount": 0 } diff --git a/tls-server-config/tls-server-config.hni b/tls-server-config/tls-server-config.hni index 7c1a6851..a40fbc07 100644 --- a/tls-server-config/tls-server-config.hni +++ b/tls-server-config/tls-server-config.hni @@ -2,7 +2,8 @@ { "name": "tls-server-config", "readableName": "TLS server configuration", - "version": "0.0.1", + "version": "1.0.0", + "coreNode": true, "maxThreadCount": 0 } diff --git a/variable/constant/constant.hni b/variable/constant/constant.hni index 861b5c77..8dc2c4b7 100644 --- a/variable/constant/constant.hni +++ b/variable/constant/constant.hni @@ -2,7 +2,8 @@ { "name": "constant", "readableName": "Constant", - "version": "0.0.1", + "version": "1.0.0", + "coreNode": true, "maxThreadCount": 0 } diff --git a/variable/toggle/toggle.hni b/variable/toggle/toggle.hni index 2c4c63ef..61dd8c29 100644 --- a/variable/toggle/toggle.hni +++ b/variable/toggle/toggle.hni @@ -2,7 +2,8 @@ { "name": "toggle", "readableName": "Toggle", - "version": "0.0.1", + "version": "1.0.0", + "coreNode": true, "maxThreadCount": 0 } diff --git a/variable/variable-in/variable-in.hni b/variable/variable-in/variable-in.hni index 5851d3f7..92647488 100644 --- a/variable/variable-in/variable-in.hni +++ b/variable/variable-in/variable-in.hni @@ -2,7 +2,8 @@ { "name": "variable-in", "readableName": "Variable input", - "version": "0.0.1", + "version": "1.0.0", + "coreNode": true, "maxThreadCount": 0 } diff --git a/variable/variable-loop-prevention-group/variable-loop-prevention-group.hni b/variable/variable-loop-prevention-group/variable-loop-prevention-group.hni index be7b6c9e..61fb2ec1 100644 --- a/variable/variable-loop-prevention-group/variable-loop-prevention-group.hni +++ b/variable/variable-loop-prevention-group/variable-loop-prevention-group.hni @@ -2,7 +2,8 @@ { "name": "variable-loop-prevention-group", "readableName": "Variable loop prevention group", - "version": "0.0.1", + "version": "1.0.0", + "coreNode": true, "maxThreadCount": 0 } diff --git a/variable/variable-out/variable-out.hni b/variable/variable-out/variable-out.hni index 0a8277f5..2cabcaef 100644 --- a/variable/variable-out/variable-out.hni +++ b/variable/variable-out/variable-out.hni @@ -2,7 +2,8 @@ { "name": "variable-out", "readableName": "Variable output", - "version": "0.0.1", + "version": "1.0.0", + "coreNode": true, "maxThreadCount": 0 } From fe14d9833b4996bd5f88853a181c4538b1fe283b Mon Sep 17 00:00:00 2001 From: "Dr. Sathya Laufer" Date: Fri, 7 Jun 2019 16:59:51 +0200 Subject: [PATCH 38/47] Fixed: Crash in change node --- Makefile.am | 2 +- basic-logic/change/MyNode.cpp | 2 +- configure.ac | 2 +- python/Factory.cpp | 42 +++ python/Factory.h | 43 ++++ python/Makefile.am | 16 ++ python/Python.cpp | 468 ++++++++++++++++++++++++++++++++++ python/Python.h | 77 ++++++ python/locales/en-US/python | 15 ++ python/python.hni | 108 ++++++++ 10 files changed, 772 insertions(+), 3 deletions(-) create mode 100644 python/Factory.cpp create mode 100644 python/Factory.h create mode 100644 python/Makefile.am create mode 100644 python/Python.cpp create mode 100644 python/Python.h create mode 100644 python/locales/en-US/python create mode 100644 python/python.hni diff --git a/Makefile.am b/Makefile.am index 1ffa9c6e..04be74e1 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,3 +1,3 @@ AUTOMAKE_OPTIONS = foreign ACLOCAL_AMFLAGS = -I m4 -I cfg -SUBDIRS = average basic-logic comment debug exec function gpio http influxdb light link modbus mqtt notification parsers passthrough ping pulsecounter presence-light press-pattern serial synchronous storage template timers tls-config tls-server-config variable +SUBDIRS = average basic-logic comment debug exec function gpio http influxdb light link modbus mqtt notification parsers passthrough ping pulsecounter presence-light press-pattern python serial synchronous storage template timers tls-config tls-server-config variable diff --git a/basic-logic/change/MyNode.cpp b/basic-logic/change/MyNode.cpp index e1c690f4..8bf57fb6 100644 --- a/basic-logic/change/MyNode.cpp +++ b/basic-logic/change/MyNode.cpp @@ -157,7 +157,7 @@ bool MyNode::init(Flows::PNodeInfo info) convertType(rule.from, rule.fromt); } else rule.from = std::make_shared(); - if(valueTypeIterator->second->stringValue == "regex") + if(valueTypeIterator != ruleStruct->structValue->end() && valueTypeIterator->second->stringValue == "regex") { rule.fromRegexSet = true; rule.fromRegex = std::regex(rule.from->stringValue, std::regex::ECMAScript); diff --git a/configure.ac b/configure.ac index ba0c5587..5a261888 100644 --- a/configure.ac +++ b/configure.ac @@ -62,4 +62,4 @@ esac #AC_ARG_ENABLE(debug, AS_HELP_STRING([--enable-debug], [enable debugging, default: no]), [case "${enableval}" in yes) debug=true ;; no) debug=false ;; *) AC_MSG_ERROR([bad value ${enableval} for --enable-debug]) ;; esac], [debug=false]) #AM_CONDITIONAL(DEBUG, test x"$debug" = x"true") -AC_OUTPUT(Makefile average/Makefile basic-logic/Makefile comment/Makefile debug/Makefile exec/Makefile function/Makefile gpio/Makefile http/Makefile influxdb/Makefile light/Makefile link/Makefile modbus/Makefile mqtt/Makefile notification/Makefile parsers/Makefile passthrough/Makefile ping/Makefile presence-light/Makefile press-pattern/Makefile pulsecounter/Makefile serial/Makefile synchronous/Makefile storage/Makefile template/Makefile timers/Makefile tls-config/Makefile tls-server-config/Makefile variable/Makefile) +AC_OUTPUT(Makefile average/Makefile basic-logic/Makefile comment/Makefile debug/Makefile exec/Makefile function/Makefile gpio/Makefile http/Makefile influxdb/Makefile light/Makefile link/Makefile modbus/Makefile mqtt/Makefile notification/Makefile parsers/Makefile passthrough/Makefile ping/Makefile presence-light/Makefile press-pattern/Makefile pulsecounter/Makefile python/Makefile serial/Makefile synchronous/Makefile storage/Makefile template/Makefile timers/Makefile tls-config/Makefile tls-server-config/Makefile variable/Makefile) diff --git a/python/Factory.cpp b/python/Factory.cpp new file mode 100644 index 00000000..eee9ca53 --- /dev/null +++ b/python/Factory.cpp @@ -0,0 +1,42 @@ +/* Copyright 2013-2019 Homegear GmbH + * + * Homegear is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Homegear is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Homegear. If not, see . + * + * In addition, as a special exception, the copyright holders give + * permission to link the code of portions of this program with the + * OpenSSL library under certain conditions as described in each + * individual source file, and distribute linked combinations + * including the two. + * You must obey the GNU General Public License in all respects + * for all of the code used other than OpenSSL. If you modify + * file(s) with this exception, you may extend this exception to your + * version of the file(s), but you are not obligated to do so. If you + * do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source + * files in the program, then also delete it here. + */ + +#include "Factory.h" +#include "Python.h" +#include "../config.h" + +Flows::INode* MyFactory::createNode(std::string path, std::string nodeNamespace, std::string type, const std::atomic_bool* frontendConnected) +{ + return new Python::Python(path, nodeNamespace, type, frontendConnected); +} + +Flows::NodeFactory* getFactory() +{ + return (Flows::NodeFactory*) (new MyFactory); +} diff --git a/python/Factory.h b/python/Factory.h new file mode 100644 index 00000000..317c3b37 --- /dev/null +++ b/python/Factory.h @@ -0,0 +1,43 @@ +/* Copyright 2013-2019 Homegear GmbH + * + * Homegear is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Homegear is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Homegear. If not, see . + * + * In addition, as a special exception, the copyright holders give + * permission to link the code of portions of this program with the + * OpenSSL library under certain conditions as described in each + * individual source file, and distribute linked combinations + * including the two. + * You must obey the GNU General Public License in all respects + * for all of the code used other than OpenSSL. If you modify + * file(s) with this exception, you may extend this exception to your + * version of the file(s), but you are not obligated to do so. If you + * do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source + * files in the program, then also delete it here. + */ + +#ifndef FACTORY_H +#define FACTORY_H + +#include + +class MyFactory : Flows::NodeFactory +{ +public: + virtual Flows::INode* createNode(std::string path, std::string nodeNamespace, std::string type, const std::atomic_bool* frontendConnected); +}; + +extern "C" Flows::NodeFactory* getFactory(); + +#endif diff --git a/python/Makefile.am b/python/Makefile.am new file mode 100644 index 00000000..2df4b283 --- /dev/null +++ b/python/Makefile.am @@ -0,0 +1,16 @@ +AUTOMAKE_OPTIONS = subdir-objects + +AM_CPPFLAGS = -Wall -std=c++11 -DFORTIFY_SOURCE=2 -DGCRYPT_NO_DEPRECATED +AM_LDFLAGS = -Wl,-rpath=/lib/homegear -Wl,-rpath=/usr/lib/homegear -Wl,-rpath=/usr/local/lib/homegear +LIBS += -Wl,-Bdynamic -lhomegear-node -lhomegear-base + +libdir = $(localstatedir)/lib/homegear/node-blue/nodes/python +lib_LTLIBRARIES = python.la +python_la_SOURCES = Factory.cpp Python.cpp +python_la_LDFLAGS =-module -avoid-version -shared +python_ladir = $(libdir) +python_la_DATA = python.hni +locale_en_usdir = $(libdir)/locales/en-US +locale_en_us_DATA = locales/en-US/python +install-python-hook: + rm -f $(DESTDIR)$(libdir)/python.la diff --git a/python/Python.cpp b/python/Python.cpp new file mode 100644 index 00000000..4561c50d --- /dev/null +++ b/python/Python.cpp @@ -0,0 +1,468 @@ +/* Copyright 2013-2019 Homegear GmbH + * + * Homegear is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Homegear is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Homegear. If not, see . + * + * In addition, as a special exception, the copyright holders give + * permission to link the code of portions of this program with the + * OpenSSL library under certain conditions as described in each + * individual source file, and distribute linked combinations + * including the two. + * You must obey the GNU General Public License in all respects + * for all of the code used other than OpenSSL. If you modify + * file(s) with this exception, you may extend this exception to your + * version of the file(s), but you are not obligated to do so. If you + * do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source + * files in the program, then also delete it here. + */ + +#include "Python.h" +#include "homegear-base/Managers/ProcessManager.h" +#include + +namespace Python +{ + +Python::Python(std::string path, std::string nodeNamespace, std::string type, const std::atomic_bool* frontendConnected) : Flows::INode(path, nodeNamespace, type, frontendConnected) +{ +} + +Python::~Python() +{ +} + +bool Python::init(Flows::PNodeInfo info) +{ + try + { + auto settingsIterator = info->info->structValue->find("filename"); + if(settingsIterator != info->info->structValue->end()) _filename = settingsIterator->second->stringValue; + + settingsIterator = info->info->structValue->find("arguments"); + if(settingsIterator != info->info->structValue->end()) _arguments = settingsIterator->second->stringValue; + + settingsIterator = info->info->structValue->find("autostart"); + if(settingsIterator != info->info->structValue->end()) _autostart = settingsIterator->second->booleanValue; + + settingsIterator = info->info->structValue->find("collect-output"); + if(settingsIterator != info->info->structValue->end()) _collectOutput = settingsIterator->second->booleanValue; + + return true; + } + catch(const std::exception& ex) + { + _out->printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__, ex.what()); + } + return false; +} + +bool Python::start() +{ + try + { + _callbackHandlerId = BaseLib::ProcessManager::registerCallbackHandler(std::function(std::bind(&Python::sigchildHandler, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4))); + + if(_autostart) startProgram(); + + return true; + } + catch(const std::exception& ex) + { + _out->printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__, ex.what()); + } + return false; +} + +void Python::stop() +{ + try + { + _autostart = false; + if(_pid != -1) kill(_pid, 15); + } + catch(const std::exception& ex) + { + _out->printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__, ex.what()); + } +} + +void Python::waitForStop() +{ + if(_pid != -1) kill(_pid, 15); + for(int32_t i = 0; i < 600; i++) + { + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + if(_pid == -1) break; + } + if(_pid != -1) + { + _out->printError("Error: Process did not finish within 60 seconds. Killing it."); + kill(_pid, 9); + close(_stdIn); + close(_stdOut); + close(_stdErr); + _stdIn = -1; + _stdOut = -1; + _stdErr = -1; + } + if(_execThread.joinable()) _execThread.join(); + if(_errorThread.joinable()) _errorThread.join(); + BaseLib::ProcessManager::unregisterCallbackHandler(_callbackHandlerId); +} + +void Python::input(const Flows::PNodeInfo info, uint32_t index, const Flows::PVariable message) +{ + try + { + if(index == 0) + { + if(_pid == -1) startProgram(); + } + else if(index == 1) + { + if(_pid != -1) kill(_pid, message->structValue->at("payload")->integerValue64); + } + else if(index == 2) + { + if(_stdIn != -1) + { + size_t totalBytesWritten = 0; + while(totalBytesWritten < message->structValue->at("payload")->stringValue.size()) + { + int bytesWritten = -1; + do + { + bytesWritten = write(_stdIn, message->structValue->at("payload")->stringValue.data(), message->structValue->at("payload")->stringValue.size()); + } while(bytesWritten == -1 && (errno == EAGAIN || errno == EINTR)); + if(bytesWritten <= 0) + { + _out->printWarning("Warning: Could not write to STDIN: " + std::string(strerror(errno))); + break; + } + totalBytesWritten += bytesWritten; + } + } + } + } + catch(const std::exception& ex) + { + _out->printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__, ex.what()); + } +} + +void Python::startProgram() +{ + try + { + if(_filename.empty()) + { + _out->printError("Error: filename is not set."); + return; + } + + { + std::lock_guard bufferGuard(_bufferMutex); + _bufferOut.clear(); + _bufferErr.clear(); + } + + if(_execThread.joinable()) _execThread.join(); + if(_errorThread.joinable()) _errorThread.join(); + _execThread = std::thread(&Python::execThread, this); + } + catch(const std::exception& ex) + { + _out->printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__, ex.what()); + } +} + +int32_t Python::getMaxFd() +{ + struct rlimit limits{}; + if(getrlimit(RLIMIT_NOFILE, &limits) == -1 || limits.rlim_cur >= INT32_MAX) + { + return 1024; + } + return limits.rlim_cur; +} + +void Python::execThread() +{ + try + { + do + { + if(_stdErr != -1) + { + close(_stdErr); + _stdErr = -1; + } + if(_errorThread.joinable()) _errorThread.join(); + + int stdIn = -1; + int stdOut = -1; + int stdErr = -1; + _pid = BaseLib::ProcessManager::systemp(_filename, BaseLib::ProcessManager::splitArguments(_arguments), getMaxFd(), stdIn, stdOut, stdErr); + _stdIn = stdIn; + _stdOut = stdOut; + _stdErr = stdErr; + + _errorThread = std::thread(&Python::errorThread, this); + + std::array buffer{}; + std::string bufferOut; + while(_stdOut != -1) + { + if(!_collectOutput) bufferOut.clear(); + auto bytesRead = 0; + do + { + bytesRead = read(_stdOut, buffer.data(), buffer.size()); + if(bytesRead > 0) + { + if(_collectOutput) + { + std::lock_guard bufferGuard(_bufferMutex); + _bufferOut.insert(_bufferOut.end(), buffer.begin(), buffer.begin() + bytesRead); + } + else bufferOut.insert(bufferOut.end(), buffer.begin(), buffer.begin() + bytesRead); + } + } while(bytesRead > 0); + + if(!_collectOutput && !bufferOut.empty()) + { + auto outputVector = BaseLib::HelperFunctions::splitAll(bufferOut, '\n'); + + Flows::PVariable message = std::make_shared(Flows::VariableType::tStruct); + Flows::PVariable outputArray = std::make_shared(Flows::VariableType::tArray); + outputArray->arrayValue->reserve(outputVector.size()); + for(int32_t i = 0; i < (signed)outputVector.size(); i++) + { + if(i == (signed)outputVector.size() - 1 && outputVector[i].empty()) continue; + outputArray->arrayValue->emplace_back(std::make_shared(std::move(outputVector[i]))); + } + message->structValue->emplace("payload", outputArray); + output(1, message); + } + } + + if(_collectOutput) + { + std::lock_guard bufferGuard(_bufferMutex); + auto outputVector = BaseLib::HelperFunctions::splitAll(_bufferOut, '\n'); + + Flows::PVariable message = std::make_shared(Flows::VariableType::tStruct); + Flows::PVariable outputArray = std::make_shared(Flows::VariableType::tArray); + outputArray->arrayValue->reserve(outputVector.size()); + for(int32_t i = 0; i < (signed)outputVector.size(); i++) + { + if(i == (signed)outputVector.size() - 1 && outputVector[i].empty()) continue; + outputArray->arrayValue->emplace_back(std::make_shared(std::move(outputVector[i]))); + } + message->structValue->emplace("payload", outputArray); + output(1, message); + } + + if(_autostart) std::this_thread::sleep_for(std::chrono::seconds(1)); + } + while(_autostart); + } + catch(const std::exception& ex) + { + _out->printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__, ex.what()); + } +} + +void Python::errorThread() +{ + try + { + std::array buffer{}; + std::string bufferErr; + while(_stdErr != -1) + { + int32_t bytesRead = 0; + if(!_collectOutput) bufferErr.clear(); + do + { + bytesRead = read(_stdErr, buffer.data(), buffer.size()); + if(bytesRead > 0) + { + if(_collectOutput) + { + std::lock_guard bufferGuard(_bufferMutex); + _bufferErr.insert(_bufferErr.end(), buffer.begin(), buffer.begin() + bytesRead); + } + else bufferErr.insert(bufferErr.end(), buffer.begin(), buffer.begin() + bytesRead); + } + } while(bytesRead > 0); + + if(!_collectOutput && !bufferErr.empty()) + { + auto outputVector = BaseLib::HelperFunctions::splitAll(bufferErr, '\n'); + + Flows::PVariable message = std::make_shared(Flows::VariableType::tStruct); + Flows::PVariable outputArray = std::make_shared(Flows::VariableType::tArray); + outputArray->arrayValue->reserve(outputVector.size()); + for(int32_t i = 0; i < (signed)outputVector.size(); i++) + { + if(i == (signed)outputVector.size() - 1 && outputVector[i].empty()) continue; + outputArray->arrayValue->emplace_back(std::make_shared(std::move(outputVector[i]))); + } + message->structValue->emplace("payload", outputArray); + output(2, message); + } + } + + if(_collectOutput) + { + std::lock_guard bufferGuard(_bufferMutex); + if(!_bufferErr.empty()) + { + auto outputVector = BaseLib::HelperFunctions::splitAll(_bufferErr, '\n'); + + Flows::PVariable message = std::make_shared(Flows::VariableType::tStruct); + Flows::PVariable outputArray = std::make_shared(Flows::VariableType::tArray); + outputArray->arrayValue->reserve(outputVector.size()); + for(int32_t i = 0; i < (signed)outputVector.size(); i++) + { + if(i == (signed)outputVector.size() - 1 && outputVector[i].empty()) continue; + outputArray->arrayValue->emplace_back(std::make_shared(std::move(outputVector[i]))); + } + message->structValue->emplace("payload", outputArray); + output(2, message); + } + } + } + catch(const std::exception& ex) + { + _out->printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__, ex.what()); + } +} + +void Python::sigchildHandler(pid_t pid, int exitCode, int signal, bool coreDumped) +{ + try + { + if(pid == _pid) + { + close(_stdIn); + close(_stdOut); + close(_stdErr); + _stdIn = -1; + _stdOut = -1; + _stdErr = -1; + _pid = -1; + Flows::PVariable message = std::make_shared(Flows::VariableType::tStruct); + message->structValue->emplace("coreDumped", std::make_shared(coreDumped)); + message->structValue->emplace("signal", std::make_shared(signal)); + message->structValue->emplace("payload", std::make_shared(exitCode)); + if(_collectOutput) + { + std::lock_guard bufferGuard(_bufferMutex); + + { + auto outputVector = BaseLib::HelperFunctions::splitAll(_bufferOut, '\n'); + + Flows::PVariable message = std::make_shared(Flows::VariableType::tStruct); + Flows::PVariable outputArray = std::make_shared(Flows::VariableType::tArray); + outputArray->arrayValue->reserve(outputVector.size()); + for(int32_t i = 0; i < (signed)outputVector.size(); i++) + { + if(i == (signed)outputVector.size() - 1 && outputVector[i].empty()) continue; + outputArray->arrayValue->emplace_back(std::make_shared(std::move(outputVector[i]))); + } + message->structValue->emplace("stdout", outputArray); + } + + if(!_bufferErr.empty()) + { + auto outputVector = BaseLib::HelperFunctions::splitAll(_bufferErr, '\n'); + + Flows::PVariable message = std::make_shared(Flows::VariableType::tStruct); + Flows::PVariable outputArray = std::make_shared(Flows::VariableType::tArray); + outputArray->arrayValue->reserve(outputVector.size()); + for(int32_t i = 0; i < (signed)outputVector.size(); i++) + { + if(i == (signed)outputVector.size() - 1 && outputVector[i].empty()) continue; + outputArray->arrayValue->emplace_back(std::make_shared(std::move(outputVector[i]))); + } + message->structValue->emplace("stderr", outputArray); + } + } + output(0, message); + } + } + catch(const std::exception& ex) + { + _out->printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__, ex.what()); + } +} + +int32_t Python::read(std::atomic_int& fd, uint8_t* buffer, int32_t bufferSize) +{ + if(fd == -1) return 0; + + timeval timeout{}; + timeout.tv_sec = 0; + timeout.tv_usec = 100000; + fd_set readFileDescriptor{}; + FD_ZERO(&readFileDescriptor); + int32_t nfds = fd + 1; + if(nfds <= 0) + { + close(fd); + fd = -1; + return -1; + } + FD_SET(fd, &readFileDescriptor); + auto bytesRead = select(nfds, &readFileDescriptor, nullptr, nullptr, &timeout); + if(bytesRead == 0) + { + return 0; + } + if(bytesRead != 1) + { + close(fd); + fd = -1; + return -1; + } + do + { + bytesRead = ::read(fd, buffer, bufferSize); + } while(bytesRead < 0 && (errno == EAGAIN || errno == EINTR)); + if(bytesRead <= 0) + { + if(bytesRead == -1) + { + if(errno == ETIMEDOUT) return 0; + else + { + close(fd); + fd = -1; + return -1; + } + } + else + { + close(fd); + fd = -1; + return -1; + } + } + if(bytesRead > bufferSize) bytesRead = bufferSize; + return bytesRead; +} + +} diff --git a/python/Python.h b/python/Python.h new file mode 100644 index 00000000..88380b46 --- /dev/null +++ b/python/Python.h @@ -0,0 +1,77 @@ +/* Copyright 2013-2019 Homegear GmbH + * + * Homegear is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Homegear is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Homegear. If not, see . + * + * In addition, as a special exception, the copyright holders give + * permission to link the code of portions of this program with the + * OpenSSL library under certain conditions as described in each + * individual source file, and distribute linked combinations + * including the two. + * You must obey the GNU General Public License in all respects + * for all of the code used other than OpenSSL. If you modify + * file(s) with this exception, you may extend this exception to your + * version of the file(s), but you are not obligated to do so. If you + * do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source + * files in the program, then also delete it here. + */ + +#ifndef RUNSCRIPT_H_ +#define RUNSCRIPT_H_ + +#include +#include +#include + +namespace Python +{ + +class Python : public Flows::INode +{ +public: + Python(std::string path, std::string nodeNamespace, std::string type, const std::atomic_bool* frontendConnected); + virtual ~Python(); + + virtual bool init(Flows::PNodeInfo info); + virtual bool start(); + virtual void stop(); + virtual void waitForStop(); +private: + int32_t _callbackHandlerId = -1; + std::string _filename; + std::string _arguments; + std::atomic_bool _autostart{false}; + bool _collectOutput = false; + std::thread _execThread; + std::thread _errorThread; + std::mutex _bufferMutex; + std::string _bufferOut; + std::string _bufferErr; + std::atomic_int _pid{-1}; + std::atomic_int _stdIn{-1}; + std::atomic_int _stdOut{-1}; + std::atomic_int _stdErr{-1}; + + virtual void input(const Flows::PNodeInfo info, uint32_t index, const Flows::PVariable message); + void startProgram(); + int32_t getMaxFd(); + void sigchildHandler(pid_t pid, int exitCode, int signal, bool coreDumped); + void execThread(); + void errorThread(); + int32_t read(std::atomic_int& fd, uint8_t* buffer, int32_t bufferSize); +}; + +} + +#endif diff --git a/python/locales/en-US/python b/python/locales/en-US/python new file mode 100644 index 00000000..9fd20d29 --- /dev/null +++ b/python/locales/en-US/python @@ -0,0 +1,15 @@ +{ + "python/python.hni": { + "python": { + "label": { + "name": "Name", + "function": "Function", + "inputs": "Inputs", + "outputs": "Outputs" + }, + "tip": "Tip: See the Info tab for help writing functions.", + "paletteHelp": "

A function block where you can write PHP code to do more interesting things.

", + "help": "

A function block where you can write PHP code to do more interesting things.

By convention it will have a $message['payload'] property containing the body of the message.

The message is passed in as an associative array called $message. Information about the node can be found in the associative array $nodeInfo. The input index is passed in $inputIndex

The Homegear object is predefined as $hg. So you can directly execute RPC methods. E. g. $hg->setValue(...).

Data storage

Data can be stored by calling setNodeData($key, $value), setFlowData($key, $value) or setGlobalData($key, $value) and retrieved with getNodeData($key), getFlowData($key) or getGlobalData($key).

Event log

You can send messages to the frontend event log by exedutiong eventLog($message).

Sending messages

The function can either return the messages it wants to pass on to the next nodes in the flow, or can call output($outputIndex, $message).

It can return:

  • a single message object - passed to nodes connected to the first output
  • an array of message objects - passed to nodes connected to the corresponding outputs

If any element of the array is itself an array of messages, multiple messages are sent to the corresponding output.

If NULL is returned, either by itself or as an element of the array, no message is passed on.

See the online reference for more help.

" + } + } +} \ No newline at end of file diff --git a/python/python.hni b/python/python.hni new file mode 100644 index 00000000..52e94201 --- /dev/null +++ b/python/python.hni @@ -0,0 +1,108 @@ + + + + From a3861f142010d2fe554845e142adf518eae6923b Mon Sep 17 00:00:00 2001 From: "Dr. Sathya Laufer" Date: Sun, 9 Jun 2019 13:06:55 +0200 Subject: [PATCH 39/47] Added python node --- CMakeLists.txt | 4 + debian/control | 2 +- exec/Exec.cpp | 39 +++-- function/function.hni | 2 +- function/locales/en-US/function | 2 +- python/Python.cpp | 256 +++++++++++++++----------------- python/Python.h | 9 +- python/locales/en-US/python | 11 +- python/python.hni | 42 ++++-- 9 files changed, 195 insertions(+), 172 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 82c0bca8..078a818b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -140,6 +140,10 @@ set(SOURCE_FILES pulsecounter/Factory.h pulsecounter/MyNode.cpp pulsecounter/MyNode.h + python/Factory.cpp + python/Factory.h + python/Python.cpp + python/Python.h serial/serial-port/Factory.cpp serial/serial-port/Factory.h serial/serial-port/MyNode.cpp diff --git a/debian/control b/debian/control index 591b602c..34502e49 100644 --- a/debian/control +++ b/debian/control @@ -8,7 +8,7 @@ Homepage: https://homegear.eu Package: homegear-nodes-core Architecture: any -Depends: ${shlibs:Depends}, ${misc:Depends}, libhomegear-node +Depends: ${shlibs:Depends}, ${misc:Depends}, libhomegear-node, python3-homegear Description: Core nodes for Homegear's Node-BLUE Homegear is a program to interface your home automation software with your smart home devices. diff --git a/exec/Exec.cpp b/exec/Exec.cpp index c6dc7003..2695adb7 100644 --- a/exec/Exec.cpp +++ b/exec/Exec.cpp @@ -99,26 +99,33 @@ void Exec::stop() void Exec::waitForStop() { - if(_pid != -1) kill(_pid, 15); - for(int32_t i = 0; i < 600; i++) + try { - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - if(_pid == -1) break; + if(_pid != -1) kill(_pid, 15); + for(int32_t i = 0; i < 600; i++) + { + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + if(_pid == -1) break; + } + if(_pid != -1) + { + _out->printError("Error: Process did not finish within 60 seconds. Killing it."); + kill(_pid, 9); + close(_stdIn); + close(_stdOut); + close(_stdErr); + _stdIn = -1; + _stdOut = -1; + _stdErr = -1; + } + if(_execThread.joinable()) _execThread.join(); + if(_errorThread.joinable()) _errorThread.join(); + BaseLib::ProcessManager::unregisterCallbackHandler(_callbackHandlerId); } - if(_pid != -1) + catch(const std::exception& ex) { - _out->printError("Error: Process did not finish within 60 seconds. Killing it."); - kill(_pid, 9); - close(_stdIn); - close(_stdOut); - close(_stdErr); - _stdIn = -1; - _stdOut = -1; - _stdErr = -1; + _out->printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__, ex.what()); } - if(_execThread.joinable()) _execThread.join(); - if(_errorThread.joinable()) _errorThread.join(); - BaseLib::ProcessManager::unregisterCallbackHandler(_callbackHandlerId); } void Exec::input(const Flows::PNodeInfo info, uint32_t index, const Flows::PVariable message) diff --git a/function/function.hni b/function/function.hni index efcd5f22..ed597e5c 100644 --- a/function/function.hni +++ b/function/function.hni @@ -47,7 +47,7 @@