From 13c182c82bf1c39dedef7ee234e1228887ae5294 Mon Sep 17 00:00:00 2001 From: matzman Date: Sun, 24 May 2015 01:08:07 +0200 Subject: [PATCH] . --- .../include/hos_cosgi_core/hos/cosgi/celix.h | 5 + .../hos/cosgi/service/core/CoreService.h | 15 +- .../cosgi/service/core/CoreServiceImpl.cpp | 4 +- .../hos/cosgi/service/core/CoreServiceImpl.h | 25 +- src/hos_cosgi_core/src/launcher.cpp | 2 +- .../DeviceRepositoryService.h | 146 +++++- .../src/BundleActivator.cpp | 42 +- .../DeviceRepositoryServiceImpl.cpp | 234 ++++++---- .../DeviceRepositoryServiceImpl.h | 142 ++++-- src/hos_cosgi_ruleengine/CMakeLists.txt | 13 +- .../service/ruleengine/RuleEngineService.h | 104 +++++ .../hos/cosgi/service/ruleengine/rule/Rule.h | 94 ---- .../service/ruleengine/rule/RuleCondition.h | 105 ----- .../service/ruleengine/ruleengine_service.h | 49 -- .../hos/cosgi/service/ruleengine/rules/Rule.h | 145 ++++++ .../service/ruleengine/rules/RulePackage.h | 127 +++++ .../rules/conditions/RuleCondition.h | 163 +++++++ .../rules/conditions/RuleConditionImpl.h | 123 +++++ ...ndle_activator.cpp => BundleActivator.cpp} | 2 +- ...viceimpl.cpp => RuleEngineServiceImpl.cpp} | 43 +- ..._serviceimpl.h => RuleEngineServiceImpl.h} | 57 ++- .../ruleengine/rule/RuleConditionImpl.cpp | 432 ------------------ .../ruleengine/rule/RuleConditionImpl.h | 142 ------ .../service/ruleengine/rule/RulePackage.h | 85 ---- .../{rule/Rule.cpp => rules/RuleImpl.cpp} | 84 ++-- .../cosgi/service/ruleengine/rules/RuleImpl.h | 100 ++++ .../RulePackageImpl.cpp} | 54 +-- .../ruleengine/rules/RulePackageImpl.h | 97 ++++ .../rules/conditions/DeviceMsgCondition.cpp | 271 +++++++++++ .../rules/conditions/DeviceMsgCondition.h | 140 ++++++ .../rules/conditions/LogicalAndCondition.cpp | 104 +++++ .../rules/conditions/LogicalAndCondition.h | 57 +++ .../rules/conditions/LogicalOrCondition.cpp | 101 ++++ .../rules/conditions/LogicalOrCondition.h | 57 +++ .../rules/conditions/RuleConditionImpl.cpp | 32 ++ src/hos_cosgi_scripting/CMakeLists.txt | 6 + .../service/scripting/ScriptingService.h | 107 +++++ .../service/scripting/scripting_service.h | 54 --- src/hos_cosgi_scripting_python/CMakeLists.txt | 7 +- ...ndle_activator.cpp => BundleActivator.cpp} | 37 +- ...iceimpl.cpp => PyScriptingServiceImpl.cpp} | 108 +++-- .../scripting/python/PyScriptingServiceImpl.h | 171 +++++++ .../scripting/python/hosapi/PyHOSAPI.cpp | 27 ++ .../scripting/python/{ => hosapi}/PyHOSAPI.h | 77 ++-- .../scripting/python/scripting_serviceimpl.h | 111 ----- .../python/{PyHOSAPI.cpp => utils/utils.cpp} | 55 +-- .../service/scripting/python/utils/utils.h | 44 ++ .../resources/DevicesHTTPResource.cpp | 9 +- .../hos_cpp_utils/hos/utils/SchemaLoader.h | 12 +- 49 files changed, 2675 insertions(+), 1546 deletions(-) create mode 100644 src/hos_cosgi_ruleengine/include/hos_cosgi_ruleengine/hos/cosgi/service/ruleengine/RuleEngineService.h delete mode 100644 src/hos_cosgi_ruleengine/include/hos_cosgi_ruleengine/hos/cosgi/service/ruleengine/rule/Rule.h delete mode 100644 src/hos_cosgi_ruleengine/include/hos_cosgi_ruleengine/hos/cosgi/service/ruleengine/rule/RuleCondition.h delete mode 100644 src/hos_cosgi_ruleengine/include/hos_cosgi_ruleengine/hos/cosgi/service/ruleengine/ruleengine_service.h create mode 100644 src/hos_cosgi_ruleengine/include/hos_cosgi_ruleengine/hos/cosgi/service/ruleengine/rules/Rule.h create mode 100644 src/hos_cosgi_ruleengine/include/hos_cosgi_ruleengine/hos/cosgi/service/ruleengine/rules/RulePackage.h create mode 100644 src/hos_cosgi_ruleengine/include/hos_cosgi_ruleengine/hos/cosgi/service/ruleengine/rules/conditions/RuleCondition.h create mode 100644 src/hos_cosgi_ruleengine/include/hos_cosgi_ruleengine/hos/cosgi/service/ruleengine/rules/conditions/RuleConditionImpl.h rename src/hos_cosgi_ruleengine/src/{bundle_activator.cpp => BundleActivator.cpp} (99%) rename src/hos_cosgi_ruleengine/src/hos/cosgi/service/ruleengine/{ruleengine_serviceimpl.cpp => RuleEngineServiceImpl.cpp} (81%) rename src/hos_cosgi_ruleengine/src/hos/cosgi/service/ruleengine/{ruleengine_serviceimpl.h => RuleEngineServiceImpl.h} (70%) delete mode 100644 src/hos_cosgi_ruleengine/src/hos/cosgi/service/ruleengine/rule/RuleConditionImpl.cpp delete mode 100644 src/hos_cosgi_ruleengine/src/hos/cosgi/service/ruleengine/rule/RuleConditionImpl.h delete mode 100644 src/hos_cosgi_ruleengine/src/hos/cosgi/service/ruleengine/rule/RulePackage.h rename src/hos_cosgi_ruleengine/src/hos/cosgi/service/ruleengine/{rule/Rule.cpp => rules/RuleImpl.cpp} (60%) create mode 100644 src/hos_cosgi_ruleengine/src/hos/cosgi/service/ruleengine/rules/RuleImpl.h rename src/hos_cosgi_ruleengine/src/hos/cosgi/service/ruleengine/{rule/RulePackage.cpp => rules/RulePackageImpl.cpp} (60%) create mode 100644 src/hos_cosgi_ruleengine/src/hos/cosgi/service/ruleengine/rules/RulePackageImpl.h create mode 100644 src/hos_cosgi_ruleengine/src/hos/cosgi/service/ruleengine/rules/conditions/DeviceMsgCondition.cpp create mode 100644 src/hos_cosgi_ruleengine/src/hos/cosgi/service/ruleengine/rules/conditions/DeviceMsgCondition.h create mode 100644 src/hos_cosgi_ruleengine/src/hos/cosgi/service/ruleengine/rules/conditions/LogicalAndCondition.cpp create mode 100644 src/hos_cosgi_ruleengine/src/hos/cosgi/service/ruleengine/rules/conditions/LogicalAndCondition.h create mode 100644 src/hos_cosgi_ruleengine/src/hos/cosgi/service/ruleengine/rules/conditions/LogicalOrCondition.cpp create mode 100644 src/hos_cosgi_ruleengine/src/hos/cosgi/service/ruleengine/rules/conditions/LogicalOrCondition.h create mode 100644 src/hos_cosgi_ruleengine/src/hos/cosgi/service/ruleengine/rules/conditions/RuleConditionImpl.cpp create mode 100644 src/hos_cosgi_scripting/include/hos_cosgi_scripting/hos/cosgi/service/scripting/ScriptingService.h delete mode 100644 src/hos_cosgi_scripting/include/hos_cosgi_scripting/hos/cosgi/service/scripting/scripting_service.h rename src/hos_cosgi_scripting_python/src/{bundle_activator.cpp => BundleActivator.cpp} (94%) rename src/hos_cosgi_scripting_python/src/hos/cosgi/service/scripting/python/{scripting_serviceimpl.cpp => PyScriptingServiceImpl.cpp} (52%) create mode 100644 src/hos_cosgi_scripting_python/src/hos/cosgi/service/scripting/python/PyScriptingServiceImpl.h create mode 100644 src/hos_cosgi_scripting_python/src/hos/cosgi/service/scripting/python/hosapi/PyHOSAPI.cpp rename src/hos_cosgi_scripting_python/src/hos/cosgi/service/scripting/python/{ => hosapi}/PyHOSAPI.h (53%) delete mode 100644 src/hos_cosgi_scripting_python/src/hos/cosgi/service/scripting/python/scripting_serviceimpl.h rename src/hos_cosgi_scripting_python/src/hos/cosgi/service/scripting/python/{PyHOSAPI.cpp => utils/utils.cpp} (60%) create mode 100644 src/hos_cosgi_scripting_python/src/hos/cosgi/service/scripting/python/utils/utils.h diff --git a/src/hos_cosgi_core/include/hos_cosgi_core/hos/cosgi/celix.h b/src/hos_cosgi_core/include/hos_cosgi_core/hos/cosgi/celix.h index a4488d87..b1ce432b 100644 --- a/src/hos_cosgi_core/include/hos_cosgi_core/hos/cosgi/celix.h +++ b/src/hos_cosgi_core/include/hos_cosgi_core/hos/cosgi/celix.h @@ -9,4 +9,9 @@ extern "C" { #include "celix/framework.h" #include "celix/linked_list_iterator.h" + #include "celix/bundle_activator.h" + #include "celix/bundle_context.h" + #include "celix/service_listener.h" + #include "celix/service_reference.h" + #include "celix/constants.h" } diff --git a/src/hos_cosgi_core/include/hos_cosgi_core/hos/cosgi/service/core/CoreService.h b/src/hos_cosgi_core/include/hos_cosgi_core/hos/cosgi/service/core/CoreService.h index 7409c2d0..88d2e2c9 100644 --- a/src/hos_cosgi_core/include/hos_cosgi_core/hos/cosgi/service/core/CoreService.h +++ b/src/hos_cosgi_core/include/hos_cosgi_core/hos/cosgi/service/core/CoreService.h @@ -22,6 +22,11 @@ namespace core { +enum class CoreServiceEventType : int { + STARTUP_FINISHED = 1, + SHUTDOWN = 2 +}; + /** * Base class for events emitted by the CoreService. */ @@ -29,12 +34,10 @@ class CoreServiceEvent { public: typedef std::chrono::system_clock::time_point timestamp_t; - enum class EventType : int { STARTUP_FINISHED = 1, SHUTDOWN = 2 }; - - const EventType eventType; + CoreServiceEventType eventType; const timestamp_t timestamp; - CoreServiceEvent(EventType type, const timestamp_t timestamp = std::chrono::system_clock::now()) + CoreServiceEvent(CoreServiceEventType type, const timestamp_t timestamp = std::chrono::system_clock::now()) : eventType(type), timestamp(timestamp) {} }; @@ -48,7 +51,7 @@ public: class StartupFinishedEvent : public CoreServiceEvent { public: - StartupFinishedEvent() : CoreServiceEvent(EventType::STARTUP_FINISHED) {} + StartupFinishedEvent() : CoreServiceEvent(CoreServiceEventType::STARTUP_FINISHED) {} }; @@ -61,7 +64,7 @@ public: class ShutdownEvent : public CoreServiceEvent { public: - ShutdownEvent() : CoreServiceEvent(EventType::SHUTDOWN) {} + ShutdownEvent() : CoreServiceEvent(CoreServiceEventType::SHUTDOWN) {} }; diff --git a/src/hos_cosgi_core/src/hos/cosgi/service/core/CoreServiceImpl.cpp b/src/hos_cosgi_core/src/hos/cosgi/service/core/CoreServiceImpl.cpp index 63d01f6e..83e01790 100644 --- a/src/hos_cosgi_core/src/hos/cosgi/service/core/CoreServiceImpl.cpp +++ b/src/hos_cosgi_core/src/hos/cosgi/service/core/CoreServiceImpl.cpp @@ -22,7 +22,9 @@ template<> const int implementation::ServiceInfo::minor = 0; CoreServiceImpl::CoreServiceImpl(ros::NodeHandle& publicNodeHandle, ros::NodeHandle& privateNodeHandle) - : publicNodeHandle(publicNodeHandle), privateNodeHandle(privateNodeHandle) {} + : publicNodeHandle(publicNodeHandle), privateNodeHandle(privateNodeHandle) { + schemaLoader.reset(new utils::SchemaLoader()); +} diff --git a/src/hos_cosgi_core/src/hos/cosgi/service/core/CoreServiceImpl.h b/src/hos_cosgi_core/src/hos/cosgi/service/core/CoreServiceImpl.h index 98cd697d..dfd5819b 100644 --- a/src/hos_cosgi_core/src/hos/cosgi/service/core/CoreServiceImpl.h +++ b/src/hos_cosgi_core/src/hos/cosgi/service/core/CoreServiceImpl.h @@ -33,43 +33,20 @@ private: public: - /** - * Constructor. - */ CoreServiceImpl(ros::NodeHandle& publicNodeHandle, ros::NodeHandle& privateNodeHandle); - //////////////////////////////////////////////////////////// - ////////////// Public Service Implementations ////////////// - //////////////////////////////////////////////////////////// + /* Public Service Implementations */ - /** - * Returns a reference to a node handle with public namespace. - * - * \return public node handle - */ virtual ros::NodeHandle& getPublicNodeHandle() override { return publicNodeHandle; } - /** - * Returns a reference to a node handle with private namespace. - * - * \return private node handle - */ virtual ros::NodeHandle& getPrivateNodeHandle() override { return privateNodeHandle; } - /** - * Request an application shutdown - */ virtual void requestShutdown() override; - /** - * Returns a reference to the schema loader. - * - * \return schema loader. - */ virtual utils::SchemaLoader& getSchemaLoader() override { return *schemaLoader; } diff --git a/src/hos_cosgi_core/src/launcher.cpp b/src/hos_cosgi_core/src/launcher.cpp index 0161c88f..7d0fe4bb 100644 --- a/src/hos_cosgi_core/src/launcher.cpp +++ b/src/hos_cosgi_core/src/launcher.cpp @@ -9,7 +9,7 @@ // Boost #include // ROS -#include "ros/ros.h" +#include // APR #include #include diff --git a/src/hos_cosgi_devicerepository/include/hos_cosgi_devicerepository/hos/cosgi/service/devicerepository/DeviceRepositoryService.h b/src/hos_cosgi_devicerepository/include/hos_cosgi_devicerepository/hos/cosgi/service/devicerepository/DeviceRepositoryService.h index 53b9890f..ae573c3c 100644 --- a/src/hos_cosgi_devicerepository/include/hos_cosgi_devicerepository/hos/cosgi/service/devicerepository/DeviceRepositoryService.h +++ b/src/hos_cosgi_devicerepository/include/hos_cosgi_devicerepository/hos/cosgi/service/devicerepository/DeviceRepositoryService.h @@ -1,5 +1,6 @@ -#ifndef _HOS_DEVICEREPOSITORY_SERVICE_H_ -#define _HOS_DEVICEREPOSITORY_SERVICE_H_ + +#pragma once + #include @@ -26,6 +27,22 @@ using namespace libvariant; +/* + * Device event type. + * + * DeviceRepositoryService emits DEVICE_ADDED, DEVICE_REMOVED. + * Device emits STATUS_UPDATE_RECEIVED, COMMAND_RECEIVED, STATE_CHANGED, DEVICE_REMOVED. + **/ +enum class DeviceEventType : int { + DEVICE_ADDED = 1, + DEVICE_REMOVED = 2, + STATUS_UPDATE_RECEIVED = 3, + COMMAND_RECEIVED = 4, + STATE_CHANGED = 5 +}; + + + /** * Base class for device events. */ @@ -33,20 +50,15 @@ class DeviceEvent { public: typedef std::chrono::system_clock::time_point timestamp_t; - enum class EventType : int { - DEVICE_ADDED = 1, - DEVICE_REMOVED = 2, - STATUS_UPDATE_RECEIVED = 3, - COMMAND_RECEIVED = 4, - STATE_CHANGED = 5 - }; + DeviceEventType eventType; + Device* const device; - const timestamp_t timestamp; - const EventType eventType; - DeviceEvent(Device* device, const EventType& type, const timestamp_t& timestamp) - : device(device), timestamp(timestamp), eventType(type) {} + const timestamp_t timestamp; + + DeviceEvent(Device* device, const DeviceEventType& type, const timestamp_t& timestamp) + : eventType(type), device(device), timestamp(timestamp) {} }; @@ -58,7 +70,7 @@ class DeviceAddedEvent : public DeviceEvent { public: DeviceAddedEvent(Device* device, const timestamp_t& timestamp) - : DeviceEvent(device, DeviceEvent::EventType::DEVICE_ADDED, timestamp) {} + : DeviceEvent(device,DeviceEventType::DEVICE_ADDED, timestamp) {} }; @@ -70,7 +82,7 @@ class DeviceRemovedEvent : public DeviceEvent { public: DeviceRemovedEvent(Device* device, const timestamp_t& timestamp) - : DeviceEvent(device, DeviceEvent::EventType::DEVICE_REMOVED, timestamp) {} + : DeviceEvent(device, DeviceEventType::DEVICE_REMOVED, timestamp) {} }; @@ -84,7 +96,7 @@ public: const Variant statusMsg; DeviceStatusMsgEvent(Device* device, const Variant& statusMsg, const timestamp_t& timestamp) - : DeviceEvent(device, DeviceEvent::EventType::STATUS_UPDATE_RECEIVED, timestamp), statusMsg(statusMsg) {} + : DeviceEvent(device, DeviceEventType::STATUS_UPDATE_RECEIVED, timestamp), statusMsg(statusMsg) {} }; @@ -98,7 +110,7 @@ public: const Variant cmdMsg; DeviceCmdMsgEvent(Device* device, const Variant& cmdMsg, const timestamp_t& timestamp) - : DeviceEvent(device, DeviceEvent::EventType::COMMAND_RECEIVED, timestamp), cmdMsg(cmdMsg) {} + : DeviceEvent(device, DeviceEventType::COMMAND_RECEIVED, timestamp), cmdMsg(cmdMsg) {} }; @@ -112,33 +124,89 @@ public: const Variant statusMsg; DeviceStateChangeEvent(Device* device, const Variant& statusMsg, const timestamp_t& timestamp) - : DeviceEvent(device, DeviceEvent::EventType::STATE_CHANGED, timestamp), statusMsg(statusMsg) {} + : DeviceEvent(device, DeviceEventType::STATE_CHANGED, timestamp), statusMsg(statusMsg) {} }; /** * Public COSGI interface of an device object. + * + * It offers following functionality: + * - Get device informations. + * - Events indicating device state changes. + * - Send commands to the device. */ class Device - : public virtual interface::ObservableObject { + : public virtual interface::ObservableObject { public: typedef std::chrono::system_clock::time_point timestamp_t; + /** + * Returns the device identifier. + * + * \return unique device identifier. + */ virtual const std::string& getDeviceID() noexcept = 0; + + /** + * Returns the device type. + * + * \return device type. + */ virtual const std::string& getDeviceType() noexcept = 0; + /** + * Returns the ROS topic the device uses for commands. + * + * \return command topic. + */ virtual const std::string& getCmdTopic() noexcept = 0; + + /** + * Returns the ROS topic the device uses for status updates. + * + * \return status updates topic. + */ virtual const std::string& getStatusTopic() noexcept = 0; + /** + * Returns the current state of the device. + * + * \return current state. + */ virtual const Variant& getCurrentState() noexcept = 0; + + /** + * Returns the timestamp of the current state. + * + * \return timestamp. + */ virtual const timestamp_t& getCurrentStateTimestamp() noexcept = 0; + /** + * Returns the last state of the device. + * + * \return last state. + */ virtual const Variant& getLastState() noexcept = 0; + + /** + * Returns the timestamp of the last state. + * + * \return timestamp. + */ virtual const timestamp_t& getLastStateTimestamp() noexcept = 0; + /** + * Sends a command to the device. + * + * May return exceptions derived from std::exception. + * + * \param[in] cmd command. + */ virtual void sendCommand(const Variant& cmd) = 0; }; @@ -146,18 +214,49 @@ public: /** - * Public COSGI interface of the device repository service - **/ + * Public COSGI interface of the device repository service. + * + * It offers following functionality: + * - Retrieve devices. + * - Events indicating new/removed devices. + */ class DeviceRepositoryService - : public virtual interface::ObservableService { + : public virtual interface::ObservableService { public: + /** + * The service name. + */ static constexpr const char* SERVICE_NAME = "hos_devicerepository_service"; + /** + * Retrieves the device with the given identifier. + * + * \param[in] device_id device identifier. + * \return device pointer, or expired pointer when no device was found. + */ virtual std::weak_ptr getDevice(const std::string& device_id) noexcept = 0; + + /** + * Returns all known devices. + * + * \return vector of device pointers. + */ virtual std::vector> getDevices() noexcept = 0; + + /** + * Returns the number of known devices. + * + * \return device count. + */ virtual size_t deviceCount() noexcept = 0; + + /** + * Returns the device ids of known devices. + * + * \return vector of device pointers. + */ virtual std::vector getDeviceIDs() noexcept = 0; }; @@ -169,6 +268,3 @@ public: } // namespace cosgi } // namespace hos - - -#endif /* _HOS_DEVICEREPOSITORY_SERVICE_H_ */ diff --git a/src/hos_cosgi_devicerepository/src/BundleActivator.cpp b/src/hos_cosgi_devicerepository/src/BundleActivator.cpp index 5a9e4ca1..9f3a9ab9 100644 --- a/src/hos_cosgi_devicerepository/src/BundleActivator.cpp +++ b/src/hos_cosgi_devicerepository/src/BundleActivator.cpp @@ -1,41 +1,42 @@ +// std #include -#include - +#include +#include +// ROS +#include +// APR #include - -#include "ros/ros.h" - -extern "C" { - #include "celix/bundle_activator.h" - #include "celix/bundle_context.h" -} - +// Celix +#include +// CoreService #include #include "hos/cosgi/service/devicerepository/DeviceRepositoryServiceImpl.h" -#include -#include - using namespace hos::cosgi; using namespace hos::cosgi::service::devicerepository; + struct UserData { apr_pool_t* memoryPool = nullptr; std::unique_ptr serviceImpl; service_registration_pt serviceReg = nullptr; }; + + celix_status_t bundleActivator_create(bundle_context_pt context, void **userData) { celix_status_t status = CELIX_START_ERROR; apr_pool_t* memoryPool = nullptr; + // Create APR memory pool apr_status_t astatus = apr_pool_create(&memoryPool, NULL); if (astatus != APR_SUCCESS) { ROS_ERROR("DeviceRepositoryService: Could not create APR memory pool (%i).", astatus); } else { + // Create user data UserData* myUserData = (UserData*)apr_palloc(memoryPool, sizeof(UserData)); if (myUserData != nullptr) { @@ -47,20 +48,24 @@ celix_status_t bundleActivator_create(bundle_context_pt context, void **userData ROS_ERROR("DeviceRepositoryService: Could not create user data struct"); apr_pool_destroy(memoryPool); } + } return status; } + celix_status_t bundleActivator_start(void * userData, bundle_context_pt context) { celix_status_t status = CELIX_SUCCESS; if (userData != nullptr) { UserData* myUserData = (UserData*)userData; - // Create hos-devicerepository-service + + // Create device repository service if (myUserData->serviceReg != nullptr) { serviceRegistration_unregister(myUserData->serviceReg); } myUserData->serviceImpl.reset(new DeviceRepositoryServiceImpl()); - // Get HOS Core Service + + // Get core service service_reference_pt ref = nullptr; bundleContext_getServiceReference(context, (char*)service::core::CoreService::SERVICE_NAME, &ref); service::core::CoreService* coreService = nullptr; @@ -70,10 +75,12 @@ celix_status_t bundleActivator_start(void * userData, bundle_context_pt context) } else { myUserData->serviceImpl->setCoreService(coreService); } - // Register hos-devicerepository-service + + // Register device repository service if (myUserData->serviceImpl->isInitialized()) { bundleContext_registerService(context, (char*)DeviceRepositoryService::SERVICE_NAME, myUserData->serviceImpl.get(), nullptr, &myUserData->serviceReg); } + ROS_INFO("DeviceRepositoryService: Bundle successfully started"); } else { ROS_ERROR("DeviceRepositoryService: Could not start bundle, user data struct is null"); @@ -81,6 +88,7 @@ celix_status_t bundleActivator_start(void * userData, bundle_context_pt context) return status; } + celix_status_t bundleActivator_stop(void * userData, bundle_context_pt context) { celix_status_t status = CELIX_ILLEGAL_STATE; if (userData != nullptr) { @@ -100,6 +108,7 @@ celix_status_t bundleActivator_stop(void * userData, bundle_context_pt context) return status; } + celix_status_t bundleActivator_destroy(void * userData, bundle_context_pt context) { celix_status_t status = CELIX_ILLEGAL_STATE; if (userData != nullptr) { @@ -116,3 +125,4 @@ celix_status_t bundleActivator_destroy(void * userData, bundle_context_pt contex } return status; } + diff --git a/src/hos_cosgi_devicerepository/src/hos/cosgi/service/devicerepository/DeviceRepositoryServiceImpl.cpp b/src/hos_cosgi_devicerepository/src/hos/cosgi/service/devicerepository/DeviceRepositoryServiceImpl.cpp index 6bd098a4..c1102270 100644 --- a/src/hos_cosgi_devicerepository/src/hos/cosgi/service/devicerepository/DeviceRepositoryServiceImpl.cpp +++ b/src/hos_cosgi_devicerepository/src/hos/cosgi/service/devicerepository/DeviceRepositoryServiceImpl.cpp @@ -1,12 +1,5 @@ -/* - * hos_itemrepository_service_impl.cpp - * - * Created on: 05.04.2015 - * Author: matzman - */ #include "DeviceRepositoryServiceImpl.h" - #include #include #include @@ -15,51 +8,65 @@ - +// Service Namespace namespace hos { namespace cosgi { namespace service { namespace devicerepository { + +// Service information template<> const char* implementation::ServiceInfo::name = service::devicerepository::DeviceRepositoryService::SERVICE_NAME; template<> const int implementation::ServiceInfo::major = 1; template<> const int implementation::ServiceInfo::minor = 0; -bool DeviceImpl::handleStatusMsg(const Variant& msg, const timestamp_t& stamp) { + +bool DeviceImpl::handleStatusMsg(const Variant& msg, const timestamp_t& stamp) noexcept { std::lock_guard lock(objMutex); - Variant tmp = currentState.Copy(); - tmp.Merge(msg); + bool stateChanged = false; - if (tmp != currentState) { // state has changed - lastState = currentState.Copy(); - lastStamp = currentStamp; - currentState = std::move(tmp); + Variant newstate = currentState.Copy(); + newstate.Merge(msg); + + if (newstate != currentState) { + lastState = std::move(currentState); + lastStamp = std::move(currentStamp); + currentState = std::move(newstate); currentStamp = stamp; stateChanged = true; } + DeviceStatusMsgEvent event(this, msg, stamp); - notifyObservers(DeviceEvent::EventType::STATUS_UPDATE_RECEIVED, &event); + notifyObservers(DeviceEventType::STATUS_UPDATE_RECEIVED, &event); + if (stateChanged){ DeviceStateChangeEvent event(this, msg, stamp); - notifyObservers(DeviceEvent::EventType::STATE_CHANGED, &event); + notifyObservers(DeviceEventType::STATE_CHANGED, &event); } + return stateChanged; } -void DeviceImpl::handleCommandMsg(const Variant& msg, const timestamp_t& stamp) { + +void DeviceImpl::handleCommandMsg(const Variant& msg, const timestamp_t& stamp) noexcept { std::lock_guard lock(objMutex); DeviceCmdMsgEvent event(this, msg, stamp); - notifyObservers(DeviceEvent::EventType::COMMAND_RECEIVED, &event); + notifyObservers(DeviceEventType::COMMAND_RECEIVED, &event); } + void DeviceImpl::sendCommand(const Variant& cmd) { std::lock_guard lock(objMutex); parent->sendDeviceCommand(this, cmd); } -void DeviceRepositoryServiceImpl::setCoreService(service::core::CoreService* coreService) { + + + + +void DeviceRepositoryServiceImpl::setCoreService(service::core::CoreService* coreService) noexcept { if (coreService && !this->coreService) { this->coreService = coreService; this->nodehandle = &coreService->getPrivateNodeHandle(); @@ -70,11 +77,15 @@ void DeviceRepositoryServiceImpl::setCoreService(service::core::CoreService* cor } } -void DeviceRepositoryServiceImpl::init() { + +void DeviceRepositoryServiceImpl::init() noexcept { + // Get public parameters nodehandle->getParam("device_repository_prefix", deviceRepositoryPrefix); nodehandle->getParam("hos_status_topic", hosStatusTopic); XmlRpc::XmlRpcValue rawValue; nodehandle->getParam(deviceRepositoryPrefix, rawValue); + + // create devices if (rawValue.getType() == XmlRpc::XmlRpcValue::TypeStruct) { for (auto dev : rawValue) { std::string devname = dev.first; @@ -87,11 +98,13 @@ void DeviceRepositoryServiceImpl::init() { } } } + hosStatusSub = nodehandle->subscribe(hosStatusTopic, 1000, &DeviceRepositoryServiceImpl::hosStatusMsgCallback, this); initialized = true; ROS_INFO_STREAM("DeviceRepositoryService: Initialized and Ready."); } + void DeviceRepositoryServiceImpl::addDevice(const std::string& name) { std::stringstream ss; ss << deviceRepositoryPrefix << "/" << name; @@ -100,28 +113,37 @@ void DeviceRepositoryServiceImpl::addDevice(const std::string& name) { addDevice(name, rawValue); } + void DeviceRepositoryServiceImpl::addDevice(const std::string& name, XmlRpc::XmlRpcValue& rawValue) { std::lock_guard lock(serviceMutex); std::string type, statusTopic, cmdTopic; + + // device type XmlRpc::XmlRpcValue& typeRaw = rawValue["type"]; if (typeRaw.getType() != XmlRpc::XmlRpcValue::TypeString) { throw std::runtime_error("type is not a string."); } - type = static_cast(typeRaw); + type = static_cast(typeRaw); + + // device status topic XmlRpc::XmlRpcValue& statusTopicRaw = rawValue["status_topic"]; if (statusTopicRaw.getType() != XmlRpc::XmlRpcValue::TypeString) { throw std::runtime_error("statusTopic is not a string."); } statusTopic = static_cast(statusTopicRaw); + + // device command topic XmlRpc::XmlRpcValue& cmdTopicRaw = rawValue["cmd_topic"]; if (cmdTopicRaw.getType() != XmlRpc::XmlRpcValue::TypeString) { throw std::runtime_error("cmdTopic is not a string."); } cmdTopic = static_cast(cmdTopicRaw); + + // create device std::shared_ptr dev = std::make_shared(this, name, type, statusTopic, cmdTopic); devices.insert({name, dev}); - // status topic + // status topic subscriber auto& entry = subscripedTopics[dev->getStatusTopic()]; if (entry.first <= 0) { entry.second = nodehandle->subscribe(dev->getStatusTopic(), 1000, &DeviceRepositoryServiceImpl::statusMsgCallback, this); @@ -130,7 +152,7 @@ void DeviceRepositoryServiceImpl::addDevice(const std::string& name, XmlRpc::Xml subscripedTopics[dev->getStatusTopic()] = entry; } - // cmd topic + // cmd topic subscriber auto& entry2 = subscripedTopics[dev->getCmdTopic()]; if (entry2.first <= 0) { entry2.second = nodehandle->subscribe(dev->getCmdTopic(), 1000, &DeviceRepositoryServiceImpl::cmdMsgCallback, this); @@ -139,7 +161,7 @@ void DeviceRepositoryServiceImpl::addDevice(const std::string& name, XmlRpc::Xml subscripedTopics[dev->getCmdTopic()] = entry2; } - // cmd publisher + // cmd topic publisher auto& entry3 = publishedTopics[dev->getCmdTopic()]; if (entry3.first <= 0) { entry3.second = nodehandle->advertise(dev->getCmdTopic(), 1000); @@ -148,13 +170,9 @@ void DeviceRepositoryServiceImpl::addDevice(const std::string& name, XmlRpc::Xml publishedTopics[dev->getCmdTopic()] = entry3; } - - entry2.first= entry2.first + 1; - subscripedTopics[dev->getCmdTopic()] = entry2; - // send device added event DeviceAddedEvent event(dev.get(), std::chrono::system_clock::now()); - notifyObservers(DeviceEvent::EventType::DEVICE_ADDED, &event); + notifyObservers(DeviceEventType::DEVICE_ADDED, &event); } @@ -163,8 +181,8 @@ void DeviceRepositoryServiceImpl::removeDevice(const std::string& name) { auto dev = devices.find(name); if (dev != devices.end()) { DeviceRemovedEvent event(dev->second.get(), std::chrono::system_clock::now()); - notifyObservers(DeviceEvent::EventType::DEVICE_REMOVED, &event); - dev->second->notifyObservers(DeviceEvent::EventType::DEVICE_REMOVED, &event); + notifyObservers(DeviceEventType::DEVICE_REMOVED, &event); + dev->second->notifyObservers(DeviceEventType::DEVICE_REMOVED, &event); devices.erase(name); } } @@ -178,6 +196,7 @@ std::weak_ptr DeviceRepositoryServiceImpl::getDevice(const std::string& } } + std::vector> DeviceRepositoryServiceImpl::getDevices() noexcept { std::lock_guard lock(serviceMutex); std::vector> retval; @@ -188,11 +207,6 @@ std::vector> DeviceRepositoryServiceImpl::getDevices() noe } -size_t DeviceRepositoryServiceImpl::deviceCount() noexcept { - return devices.size(); -} - - std::vector DeviceRepositoryServiceImpl::getDeviceIDs() noexcept { std::lock_guard lock(serviceMutex); std::vector retval; @@ -203,90 +217,117 @@ std::vector DeviceRepositoryServiceImpl::getDeviceIDs() noexcept { } -void DeviceRepositoryServiceImpl::statusMsgCallback(const hos_core_msgs::DeviceStatusUpdate::ConstPtr& msg) { +void DeviceRepositoryServiceImpl::statusMsgCallback(const hos_core_msgs::DeviceStatusUpdate::ConstPtr& msg) noexcept { std::lock_guard lock(serviceMutex); - std::cout << "HOSDeviceRepositoryServiceImpl::statusMsgCallback" << std::endl; - Variant s = coreService->getSchemaLoader().GetSchema(std::string("hos:schema://").append(msg->device_type).append("/status")); - Variant m = Deserialize(msg->msg_body, SERIALIZE_JSON); - SchemaResult sr = SchemaValidate(s, m, &coreService->getSchemaLoader()); - if (!sr.Error()) { - auto dev = devices.find(msg->device_id); - if (dev != devices.end()) { - auto stamp = std::chrono::system_clock::now(); - bool stateChanged = dev->second->handleStatusMsg(m, stamp); - DeviceStatusMsgEvent event(dev->second.get(), m, stamp); - notifyObservers(DeviceEvent::EventType::STATUS_UPDATE_RECEIVED, &event); - if (stateChanged) { - DeviceStateChangeEvent event(dev->second.get(), m, stamp); - notifyObservers(DeviceEvent::EventType::STATE_CHANGED, &event); + + try { + // Verify message using appropriate json schema. + auto schemaUrl = (boost::format("hos:schema://%1%/status") % msg->device_type).str(); + Variant s = coreService->getSchemaLoader().GetSchema(schemaUrl); + Variant m = Deserialize(msg->msg_body, SERIALIZE_JSON); + SchemaResult sr = SchemaValidate(s, m, &coreService->getSchemaLoader()); + if (!sr.Error()) { + + // Get recipient and pass on message + auto dev = devices.find(msg->device_id); + if (dev != devices.end()) { + auto stamp = std::chrono::system_clock::now(); + dev->second->handleStatusMsg(m, stamp); } + + } else { + ROS_ERROR_STREAM("Could not validate status message: " << sr.PrettyPrintMessage()); } - } else { - ROS_ERROR_STREAM("Could not validate status message: " << sr.PrettyPrintMessage()); + } catch (std::exception& e) { + ROS_ERROR_STREAM("Exception while handling device status message: " << e.what()); } } -void DeviceRepositoryServiceImpl::cmdMsgCallback(const hos_core_msgs::DeviceCommand::ConstPtr& msg) { + +void DeviceRepositoryServiceImpl::cmdMsgCallback(const hos_core_msgs::DeviceCommand::ConstPtr& msg) noexcept { std::lock_guard lock(serviceMutex); - std::cout << "HOSDeviceRepositoryServiceImpl::cmdMsgCallback" << std::endl; - Variant s = coreService->getSchemaLoader().GetSchema(std::string("hos:schema://").append(msg->device_type).append("/command")); - Variant m = Deserialize(msg->msg_body, SERIALIZE_JSON); - auto test = msg->stamp; - SchemaResult sr = SchemaValidate(s, m, &coreService->getSchemaLoader()); - if (!sr.Error()) { - auto dev = devices.find(msg->device_id); - if (dev != devices.end()) { - auto stamp = std::chrono::system_clock::now(); - dev->second->handleCommandMsg(m, stamp); - DeviceCmdMsgEvent event(dev->second.get(), m, stamp); - notifyObservers(DeviceEvent::EventType::STATUS_UPDATE_RECEIVED, &event); + + try { + // Verify message using appropriate json schema. + auto schemaUrl = (boost::format("hos:schema://%1%/command") % msg->device_type).str(); + Variant s = coreService->getSchemaLoader().GetSchema(schemaUrl); + Variant m = Deserialize(msg->msg_body, SERIALIZE_JSON); + auto test = msg->stamp; + SchemaResult sr = SchemaValidate(s, m, &coreService->getSchemaLoader()); + if (!sr.Error()) { + + // Get recipient and pass on message + auto dev = devices.find(msg->device_id); + if (dev != devices.end()) { + auto stamp = std::chrono::system_clock::now(); + dev->second->handleCommandMsg(m, stamp); + } + + } else { + ROS_ERROR_STREAM("Could not validate command message: " << sr.PrettyPrintMessage()); } - } else { - ROS_ERROR_STREAM("Could not validate command message: " << sr.PrettyPrintMessage()); + } catch (std::exception& e) { + ROS_ERROR_STREAM("Exception while handling device command message: " << e.what()); } } -void DeviceRepositoryServiceImpl::hosStatusMsgCallback(const hos_core_msgs::HosStatusUpdate::ConstPtr& msg) { + +void DeviceRepositoryServiceImpl::hosStatusMsgCallback(const hos_core_msgs::HosStatusUpdate::ConstPtr& msg) noexcept { std::lock_guard lock(serviceMutex); - std::cout << "HOSDeviceRepositoryServiceImpl::hosStatusMsgCallback" << std::endl; - Variant s = coreService->getSchemaLoader().GetSchema(std::string("hos:schema://core/msgs/hosstatusupdate")); - Variant m = Deserialize(msg->msg_body, SERIALIZE_JSON); - SchemaResult sr = SchemaValidate(s, m, &coreService->getSchemaLoader()); - if (!sr.Error()) { - if (m.Contains("devices")) { - auto d = m["devices"]; - if (d.Contains("added") && d["added"].IsList()) { - for (auto dev : d["added"].AsList()) { - if (dev.IsString()) { - std::string devname = dev.AsString(); - if (devices.find(devname) == devices.end()) { - addDevice(devname); + + try { + // Verify message using appropriate json schema. + Variant s = coreService->getSchemaLoader().GetSchema(std::string("hos:schema://core/msgs/hosstatusupdate")); + Variant m = Deserialize(msg->msg_body, SERIALIZE_JSON); + SchemaResult sr = SchemaValidate(s, m, &coreService->getSchemaLoader()); + if (!sr.Error()) { + + // we are only interested in the device part. + if (m.Contains("devices")) { + auto d = m["devices"]; + // devices added + if (d.Contains("added") && d["added"].IsList()) { + for (auto dev : d["added"].AsList()) { + if (dev.IsString()) { + std::string devname = dev.AsString(); + if (devices.find(devname) == devices.end()) { + addDevice(devname); + } } } } - } - if (d.Contains("removed") && d["removed"].IsList()) { - for (auto dev : d["removed"].AsList()) { - if (dev.IsString()) { - std::string devname = dev.AsString(); - if (devices.find(devname) != devices.end()) { - removeDevice(devname); + // devices removed + if (d.Contains("removed") && d["removed"].IsList()) { + for (auto dev : d["removed"].AsList()) { + if (dev.IsString()) { + std::string devname = dev.AsString(); + if (devices.find(devname) != devices.end()) { + removeDevice(devname); + } } } } } + + } else { + ROS_ERROR_STREAM("Could not validate hos status message: " << sr.PrettyPrintMessage()); } - } else { - ROS_ERROR_STREAM("Could not validate hos status message: " << sr.PrettyPrintMessage()); + } catch (std::exception& e) { + ROS_ERROR_STREAM("Exception while handling hos status message: " << e.what()); } } -void DeviceRepositoryServiceImpl::sendDeviceCommand(Device* device, const Variant& cmd) { +void DeviceRepositoryServiceImpl::sendDeviceCommand(DeviceImpl* device, const Variant& cmd) { + std::lock_guard lock(serviceMutex); + + // Verify message using appropriate json schema. auto schemaUrl = (boost::format("hos:schema://%1%/command") % device->getDeviceType()).str(); Variant s = coreService->getSchemaLoader().GetSchema(schemaUrl); SchemaResult sr = SchemaValidate(s, cmd, &coreService->getSchemaLoader()); if (!sr.Error()) { + + // Retrieve appropriate publisher and send message auto entry = publishedTopics[device->getCmdTopic()]; if (entry.first > 0) { hos_core_msgs::DeviceCommand command; @@ -296,11 +337,14 @@ void DeviceRepositoryServiceImpl::sendDeviceCommand(Device* device, const Varian command.msg_body = Serialize(cmd, SERIALIZE_JSON); command.stamp = ros::Time::now(); entry.second.publish(command); + } else { - ROS_ERROR_STREAM("Could not find device command publisher for topic " << device->getCmdTopic()); + auto msg = (boost::format("Could not find device command publisher for topic %1%") % device->getCmdTopic()).str(); + throw std::runtime_error(msg); } } else { - ROS_ERROR_STREAM("Could not validate device command: " << sr.PrettyPrintMessage()); + auto msg = (boost::format("Could not validate device command: %1%") % sr.PrettyPrintMessage()).str(); + throw std::runtime_error(msg); } } diff --git a/src/hos_cosgi_devicerepository/src/hos/cosgi/service/devicerepository/DeviceRepositoryServiceImpl.h b/src/hos_cosgi_devicerepository/src/hos/cosgi/service/devicerepository/DeviceRepositoryServiceImpl.h index ffe0022b..2699a8bc 100644 --- a/src/hos_cosgi_devicerepository/src/hos/cosgi/service/devicerepository/DeviceRepositoryServiceImpl.h +++ b/src/hos_cosgi_devicerepository/src/hos/cosgi/service/devicerepository/DeviceRepositoryServiceImpl.h @@ -1,27 +1,25 @@ -/* - * hos_itemrepository_service_impl.h - * - * Created on: 05.04.2015 - * Author: matzman - */ -#ifndef SRC_HOS_COSGI_SERVICE_DEVICEREPOSITORY_DEVICEREPOSITORY_SERVICEIMPL_H_ -#define SRC_HOS_COSGI_SERVICE_DEVICEREPOSITORY_DEVICEREPOSITORY_SERVICEIMPL_H_ +#pragma once -#include + +#include +// hos includes +#include +#include +#include +#include +#include #include +// libvariant includes +#include +// ROS includes +#include +// std includes #include #include #include #include #include -#include -#include -#include -#include -#include -#include -#include using namespace libvariant; @@ -29,10 +27,15 @@ using namespace libvariant; namespace std { +/** + * Specialization of std::hash for DeviceEventType. + * + * Required by std::map. + */ template <> -class hash { +class hash { public : - size_t operator()(const hos::cosgi::service::devicerepository::DeviceEvent::EventType& x) const { + size_t operator()(const hos::cosgi::service::devicerepository::DeviceEventType& x) const { return static_cast(x); } }; @@ -52,22 +55,26 @@ class DeviceRepositoryServiceImpl; /** - * Device implementation + * Implementation of Device. **/ class DeviceImpl : public virtual Device, - public virtual implementation::ObservableObject { + public virtual implementation::ObservableObject { private: DeviceRepositoryServiceImpl* parent; std::recursive_mutex objMutex; + std::string deviceID; std::string deviceType; + std::string statusTopic; std::string cmdTopic; + Variant currentState; timestamp_t currentStamp; + Variant lastState; timestamp_t lastStamp; @@ -77,26 +84,46 @@ public: const std::string& deviceType, const std::string& statusTopic, const std::string& cmdTopic ) - : parent(parent), deviceID(deviceID), deviceType(deviceType), statusTopic(statusTopic), cmdTopic(cmdTopic) {} + : parent(parent), deviceID(deviceID), deviceType(deviceType), + statusTopic(statusTopic), cmdTopic(cmdTopic) {} + /* Public Service Implementations */ - virtual const std::string& getDeviceID() noexcept override { return deviceID; } - virtual const std::string& getDeviceType() noexcept override { return deviceType; } - virtual const std::string& getCmdTopic() noexcept override { return cmdTopic; } - virtual const std::string& getStatusTopic() noexcept override { return statusTopic; } + virtual const std::string& getDeviceID() noexcept override { + return deviceID; + } + virtual const std::string& getDeviceType() noexcept override { + return deviceType; + } + virtual const std::string& getCmdTopic() noexcept override { + return cmdTopic; + } + virtual const std::string& getStatusTopic() noexcept override { + return statusTopic; + } - virtual const Variant& getCurrentState() noexcept override { return currentState; } - virtual const timestamp_t& getCurrentStateTimestamp() noexcept override { return currentStamp; } - virtual const Variant& getLastState() noexcept override { return lastState; } - virtual const timestamp_t& getLastStateTimestamp() noexcept override { return lastStamp; } + virtual const Variant& getCurrentState() noexcept override { + return currentState; + } + virtual const timestamp_t& getCurrentStateTimestamp() noexcept override { + return currentStamp; + } + virtual const Variant& getLastState() noexcept override { + return lastState; + } + virtual const timestamp_t& getLastStateTimestamp() noexcept override { + return lastStamp; + } virtual void sendCommand(const Variant& cmd) override; - /* Internal Stuff */ - bool handleStatusMsg(const Variant& msg, const timestamp_t& stamp); - void handleCommandMsg(const Variant& msg, const timestamp_t& stamp); + /* Internal */ + + bool handleStatusMsg(const Variant& msg, const timestamp_t& stamp) noexcept; + + void handleCommandMsg(const Variant& msg, const timestamp_t& stamp) noexcept; }; @@ -107,9 +134,12 @@ public: **/ class DeviceRepositoryServiceImpl : public virtual DeviceRepositoryService, - public virtual implementation::ObservableService { + public virtual implementation::ObservableService { private: + typedef std::map> devicesMap_t; + typedef std::map> subscripedTopicMap_t; + typedef std::map> publishedTopicMap_t; std::recursive_mutex serviceMutex; @@ -119,52 +149,68 @@ private: std::string deviceRepositoryPrefix = "/hos/devices/repository"; std::string hosStatusTopic = "/hos/status/updates"; - typedef std::map> devicesMap_t; devicesMap_t devices; - typedef std::map> subscripedTopicMap_t; - typedef std::map> publishedTopicMap_t; + subscripedTopicMap_t subscripedTopics; publishedTopicMap_t publishedTopics; - long msgSeqNumber = 0; ros::Subscriber hosStatusSub; + long msgSeqNumber = 0; bool initialized = false; - void init(); + void init() noexcept; void addDevice(const std::string& name); + void addDevice(const std::string& name, XmlRpc::XmlRpcValue& rawValue); + void removeDevice(const std::string& name); public: /* Initialization */ - void setCoreService(service::core::CoreService* coreService); + void setCoreService(service::core::CoreService* coreService) noexcept; + /* Public Service Implementations */ virtual std::weak_ptr getDevice(const std::string& device_id) noexcept override; + virtual std::vector> getDevices() noexcept override; - virtual size_t deviceCount() noexcept override; + + virtual size_t deviceCount() noexcept override { + return devices.size(); + } + virtual std::vector getDeviceIDs() noexcept override; + /* Message Callbacks */ - void statusMsgCallback(const hos_core_msgs::DeviceStatusUpdate::ConstPtr& msg); - void cmdMsgCallback(const hos_core_msgs::DeviceCommand::ConstPtr& msg); - void hosStatusMsgCallback(const hos_core_msgs::HosStatusUpdate::ConstPtr& msg); + /** ROS subscriber callback for device status messages. */ + void statusMsgCallback(const hos_core_msgs::DeviceStatusUpdate::ConstPtr& msg) noexcept; - /* Internal Stuff */ + /** ROS subscriber callback for device command messages. */ + void cmdMsgCallback(const hos_core_msgs::DeviceCommand::ConstPtr& msg) noexcept; + + /** ROS subscriber callback for hos status messages. */ + void hosStatusMsgCallback(const hos_core_msgs::HosStatusUpdate::ConstPtr& msg) noexcept; + + + /* Internal */ + + bool isInitialized() noexcept { + return initialized; + } + + void sendDeviceCommand(DeviceImpl* device, const Variant& cmd); - bool isInitialized() noexcept { return initialized; } - void sendDeviceCommand(Device* device, const Variant& cmd); }; + } // namespace devicerepository } // namespace service } // namespace cosgi } // namespace hos - -#endif /* SRC_HOS_COSGI_SERVICE_DEVICEREPOSITORY_DEVICEREPOSITORY_SERVICEIMPL_H_ */ diff --git a/src/hos_cosgi_ruleengine/CMakeLists.txt b/src/hos_cosgi_ruleengine/CMakeLists.txt index 3f6f339f..c0b26819 100644 --- a/src/hos_cosgi_ruleengine/CMakeLists.txt +++ b/src/hos_cosgi_ruleengine/CMakeLists.txt @@ -120,11 +120,14 @@ set(BUNDLE_NAME "hos_ruleengine") bundle(hos_cosgi_ruleengine SOURCES - ${PROJECT_SOURCE_DIR}/src/bundle_activator - ${PROJECT_SOURCE_DIR}/src/hos/cosgi/service/ruleengine/ruleengine_serviceimpl - ${PROJECT_SOURCE_DIR}/src/hos/cosgi/service/ruleengine/rule/RulePackage - ${PROJECT_SOURCE_DIR}/src/hos/cosgi/service/ruleengine/rule/Rule - ${PROJECT_SOURCE_DIR}/src/hos/cosgi/service/ruleengine/rule/RuleConditionImpl + ${PROJECT_SOURCE_DIR}/src/BundleActivator + ${PROJECT_SOURCE_DIR}/src/hos/cosgi/service/ruleengine/RuleEngineServiceImpl + ${PROJECT_SOURCE_DIR}/src/hos/cosgi/service/ruleengine/rules/RulePackageImpl + ${PROJECT_SOURCE_DIR}/src/hos/cosgi/service/ruleengine/rules/RuleImpl + ${PROJECT_SOURCE_DIR}/src/hos/cosgi/service/ruleengine/rules/conditions/RuleConditionImpl + ${PROJECT_SOURCE_DIR}/src/hos/cosgi/service/ruleengine/rules/conditions/LogicalOrCondition + ${PROJECT_SOURCE_DIR}/src/hos/cosgi/service/ruleengine/rules/conditions/LogicalAndCondition + ${PROJECT_SOURCE_DIR}/src/hos/cosgi/service/ruleengine/rules/conditions/DeviceMsgCondition ) target_link_libraries(hos_cosgi_ruleengine ${catkin_LIBRARIES} diff --git a/src/hos_cosgi_ruleengine/include/hos_cosgi_ruleengine/hos/cosgi/service/ruleengine/RuleEngineService.h b/src/hos_cosgi_ruleengine/include/hos_cosgi_ruleengine/hos/cosgi/service/ruleengine/RuleEngineService.h new file mode 100644 index 00000000..1f514b19 --- /dev/null +++ b/src/hos_cosgi_ruleengine/include/hos_cosgi_ruleengine/hos/cosgi/service/ruleengine/RuleEngineService.h @@ -0,0 +1,104 @@ + +#pragma once + + + +#include +#include +#include +#include + + +// forward declarations +namespace libvariant { class Variant; } + + +namespace hos { +namespace cosgi { +namespace service { +namespace ruleengine { + + +// forward declarations +namespace rules { +class RulePackage; +class Rule; +namespace conditions { class RuleCondition; } +} + + + +/** + * Public abstract COSGI interface of a rule condition factory. + */ +class RuleConditionFactory + : public virtual interface::Object { + +public: + + /** + * Creates and returns a condition from the given json config. + * + * \param[in] parent parent rule + * \param[in] json config + * \return new rule condition + */ + virtual std::shared_ptr createCondition(rules::Rule* parent, const libvariant::Variant& raw) = 0; + +}; + + + +/** + * Public COSGI interface of the rule engine service. + */ +class RuleEngineService + : public virtual interface::Service { + +public: + + /** + * The service name. + */ + static constexpr const char* SERVICE_NAME = "hos_ruleengine_service"; + + /** + * Returns the rulepackage with the given name. + * + * \param[in] name name + * \return weak reference to rule package (an expired reference is returned if the wanted rule package does not exists). + */ + virtual std::weak_ptr getRulePackage(const std::string& name) noexcept = 0; + + /** + * Returns a vector containing all known rulepackages. + * + * \return vector of weak references. + */ + virtual std::vector> getRulePackages() noexcept = 0; + + + /** + * Returns the number of known rule packages. + * + * \return rule package count. + */ + virtual size_t getRulePackageCount() noexcept = 0; + + /** + * Registers a rule condition factory under the given type identifier. + * + * \param[in] type condition type identifier. + * \param[in] factory rule condition factory. + */ + virtual void registerRuleConditionFactory(const std::string& type, std::shared_ptr factory) noexcept = 0; +}; + + + +} // namespace ruleengine +} // namespace service +} // namespace cosgi +} // namespace hos + + diff --git a/src/hos_cosgi_ruleengine/include/hos_cosgi_ruleengine/hos/cosgi/service/ruleengine/rule/Rule.h b/src/hos_cosgi_ruleengine/include/hos_cosgi_ruleengine/hos/cosgi/service/ruleengine/rule/Rule.h deleted file mode 100644 index 1fbc9073..00000000 --- a/src/hos_cosgi_ruleengine/include/hos_cosgi_ruleengine/hos/cosgi/service/ruleengine/rule/Rule.h +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Rule.h - * - * Created on: 29.04.2015 - * Author: matzman - */ - -#ifndef SRC_HOS_COSGI_SERVICE_RULEENGINE_RULE_RULE_H_ -#define SRC_HOS_COSGI_SERVICE_RULEENGINE_RULE_RULE_H_ - - -#include -#include -#include -#include -#include "RuleCondition.h" - - -// Forward declarations -namespace hos { -namespace cosgi { -namespace service { -namespace devicerepository { -class Device; -class DeviceEvent; -class DeviceRepositoryService; -} -namespace ruleengine { -namespace rule { -class RulePackage; -} -} -namespace scripting { -class ScriptingService; -class GlobalScriptContext; -class Script; -} -} -} -namespace utils { -class SchemaLoader; -} -} - - -namespace hos { -namespace cosgi { -namespace service { -namespace ruleengine { -namespace rule { - -using namespace ::libvariant; - -class Rule { -private: - std::shared_ptr script; - std::shared_ptr exitScript; - std::shared_ptr condition; - bool enabled = false; - bool currentState = false; - -public: - RulePackage* const parent; - const std::string name; - - Rule(RulePackage* parent, const std::string name); - static std::shared_ptr createRule(RulePackage* parent, const std::string name, Variant raw); - - bool getCurrentState() { return currentState; } - bool evaluate(); - void execute(Variant lContext = Variant()); - void executeExitScript(Variant lContext = Variant()); - - bool isEnabled() { return enabled; } - void enable(); - void disable(); - - void conditionStateChanged(bool newState, RuleCondition::Category category, Variant lcontext = Variant()); - - std::shared_ptr createRuleCondition(Variant raw); - - utils::SchemaLoader* getHOSSchemaLoader(); - devicerepository::DeviceRepositoryService* getDeviceRepository(); -}; - - -} /* namespace rule */ -} /* namespace ruleengine */ -} /* namespace service */ -} /* namespace cosgi */ -} /* namespace hos */ - - -#endif /* SRC_HOS_COSGI_SERVICE_RULEENGINE_RULE_RULE_H_ */ diff --git a/src/hos_cosgi_ruleengine/include/hos_cosgi_ruleengine/hos/cosgi/service/ruleengine/rule/RuleCondition.h b/src/hos_cosgi_ruleengine/include/hos_cosgi_ruleengine/hos/cosgi/service/ruleengine/rule/RuleCondition.h deleted file mode 100644 index 9c45fa32..00000000 --- a/src/hos_cosgi_ruleengine/include/hos_cosgi_ruleengine/hos/cosgi/service/ruleengine/rule/RuleCondition.h +++ /dev/null @@ -1,105 +0,0 @@ -/* - * RuleCondition.h - * - * Created on: 29.04.2015 - * Author: matzman - */ - -#ifndef SRC_HOS_COSGI_SERVICE_RULEENGINE_RULE_RULECONDITION_H_ -#define SRC_HOS_COSGI_SERVICE_RULEENGINE_RULE_RULECONDITION_H_ - -#include -#include -#include - - - - -namespace hos { -namespace cosgi { -namespace service { -namespace ruleengine { -namespace rule { - -class Rule; -class RuleCompositeCondition; - -using namespace ::libvariant; - - -class RuleCondition { -public: - enum class Category : int { Event, State, Mixed }; - -protected: - bool currentState = false; - Category category; - RuleCompositeCondition* compositeParent = nullptr; - bool enabled = false; - - void notifyStateChangeToParent(bool newState, Category category, Variant lcontext = Variant()); - -public: - Rule* const parent; - const std::string type; - - RuleCondition(Rule* parent, Category category, const std::string type) : - parent(parent), category(category), type(type) {} - virtual ~RuleCondition() {} - void setCompositeParent(RuleCompositeCondition* compositeParent) { - this->compositeParent = compositeParent; - } - - virtual Category getCategory() { return category; } - - bool getCurrentState() { return currentState; } - virtual bool evaluate() { return currentState; } - - bool isEnabled() { return enabled; } - virtual void enable() { enabled = true; } - virtual void disable() { enabled = false; } -}; - - -class RuleCompositeCondition : public RuleCondition { -protected: - std::vector> children; -public: - RuleCompositeCondition(Rule* parent, Category category, std::string type) : - RuleCondition(parent, category, type) {} - - virtual void childStateChanged(RuleCondition* child, bool newState, Category category, Variant lcontext = Variant()) = 0; - - virtual void addChild(std::shared_ptr child) { - if (child) { - child->setCompositeParent(this); - children.push_back(child); - } - }; - - virtual void enable() { - if (!enabled) { - for (auto c : children) { - c->enable(); - } - enabled = true; - } - } - virtual void disable() { - if (enabled) { - for (auto c : children) { - c->disable(); - } - enabled = false; - } - } -}; - - -} /* namespace rule */ -} /* namespace ruleengine */ -} /* namespace service */ -} /* namespace cosgi */ -} /* namespace hos */ - -#endif /* SRC_HOS_COSGI_SERVICE_RULEENGINE_RULE_RULECONDITION_H_ */ diff --git a/src/hos_cosgi_ruleengine/include/hos_cosgi_ruleengine/hos/cosgi/service/ruleengine/ruleengine_service.h b/src/hos_cosgi_ruleengine/include/hos_cosgi_ruleengine/hos/cosgi/service/ruleengine/ruleengine_service.h deleted file mode 100644 index c84b32cb..00000000 --- a/src/hos_cosgi_ruleengine/include/hos_cosgi_ruleengine/hos/cosgi/service/ruleengine/ruleengine_service.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * hos_ruleengine_service.h - * - * Created on: 26.04.2015 - * Author: matzman - */ - -#ifndef INCLUDE_HOS_COSGI_RULEENGINE_HOS_COSGI_SERVICE_RULEENGINE_RULEENGINE_SERVICE_H_ -#define INCLUDE_HOS_COSGI_RULEENGINE_HOS_COSGI_SERVICE_RULEENGINE_RULEENGINE_SERVICE_H_ - - -#include -#include -#include - - -namespace hos { -namespace cosgi { -namespace service { -namespace ruleengine { - -namespace rule { -class RuleCondition; -class Rule; -} - -class RuleConditionFactory : public virtual interface::Object { -public: - virtual std::shared_ptr createCondition(rule::Rule* parent, ::libvariant::Variant raw) = 0; -}; - - -class RuleEngineService : public virtual interface::Service { -public: - static constexpr const char* SERVICE_NAME = "hos_ruleengine_service"; - - virtual void registerRuleConditionFactory(std::string type, std::shared_ptr factory) = 0; -}; - - -} // namespace ruleengine -} // namespace service -} // namespace cosgi -} // namespace hos - - - - -#endif /* INCLUDE_HOS_COSGI_RULEENGINE_HOS_COSGI_SERVICE_RULEENGINE_RULEENGINE_SERVICE_H_ */ diff --git a/src/hos_cosgi_ruleengine/include/hos_cosgi_ruleengine/hos/cosgi/service/ruleengine/rules/Rule.h b/src/hos_cosgi_ruleengine/include/hos_cosgi_ruleengine/hos/cosgi/service/ruleengine/rules/Rule.h new file mode 100644 index 00000000..6bc0338e --- /dev/null +++ b/src/hos_cosgi_ruleengine/include/hos_cosgi_ruleengine/hos/cosgi/service/ruleengine/rules/Rule.h @@ -0,0 +1,145 @@ + +#pragma once + + +#include +#include + + + +namespace hos { +namespace cosgi { +namespace service { + + +// forward declarations +namespace devicerepository { class DeviceRepositoryService; } + + +// main namespace +namespace ruleengine { +namespace rules { + + +// Forward declarations +namespace conditions { +class RuleCondition; +enum class ConditionTriggerType : int; +} +class RulePackage; + + + +/** + * Public COSGI interface of a rule. + */ +class Rule + : public virtual interface::Object { + +public: + + /** + * Returns a reference to the parent rule package. + * + * \return rule package. + */ + virtual RulePackage* getParent() noexcept = 0; + + + /** + * Returns the name of the rule. + * + * \return name. + */ + virtual const std::string& getName() noexcept = 0; + + /** + * Returns whether the rule is currently active. + * + * Only conditions of trigger type "state" can activate a rule. + * + * \return whether rule is active. + */ + virtual bool isActive() noexcept = 0; + + /** + * Evaluates the conditions of the rule and may execute associated scripts. + * + * Only active conditions of trigger type "state" can return true here. + * + * May return exceptions derived from std::exception. + * + * \return evaluation result. + */ + virtual bool evaluate() = 0; + + /** + * Executes the associated script. + * + * May return exceptions derived from std::exception. + * + * \param[in] local context. + */ + virtual void executeScript(const libvariant::Variant& lContext = libvariant::Variant()) = 0; + + /** + * Executes the associated exit script. + * + * May return exceptions derived from std::exception. + * + * \param[in] local context. + */ + virtual void executeExitScript(const libvariant::Variant& lContext = libvariant::Variant()) = 0; + + /** + * Returns whether this rule is enabled. + * + * \return enabled + */ + virtual bool isEnabled() noexcept = 0; + + /** + * Enable/Disable this rule package. + * + * \param[in] enabled. + */ + virtual void setEnabled(bool enabled) noexcept = 0; + + + /* Interface for third-party conditions. */ + + /** + * Creates and returns a new condition based on the given json config. + * + * \param[in] json config. + * \return new rule condition. + */ + virtual std::shared_ptr createRuleCondition(const libvariant::Variant& raw) = 0; + + /** + * Indicates that one of the conditions of the rule changed state. + * + * \param[in] newState new state. + * \param[in] trigger trigger type. + * \param[in] lcontext local context that should be passed to script. + */ + virtual void conditionStateChanged(bool newState, const conditions::ConditionTriggerType& trigger, + const libvariant::Variant& lcontext = libvariant::Variant()) noexcept = 0; + + /** + * Returns a reference to the device repository service. + * + * \return device repository service. + */ + virtual devicerepository::DeviceRepositoryService* getDeviceRepository() noexcept = 0; + +}; + + + +} /* namespace rules */ +} /* namespace ruleengine */ +} /* namespace service */ +} /* namespace cosgi */ +} /* namespace hos */ + diff --git a/src/hos_cosgi_ruleengine/include/hos_cosgi_ruleengine/hos/cosgi/service/ruleengine/rules/RulePackage.h b/src/hos_cosgi_ruleengine/include/hos_cosgi_ruleengine/hos/cosgi/service/ruleengine/rules/RulePackage.h new file mode 100644 index 00000000..e6b5f0ea --- /dev/null +++ b/src/hos_cosgi_ruleengine/include/hos_cosgi_ruleengine/hos/cosgi/service/ruleengine/rules/RulePackage.h @@ -0,0 +1,127 @@ + +#pragma once + + +#include +#include +#include +#include + + + +// Forward declarations +namespace hos { namespace cosgi { namespace service { +namespace scripting { class ScriptingService; } +namespace scripting { class GlobalScriptContext; } +} } } + + + +namespace hos { +namespace cosgi { +namespace service { +namespace ruleengine { + + +// Forward declarations +class RuleEngineService; + + +// main namespace +namespace rules { + + +// Forward declarations +class Rule; + + + +/** + * Public COSGI interface of a rule package. + */ +class RulePackage + : public virtual interface::Object { + +public: + + /** + * Returns a reference to the rule engine service. + * + * \return rule engine service. + */ + virtual service::ruleengine::RuleEngineService* getParent() noexcept = 0; + + /** + * Returns the name of the rule package. + * + * \return name. + */ + virtual const std::string& getName() noexcept = 0; + + /** + * Returns an identifier indicating the scripting language. + * + * \return scripting language identifier. + */ + virtual const std::string& getLanguage() noexcept = 0; + + /** + * Returns the rule with the given name. + * + * \param[in] name rule name. + * \return weak reference to rule (expired reference in case of rule not found). + */ + virtual std::weak_ptr getRule(const std::string& name) noexcept = 0; + + /** + * Returns all rules contained within the rule package + * + * \return vector of weak references. + */ + virtual std::vector> getRules() noexcept = 0; + + /** + * Returns the number of contained rules. + * + * \return rule count. + */ + virtual size_t getRuleCount() noexcept = 0; + + /** + * Returns whether this rule package is enabled. + * + * \return enabled + */ + virtual bool isEnabled() noexcept = 0; + + /** + * Enable/Disable this rule package. + * + * \param[in] enabled. + */ + virtual void setEnabled(bool enabled) noexcept = 0; + + /** + * Returns a reference to the associated scripting service. + * + * \return scripting service. + */ + virtual service::scripting::ScriptingService* getScriptingService() noexcept = 0; + + /** + * Returns a reference to the associated global script context. + * + * \return global script context. + */ + virtual std::weak_ptr getGlobalScriptContext() noexcept = 0; + +}; + + + +} /* namespace rules */ +} /* namespace ruleengine */ +} /* namespace service */ +} /* namespace cosgi */ +} /* namespace hos */ + diff --git a/src/hos_cosgi_ruleengine/include/hos_cosgi_ruleengine/hos/cosgi/service/ruleengine/rules/conditions/RuleCondition.h b/src/hos_cosgi_ruleengine/include/hos_cosgi_ruleengine/hos/cosgi/service/ruleengine/rules/conditions/RuleCondition.h new file mode 100644 index 00000000..903b2162 --- /dev/null +++ b/src/hos_cosgi_ruleengine/include/hos_cosgi_ruleengine/hos/cosgi/service/ruleengine/rules/conditions/RuleCondition.h @@ -0,0 +1,163 @@ + +#pragma once + + +#include +#include +#include +#include + + + +namespace hos { +namespace cosgi { +namespace service { +namespace ruleengine { +namespace rules { + + +// forward declarations +class Rule; + + +// main namespace +namespace conditions { + + +// forward declarations +class RuleCompositeCondition; + + + +/* + * Condition trigger type. + * + * Conditions can have two different trigger types: + * - Event trigger: Conditions are triggered by events happening. + * - State trigger: Condition are triggered by device state. + * + * "Both" indicates that an condition has both trigger types. + **/ +enum class ConditionTriggerType : int { + Event = 1, + State = 2, + Both = 3 +}; + + + +/** + * Public COSGI interface of a rule condition. + */ +class RuleCondition + : public virtual interface::Object { + +public: + + /** + * Returns the type of the condition. + * + * \return condition type. + */ + virtual const std::string& getType() noexcept = 0; + + /** + * Returns the trigger type of the condition. + * + * \return trigger type. + */ + virtual ConditionTriggerType getTriggerType() noexcept = 0; + + /** + * Returns the current state of the condition. + * + * This function returns a cached value. + * + * \return current state. + */ + virtual bool getCurrentState() noexcept = 0; + + /** + * Evaluates the condition and returns the result. + * + * \return evaluation result. + */ + virtual bool evaluate() noexcept = 0; + + /** + * Returns a reference to the parent rule. + * + * \return parent rule. + */ + virtual rules::Rule* getParent() noexcept = 0; + + /** + * Returns whether this rule condition is enabled. + * + * \return enabled. + */ + virtual bool isEnabled() noexcept = 0; + + /** + * Enable/Disable this rule condition. + * + * \param[in] enabled. + */ + virtual void setEnabled(bool enabled) noexcept = 0; + + + /* Interface for third-party conditions. */ + + /** + * Sets the composite condition this condition is contained within. + * + * \param[in] parent composite condition. + */ + virtual void setCompositeParent(RuleCompositeCondition* compositeParent) noexcept = 0; + +}; + + + +/** + * Public COSGI interface of a composite condition. + */ +class RuleCompositeCondition + : public virtual RuleCondition { + +public: + + /* Interface for third-party conditions. */ + + /** + * Indicates that a contained condition has changed its state. + * + * \param[in] child condition whose state has changed. + * \param[in] newState new state. + * \param[in] trigger trigger type. + * \param[in] lcontext local context that should be passed to the script. + */ + virtual void childStateChanged( RuleCondition* child, + bool newState, + ConditionTriggerType trigger, + const libvariant::Variant& lcontext = libvariant::Variant() ) noexcept = 0; + + + /** + * Adds a new condition. + * + * \param[in] child child condition. + */ + virtual void addChild(std::shared_ptr child) = 0; + +}; + + + +} /* namespace conditions */ +} /* namespace rules */ +} /* namespace ruleengine */ +} /* namespace service */ +} /* namespace cosgi */ +} /* namespace hos */ + diff --git a/src/hos_cosgi_ruleengine/include/hos_cosgi_ruleengine/hos/cosgi/service/ruleengine/rules/conditions/RuleConditionImpl.h b/src/hos_cosgi_ruleengine/include/hos_cosgi_ruleengine/hos/cosgi/service/ruleengine/rules/conditions/RuleConditionImpl.h new file mode 100644 index 00000000..fced1ba1 --- /dev/null +++ b/src/hos_cosgi_ruleengine/include/hos_cosgi_ruleengine/hos/cosgi/service/ruleengine/rules/conditions/RuleConditionImpl.h @@ -0,0 +1,123 @@ + +#pragma once + + +#include "RuleCondition.h" + + + +// main namespace +namespace hos { +namespace cosgi { +namespace service { +namespace ruleengine { +namespace rules { +namespace conditions { + + + +/** + * Base class for condition implementations. + */ +class RuleConditionImpl + : public virtual RuleCondition { + +protected: + + rules::Rule* parent; + ConditionTriggerType triggerType; + std::string type; + + bool currentState = false; + RuleCompositeCondition* compositeParent = nullptr; + bool enabled = false; + + void notifyStateChangeToParent(bool newState, ConditionTriggerType category, const libvariant::Variant& lcontext = libvariant::Variant()); + +public: + + RuleConditionImpl(rules::Rule* parent, ConditionTriggerType triggerType, const std::string& type) : + parent(parent), triggerType(triggerType), type(type) {} + + virtual rules::Rule* getParent() noexcept override { + return parent; + } + + virtual void setCompositeParent(RuleCompositeCondition* compositeParent) noexcept override { + this->compositeParent = compositeParent; + } + + virtual const std::string& getType() noexcept override { + return type; + } + + virtual ConditionTriggerType getTriggerType() noexcept override { + return triggerType; + } + + virtual bool getCurrentState() noexcept override { + return currentState; + } + + virtual bool evaluate() noexcept override { + return currentState; + } + + virtual bool isEnabled() noexcept override { + return enabled; + } + + virtual void setEnabled(bool enabled) noexcept override { + this->enabled; + } + +}; + + + +/** + * Base class for composite condition implementations. + */ +class RuleCompositeConditionImpl + : public virtual RuleCompositeCondition, + public RuleConditionImpl { + +protected: + std::vector> children; + +public: + + RuleCompositeConditionImpl(Rule* parent, ConditionTriggerType category, std::string type) : + RuleConditionImpl(parent, category, type) {} + + virtual void childStateChanged( RuleCondition* child, + bool newState, + ConditionTriggerType category, + const libvariant::Variant& lcontext = libvariant::Variant() ) noexcept override = 0; + + virtual void addChild(std::shared_ptr child) override { + if (child) { + child->setCompositeParent(this); + children.push_back(child); + } + }; + + virtual void setEnabled(bool enabled) noexcept override { + if (this->enabled != enabled) { + for (auto c : children) { + c->setEnabled(enabled); + } + this->enabled = enabled; + } + } +}; + + + +} /* namespace conditions */ +} /* namespace rules */ +} /* namespace ruleengine */ +} /* namespace service */ +} /* namespace cosgi */ +} /* namespace hos */ + diff --git a/src/hos_cosgi_ruleengine/src/bundle_activator.cpp b/src/hos_cosgi_ruleengine/src/BundleActivator.cpp similarity index 99% rename from src/hos_cosgi_ruleengine/src/bundle_activator.cpp rename to src/hos_cosgi_ruleengine/src/BundleActivator.cpp index 38a3a722..d20e6a5c 100644 --- a/src/hos_cosgi_ruleengine/src/bundle_activator.cpp +++ b/src/hos_cosgi_ruleengine/src/BundleActivator.cpp @@ -21,7 +21,7 @@ extern "C" { } #include -#include "hos/cosgi/service/ruleengine/ruleengine_serviceimpl.h" +#include "hos/cosgi/service/ruleengine/RuleEngineServiceImpl.h" #include #include diff --git a/src/hos_cosgi_ruleengine/src/hos/cosgi/service/ruleengine/ruleengine_serviceimpl.cpp b/src/hos_cosgi_ruleengine/src/hos/cosgi/service/ruleengine/RuleEngineServiceImpl.cpp similarity index 81% rename from src/hos_cosgi_ruleengine/src/hos/cosgi/service/ruleengine/ruleengine_serviceimpl.cpp rename to src/hos_cosgi_ruleengine/src/hos/cosgi/service/ruleengine/RuleEngineServiceImpl.cpp index 208e37aa..a4e0618e 100644 --- a/src/hos_cosgi_ruleengine/src/hos/cosgi/service/ruleengine/ruleengine_serviceimpl.cpp +++ b/src/hos_cosgi_ruleengine/src/hos/cosgi/service/ruleengine/RuleEngineServiceImpl.cpp @@ -5,7 +5,6 @@ * Author: matzman */ -#include "ruleengine_serviceimpl.h" #include #include #include @@ -16,8 +15,12 @@ #include #include -#include "rule/RulePackage.h" -#include "rule/RuleConditionImpl.h" +#include "RuleEngineServiceImpl.h" + +#include "rules/conditions/LogicalAndCondition.h" +#include "rules/conditions/LogicalOrCondition.h" +#include "rules/conditions/DeviceMsgCondition.h" +#include "rules/RulePackageImpl.h" namespace hos { @@ -62,11 +65,11 @@ void RuleEngineServiceImpl::init() { XmlRpc::XmlRpcValue rawValue; nodehandle->getParam(ruleEnginePrefix, rawValue); schemaLoader.reset(new utils::SchemaLoader()); - registerRuleConditionFactory("logical_or", std::make_shared()); - registerRuleConditionFactory("logical_and", std::make_shared()); - registerRuleConditionFactory("devicestatusmsg", std::make_shared()); - registerRuleConditionFactory("devicecommandmsg", std::make_shared()); - registerRuleConditionFactory("devicestatechange", std::make_shared()); + registerRuleConditionFactory("logical_or", std::make_shared()); + registerRuleConditionFactory("logical_and", std::make_shared()); + registerRuleConditionFactory("devicestatusmsg", std::make_shared()); + registerRuleConditionFactory("devicecommandmsg", std::make_shared()); + registerRuleConditionFactory("devicestatechange", std::make_shared()); if (rawValue.getType() == XmlRpc::XmlRpcValue::TypeStruct) { for (auto pkg : rawValue) { std::string pname = pkg.first; @@ -96,11 +99,11 @@ void RuleEngineServiceImpl::addRulePkg(std::string name, std::string rawValue) { Variant m = Deserialize(rawValue, SERIALIZE_JSON); SchemaResult sr = SchemaValidate(s, m, schemaLoader.get()); if (!sr.Error()) { - std::shared_ptr pkg = rule::RulePackage::createRulePackage(this, name, m); + std::shared_ptr pkg = rules::RulePackageImpl::createRulePackage(this, name, m); if (pkg) { if (pkg->getScriptingService()) { rulePkgs.insert({name, pkg}); - pkg->enable(); + pkg->setEnabled(true); } else { noExecRulePkgs.insert({name, pkg}); std::cout << "No execution service found for rule package \"" << name << "\"" << std::endl; @@ -142,17 +145,17 @@ scripting::ScriptingService* RuleEngineServiceImpl::getExecutionBackend(std::str } -void RuleEngineServiceImpl::registerRuleConditionFactory(std::string type, std::shared_ptr factory) { +void RuleEngineServiceImpl::registerRuleConditionFactory(const std::string& type, std::shared_ptr factory) noexcept { condFactories.insert({type, factory}); } -std::shared_ptr RuleEngineServiceImpl::getRulePackage(const std::string& name) { - std::shared_ptr retval; +std::weak_ptr RuleEngineServiceImpl::getRulePackage(const std::string& name) noexcept { + std::weak_ptr retval; try { retval = rulePkgs.at(name); } catch(...) {} - if (!retval) { + if (retval.expired()) { try { retval = noExecRulePkgs.at(name); } catch(...) {} @@ -160,6 +163,18 @@ std::shared_ptr RuleEngineServiceImpl::getRulePackage(const s return retval; } + +std::vector> RuleEngineServiceImpl::getRulePackages() noexcept { + +} + + +size_t RuleEngineServiceImpl::getRulePackageCount() noexcept { + +} + + + } // namespace ruleengine } // namespace service } // namespace cosgi diff --git a/src/hos_cosgi_ruleengine/src/hos/cosgi/service/ruleengine/ruleengine_serviceimpl.h b/src/hos_cosgi_ruleengine/src/hos/cosgi/service/ruleengine/RuleEngineServiceImpl.h similarity index 70% rename from src/hos_cosgi_ruleengine/src/hos/cosgi/service/ruleengine/ruleengine_serviceimpl.h rename to src/hos_cosgi_ruleengine/src/hos/cosgi/service/ruleengine/RuleEngineServiceImpl.h index d93d463b..b4351a8d 100644 --- a/src/hos_cosgi_ruleengine/src/hos/cosgi/service/ruleengine/ruleengine_serviceimpl.h +++ b/src/hos_cosgi_ruleengine/src/hos/cosgi/service/ruleengine/RuleEngineServiceImpl.h @@ -1,12 +1,7 @@ -/* - * hos_ruleengine_service_impl.h - * - * Created on: 26.04.2015 - * Author: matzman - */ -#ifndef INCLUDE_HOS_COSGI_RULEENGINE_HOS_RULEENGINE_SERVICE_IMPL_H_ -#define INCLUDE_HOS_COSGI_RULEENGINE_HOS_RULEENGINE_SERVICE_IMPL_H_ +#pragma once + + #include #include @@ -15,10 +10,10 @@ #include #include #include -#include -#include #include #include +#include +#include #include #include #include @@ -45,18 +40,19 @@ namespace hos { namespace cosgi { namespace service { namespace ruleengine { -namespace rule { -class RulePackage; +namespace rules { +class RulePackageImpl; } -class RuleEngineServiceImpl; + class RuleEngineServiceImpl : public virtual RuleEngineService, public virtual implementation::Service { + private: - typedef std::map> rulePkgMap_t; + typedef std::map> rulePkgMap_t; typedef std::map execServiceMap_t; typedef std::map> condFactoryMap_t; @@ -82,39 +78,52 @@ private: void init(); public: + /* Initialization */ void setCoreService(service::core::CoreService* coreService); + void setRepoService(devicerepository::DeviceRepositoryService* repoService); + /* Public Service Implementations */ - virtual void registerRuleConditionFactory(std::string type, std::shared_ptr factory) override; + virtual void registerRuleConditionFactory(const std::string& type, std::shared_ptr factory) noexcept override; + + virtual std::weak_ptr getRulePackage(const std::string& name) noexcept override; + + virtual std::vector> getRulePackages() noexcept override; + + + virtual size_t getRulePackageCount() noexcept override; + /* Internal Stuff */ bool isInitialized() { return initialized; } void addExecutionBackend(scripting::ScriptingService* service); + scripting::ScriptingService* getExecutionBackend(std::string language); - std::shared_ptr getRuleConditionFactory(std::string type) { return condFactories[type]; } + std::shared_ptr getRuleConditionFactory(std::string type) { + return condFactories[type]; + } - utils::SchemaLoader* getHOSSchemaLoader() { return schemaLoader.get(); } + utils::SchemaLoader* getHOSSchemaLoader() { + return schemaLoader.get(); + } - devicerepository::DeviceRepositoryService* getDeviceRepository() { return repoService; } + devicerepository::DeviceRepositoryService* getDeviceRepository() { + return repoService; + } - std::shared_ptr getRulePackage(const std::string& name); }; + } // namespace ruleengine } // namespace service } // namespace cosgi } // namespace hos - - - - -#endif /* INCLUDE_HOS_COSGI_RULEENGINE_HOS_RULEENGINE_SERVICE_IMPL_H_ */ diff --git a/src/hos_cosgi_ruleengine/src/hos/cosgi/service/ruleengine/rule/RuleConditionImpl.cpp b/src/hos_cosgi_ruleengine/src/hos/cosgi/service/ruleengine/rule/RuleConditionImpl.cpp deleted file mode 100644 index 50c05f20..00000000 --- a/src/hos_cosgi_ruleengine/src/hos/cosgi/service/ruleengine/rule/RuleConditionImpl.cpp +++ /dev/null @@ -1,432 +0,0 @@ -/* - * RuleCondition.cpp - * - * Created on: 29.04.2015 - * Author: matzman - */ - -#include "RuleConditionImpl.h" -#include -#include -#include - -namespace hos { -namespace cosgi { -namespace service { -namespace ruleengine { -namespace rule { - - -std::shared_ptr LogicalOrConditionFactory::createCondition(rule::Rule* parent, Variant raw) { - std::shared_ptr retval; - if (raw.HasPath("conditions") && raw["conditions"].IsList() && raw["conditions"].Size() > 0) { - retval = std::make_shared(parent); - for (auto c : raw["conditions"].AsList()) { - auto cond = parent->createRuleCondition(c); - if (cond) { - retval->addChild(cond); - } else { - throw std::runtime_error("Could not create condition."); - } - } - } else { - throw std::runtime_error("Malformed JSON: missing property condition or is not a list."); - } - return std::static_pointer_cast(retval); -} - - -std::shared_ptr LogicalAndConditionFactory::createCondition(rule::Rule* parent, Variant raw) { - std::shared_ptr retval; - if (raw.HasPath("conditions") && raw["conditions"].IsList() && raw["conditions"].Size() > 0) { - retval = std::make_shared(parent); - for (auto c : raw["conditions"].AsList()) { - auto cond = parent->createRuleCondition(c); - if (cond) { - retval->addChild(cond); - } else { - throw std::runtime_error("Could not create condition."); - } - } - } else { - throw std::runtime_error("Malformed JSON: missing property condition or is not a list."); - } - return std::static_pointer_cast(retval); -} - - - -void RuleCondition::notifyStateChangeToParent(bool newState, Category category, Variant lcontext) { - if (compositeParent) { - compositeParent->childStateChanged(this, newState, category, lcontext); - } else { - parent->conditionStateChanged(newState, category, lcontext); - } -} - - -void LogicalOrCondition::addChild(std::shared_ptr child) { - if (child->getCategory() == Category::Event) { - eventChildrenCount++; - } else if (child->getCategory() == Category::State) { - stateChildrenCount++; - } else { - mixedChildrenCount++; - } - if (mixedChildrenCount > 0 || (eventChildrenCount > 0 && stateChildrenCount > 0)) { - category = Category::Mixed; - } else if (eventChildrenCount > 0 && stateChildrenCount == 0) { - category = Category::Event; - } else { - category = Category::State; - } - RuleCompositeCondition::addChild(child); -} - - -void LogicalOrCondition::childStateChanged(RuleCondition* child, bool newState, Category category, Variant lcontext) { - if (newState && category == Category::Event && !currentState) { - notifyStateChangeToParent(true, category, lcontext); - } else if (category == Category::State) { - if (newState) { - trueChildrenCount++; - if (!currentState) { - currentState = true; - notifyStateChangeToParent(true, category, lcontext); - } - } else { - trueChildrenCount--; - if (currentState && trueChildrenCount <= 0) { - currentState = false; - notifyStateChangeToParent(false, category, lcontext); - } - } - } -} - - -bool LogicalOrCondition::evaluate() { - // Event conditions always evaluate to false - if (category == Category::Event) { - return false; - } - trueChildrenCount = 0; - for (auto c : children) { - if (c->evaluate()) { - trueChildrenCount++; - } - } - currentState = trueChildrenCount > 0; - return currentState; -} - - - -void LogicalAndCondition::addChild(std::shared_ptr child) { - if (child->getCategory() == Category::Event) { - if (eventChildrenCount > 0) { - throw new std::runtime_error("Cannot have an and condition with more than two event conditions."); - } - eventChildrenCount++; - } else if (child->getCategory() == Category::State) { - stateChildrenCount++; - } else { - mixedChildrenCount++; - } - if (mixedChildrenCount > 0 || (eventChildrenCount > 0 && stateChildrenCount > 0)) { - category = Category::Mixed; - } else if (eventChildrenCount > 0 && stateChildrenCount == 0) { - category = Category::Event; - } else { - category = Category::State; - } - RuleCompositeCondition::addChild(child); -} - -void LogicalAndCondition::childStateChanged(RuleCondition* child, bool newState, Category category, Variant lcontext) { - if (newState && category == Category::Event && !currentState && trueChildrenCount >= children.size()-1) { - notifyStateChangeToParent(true, category); - } else if (category == Category::State) { - if (newState) { - trueChildrenCount++; - if (!currentState && trueChildrenCount >= children.size()) { - currentState = true; - notifyStateChangeToParent(true, category, lcontext); - } - } else { - trueChildrenCount--; - if (currentState) { - currentState = false; - notifyStateChangeToParent(false, category, lcontext); - } - } - } -} - - - - -bool LogicalAndCondition::evaluate() { - // Event conditions always evaluate to false - if (category == Category::Event) { - return false; - } - trueChildrenCount = 0; - for (auto c : children) { - if (c->evaluate()) { - trueChildrenCount++; - } - } - currentState = trueChildrenCount == children.size(); - return currentState; -} - - - -void DeviceMsgCondition::notificationReceived(interface::Observer::observable_type* o, const devicerepository::DeviceEvent::EventType& key, const devicerepository::DeviceEvent* e) noexcept { - switch (key) { - case devicerepository::DeviceEvent::EventType::DEVICE_ADDED: - if (!device && this->deviceID.compare(e->device->getDeviceID()) == 0) { - ROS_INFO_STREAM("Rule " << parent->name << ": Device " << deviceID << " appeared."); - device = e->device; - parent->getDeviceRepository()->deleteObserver(devicerepository::DeviceEvent::EventType::DEVICE_ADDED, this); - _registerAsListener(); - } - break; - case devicerepository::DeviceEvent::EventType::DEVICE_REMOVED: - if (device && device->getDeviceID().compare(e->device->getDeviceID()) == 0) { - ROS_ERROR_STREAM("Rule " << parent->name << ": Device " << deviceID << " disappeared."); - device->deleteObserver(this); - device = nullptr; - parent->getDeviceRepository()->addObserver(devicerepository::DeviceEvent::EventType::DEVICE_ADDED, this); - } - break; - default: - break; - } -} - -void DeviceMsgCondition::enable() { - auto repoService = parent->getDeviceRepository(); - auto device = repoService->getDevice(deviceID); - if (!device.expired()) { - this->device = device.lock().get(); - _registerAsListener(); - } else { - ROS_ERROR_STREAM("Rule " << parent->name << ": Could not find device " << deviceID); - parent->getDeviceRepository()->addObserver(devicerepository::DeviceEvent::EventType::DEVICE_ADDED, this); - } -} - -void DeviceMsgCondition::disable() { - if (device) { - device->deleteObserver(this); - device = nullptr; - } - parent->getDeviceRepository()->deleteObserver(this); -} - -void DeviceMsgCondition::_registerAsListener() { - if (device) { - for (auto k : _listenerKeys) { - device->addObserver(k, this); - } - } -} - - - - -std::shared_ptr DeviceStatusMsgConditionFactory::createCondition(rule::Rule* parent, Variant raw) { - std::shared_ptr retval; - std::string deviceID; - if (raw.HasPath("deviceid") && raw["deviceid"].IsString()) { - deviceID = raw["deviceid"].AsString(); - } else { - throw std::runtime_error("Malformed JSON: missing property deviceid or is not an string."); - } - std::string msgPointer; - if (raw.HasPath("msgpointer")) { - if (raw["msgpointer"].IsString()) { - msgPointer = raw["msgpointer"].AsString(); - } else { - throw std::runtime_error("Malformed JSON: property msgpointer is not an string."); - } - } - Variant msgValue; - if (raw.HasPath("msgvalue")) { - msgValue = raw["msgvalue"]; - } - retval = std::make_shared(parent, deviceID, msgPointer, msgValue); - return retval; -} - -void DeviceStatusMsgCondition::notificationReceived(interface::Observer::observable_type* o, const devicerepository::DeviceEvent::EventType& key, const devicerepository::DeviceEvent* e) noexcept { - switch (e->eventType) { - case devicerepository::DeviceEvent::EventType::STATUS_UPDATE_RECEIVED: - if (device && device == e->device) { - auto e2 = static_cast(e); - bool notify = true; - Variant value; - if (msgPointer.empty()) { - value = e2->statusMsg; - } else if (e2->statusMsg.HasPath(msgPointer)) { - value = e2->statusMsg.GetPath(msgPointer); - } else { - notify = false; - } - if (notify && !msgValue.IsNull()) { - if (msgValue != value) { - notify = false; - } - } - if (notify) { - Variant lcontext; - lcontext["event_deviceid"] = Variant(e->device->getDeviceID()); - lcontext["event_type"] = Variant("statusmsg"); - lcontext["event_msg"] = e2->statusMsg; - notifyStateChangeToParent(true, Category::Event, lcontext); - } - } - break; - default: - DeviceMsgCondition::notificationReceived(o, key, e); - break; - } -} - - -std::shared_ptr DeviceCommandMsgConditionFactory::createCondition(rule::Rule* parent, Variant raw) { - std::shared_ptr retval; - std::string deviceID; - if (raw.HasPath("deviceid") && raw["deviceid"].IsString()) { - deviceID = raw["deviceid"].AsString(); - } else { - throw std::runtime_error("Malformed JSON: missing property deviceid or is not an string."); - } - std::string msgPointer; - if (raw.HasPath("msgpointer")) { - if (raw["msgpointer"].IsString()) { - msgPointer = raw["msgpointer"].AsString(); - } else { - throw std::runtime_error("Malformed JSON: property msgpointer is not an string."); - } - } - Variant msgValue; - if (raw.HasPath("msgvalue")) { - msgValue = raw["msgvalue"]; - } - retval = std::make_shared(parent, deviceID, msgPointer, msgValue); - return retval; -} - -void DeviceCommandMsgCondition::notificationReceived(interface::Observer::observable_type* o, const devicerepository::DeviceEvent::EventType& key, const devicerepository::DeviceEvent* e) noexcept { - switch (e->eventType) { - case devicerepository::DeviceEvent::EventType::COMMAND_RECEIVED: - if (device && device == e->device) { - auto e2 = static_cast(e); - bool notify = true; - Variant value; - if (msgPointer.empty()) { - value = e2->cmdMsg; - } else if (e2->cmdMsg.HasPath(msgPointer)) { - value = e2->cmdMsg.GetPath(msgPointer); - } else { - notify = false; - } - if (notify && !msgValue.IsNull()) { - if (msgValue != value) { - notify = false; - } - } - if (notify) { - Variant lcontext; - lcontext["event_deviceid"] = Variant(e->device->getDeviceID()); - lcontext["event_type"] = Variant("commandmsg"); - lcontext["event_msg"] = e2->cmdMsg; - notifyStateChangeToParent(true, Category::Event, lcontext); - } - } - break; - default: - DeviceMsgCondition::notificationReceived(o, key, e); - break; - } -} - - -std::shared_ptr DeviceStateChangeConditionFactory::createCondition(rule::Rule* parent, Variant raw) { - std::shared_ptr retval; - std::string deviceID; - if (raw.HasPath("deviceid") && raw["deviceid"].IsString()) { - deviceID = raw["deviceid"].AsString(); - } else { - throw std::runtime_error("Malformed JSON: missing property deviceid or is not an string."); - } - std::string statePointer; - if (raw.HasPath("msgpointer")) { - if (raw["msgpointer"].IsString()) { - statePointer = raw["msgpointer"].AsString(); - } else { - throw std::runtime_error("Malformed JSON: property msgpointer is not an string."); - } - } - Variant fromValue; - if (raw.HasPath("fromvalue")) { - fromValue = raw["fromvalue"]; - } - Variant toValue; - if (raw.HasPath("tovalue")) { - toValue = raw["tovalue"]; - } - retval = std::make_shared(parent, deviceID, statePointer, fromValue, toValue); - return retval; -} - - -void DeviceStateChangeCondition::notificationReceived(interface::Observer::observable_type* o, const devicerepository::DeviceEvent::EventType& key, const devicerepository::DeviceEvent* e) noexcept { - switch (e->eventType) { - case devicerepository::DeviceEvent::EventType::STATE_CHANGED: - if (device && device == e->device) { - auto e2 = static_cast(e); - bool notify = true; - Variant current, last; - if (msgPointer.empty()) { - current = device->getCurrentState(); - last = device->getLastState(); - } else if (device->getCurrentState().HasPath(msgPointer) && device->getLastState().HasPath(msgPointer)) { - current = device->getCurrentState().GetPath(msgPointer); - last = device->getLastState().GetPath(msgPointer); - } else { - notify = false; - } - if (notify) { - if (current == last) { - notify = false; - } else if (!fromValue.IsNull() && fromValue != last) { - notify = false; - } else if (!msgValue.IsNull() && msgValue != current) { - notify = false; - } - } - if (notify) { - Variant lcontext; - lcontext["event_deviceid"] = Variant(e->device->getDeviceID()); - lcontext["event_type"] = Variant("statechange"); - lcontext["event_msg"] = e2->statusMsg; - notifyStateChangeToParent(true, Category::Event, lcontext); - } - } - break; - default: - DeviceMsgCondition::notificationReceived(o, key, e); - break; - } -} - - -} /* namespace rule */ -} /* namespace ruleengine */ -} /* namespace service */ -} /* namespace cosgi */ -} /* namespace hos */ diff --git a/src/hos_cosgi_ruleengine/src/hos/cosgi/service/ruleengine/rule/RuleConditionImpl.h b/src/hos_cosgi_ruleengine/src/hos/cosgi/service/ruleengine/rule/RuleConditionImpl.h deleted file mode 100644 index 3042d760..00000000 --- a/src/hos_cosgi_ruleengine/src/hos/cosgi/service/ruleengine/rule/RuleConditionImpl.h +++ /dev/null @@ -1,142 +0,0 @@ -/* - * RuleConditionImpl.h - * - * Created on: 11.05.2015 - * Author: matzman - */ - -#ifndef SRC_HOS_COSGI_SERVICE_RULEENGINE_RULE_RULECONDITIONIMPL_H_ -#define SRC_HOS_COSGI_SERVICE_RULEENGINE_RULE_RULECONDITIONIMPL_H_ - -#include -#include -#include -#include - - -// Service Namespace -namespace hos { -namespace cosgi { -namespace service { -namespace ruleengine { -namespace rule { - -using namespace ::libvariant; - - -class LogicalOrCondition : public RuleCompositeCondition { -protected: - unsigned eventChildrenCount = 0; - unsigned stateChildrenCount = 0; - unsigned mixedChildrenCount = 0; - unsigned trueChildrenCount = 0; -public: - LogicalOrCondition(Rule* parent) : - RuleCompositeCondition(parent, Category::Mixed, "logical_or") {} - - virtual void addChild(std::shared_ptr child) override; - virtual void childStateChanged(RuleCondition* child, bool newState, Category category, Variant lcontext = Variant()) override; - virtual bool evaluate() override; -}; - -class LogicalOrConditionFactory : public virtual RuleConditionFactory { -public: - virtual std::shared_ptr createCondition(rule::Rule* parent, ::libvariant::Variant raw) override; -}; - - -class LogicalAndCondition : public RuleCompositeCondition { -protected: - unsigned eventChildrenCount = 0; - unsigned stateChildrenCount = 0; - unsigned mixedChildrenCount = 0; - unsigned trueChildrenCount = 0; -public: - LogicalAndCondition(Rule* parent) : - RuleCompositeCondition(parent, Category::Mixed, "logical_and") {} - - virtual void addChild(std::shared_ptr child) override; - virtual void childStateChanged(RuleCondition* child, bool newState, Category category, Variant lcontext = Variant()) override; - virtual bool evaluate() override; -}; - -class LogicalAndConditionFactory : public virtual RuleConditionFactory { -public: - virtual std::shared_ptr createCondition(rule::Rule* parent, ::libvariant::Variant raw) override; -}; - - -class DeviceMsgCondition : public RuleCondition, public virtual interface::Observer { -protected: - const std::string deviceID; - devicerepository::Device* device = nullptr; - const std::string msgPointer; - const Variant msgValue; - std::set _listenerKeys; - - virtual void _registerAsListener(); - -public: - DeviceMsgCondition(Rule* parent, Category category, std::string type, const std::string deviceID, const std::string msgPointer, const Variant msgValue, std::set _listenerKeys) : - RuleCondition(parent, category, type), deviceID(deviceID), msgPointer(msgPointer), msgValue(msgValue), _listenerKeys(_listenerKeys) {} - - virtual void notificationReceived(interface::Observer::observable_type* o, const devicerepository::DeviceEvent::EventType& key, const devicerepository::DeviceEvent* e) noexcept override; - virtual void enable() override; - virtual void disable() override; -}; - - -class DeviceStatusMsgCondition : public DeviceMsgCondition { -public: - DeviceStatusMsgCondition(Rule* parent, const std::string deviceID, const std::string msgPointer, const Variant msgValue) : - DeviceMsgCondition(parent, Category::Event, "devicestatusmsg", deviceID, msgPointer, msgValue, {devicerepository::DeviceEvent::EventType::STATUS_UPDATE_RECEIVED}) {} - - virtual void notificationReceived(interface::Observer::observable_type* o, const devicerepository::DeviceEvent::EventType& key, const devicerepository::DeviceEvent* e) noexcept override; -}; - -class DeviceStatusMsgConditionFactory : public virtual RuleConditionFactory { -public: - virtual std::shared_ptr createCondition(rule::Rule* parent, Variant raw) override; -}; - - -class DeviceCommandMsgCondition : public DeviceMsgCondition { -public: - DeviceCommandMsgCondition(Rule* parent, const std::string deviceID, const std::string msgPointer, const Variant msgValue) : - DeviceMsgCondition(parent, Category::Event, "devicecommandmsg", deviceID, msgPointer, msgValue, {devicerepository::DeviceEvent::EventType::COMMAND_RECEIVED}) {} - - virtual void notificationReceived(interface::Observer::observable_type* o, const devicerepository::DeviceEvent::EventType& key, const devicerepository::DeviceEvent* e) noexcept override; -}; - -class DeviceCommandMsgConditionFactory : public virtual RuleConditionFactory { -public: - virtual std::shared_ptr createCondition(rule::Rule* parent, ::libvariant::Variant raw) override; -}; - - -class DeviceStateChangeCondition : public DeviceMsgCondition { -protected: - const Variant fromValue; -public: - DeviceStateChangeCondition(Rule* parent, const std::string deviceID, const std::string statePointer, const Variant fromValue, const Variant toValue) : - DeviceMsgCondition(parent, Category::Event, "devicestatechange", deviceID, statePointer, toValue, {devicerepository::DeviceEvent::EventType::STATE_CHANGED}), fromValue(fromValue) {} - - virtual void notificationReceived(interface::Observer::observable_type* o, const devicerepository::DeviceEvent::EventType& key, const devicerepository::DeviceEvent* e) noexcept override; -}; - -class DeviceStateChangeConditionFactory : public virtual RuleConditionFactory { -public: - virtual std::shared_ptr createCondition(rule::Rule* parent, Variant raw) override; -}; - - - - -} /* namespace rule */ -} /* namespace ruleengine */ -} /* namespace service */ -} /* namespace cosgi */ -} /* namespace hos */ - - -#endif /* SRC_HOS_COSGI_SERVICE_RULEENGINE_RULE_RULECONDITIONIMPL_H_ */ diff --git a/src/hos_cosgi_ruleengine/src/hos/cosgi/service/ruleengine/rule/RulePackage.h b/src/hos_cosgi_ruleengine/src/hos/cosgi/service/ruleengine/rule/RulePackage.h deleted file mode 100644 index 1388a564..00000000 --- a/src/hos_cosgi_ruleengine/src/hos/cosgi/service/ruleengine/rule/RulePackage.h +++ /dev/null @@ -1,85 +0,0 @@ -/* - * RulePackage.h - * - * Created on: 29.04.2015 - * Author: matzman - */ - -#ifndef SRC_HOS_COSGI_SERVICE_RULEENGINE_RULE_RULEPACKAGE_H_ -#define SRC_HOS_COSGI_SERVICE_RULEENGINE_RULE_RULEPACKAGE_H_ - - -#include -#include -#include -#include -#include -#include - - -// Forward declarations -namespace hos { -namespace cosgi { -namespace service { -namespace ruleengine { -class RuleEngineServiceImpl; -class RuleConditionFactory; -namespace rule { -class Rule; -} -} -namespace scripting { -class GlobalScriptContext; -} -} -} -} - - -namespace hos { -namespace cosgi { -namespace service { -namespace ruleengine { -namespace rule { - -using namespace ::libvariant; - -class RulePackage { -private: - typedef std::map> ruleMap_t; - - service::scripting::ScriptingService* executionService = nullptr; - std::shared_ptr globalContext; - ruleMap_t rules; - Variant rawValue; - bool enabled = false; - - -public: - service::ruleengine::RuleEngineServiceImpl* const parent; - const std::string name; - const std::string language; - - static std::shared_ptr createRulePackage(service::ruleengine::RuleEngineServiceImpl* parent, std::string name, Variant raw); - RulePackage(service::ruleengine::RuleEngineServiceImpl* parent, const std::string name, const std::string language, Variant raw); - virtual ~RulePackage(); - - std::shared_ptr getRule(const std::string& name); - size_t getRules(std::vector>& rules); - - bool isEnabled() { return enabled; } - void enable(); - void disable(); - - service::scripting::ScriptingService* getScriptingService() { return executionService; } - std::shared_ptr getGlobalScriptContext() { return globalContext; } - std::shared_ptr getConditionFactory(std::string type); -}; - -} /* namespace rule */ -} /* namespace ruleengine */ -} /* namespace service */ -} /* namespace cosgi */ -} /* namespace hos */ - -#endif /* SRC_HOS_COSGI_SERVICE_RULEENGINE_RULE_RULEPACKAGE_H_ */ diff --git a/src/hos_cosgi_ruleengine/src/hos/cosgi/service/ruleengine/rule/Rule.cpp b/src/hos_cosgi_ruleengine/src/hos/cosgi/service/ruleengine/rules/RuleImpl.cpp similarity index 60% rename from src/hos_cosgi_ruleengine/src/hos/cosgi/service/ruleengine/rule/Rule.cpp rename to src/hos_cosgi_ruleengine/src/hos/cosgi/service/ruleengine/rules/RuleImpl.cpp index ea367f30..665f9b8d 100644 --- a/src/hos_cosgi_ruleengine/src/hos/cosgi/service/ruleengine/rule/Rule.cpp +++ b/src/hos_cosgi_ruleengine/src/hos/cosgi/service/ruleengine/rules/RuleImpl.cpp @@ -6,39 +6,36 @@ */ -#include +#include "../rules/RuleImpl.h" -#include - -#include #include -#include "RulePackage.h" -#include #include +#include +#include +#include #include -#include "../ruleengine_serviceimpl.h" +#include + +#include "../rules/RulePackageImpl.h" + +#include "../RuleEngineServiceImpl.h" namespace hos { namespace cosgi { namespace service { namespace ruleengine { -namespace rule { +namespace rules { -Rule::Rule(RulePackage* parent, std::string name) +RuleImpl::RuleImpl(RulePackageImpl* parent, std::string name) : parent(parent), name(name) {} - -utils::SchemaLoader* Rule::getHOSSchemaLoader() { - return parent->parent->getHOSSchemaLoader(); -} - -std::shared_ptr Rule::createRuleCondition(Variant condition) { - std::shared_ptr retval; +std::shared_ptr RuleImpl::createRuleCondition(const Variant& condition) { + std::shared_ptr retval; if (condition.IsMap() && condition.HasPath("type") && condition["type"].IsString()) { if (condition.HasPath("$schema")) { - SchemaResult sr = SchemaValidate(Variant(), condition, getHOSSchemaLoader()); + SchemaResult sr = SchemaValidate(Variant(), condition, parent->parent->getHOSSchemaLoader()); if (sr.Error()) { throw std::runtime_error(sr.PrettyPrintMessage()); } @@ -60,13 +57,13 @@ std::shared_ptr Rule::createRuleCondition(Variant condition) { return retval; } -std::shared_ptr Rule::createRule(RulePackage* parent, std::string name, Variant raw) { - std::shared_ptr retval; - if (parent->getRule(name)) { +std::shared_ptr RuleImpl::createRule(RulePackageImpl* parent, std::string name, Variant raw) { + std::shared_ptr retval; + if (!parent->getRule(name).expired()) { throw std::runtime_error("Rule with the same name already exists."); } if (raw.IsMap()) { - retval = std::make_shared(parent, name); + retval = std::make_shared(parent, name); if (raw.HasPath("condition") && raw["condition"].IsMap()) { auto condition = raw["condition"]; auto cond = retval->createRuleCondition(condition); @@ -78,7 +75,7 @@ std::shared_ptr Rule::createRule(RulePackage* parent, std::string name, Va auto scriptFactory = parent->getScriptingService(); //ToDo: Create Condition if (raw.HasPath("script") && raw["script"].IsMap()) { - auto script = scriptFactory->createScript(parent->getGlobalScriptContext().get(), raw["script"]); + auto script = scriptFactory->createScript(raw["script"]); if (script) { retval->script = script; } else { @@ -88,7 +85,7 @@ std::shared_ptr Rule::createRule(RulePackage* parent, std::string name, Va throw std::runtime_error("Malformed JSON: missing property script or is not an object."); } if (raw.HasPath("exitscript") && raw["exitscript"].IsMap()) { - auto script = scriptFactory->createScript(parent->getGlobalScriptContext().get(), raw["exitscript"]); + auto script = scriptFactory->createScript(raw["exitscript"]); if (script) { retval->exitScript = script; } else { @@ -105,43 +102,34 @@ std::shared_ptr Rule::createRule(RulePackage* parent, std::string name, Va } -devicerepository::DeviceRepositoryService* Rule::getDeviceRepository() { +devicerepository::DeviceRepositoryService* RuleImpl::getDeviceRepository() noexcept { return parent->parent->getDeviceRepository(); } -void Rule::enable() { - if (!enabled) { +void RuleImpl::setEnabled(bool enabled) noexcept { + if (this->enabled != enabled) { if (condition) { - condition->enable(); + condition->setEnabled(enabled); } - enabled = true; - } -} - -void Rule::disable() { - if (enabled) { - if (condition) { - condition->disable(); - } - enabled = false; + this->enabled = enabled; } } -void Rule::execute(Variant lContext) { +void RuleImpl::executeScript(const libvariant::Variant& lContext) { if (enabled && script) { try { - script->exec(parent->getGlobalScriptContext().get(), lContext); + script->execute(parent->getGlobalScriptContext().lock(), lContext); } catch(std::exception& e) { ROS_ERROR_STREAM(e.what()); } } } -void Rule::executeExitScript(Variant lContext) { +void RuleImpl::executeExitScript(const libvariant::Variant& lContext) { if (enabled && exitScript) { try { - exitScript->exec(parent->getGlobalScriptContext().get(), lContext); + exitScript->execute(parent->getGlobalScriptContext().lock(), lContext); } catch(std::exception& e) { ROS_ERROR_STREAM(e.what()); } @@ -149,13 +137,13 @@ void Rule::executeExitScript(Variant lContext) { } -bool Rule::evaluate() { +bool RuleImpl::evaluate() { if (condition) { bool retval = condition->evaluate(); if (retval != currentState) { currentState = retval; if (retval) { - execute(); + executeScript(); } else { executeExitScript(); } @@ -168,13 +156,13 @@ bool Rule::evaluate() { } -void Rule::conditionStateChanged(bool newState, RuleCondition::Category category, Variant lcontext) { - if (newState && !currentState && category == RuleCondition::Category::Event) { - execute(lcontext); - } else if (category == RuleCondition::Category::State && newState != currentState) { +void RuleImpl::conditionStateChanged(bool newState, const conditions::ConditionTriggerType& category, const Variant& lcontext) noexcept { + if (newState && !currentState && category == conditions::ConditionTriggerType::Event) { + executeScript(lcontext); + } else if (category == conditions::ConditionTriggerType::State && newState != currentState) { currentState = newState; if (newState) { - execute(lcontext); + executeScript(lcontext); } else { executeExitScript(lcontext); } diff --git a/src/hos_cosgi_ruleengine/src/hos/cosgi/service/ruleengine/rules/RuleImpl.h b/src/hos_cosgi_ruleengine/src/hos/cosgi/service/ruleengine/rules/RuleImpl.h new file mode 100644 index 00000000..ba54de4f --- /dev/null +++ b/src/hos_cosgi_ruleengine/src/hos/cosgi/service/ruleengine/rules/RuleImpl.h @@ -0,0 +1,100 @@ + +#pragma once + + + +#include +#include +#include + +#include "RulePackageImpl.h" +#include +#include +#include +#include + + +// Forward declarations +namespace hos { +namespace cosgi { +namespace service { +namespace devicerepository { +class Device; +class DeviceEvent; +class DeviceRepositoryService; +} +namespace ruleengine { +namespace rules { +class RulePackageImpl; +} +} +namespace scripting { +class ScriptingService; +class GlobalScriptContext; +class Script; +} +} +} +namespace utils { +class SchemaLoader; +} +} + + +namespace hos { +namespace cosgi { +namespace service { +namespace ruleengine { +namespace rules { + +using namespace ::libvariant; + +class RuleImpl : public virtual Rule { +private: + std::shared_ptr script; + std::shared_ptr exitScript; + std::shared_ptr condition; + bool enabled = false; + bool currentState = false; + +public: + RulePackageImpl* const parent; + const std::string name; + + RuleImpl(RulePackageImpl* parent, const std::string name); + static std::shared_ptr createRule(RulePackageImpl* parent, const std::string name, Variant raw); + + virtual RulePackage* getParent() noexcept { + return dynamic_cast(parent); + } + + virtual const std::string& getName() noexcept { + return name; + } + + virtual bool isActive() noexcept override { + return currentState; + } + virtual bool evaluate() override; + virtual void executeScript(const libvariant::Variant& lContext = libvariant::Variant()) override; + virtual void executeExitScript(const libvariant::Variant& lContext = libvariant::Variant()) override; + + virtual bool isEnabled() noexcept override { + return enabled; + } + virtual void setEnabled(bool enabled) noexcept override; + + void conditionStateChanged(bool newState, const conditions::ConditionTriggerType& category, const Variant& lcontext = Variant()) noexcept override; + + std::shared_ptr createRuleCondition(const Variant& raw) override; + devicerepository::DeviceRepositoryService* getDeviceRepository() noexcept override; +}; + + + +} /* namespace rules */ +} /* namespace ruleengine */ +} /* namespace service */ +} /* namespace cosgi */ +} /* namespace hos */ + diff --git a/src/hos_cosgi_ruleengine/src/hos/cosgi/service/ruleengine/rule/RulePackage.cpp b/src/hos_cosgi_ruleengine/src/hos/cosgi/service/ruleengine/rules/RulePackageImpl.cpp similarity index 60% rename from src/hos_cosgi_ruleengine/src/hos/cosgi/service/ruleengine/rule/RulePackage.cpp rename to src/hos_cosgi_ruleengine/src/hos/cosgi/service/ruleengine/rules/RulePackageImpl.cpp index aea80299..8d3e16ad 100644 --- a/src/hos_cosgi_ruleengine/src/hos/cosgi/service/ruleengine/rule/RulePackage.cpp +++ b/src/hos_cosgi_ruleengine/src/hos/cosgi/service/ruleengine/rules/RulePackageImpl.cpp @@ -5,29 +5,29 @@ * Author: matzman */ -#include "RulePackage.h" +#include "RuleImpl.h" #include #include #include -#include "../ruleengine_serviceimpl.h" -#include +#include "../RuleEngineServiceImpl.h" +#include "RulePackageImpl.h" namespace hos { namespace cosgi { namespace service { namespace ruleengine { -namespace rule { +namespace rules { -RulePackage::RulePackage(service::ruleengine::RuleEngineServiceImpl* parent, const std::string name, const std::string language, const Variant raw) +RulePackageImpl::RulePackageImpl(service::ruleengine::RuleEngineServiceImpl* parent, const std::string name, const std::string language, const Variant raw) : parent(parent), name(name), language(language), rawValue(raw) {} -RulePackage::~RulePackage() {} +RulePackageImpl::~RulePackageImpl() {} -std::shared_ptr RulePackage::createRulePackage(service::ruleengine::RuleEngineServiceImpl* parent, std::string name, Variant raw) { - std::shared_ptr retval; - if (parent->getRulePackage(name)) { +std::shared_ptr RulePackageImpl::createRulePackage(service::ruleengine::RuleEngineServiceImpl* parent, std::string name, Variant raw) { + std::shared_ptr retval; + if (!parent->getRulePackage(name).expired()) { std::stringstream ss; ss << "Could not create package " << name << ": There already exists a rule package with the same name."; throw std::runtime_error(ss.str()); @@ -39,7 +39,7 @@ std::shared_ptr RulePackage::createRulePackage(service::ruleengine: } else { throw std::runtime_error("Rule package JSON is missing language parameter or parameter has wrong type."); } - retval = std::make_shared(parent, name, language, raw); + retval = std::make_shared(parent, name, language, raw); retval->executionService = parent->getExecutionBackend(retval->language); if (retval->executionService) { if (raw.HasPath("globalcontext")) { @@ -48,7 +48,7 @@ std::shared_ptr RulePackage::createRulePackage(service::ruleengine: if (raw.HasPath("rules") && raw["rules"].IsMap()) { for (auto r : raw["rules"].AsMap()) { try { - std::shared_ptr rule = Rule::createRule(retval.get(), r.first, r.second); + std::shared_ptr rule = RuleImpl::createRule(retval.get(), r.first, r.second); if (rule) { retval->rules.insert({r.first, rule}); } @@ -70,49 +70,39 @@ std::shared_ptr RulePackage::createRulePackage(service::ruleengine: } -std::shared_ptr RulePackage::getRule(const std::string& name) { - std::shared_ptr retval; +std::weak_ptr RulePackageImpl::getRule(const std::string& name) noexcept { + std::weak_ptr retval; try { retval = rules.at(name); } catch (...) {} return retval; } -size_t RulePackage::getRules(std::vector>& rules) { - size_t retval = 0; +std::vector> RulePackageImpl::getRules() noexcept { + std::vector> retval; for (auto item : this->rules) { - rules.push_back(item.second); - retval++; + retval.push_back(dynamic_pointer_cast(item.second)); } return retval; } -std::shared_ptr RulePackage::getConditionFactory(std::string type) { +std::shared_ptr RulePackageImpl::getConditionFactory(std::string type) { return parent->getRuleConditionFactory(type); } -void RulePackage::enable() { - if (!enabled) { +void RulePackageImpl::setEnabled(bool enabled) noexcept { + if (this->enabled != enabled) { for (auto r : rules) { - r.second->enable(); + r.second->setEnabled(enabled); } - enabled = true; + this->enabled = enabled; } } -void RulePackage::disable() { - if (enabled) { - for (auto r : rules) { - r.second->enable(); - } - enabled = false; - } -} - -} /* namespace rule */ +} /* namespace rules */ } /* namespace ruleengine */ } /* namespace service */ } /* namespace cosgi */ diff --git a/src/hos_cosgi_ruleengine/src/hos/cosgi/service/ruleengine/rules/RulePackageImpl.h b/src/hos_cosgi_ruleengine/src/hos/cosgi/service/ruleengine/rules/RulePackageImpl.h new file mode 100644 index 00000000..fc69e5ba --- /dev/null +++ b/src/hos_cosgi_ruleengine/src/hos/cosgi/service/ruleengine/rules/RulePackageImpl.h @@ -0,0 +1,97 @@ + +#pragma once + + + +#include +#include +#include "../RuleEngineServiceImpl.h" +#include +#include +#include +#include +#include + + +// Forward declarations +namespace hos { +namespace cosgi { +namespace service { +namespace ruleengine { +class RuleEngineServiceImpl; +class RuleConditionFactory; +namespace rules { +class RuleImpl; +} +} +namespace scripting { +class GlobalScriptContext; +} +} +} +} + + +namespace hos { +namespace cosgi { +namespace service { +namespace ruleengine { +namespace rules { + +using namespace ::libvariant; + +class RulePackageImpl + : public virtual RulePackage { + +private: + + typedef std::map> ruleMap_t; + + service::scripting::ScriptingService* executionService = nullptr; + std::shared_ptr globalContext; + ruleMap_t rules; + Variant rawValue; + bool enabled = false; + + +public: + + static std::shared_ptr createRulePackage(service::ruleengine::RuleEngineServiceImpl* parent, std::string name, Variant raw); + RulePackageImpl(service::ruleengine::RuleEngineServiceImpl* parent, const std::string name, const std::string language, Variant raw); + virtual ~RulePackageImpl(); + + service::ruleengine::RuleEngineServiceImpl* const parent; + const std::string name; + const std::string language; + + virtual service::ruleengine::RuleEngineService* getParent() noexcept override { + return dynamic_cast(parent); + } + + virtual const std::string& getName() noexcept override { + return name; + }; + virtual const std::string& getLanguage() noexcept override { + return language; + } + + virtual std::weak_ptr getRule(const std::string& name) noexcept override; + virtual std::vector> getRules() noexcept override; + virtual size_t getRuleCount() noexcept override { return rules.size(); } + + virtual bool isEnabled() noexcept override { + return enabled; + } + virtual void setEnabled(bool enabled) noexcept override; + + virtual service::scripting::ScriptingService* getScriptingService() noexcept override { return executionService; } + virtual std::weak_ptr getGlobalScriptContext() noexcept override { return globalContext; } + std::shared_ptr getConditionFactory(std::string type); +}; + +} /* namespace rules */ +} /* namespace ruleengine */ +} /* namespace service */ +} /* namespace cosgi */ +} /* namespace hos */ + diff --git a/src/hos_cosgi_ruleengine/src/hos/cosgi/service/ruleengine/rules/conditions/DeviceMsgCondition.cpp b/src/hos_cosgi_ruleengine/src/hos/cosgi/service/ruleengine/rules/conditions/DeviceMsgCondition.cpp new file mode 100644 index 00000000..12155647 --- /dev/null +++ b/src/hos_cosgi_ruleengine/src/hos/cosgi/service/ruleengine/rules/conditions/DeviceMsgCondition.cpp @@ -0,0 +1,271 @@ + + +#include "DeviceMsgCondition.h" +#include +#include "../RuleImpl.h" +#include + + + +namespace hos { +namespace cosgi { +namespace service { +namespace ruleengine { +namespace rules { +namespace conditions { + + + + +void DeviceMsgCondition::notificationReceived(interface::Observer::observable_type* o, const devicerepository::DeviceEventType& key, const devicerepository::DeviceEvent* e) noexcept { + switch (key) { + case devicerepository::DeviceEventType::DEVICE_ADDED: + if (!device && this->deviceID.compare(e->device->getDeviceID()) == 0) { + ROS_INFO_STREAM("Rule " << parent->getName() << ": Device " << deviceID << " appeared."); + device = e->device; + parent->getDeviceRepository()->deleteObserver(devicerepository::DeviceEventType::DEVICE_ADDED, this); + _registerAsListener(); + } + break; + case devicerepository::DeviceEventType::DEVICE_REMOVED: + if (device && device->getDeviceID().compare(e->device->getDeviceID()) == 0) { + ROS_ERROR_STREAM("Rule " << parent->getName() << ": Device " << deviceID << " disappeared."); + device->deleteObserver(this); + device = nullptr; + parent->getDeviceRepository()->addObserver(devicerepository::DeviceEventType::DEVICE_ADDED, this); + } + break; + default: + break; + } +} + +void DeviceMsgCondition::setEnabled(bool enabled) noexcept { + if (this->enabled != enabled) { + if (enabled) { + auto repoService = parent->getDeviceRepository(); + auto device = repoService->getDevice(deviceID); + if (!device.expired()) { + this->device = device.lock().get(); + _registerAsListener(); + } else { + ROS_ERROR_STREAM("Rule " << parent->getName() << ": Could not find device " << deviceID); + parent->getDeviceRepository()->addObserver(devicerepository::DeviceEventType::DEVICE_ADDED, this); + } + } else { + if (device) { + device->deleteObserver(this); + device = nullptr; + } + parent->getDeviceRepository()->deleteObserver(this); + } + this->enabled = enabled; + } +} + +void DeviceMsgCondition::_registerAsListener() { + if (device) { + for (auto k : _listenerKeys) { + device->addObserver(k, this); + } + } +} + + + + +std::shared_ptr DeviceStatusMsgConditionFactory::createCondition(rules::Rule* parent, const Variant& raw) { + std::shared_ptr retval; + std::string deviceID; + if (raw.HasPath("deviceid") && raw["deviceid"].IsString()) { + deviceID = raw["deviceid"].AsString(); + } else { + throw std::runtime_error("Malformed JSON: missing property deviceid or is not an string."); + } + std::string msgPointer; + if (raw.HasPath("msgpointer")) { + if (raw["msgpointer"].IsString()) { + msgPointer = raw["msgpointer"].AsString(); + } else { + throw std::runtime_error("Malformed JSON: property msgpointer is not an string."); + } + } + Variant msgValue; + if (raw.HasPath("msgvalue")) { + msgValue = raw["msgvalue"]; + } + retval = std::make_shared(parent, deviceID, msgPointer, msgValue); + return retval; +} + +void DeviceStatusMsgCondition::notificationReceived(interface::Observer::observable_type* o, const devicerepository::DeviceEventType& key, const devicerepository::DeviceEvent* e) noexcept { + switch (e->eventType) { + case devicerepository::DeviceEventType::STATUS_UPDATE_RECEIVED: + if (device && device == e->device) { + auto e2 = static_cast(e); + bool notify = true; + Variant value; + if (msgPointer.empty()) { + value = e2->statusMsg; + } else if (e2->statusMsg.HasPath(msgPointer)) { + value = e2->statusMsg.GetPath(msgPointer); + } else { + notify = false; + } + if (notify && !msgValue.IsNull()) { + if (msgValue != value) { + notify = false; + } + } + if (notify) { + Variant lcontext; + lcontext["event_deviceid"] = Variant(e->device->getDeviceID()); + lcontext["event_type"] = Variant("statusmsg"); + lcontext["event_msg"] = e2->statusMsg; + notifyStateChangeToParent(true, ConditionTriggerType::Event, lcontext); + } + } + break; + default: + DeviceMsgCondition::notificationReceived(o, key, e); + break; + } +} + + +std::shared_ptr DeviceCommandMsgConditionFactory::createCondition(rules::Rule* parent, const Variant& raw) { + std::shared_ptr retval; + std::string deviceID; + if (raw.HasPath("deviceid") && raw["deviceid"].IsString()) { + deviceID = raw["deviceid"].AsString(); + } else { + throw std::runtime_error("Malformed JSON: missing property deviceid or is not an string."); + } + std::string msgPointer; + if (raw.HasPath("msgpointer")) { + if (raw["msgpointer"].IsString()) { + msgPointer = raw["msgpointer"].AsString(); + } else { + throw std::runtime_error("Malformed JSON: property msgpointer is not an string."); + } + } + Variant msgValue; + if (raw.HasPath("msgvalue")) { + msgValue = raw["msgvalue"]; + } + retval = std::make_shared(parent, deviceID, msgPointer, msgValue); + return retval; +} + +void DeviceCommandMsgCondition::notificationReceived(interface::Observer::observable_type* o, const devicerepository::DeviceEventType& key, const devicerepository::DeviceEvent* e) noexcept { + switch (e->eventType) { + case devicerepository::DeviceEventType::COMMAND_RECEIVED: + if (device && device == e->device) { + auto e2 = static_cast(e); + bool notify = true; + Variant value; + if (msgPointer.empty()) { + value = e2->cmdMsg; + } else if (e2->cmdMsg.HasPath(msgPointer)) { + value = e2->cmdMsg.GetPath(msgPointer); + } else { + notify = false; + } + if (notify && !msgValue.IsNull()) { + if (msgValue != value) { + notify = false; + } + } + if (notify) { + Variant lcontext; + lcontext["event_deviceid"] = Variant(e->device->getDeviceID()); + lcontext["event_type"] = Variant("commandmsg"); + lcontext["event_msg"] = e2->cmdMsg; + notifyStateChangeToParent(true, ConditionTriggerType::Event, lcontext); + } + } + break; + default: + DeviceMsgCondition::notificationReceived(o, key, e); + break; + } +} + + +std::shared_ptr DeviceStateChangeConditionFactory::createCondition(rules::Rule* parent, const Variant& raw) { + std::shared_ptr retval; + std::string deviceID; + if (raw.HasPath("deviceid") && raw["deviceid"].IsString()) { + deviceID = raw["deviceid"].AsString(); + } else { + throw std::runtime_error("Malformed JSON: missing property deviceid or is not an string."); + } + std::string statePointer; + if (raw.HasPath("msgpointer")) { + if (raw["msgpointer"].IsString()) { + statePointer = raw["msgpointer"].AsString(); + } else { + throw std::runtime_error("Malformed JSON: property msgpointer is not an string."); + } + } + Variant fromValue; + if (raw.HasPath("fromvalue")) { + fromValue = raw["fromvalue"]; + } + Variant toValue; + if (raw.HasPath("tovalue")) { + toValue = raw["tovalue"]; + } + retval = std::make_shared(parent, deviceID, statePointer, fromValue, toValue); + return retval; +} + + +void DeviceStateChangeCondition::notificationReceived(interface::Observer::observable_type* o, const devicerepository::DeviceEventType& key, const devicerepository::DeviceEvent* e) noexcept { + switch (e->eventType) { + case devicerepository::DeviceEventType::STATE_CHANGED: + if (device && device == e->device) { + auto e2 = static_cast(e); + bool notify = true; + Variant current, last; + if (msgPointer.empty()) { + current = device->getCurrentState(); + last = device->getLastState(); + } else if (device->getCurrentState().HasPath(msgPointer) && device->getLastState().HasPath(msgPointer)) { + current = device->getCurrentState().GetPath(msgPointer); + last = device->getLastState().GetPath(msgPointer); + } else { + notify = false; + } + if (notify) { + if (current == last) { + notify = false; + } else if (!fromValue.IsNull() && fromValue != last) { + notify = false; + } else if (!msgValue.IsNull() && msgValue != current) { + notify = false; + } + } + if (notify) { + Variant lcontext; + lcontext["event_deviceid"] = Variant(e->device->getDeviceID()); + lcontext["event_type"] = Variant("statechange"); + lcontext["event_msg"] = e2->statusMsg; + notifyStateChangeToParent(true, ConditionTriggerType::Event, lcontext); + } + } + break; + default: + DeviceMsgCondition::notificationReceived(o, key, e); + break; + } +} + + +} /* namespace conditions */ +} /* namespace rules */ +} /* namespace ruleengine */ +} /* namespace service */ +} /* namespace cosgi */ +} /* namespace hos */ + diff --git a/src/hos_cosgi_ruleengine/src/hos/cosgi/service/ruleengine/rules/conditions/DeviceMsgCondition.h b/src/hos_cosgi_ruleengine/src/hos/cosgi/service/ruleengine/rules/conditions/DeviceMsgCondition.h new file mode 100644 index 00000000..cf7054c2 --- /dev/null +++ b/src/hos_cosgi_ruleengine/src/hos/cosgi/service/ruleengine/rules/conditions/DeviceMsgCondition.h @@ -0,0 +1,140 @@ + +#pragma once + + +#include +#include +#include +#include + + +// main Namespace +namespace hos { +namespace cosgi { +namespace service { +namespace ruleengine { +namespace rules { +namespace conditions { + + +using namespace libvariant; + + + +class DeviceMsgCondition + : public RuleConditionImpl, + public virtual interface::Observer { + +protected: + const std::string deviceID; + devicerepository::Device* device = nullptr; + const std::string msgPointer; + const Variant msgValue; + std::set _listenerKeys; + + virtual void _registerAsListener(); + +public: + DeviceMsgCondition( Rule* parent, + ConditionTriggerType category, + const std::string& type, + const std::string& deviceID, + const std::string& msgPointer, + const Variant& msgValue, + const std::set _listenerKeys ) + : RuleConditionImpl(parent, category, type), deviceID(deviceID), + msgPointer(msgPointer), msgValue(msgValue), _listenerKeys(_listenerKeys) {} + + virtual void notificationReceived( interface::Observer::observable_type* o, + const devicerepository::DeviceEventType& key, + const devicerepository::DeviceEvent* e) noexcept override; + + virtual void setEnabled(bool enabled) noexcept override; +}; + + + +class DeviceStatusMsgCondition + : public DeviceMsgCondition { + +public: + DeviceStatusMsgCondition( Rule* parent, + const std::string& deviceID, + const std::string& msgPointer, + const Variant& msgValue) + : DeviceMsgCondition(parent, ConditionTriggerType::Event, "devicestatusmsg", deviceID, + msgPointer, msgValue, {devicerepository::DeviceEventType::STATUS_UPDATE_RECEIVED}) {} + + virtual void notificationReceived( interface::Observer::observable_type* o, + const devicerepository::DeviceEventType& key, + const devicerepository::DeviceEvent* e ) noexcept override; +}; + + + +class DeviceStatusMsgConditionFactory + : public RuleConditionFactory { +public: + virtual std::shared_ptr createCondition(rules::Rule* parent, const Variant& raw) override; +}; + + + +class DeviceCommandMsgCondition + : public DeviceMsgCondition { + +public: + DeviceCommandMsgCondition(Rule* parent, const std::string deviceID, const std::string msgPointer, const Variant msgValue) + : DeviceMsgCondition(parent, ConditionTriggerType::Event, "devicecommandmsg", deviceID, + msgPointer, msgValue, {devicerepository::DeviceEventType::COMMAND_RECEIVED}) {} + + virtual void notificationReceived( interface::Observer::observable_type* o, + const devicerepository::DeviceEventType& key, + const devicerepository::DeviceEvent* e ) noexcept override; +}; + + +class DeviceCommandMsgConditionFactory + : public RuleConditionFactory { +public: + virtual std::shared_ptr createCondition(rules::Rule* parent, const Variant& raw) override; +}; + + + +class DeviceStateChangeCondition + : public DeviceMsgCondition { + +protected: + const Variant fromValue; + +public: + DeviceStateChangeCondition( Rule* parent, + const std::string deviceID, + const std::string statePointer, + const Variant fromValue, + const Variant toValue ) + : DeviceMsgCondition(parent, ConditionTriggerType::Event, "devicestatechange", deviceID, statePointer, + toValue, {devicerepository::DeviceEventType::STATE_CHANGED}), fromValue(fromValue) {} + + virtual void notificationReceived(interface::Observer::observable_type* o, const devicerepository::DeviceEventType& key, const devicerepository::DeviceEvent* e) noexcept override; +}; + + +class DeviceStateChangeConditionFactory + : public RuleConditionFactory { +public: + virtual std::shared_ptr createCondition(rules::Rule* parent, const Variant& raw) override; +}; + + + +} /* namespace conditions */ +} /* namespace rules */ +} /* namespace ruleengine */ +} /* namespace service */ +} /* namespace cosgi */ +} /* namespace hos */ diff --git a/src/hos_cosgi_ruleengine/src/hos/cosgi/service/ruleengine/rules/conditions/LogicalAndCondition.cpp b/src/hos_cosgi_ruleengine/src/hos/cosgi/service/ruleengine/rules/conditions/LogicalAndCondition.cpp new file mode 100644 index 00000000..e9c928be --- /dev/null +++ b/src/hos_cosgi_ruleengine/src/hos/cosgi/service/ruleengine/rules/conditions/LogicalAndCondition.cpp @@ -0,0 +1,104 @@ + +#include "LogicalAndCondition.h" +#include + + + +namespace hos { +namespace cosgi { +namespace service { +namespace ruleengine { +namespace rules { +namespace conditions { + + + +std::shared_ptr LogicalAndConditionFactory::createCondition(rules::Rule* parent, const Variant& raw) { + std::shared_ptr retval; + if (raw.HasPath("conditions") && raw["conditions"].IsList() && raw["conditions"].Size() > 0) { + retval = std::make_shared(parent); + for (auto c : raw["conditions"].AsList()) { + auto cond = parent->createRuleCondition(c); + if (cond) { + retval->addChild(cond); + } else { + throw std::runtime_error("Could not create condition."); + } + } + } else { + throw std::runtime_error("Malformed JSON: missing property condition or is not a list."); + } + return std::static_pointer_cast(retval); +} + + + +void LogicalAndCondition::addChild(std::shared_ptr child) { + if (child->getTriggerType() == ConditionTriggerType::Event) { + if (eventChildrenCount > 0) { + throw new std::runtime_error("Cannot have an and condition with more than two event conditions."); + } + eventChildrenCount++; + } else if (child->getTriggerType() == ConditionTriggerType::State) { + stateChildrenCount++; + } else { + mixedChildrenCount++; + } + if (mixedChildrenCount > 0 || (eventChildrenCount > 0 && stateChildrenCount > 0)) { + triggerType = ConditionTriggerType::Both; + } else if (eventChildrenCount > 0 && stateChildrenCount == 0) { + triggerType = ConditionTriggerType::Event; + } else { + triggerType = ConditionTriggerType::State; + } + RuleCompositeConditionImpl::addChild(child); +} + + + +void LogicalAndCondition::childStateChanged(RuleCondition* child, bool newState, ConditionTriggerType category, const Variant& lcontext) noexcept { + if (newState && category == ConditionTriggerType::Event && !currentState && trueChildrenCount >= children.size()-1) { + notifyStateChangeToParent(true, category); + } else if (category == ConditionTriggerType::State) { + if (newState) { + trueChildrenCount++; + if (!currentState && trueChildrenCount >= children.size()) { + currentState = true; + notifyStateChangeToParent(true, category, lcontext); + } + } else { + trueChildrenCount--; + if (currentState) { + currentState = false; + notifyStateChangeToParent(false, category, lcontext); + } + } + } +} + + + +bool LogicalAndCondition::evaluate() noexcept { + // Event conditions always evaluate to false + if (triggerType == ConditionTriggerType::Event) { + return false; + } + trueChildrenCount = 0; + for (auto c : children) { + if (c->evaluate()) { + trueChildrenCount++; + } + } + currentState = trueChildrenCount == children.size(); + return currentState; +} + + + +} /* namespace conditions */ +} /* namespace rules */ +} /* namespace ruleengine */ +} /* namespace service */ +} /* namespace cosgi */ +} /* namespace hos */ + diff --git a/src/hos_cosgi_ruleengine/src/hos/cosgi/service/ruleengine/rules/conditions/LogicalAndCondition.h b/src/hos_cosgi_ruleengine/src/hos/cosgi/service/ruleengine/rules/conditions/LogicalAndCondition.h new file mode 100644 index 00000000..4aff47c5 --- /dev/null +++ b/src/hos_cosgi_ruleengine/src/hos/cosgi/service/ruleengine/rules/conditions/LogicalAndCondition.h @@ -0,0 +1,57 @@ + +#pragma once + + +#include +#include + + +// Service Namespace +namespace hos { +namespace cosgi { +namespace service { +namespace ruleengine { +namespace rules { +namespace conditions { + +using namespace libvariant; + + + +class LogicalAndCondition + : public RuleCompositeConditionImpl { + +protected: + unsigned eventChildrenCount = 0; + unsigned stateChildrenCount = 0; + unsigned mixedChildrenCount = 0; + unsigned trueChildrenCount = 0; + +public: + LogicalAndCondition(Rule* parent) : + RuleCompositeConditionImpl(parent, ConditionTriggerType::Both, "logical_and") {} + + virtual void addChild(std::shared_ptr child) override; + + virtual void childStateChanged(RuleCondition* child, bool newState, ConditionTriggerType category, + const Variant& lcontext = Variant()) noexcept override; + + virtual bool evaluate() noexcept override; +}; + + + +class LogicalAndConditionFactory + : public RuleConditionFactory { +public: + virtual std::shared_ptr createCondition(rules::Rule* parent, const Variant& raw) override; +}; + + + +} /* namespace conditions */ +} /* namespace rules */ +} /* namespace ruleengine */ +} /* namespace service */ +} /* namespace cosgi */ +} /* namespace hos */ diff --git a/src/hos_cosgi_ruleengine/src/hos/cosgi/service/ruleengine/rules/conditions/LogicalOrCondition.cpp b/src/hos_cosgi_ruleengine/src/hos/cosgi/service/ruleengine/rules/conditions/LogicalOrCondition.cpp new file mode 100644 index 00000000..1f0e4cf1 --- /dev/null +++ b/src/hos_cosgi_ruleengine/src/hos/cosgi/service/ruleengine/rules/conditions/LogicalOrCondition.cpp @@ -0,0 +1,101 @@ + +#include "LogicalOrCondition.h" +#include + + +namespace hos { +namespace cosgi { +namespace service { +namespace ruleengine { +namespace rules { +namespace conditions { + + + +std::shared_ptr LogicalOrConditionFactory::createCondition(rules::Rule* parent, const Variant& raw) { + std::shared_ptr retval; + if (raw.HasPath("conditions") && raw["conditions"].IsList() && raw["conditions"].Size() > 0) { + retval = std::make_shared(parent); + for (auto c : raw["conditions"].AsList()) { + auto cond = parent->createRuleCondition(c); + if (cond) { + retval->addChild(cond); + } else { + throw std::runtime_error("Could not create condition."); + } + } + } else { + throw std::runtime_error("Malformed JSON: missing property condition or is not a list."); + } + return std::static_pointer_cast(retval); +} + + + +void LogicalOrCondition::addChild(std::shared_ptr child) { + if (child->getTriggerType() == ConditionTriggerType::Event) { + eventChildrenCount++; + } else if (child->getTriggerType() == ConditionTriggerType::State) { + stateChildrenCount++; + } else { + mixedChildrenCount++; + } + if (mixedChildrenCount > 0 || (eventChildrenCount > 0 && stateChildrenCount > 0)) { + triggerType = ConditionTriggerType::Both; + } else if (eventChildrenCount > 0 && stateChildrenCount == 0) { + triggerType = ConditionTriggerType::Event; + } else { + triggerType = ConditionTriggerType::State; + } + RuleCompositeConditionImpl::addChild(child); +} + + + +void LogicalOrCondition::childStateChanged(RuleCondition* child, bool newState, + ConditionTriggerType category, const Variant& lcontext) noexcept { + if (newState && category == ConditionTriggerType::Event && !currentState) { + notifyStateChangeToParent(true, category, lcontext); + } else if (category == ConditionTriggerType::State) { + if (newState) { + trueChildrenCount++; + if (!currentState) { + currentState = true; + notifyStateChangeToParent(true, category, lcontext); + } + } else { + trueChildrenCount--; + if (currentState && trueChildrenCount <= 0) { + currentState = false; + notifyStateChangeToParent(false, category, lcontext); + } + } + } +} + + + +bool LogicalOrCondition::evaluate() noexcept { + // Event conditions always evaluate to false + if (triggerType == ConditionTriggerType::Event) { + return false; + } + trueChildrenCount = 0; + for (auto c : children) { + if (c->evaluate()) { + trueChildrenCount++; + } + } + currentState = trueChildrenCount > 0; + return currentState; +} + + + +} /* namespace conditions */ +} /* namespace rules */ +} /* namespace ruleengine */ +} /* namespace service */ +} /* namespace cosgi */ +} /* namespace hos */ + diff --git a/src/hos_cosgi_ruleengine/src/hos/cosgi/service/ruleengine/rules/conditions/LogicalOrCondition.h b/src/hos_cosgi_ruleengine/src/hos/cosgi/service/ruleengine/rules/conditions/LogicalOrCondition.h new file mode 100644 index 00000000..cd545d02 --- /dev/null +++ b/src/hos_cosgi_ruleengine/src/hos/cosgi/service/ruleengine/rules/conditions/LogicalOrCondition.h @@ -0,0 +1,57 @@ + +#pragma once + + +#include +#include + + +// Service Namespace +namespace hos { +namespace cosgi { +namespace service { +namespace ruleengine { +namespace rules { +namespace conditions { + +using namespace libvariant; + + + +class LogicalOrCondition + : public RuleCompositeConditionImpl { + +protected: + unsigned eventChildrenCount = 0; + unsigned stateChildrenCount = 0; + unsigned mixedChildrenCount = 0; + unsigned trueChildrenCount = 0; + +public: + LogicalOrCondition(Rule* parent) : + RuleCompositeConditionImpl(parent, ConditionTriggerType::Both, "logical_or") {} + + virtual void addChild(std::shared_ptr child) override; + virtual void childStateChanged(RuleCondition* child, bool newState, ConditionTriggerType category, + const libvariant::Variant& lcontext = libvariant::Variant()) noexcept override; + virtual bool evaluate() noexcept override; + +}; + + + +class LogicalOrConditionFactory + : public RuleConditionFactory { +public: + virtual std::shared_ptr createCondition(rules::Rule* parent, const Variant& raw) override; +}; + + + +} /* namespace conditions */ +} /* namespace rules */ +} /* namespace ruleengine */ +} /* namespace service */ +} /* namespace cosgi */ +} /* namespace hos */ + diff --git a/src/hos_cosgi_ruleengine/src/hos/cosgi/service/ruleengine/rules/conditions/RuleConditionImpl.cpp b/src/hos_cosgi_ruleengine/src/hos/cosgi/service/ruleengine/rules/conditions/RuleConditionImpl.cpp new file mode 100644 index 00000000..50a0b337 --- /dev/null +++ b/src/hos_cosgi_ruleengine/src/hos/cosgi/service/ruleengine/rules/conditions/RuleConditionImpl.cpp @@ -0,0 +1,32 @@ + +#include +#include "../RuleImpl.h" + + + +namespace hos { +namespace cosgi { +namespace service { +namespace ruleengine { +namespace rules { +namespace conditions { + + + +void RuleConditionImpl::notifyStateChangeToParent(bool newState, ConditionTriggerType category, const libvariant::Variant& lcontext) { + if (compositeParent) { + compositeParent->childStateChanged(this, newState, category, lcontext); + } else { + parent->conditionStateChanged(newState, category, lcontext); + } +} + + + +} /* namespace conditions */ +} /* namespace rules */ +} /* namespace ruleengine */ +} /* namespace service */ +} /* namespace cosgi */ +} /* namespace hos */ + diff --git a/src/hos_cosgi_scripting/CMakeLists.txt b/src/hos_cosgi_scripting/CMakeLists.txt index d4fff600..95eb45ae 100644 --- a/src/hos_cosgi_scripting/CMakeLists.txt +++ b/src/hos_cosgi_scripting/CMakeLists.txt @@ -93,6 +93,12 @@ catkin_package( ## Build ## ########### +include_directories( + include/${PROJECT_NAME}/ + ${catkin_INCLUDE_DIRS} + ${libvariant_INCLUDE_DIR} +) + ## Specify additional locations of header files ## Your package locations should be listed before other locations # include_directories(include) diff --git a/src/hos_cosgi_scripting/include/hos_cosgi_scripting/hos/cosgi/service/scripting/ScriptingService.h b/src/hos_cosgi_scripting/include/hos_cosgi_scripting/hos/cosgi/service/scripting/ScriptingService.h new file mode 100644 index 00000000..e7f73239 --- /dev/null +++ b/src/hos_cosgi_scripting/include/hos_cosgi_scripting/hos/cosgi/service/scripting/ScriptingService.h @@ -0,0 +1,107 @@ + +#pragma once + + + +#include +#include +#include + + + +// Service Namespace +namespace hos { +namespace cosgi { +namespace service { +namespace scripting { + + + +/** + * Public COSGI interface of an abstract global script context. + * + * It persists the global context across script executions (to implement global variables). + * + * Currently it is just an opaque handle that is passed to the script execution handle. + */ +class GlobalScriptContext + : public virtual interface::Object {}; + + + +/** + * Public COSGI interface of an abstract script object. + * + * This object is supposed to be stateless, every state is carried in the global script context. + */ +class Script + : public virtual interface::Object { + +public: + + /** + * Execute the script object with the given contexts. + * + * May return exceptions derived from std::exception. + * + * \param[in] gContext reference to global context. + * \param[in] lContext map with local variables. + */ + virtual void execute( std::shared_ptr gContext = std::shared_ptr(), + const libvariant::Variant& lContext = libvariant::Variant() ) = 0; +}; + + + +/** + * Public COSGI interface of a scripting service. + * + * It offers following functionality: + * - Create scripts and related objects. + */ +class ScriptingService + : public virtual interface::Service { + +public: + + /** + * Common name for every scripting service implementation. + */ + static constexpr const char* SERVICE_NAME = "hos_scripting_service"; + + /** + * Returns an unique identifier indicating the scripting language. + * + * \return unique language identifier. + */ + virtual const char* getLanguage() noexcept = 0; + + /** + * Creates and returns a global context object from the given json global context config. + * + * May return exceptions derived from std::exception. + * + * \param raw json config. + * \return global context object. + */ + virtual std::shared_ptr createGlobalScriptContext(const libvariant::Variant& raw) = 0; + + /** + * Creates and returns a script object from the given json script config. + * + * May return exceptions derived from std::exception. + * + * \param raw json config. + * \return global context object. + */ + virtual std::shared_ptr