This commit is contained in:
2015-05-24 01:08:07 +02:00
parent 3626b45dfc
commit 13c182c82b
49 changed files with 2675 additions and 1546 deletions
+8 -5
View File
@@ -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}
@@ -0,0 +1,104 @@
#pragma once
#include <hos/cosgi/service/Interface.h>
#include <string>
#include <vector>
#include <memory>
// 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<rules::conditions::RuleCondition> 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<rules::RulePackage> getRulePackage(const std::string& name) noexcept = 0;
/**
* Returns a vector containing all known rulepackages.
*
* \return vector of weak references.
*/
virtual std::vector<std::weak_ptr<rules::RulePackage>> 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<RuleConditionFactory> factory) noexcept = 0;
};
} // namespace ruleengine
} // namespace service
} // namespace cosgi
} // namespace hos
@@ -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 <string>
#include <memory>
#include <vector>
#include <Variant/Variant.h>
#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<scripting::Script> script;
std::shared_ptr<scripting::Script> exitScript;
std::shared_ptr<RuleCondition> 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<Rule> 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<RuleCondition> 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_ */
@@ -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 <memory>
#include <vector>
#include <Variant/Variant.h>
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<std::shared_ptr<RuleCondition>> 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<RuleCondition> 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_ */
@@ -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 <hos/cosgi/service/Interface.h>
#include <memory>
#include <Variant/Variant.h>
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<rule::RuleCondition> 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<RuleConditionFactory> factory) = 0;
};
} // namespace ruleengine
} // namespace service
} // namespace cosgi
} // namespace hos
#endif /* INCLUDE_HOS_COSGI_RULEENGINE_HOS_COSGI_SERVICE_RULEENGINE_RULEENGINE_SERVICE_H_ */
@@ -0,0 +1,145 @@
#pragma once
#include <hos/cosgi/service/Interface.h>
#include <Variant/Variant.h>
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<conditions::RuleCondition> 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 */
@@ -0,0 +1,127 @@
#pragma once
#include <hos/cosgi/service/Interface.h>
#include <vector>
#include <memory>
#include <string>
// 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<Rule> getRule(const std::string& name) noexcept = 0;
/**
* Returns all rules contained within the rule package
*
* \return vector of weak references.
*/
virtual std::vector<std::weak_ptr<Rule>> 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<scripting::GlobalScriptContext> getGlobalScriptContext() noexcept = 0;
};
} /* namespace rules */
} /* namespace ruleengine */
} /* namespace service */
} /* namespace cosgi */
} /* namespace hos */
@@ -0,0 +1,163 @@
#pragma once
#include <hos/cosgi/service/Interface.h>
#include <memory>
#include <vector>
#include <Variant/Variant.h>
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<RuleCondition> child) = 0;
};
} /* namespace conditions */
} /* namespace rules */
} /* namespace ruleengine */
} /* namespace service */
} /* namespace cosgi */
} /* namespace hos */
@@ -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<std::shared_ptr<RuleCondition>> 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<RuleCondition> 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 */
@@ -21,7 +21,7 @@ extern "C" {
}
#include <hos/cosgi/service/core/CoreService.h>
#include "hos/cosgi/service/ruleengine/ruleengine_serviceimpl.h"
#include "hos/cosgi/service/ruleengine/RuleEngineServiceImpl.h"
#include <memory>
#include <mutex>
@@ -5,7 +5,6 @@
* Author: matzman
*/
#include "ruleengine_serviceimpl.h"
#include <ros/ros.h>
#include <exception>
#include <iostream>
@@ -16,8 +15,12 @@
#include <hos/cosgi/service/devicerepository/DeviceRepositoryService.h>
#include <Variant/Variant.h>
#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<rule::LogicalOrConditionFactory>());
registerRuleConditionFactory("logical_and", std::make_shared<rule::LogicalAndConditionFactory>());
registerRuleConditionFactory("devicestatusmsg", std::make_shared<rule::DeviceStatusMsgConditionFactory>());
registerRuleConditionFactory("devicecommandmsg", std::make_shared<rule::DeviceCommandMsgConditionFactory>());
registerRuleConditionFactory("devicestatechange", std::make_shared<rule::DeviceStateChangeConditionFactory>());
registerRuleConditionFactory("logical_or", std::make_shared<rules::conditions::LogicalOrConditionFactory>());
registerRuleConditionFactory("logical_and", std::make_shared<rules::conditions::LogicalAndConditionFactory>());
registerRuleConditionFactory("devicestatusmsg", std::make_shared<rules::conditions::DeviceStatusMsgConditionFactory>());
registerRuleConditionFactory("devicecommandmsg", std::make_shared<rules::conditions::DeviceCommandMsgConditionFactory>());
registerRuleConditionFactory("devicestatechange", std::make_shared<rules::conditions::DeviceStateChangeConditionFactory>());
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<rule::RulePackage> pkg = rule::RulePackage::createRulePackage(this, name, m);
std::shared_ptr<rules::RulePackageImpl> 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<RuleConditionFactory> factory) {
void RuleEngineServiceImpl::registerRuleConditionFactory(const std::string& type, std::shared_ptr<RuleConditionFactory> factory) noexcept {
condFactories.insert({type, factory});
}
std::shared_ptr<rule::RulePackage> RuleEngineServiceImpl::getRulePackage(const std::string& name) {
std::shared_ptr<rule::RulePackage> retval;
std::weak_ptr<rules::RulePackage> RuleEngineServiceImpl::getRulePackage(const std::string& name) noexcept {
std::weak_ptr<rules::RulePackage> 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<rule::RulePackage> RuleEngineServiceImpl::getRulePackage(const s
return retval;
}
std::vector<std::weak_ptr<rules::RulePackage>> RuleEngineServiceImpl::getRulePackages() noexcept {
}
size_t RuleEngineServiceImpl::getRulePackageCount() noexcept {
}
} // namespace ruleengine
} // namespace service
} // namespace cosgi
@@ -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 <mutex>
#include <map>
@@ -15,10 +10,10 @@
#include <hos_core_msgs/DeviceStatusUpdate.h>
#include <hos_core_msgs/HosStatusUpdate.h>
#include <hos/utils/SchemaLoader.h>
#include <hos/cosgi/service/ruleengine/ruleengine_service.h>
#include <hos/cosgi/service/scripting/scripting_service.h>
#include <hos/cosgi/service/devicerepository/DeviceRepositoryService.h>
#include <hos/cosgi/service/Implementation.h>
#include <hos/cosgi/service/ruleengine/RuleEngineService.h>
#include <hos/cosgi/service/scripting/ScriptingService.h>
#include <ros/ros.h>
#include <memory>
#include <string>
@@ -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<RuleEngineServiceImpl> {
private:
typedef std::map<std::string, std::shared_ptr<rule::RulePackage>> rulePkgMap_t;
typedef std::map<std::string, std::shared_ptr<rules::RulePackageImpl>> rulePkgMap_t;
typedef std::map<std::string, scripting::ScriptingService*> execServiceMap_t;
typedef std::map<std::string, std::shared_ptr<RuleConditionFactory>> 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<RuleConditionFactory> factory) override;
virtual void registerRuleConditionFactory(const std::string& type, std::shared_ptr<RuleConditionFactory> factory) noexcept override;
virtual std::weak_ptr<rules::RulePackage> getRulePackage(const std::string& name) noexcept override;
virtual std::vector<std::weak_ptr<rules::RulePackage>> 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<RuleConditionFactory> getRuleConditionFactory(std::string type) { return condFactories[type]; }
std::shared_ptr<RuleConditionFactory> 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<rule::RulePackage> getRulePackage(const std::string& name);
};
} // namespace ruleengine
} // namespace service
} // namespace cosgi
} // namespace hos
#endif /* INCLUDE_HOS_COSGI_RULEENGINE_HOS_RULEENGINE_SERVICE_IMPL_H_ */
@@ -1,432 +0,0 @@
/*
* RuleCondition.cpp
*
* Created on: 29.04.2015
* Author: matzman
*/
#include "RuleConditionImpl.h"
#include <hos/cosgi/service/ruleengine/rule/Rule.h>
#include <hos/cosgi/service/devicerepository/DeviceRepositoryService.h>
#include <ros/ros.h>
namespace hos {
namespace cosgi {
namespace service {
namespace ruleengine {
namespace rule {
std::shared_ptr<rule::RuleCondition> LogicalOrConditionFactory::createCondition(rule::Rule* parent, Variant raw) {
std::shared_ptr<rule::LogicalOrCondition> retval;
if (raw.HasPath("conditions") && raw["conditions"].IsList() && raw["conditions"].Size() > 0) {
retval = std::make_shared<rule::LogicalOrCondition>(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<rule::RuleCondition>(retval);
}
std::shared_ptr<rule::RuleCondition> LogicalAndConditionFactory::createCondition(rule::Rule* parent, Variant raw) {
std::shared_ptr<rule::LogicalAndCondition> retval;
if (raw.HasPath("conditions") && raw["conditions"].IsList() && raw["conditions"].Size() > 0) {
retval = std::make_shared<rule::LogicalAndCondition>(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<rule::RuleCondition>(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<RuleCondition> 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<RuleCondition> 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<devicerepository::DeviceEvent::EventType, devicerepository::DeviceEvent>::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<rule::RuleCondition> DeviceStatusMsgConditionFactory::createCondition(rule::Rule* parent, Variant raw) {
std::shared_ptr<rule::DeviceStatusMsgCondition> 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<rule::DeviceStatusMsgCondition>(parent, deviceID, msgPointer, msgValue);
return retval;
}
void DeviceStatusMsgCondition::notificationReceived(interface::Observer<devicerepository::DeviceEvent::EventType, devicerepository::DeviceEvent>::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<const devicerepository::DeviceStatusMsgEvent*>(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<rule::RuleCondition> DeviceCommandMsgConditionFactory::createCondition(rule::Rule* parent, Variant raw) {
std::shared_ptr<rule::DeviceCommandMsgCondition> 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<rule::DeviceCommandMsgCondition>(parent, deviceID, msgPointer, msgValue);
return retval;
}
void DeviceCommandMsgCondition::notificationReceived(interface::Observer<devicerepository::DeviceEvent::EventType, devicerepository::DeviceEvent>::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<const devicerepository::DeviceCmdMsgEvent*>(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<rule::RuleCondition> DeviceStateChangeConditionFactory::createCondition(rule::Rule* parent, Variant raw) {
std::shared_ptr<rule::DeviceStateChangeCondition> 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<DeviceStateChangeCondition>(parent, deviceID, statePointer, fromValue, toValue);
return retval;
}
void DeviceStateChangeCondition::notificationReceived(interface::Observer<devicerepository::DeviceEvent::EventType, devicerepository::DeviceEvent>::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<const devicerepository::DeviceStatusMsgEvent*>(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 */
@@ -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 <hos/cosgi/service/ruleengine/rule/RuleCondition.h>
#include <hos/cosgi/service/ruleengine/ruleengine_service.h>
#include <hos/cosgi/service/devicerepository/DeviceRepositoryService.h>
#include <set>
// 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<RuleCondition> 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<rule::RuleCondition> 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<RuleCondition> 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<rule::RuleCondition> createCondition(rule::Rule* parent, ::libvariant::Variant raw) override;
};
class DeviceMsgCondition : public RuleCondition, public virtual interface::Observer<devicerepository::DeviceEvent::EventType, devicerepository::DeviceEvent> {
protected:
const std::string deviceID;
devicerepository::Device* device = nullptr;
const std::string msgPointer;
const Variant msgValue;
std::set<devicerepository::DeviceEvent::EventType> _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<devicerepository::DeviceEvent::EventType> _listenerKeys) :
RuleCondition(parent, category, type), deviceID(deviceID), msgPointer(msgPointer), msgValue(msgValue), _listenerKeys(_listenerKeys) {}
virtual void notificationReceived(interface::Observer<devicerepository::DeviceEvent::EventType, devicerepository::DeviceEvent>::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<devicerepository::DeviceEvent::EventType, devicerepository::DeviceEvent>::observable_type* o, const devicerepository::DeviceEvent::EventType& key, const devicerepository::DeviceEvent* e) noexcept override;
};
class DeviceStatusMsgConditionFactory : public virtual RuleConditionFactory {
public:
virtual std::shared_ptr<rule::RuleCondition> 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<devicerepository::DeviceEvent::EventType, devicerepository::DeviceEvent>::observable_type* o, const devicerepository::DeviceEvent::EventType& key, const devicerepository::DeviceEvent* e) noexcept override;
};
class DeviceCommandMsgConditionFactory : public virtual RuleConditionFactory {
public:
virtual std::shared_ptr<rule::RuleCondition> 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<devicerepository::DeviceEvent::EventType, devicerepository::DeviceEvent>::observable_type* o, const devicerepository::DeviceEvent::EventType& key, const devicerepository::DeviceEvent* e) noexcept override;
};
class DeviceStateChangeConditionFactory : public virtual RuleConditionFactory {
public:
virtual std::shared_ptr<rule::RuleCondition> 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_ */
@@ -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 <hos/cosgi/service/scripting/scripting_service.h>
#include <vector>
#include <map>
#include <memory>
#include <string>
#include <Variant/Variant.h>
// 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<std::string, std::shared_ptr<Rule>> ruleMap_t;
service::scripting::ScriptingService* executionService = nullptr;
std::shared_ptr<scripting::GlobalScriptContext> 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<RulePackage> 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<Rule> getRule(const std::string& name);
size_t getRules(std::vector<std::shared_ptr<Rule>>& rules);
bool isEnabled() { return enabled; }
void enable();
void disable();
service::scripting::ScriptingService* getScriptingService() { return executionService; }
std::shared_ptr<scripting::GlobalScriptContext> getGlobalScriptContext() { return globalContext; }
std::shared_ptr<service::ruleengine::RuleConditionFactory> getConditionFactory(std::string type);
};
} /* namespace rule */
} /* namespace ruleengine */
} /* namespace service */
} /* namespace cosgi */
} /* namespace hos */
#endif /* SRC_HOS_COSGI_SERVICE_RULEENGINE_RULE_RULEPACKAGE_H_ */
@@ -6,39 +6,36 @@
*/
#include <hos/cosgi/service/ruleengine/rule/Rule.h>
#include "../rules/RuleImpl.h"
#include <hos/cosgi/service/scripting/scripting_service.h>
#include <hos/cosgi/service/ruleengine/rule/RuleCondition.h>
#include <ros/ros.h>
#include "RulePackage.h"
#include <hos/cosgi/service/ruleengine/ruleengine_service.h>
#include <hos/cosgi/service/devicerepository/DeviceRepositoryService.h>
#include <hos/cosgi/service/ruleengine/rules/conditions/RuleCondition.h>
#include <hos/cosgi/service/ruleengine/RuleEngineService.h>
#include <hos/cosgi/service/scripting/ScriptingService.h>
#include <hos/utils/SchemaLoader.h>
#include "../ruleengine_serviceimpl.h"
#include <Variant/Variant.h>
#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<RuleCondition> Rule::createRuleCondition(Variant condition) {
std::shared_ptr<RuleCondition> retval;
std::shared_ptr<conditions::RuleCondition> RuleImpl::createRuleCondition(const Variant& condition) {
std::shared_ptr<conditions::RuleCondition> 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<RuleCondition> Rule::createRuleCondition(Variant condition) {
return retval;
}
std::shared_ptr<Rule> Rule::createRule(RulePackage* parent, std::string name, Variant raw) {
std::shared_ptr<Rule> retval;
if (parent->getRule(name)) {
std::shared_ptr<RuleImpl> RuleImpl::createRule(RulePackageImpl* parent, std::string name, Variant raw) {
std::shared_ptr<RuleImpl> retval;
if (!parent->getRule(name).expired()) {
throw std::runtime_error("Rule with the same name already exists.");
}
if (raw.IsMap()) {
retval = std::make_shared<Rule>(parent, name);
retval = std::make_shared<RuleImpl>(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> 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> 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> 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);
}
@@ -0,0 +1,100 @@
#pragma once
#include <hos/cosgi/service/ruleengine/rules/conditions/RuleCondition.h>
#include <hos/cosgi/service/ruleengine/rules/Rule.h>
#include <hos/cosgi/service/ruleengine/rules/RulePackage.h>
#include "RulePackageImpl.h"
#include <string>
#include <memory>
#include <vector>
#include <Variant/Variant.h>
// 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<scripting::Script> script;
std::shared_ptr<scripting::Script> exitScript;
std::shared_ptr<conditions::RuleCondition> 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<RuleImpl> createRule(RulePackageImpl* parent, const std::string name, Variant raw);
virtual RulePackage* getParent() noexcept {
return dynamic_cast<RulePackage*>(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<conditions::RuleCondition> createRuleCondition(const Variant& raw) override;
devicerepository::DeviceRepositoryService* getDeviceRepository() noexcept override;
};
} /* namespace rules */
} /* namespace ruleengine */
} /* namespace service */
} /* namespace cosgi */
} /* namespace hos */
@@ -5,29 +5,29 @@
* Author: matzman
*/
#include "RulePackage.h"
#include "RuleImpl.h"
#include <ros/ros.h>
#include <exception>
#include <Variant/Variant.h>
#include "../ruleengine_serviceimpl.h"
#include <hos/cosgi/service/ruleengine/rule/Rule.h>
#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> RulePackage::createRulePackage(service::ruleengine::RuleEngineServiceImpl* parent, std::string name, Variant raw) {
std::shared_ptr<RulePackage> retval;
if (parent->getRulePackage(name)) {
std::shared_ptr<RulePackageImpl> RulePackageImpl::createRulePackage(service::ruleengine::RuleEngineServiceImpl* parent, std::string name, Variant raw) {
std::shared_ptr<RulePackageImpl> 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> 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<RulePackage>(parent, name, language, raw);
retval = std::make_shared<RulePackageImpl>(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> RulePackage::createRulePackage(service::ruleengine:
if (raw.HasPath("rules") && raw["rules"].IsMap()) {
for (auto r : raw["rules"].AsMap()) {
try {
std::shared_ptr<Rule> rule = Rule::createRule(retval.get(), r.first, r.second);
std::shared_ptr<RuleImpl> 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> RulePackage::createRulePackage(service::ruleengine:
}
std::shared_ptr<Rule> RulePackage::getRule(const std::string& name) {
std::shared_ptr<Rule> retval;
std::weak_ptr<Rule> RulePackageImpl::getRule(const std::string& name) noexcept {
std::weak_ptr<Rule> retval;
try {
retval = rules.at(name);
} catch (...) {}
return retval;
}
size_t RulePackage::getRules(std::vector<std::shared_ptr<Rule>>& rules) {
size_t retval = 0;
std::vector<std::weak_ptr<Rule>> RulePackageImpl::getRules() noexcept {
std::vector<std::weak_ptr<Rule>> retval;
for (auto item : this->rules) {
rules.push_back(item.second);
retval++;
retval.push_back(dynamic_pointer_cast<Rule>(item.second));
}
return retval;
}
std::shared_ptr<RuleConditionFactory> RulePackage::getConditionFactory(std::string type) {
std::shared_ptr<RuleConditionFactory> 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 */
@@ -0,0 +1,97 @@
#pragma once
#include <hos/cosgi/service/ruleengine/rules/RulePackage.h>
#include <hos/cosgi/service/scripting/ScriptingService.h>
#include "../RuleEngineServiceImpl.h"
#include <vector>
#include <map>
#include <memory>
#include <string>
#include <Variant/Variant.h>
// 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<std::string, std::shared_ptr<RuleImpl>> ruleMap_t;
service::scripting::ScriptingService* executionService = nullptr;
std::shared_ptr<scripting::GlobalScriptContext> globalContext;
ruleMap_t rules;
Variant rawValue;
bool enabled = false;
public:
static std::shared_ptr<RulePackageImpl> 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<service::ruleengine::RuleEngineService*>(parent);
}
virtual const std::string& getName() noexcept override {
return name;
};
virtual const std::string& getLanguage() noexcept override {
return language;
}
virtual std::weak_ptr<Rule> getRule(const std::string& name) noexcept override;
virtual std::vector<std::weak_ptr<Rule>> 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<scripting::GlobalScriptContext> getGlobalScriptContext() noexcept override { return globalContext; }
std::shared_ptr<service::ruleengine::RuleConditionFactory> getConditionFactory(std::string type);
};
} /* namespace rules */
} /* namespace ruleengine */
} /* namespace service */
} /* namespace cosgi */
} /* namespace hos */
@@ -0,0 +1,271 @@
#include "DeviceMsgCondition.h"
#include <hos/cosgi/service/devicerepository/DeviceRepositoryService.h>
#include "../RuleImpl.h"
#include <ros/ros.h>
namespace hos {
namespace cosgi {
namespace service {
namespace ruleengine {
namespace rules {
namespace conditions {
void DeviceMsgCondition::notificationReceived(interface::Observer<devicerepository::DeviceEventType, devicerepository::DeviceEvent>::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<RuleCondition> DeviceStatusMsgConditionFactory::createCondition(rules::Rule* parent, const Variant& raw) {
std::shared_ptr<DeviceStatusMsgCondition> 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<DeviceStatusMsgCondition>(parent, deviceID, msgPointer, msgValue);
return retval;
}
void DeviceStatusMsgCondition::notificationReceived(interface::Observer<devicerepository::DeviceEventType, devicerepository::DeviceEvent>::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<const devicerepository::DeviceStatusMsgEvent*>(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<RuleCondition> DeviceCommandMsgConditionFactory::createCondition(rules::Rule* parent, const Variant& raw) {
std::shared_ptr<DeviceCommandMsgCondition> 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<DeviceCommandMsgCondition>(parent, deviceID, msgPointer, msgValue);
return retval;
}
void DeviceCommandMsgCondition::notificationReceived(interface::Observer<devicerepository::DeviceEventType, devicerepository::DeviceEvent>::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<const devicerepository::DeviceCmdMsgEvent*>(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<RuleCondition> DeviceStateChangeConditionFactory::createCondition(rules::Rule* parent, const Variant& raw) {
std::shared_ptr<DeviceStateChangeCondition> 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<DeviceStateChangeCondition>(parent, deviceID, statePointer, fromValue, toValue);
return retval;
}
void DeviceStateChangeCondition::notificationReceived(interface::Observer<devicerepository::DeviceEventType, devicerepository::DeviceEvent>::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<const devicerepository::DeviceStatusMsgEvent*>(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 */
@@ -0,0 +1,140 @@
#pragma once
#include <hos/cosgi/service/ruleengine/rules/conditions/RuleConditionImpl.h>
#include <hos/cosgi/service/ruleengine/RuleEngineService.h>
#include <hos/cosgi/service/devicerepository/DeviceRepositoryService.h>
#include <set>
// 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<devicerepository::DeviceEventType, devicerepository::DeviceEvent> {
protected:
const std::string deviceID;
devicerepository::Device* device = nullptr;
const std::string msgPointer;
const Variant msgValue;
std::set<devicerepository::DeviceEventType> _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<devicerepository::DeviceEventType> _listenerKeys )
: RuleConditionImpl(parent, category, type), deviceID(deviceID),
msgPointer(msgPointer), msgValue(msgValue), _listenerKeys(_listenerKeys) {}
virtual void notificationReceived( interface::Observer<devicerepository::DeviceEventType,
devicerepository::DeviceEvent>::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<devicerepository::DeviceEventType,
devicerepository::DeviceEvent>::observable_type* o,
const devicerepository::DeviceEventType& key,
const devicerepository::DeviceEvent* e ) noexcept override;
};
class DeviceStatusMsgConditionFactory
: public RuleConditionFactory {
public:
virtual std::shared_ptr<RuleCondition> 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<devicerepository::DeviceEventType,
devicerepository::DeviceEvent>::observable_type* o,
const devicerepository::DeviceEventType& key,
const devicerepository::DeviceEvent* e ) noexcept override;
};
class DeviceCommandMsgConditionFactory
: public RuleConditionFactory {
public:
virtual std::shared_ptr<RuleCondition> 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<devicerepository::DeviceEventType, devicerepository::DeviceEvent>::observable_type* o, const devicerepository::DeviceEventType& key, const devicerepository::DeviceEvent* e) noexcept override;
};
class DeviceStateChangeConditionFactory
: public RuleConditionFactory {
public:
virtual std::shared_ptr<RuleCondition> createCondition(rules::Rule* parent, const Variant& raw) override;
};
} /* namespace conditions */
} /* namespace rules */
} /* namespace ruleengine */
} /* namespace service */
} /* namespace cosgi */
} /* namespace hos */
@@ -0,0 +1,104 @@
#include "LogicalAndCondition.h"
#include <hos/cosgi/service/ruleengine/rules/Rule.h>
namespace hos {
namespace cosgi {
namespace service {
namespace ruleengine {
namespace rules {
namespace conditions {
std::shared_ptr<RuleCondition> LogicalAndConditionFactory::createCondition(rules::Rule* parent, const Variant& raw) {
std::shared_ptr<LogicalAndCondition> retval;
if (raw.HasPath("conditions") && raw["conditions"].IsList() && raw["conditions"].Size() > 0) {
retval = std::make_shared<LogicalAndCondition>(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<RuleConditionImpl>(retval);
}
void LogicalAndCondition::addChild(std::shared_ptr<RuleCondition> 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 */
@@ -0,0 +1,57 @@
#pragma once
#include <hos/cosgi/service/ruleengine/rules/conditions/RuleConditionImpl.h>
#include <hos/cosgi/service/ruleengine/RuleEngineService.h>
// 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<RuleCondition> 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<RuleCondition> createCondition(rules::Rule* parent, const Variant& raw) override;
};
} /* namespace conditions */
} /* namespace rules */
} /* namespace ruleengine */
} /* namespace service */
} /* namespace cosgi */
} /* namespace hos */
@@ -0,0 +1,101 @@
#include "LogicalOrCondition.h"
#include <hos/cosgi/service/ruleengine/rules/Rule.h>
namespace hos {
namespace cosgi {
namespace service {
namespace ruleengine {
namespace rules {
namespace conditions {
std::shared_ptr<RuleCondition> LogicalOrConditionFactory::createCondition(rules::Rule* parent, const Variant& raw) {
std::shared_ptr<LogicalOrCondition> retval;
if (raw.HasPath("conditions") && raw["conditions"].IsList() && raw["conditions"].Size() > 0) {
retval = std::make_shared<LogicalOrCondition>(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<RuleConditionImpl>(retval);
}
void LogicalOrCondition::addChild(std::shared_ptr<RuleCondition> 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 */
@@ -0,0 +1,57 @@
#pragma once
#include <hos/cosgi/service/ruleengine/rules/conditions/RuleConditionImpl.h>
#include <hos/cosgi/service/ruleengine/RuleEngineService.h>
// 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<RuleCondition> 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<RuleCondition> createCondition(rules::Rule* parent, const Variant& raw) override;
};
} /* namespace conditions */
} /* namespace rules */
} /* namespace ruleengine */
} /* namespace service */
} /* namespace cosgi */
} /* namespace hos */
@@ -0,0 +1,32 @@
#include <hos/cosgi/service/ruleengine/rules/conditions/RuleConditionImpl.h>
#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 */