.
This commit is contained in:
@@ -21,47 +21,97 @@
|
||||
".*": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"body": {
|
||||
"script": {
|
||||
"type": "object"
|
||||
},
|
||||
"conditions": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"items": {
|
||||
"type": "object",
|
||||
"anyOf": [
|
||||
{ "$ref": "#/definitions/DeviceMsgCondition" },
|
||||
{ "$ref": "#/definitions/DeviceStatusCondition" },
|
||||
{ "$ref": "#/definitions/TimeCondition" },
|
||||
{ "$ref": "#/definitions/SystemCondition" }
|
||||
]
|
||||
}
|
||||
"exitscript": {
|
||||
"type": "object"
|
||||
},
|
||||
"condition": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string"
|
||||
},
|
||||
"$schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"additionalProperties": true,
|
||||
"required": [ "type" ]
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": [ "conditions", "body" ]
|
||||
"required": [ "condition", "script" ]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"definitions": {
|
||||
"DeviceMsgCondition": {
|
||||
"LogicalOrCondition": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"type": {
|
||||
"description": "",
|
||||
"type": "string",
|
||||
"pattern": "devicemsg"
|
||||
"pattern": "logical_or"
|
||||
},
|
||||
"$schema": {
|
||||
"type": "string",
|
||||
"pattern": "hos:schema://core/configs/rulepackage#/definitions/LogicalOrCondition"
|
||||
},
|
||||
"conditions": {
|
||||
"description": "",
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"items": {
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": [ "type", "conditions" ]
|
||||
},
|
||||
"LogicalAndCondition": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"type": {
|
||||
"description": "",
|
||||
"type": "string",
|
||||
"pattern": "logical_or"
|
||||
},
|
||||
"$schema": {
|
||||
"type": "string",
|
||||
"pattern": "hos:schema://core/configs/rulepackage#/definitions/LogicalAndCondition"
|
||||
},
|
||||
"conditions": {
|
||||
"description": "",
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"items": {
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": [ "type", "conditions" ]
|
||||
},
|
||||
"DeviceStatusMsgCondition": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"type": {
|
||||
"description": "",
|
||||
"type": "string",
|
||||
"pattern": "devicestatusmsg"
|
||||
},
|
||||
"$schema": {
|
||||
"type": "string",
|
||||
"pattern": "hos:schema://core/configs/rulepackage#/definitions/DeviceStatusMsgCondition"
|
||||
},
|
||||
"deviceid": {
|
||||
"description": "",
|
||||
"type": "string"
|
||||
},
|
||||
"msgtype": {
|
||||
"description": "",
|
||||
"type": "string",
|
||||
"enum": ["status", "command"]
|
||||
},
|
||||
"msgpointer": {
|
||||
"description": "",
|
||||
"type": "string"
|
||||
@@ -72,19 +122,50 @@
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"anyOf": [
|
||||
{ "required": [ "type", "deviceid" ] },
|
||||
{ "required": [ "type", "deviceid", "msgtype", "msgvalue" ] },
|
||||
{ "required": [ "type", "deviceid", "msgtype", "msgpointer" ] }
|
||||
]
|
||||
{ "required": [ "type", "deviceid" ] }
|
||||
]
|
||||
},
|
||||
"DeviceStatusCondition": {
|
||||
"DeviceCommandMsgCondition": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"type": {
|
||||
"description": "",
|
||||
"type": "string",
|
||||
"pattern": "devicestatus"
|
||||
"pattern": "devicecommandmsg"
|
||||
},
|
||||
"$schema": {
|
||||
"type": "string",
|
||||
"pattern": "hos:schema://core/configs/rulepackage#/definitions/DeviceCommandMsgCondition"
|
||||
},
|
||||
"deviceid": {
|
||||
"description": "",
|
||||
"type": "string"
|
||||
},
|
||||
"msgpointer": {
|
||||
"description": "",
|
||||
"type": "string"
|
||||
},
|
||||
"msgvalue": {
|
||||
"description": ""
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"anyOf": [
|
||||
{ "required": [ "type", "deviceid" ] }
|
||||
]
|
||||
},
|
||||
"DeviceStateChangeCondition": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"type": {
|
||||
"description": "",
|
||||
"type": "string",
|
||||
"pattern": "devicestatechange"
|
||||
},
|
||||
"$schema": {
|
||||
"type": "string",
|
||||
"pattern": "hos:schema://core/configs/rulepackage#/definitions/DeviceStateChangeCondition"
|
||||
},
|
||||
"deviceid": {
|
||||
"description": "",
|
||||
"type": "string"
|
||||
@@ -113,6 +194,10 @@
|
||||
"type": "string",
|
||||
"pattern": "time"
|
||||
},
|
||||
"$schema": {
|
||||
"type": "string",
|
||||
"pattern": "hos:schema://core/configs/rulepackage#/definitions/TimeCondition"
|
||||
},
|
||||
"cron": {
|
||||
"description": "",
|
||||
"type": "string"
|
||||
@@ -135,6 +220,10 @@
|
||||
"type": "string",
|
||||
"pattern": "systemevent"
|
||||
},
|
||||
"$schema": {
|
||||
"type": "string",
|
||||
"pattern": "hos:schema://core/configs/rulepackage#/definitions/SystemCondition"
|
||||
},
|
||||
"event": {
|
||||
"description": "",
|
||||
"type": "string",
|
||||
|
||||
@@ -124,7 +124,7 @@ bundle(hos_cosgi_ruleengine
|
||||
${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/RuleCondition
|
||||
${PROJECT_SOURCE_DIR}/src/hos/cosgi/service/ruleengine/rule/RuleConditionImpl
|
||||
)
|
||||
target_link_libraries(hos_cosgi_ruleengine
|
||||
${catkin_LIBRARIES}
|
||||
|
||||
+94
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* 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 libvariant {
|
||||
class HosSchemaLoader;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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);
|
||||
|
||||
libvariant::HosSchemaLoader* getHOSSchemaLoader();
|
||||
devicerepository::DeviceRepositoryService* getDeviceRepository();
|
||||
};
|
||||
|
||||
|
||||
} /* namespace rule */
|
||||
} /* namespace ruleengine */
|
||||
} /* namespace service */
|
||||
} /* namespace cosgi */
|
||||
} /* namespace hos */
|
||||
|
||||
|
||||
#endif /* SRC_HOS_COSGI_SERVICE_RULEENGINE_RULE_RULE_H_ */
|
||||
+105
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* 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_ */
|
||||
+14
@@ -11,6 +11,8 @@
|
||||
|
||||
#include <hos/common/observable.h>
|
||||
#include <hos/cosgi/service/interface.h>
|
||||
#include <memory>
|
||||
#include <Variant/Variant.h>
|
||||
|
||||
|
||||
namespace hos {
|
||||
@@ -18,10 +20,22 @@ 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;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ecore:EPackage xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:ecore="http://www.eclipse.org/emf/2002/Ecore">
|
||||
<eClassifiers xsi:type="ecore:EClass" name="RuleEngine">
|
||||
<eStructuralFeatures xsi:type="ecore:EReference" name="pkgs" upperBound="-1" eType="#//RulePkg"
|
||||
containment="true"/>
|
||||
<eStructuralFeatures xsi:type="ecore:EReference" name="factories" lowerBound="1"
|
||||
upperBound="-1" eType="#//ConditionFactory" containment="true"/>
|
||||
</eClassifiers>
|
||||
<eClassifiers xsi:type="ecore:EClass" name="RulePkg">
|
||||
<eStructuralFeatures xsi:type="ecore:EReference" name="context" eType="#//GlobalScriptContext"
|
||||
containment="true"/>
|
||||
<eStructuralFeatures xsi:type="ecore:EReference" name="rules" lowerBound="1" upperBound="-1"
|
||||
eType="#//Rule" containment="true"/>
|
||||
</eClassifiers>
|
||||
<eClassifiers xsi:type="ecore:EClass" name="Rule">
|
||||
<eOperations name="evaluate"/>
|
||||
<eOperations name="execute"/>
|
||||
<eOperations name="condStateChanged"/>
|
||||
<eStructuralFeatures xsi:type="ecore:EReference" name="script" lowerBound="1"
|
||||
upperBound="2" eType="#//Script" containment="true"/>
|
||||
<eStructuralFeatures xsi:type="ecore:EReference" name="condition" lowerBound="1"
|
||||
eType="#//Condition" containment="true"/>
|
||||
</eClassifiers>
|
||||
<eClassifiers xsi:type="ecore:EClass" name="GlobalScriptContext" abstract="true"
|
||||
interface="true"/>
|
||||
<eClassifiers xsi:type="ecore:EClass" name="Script"/>
|
||||
<eClassifiers xsi:type="ecore:EClass" name="Condition">
|
||||
<eOperations name="evaluate"/>
|
||||
<eStructuralFeatures xsi:type="ecore:EAttribute" name="status"/>
|
||||
<eStructuralFeatures xsi:type="ecore:EAttribute" name="category"/>
|
||||
<eStructuralFeatures xsi:type="ecore:EAttribute" name="type"/>
|
||||
<eStructuralFeatures xsi:type="ecore:EAttribute" name="currentState"/>
|
||||
</eClassifiers>
|
||||
<eClassifiers xsi:type="ecore:EClass" name="ConditionFactory"/>
|
||||
<eClassifiers xsi:type="ecore:EClass" name="CompositeCondition" eSuperTypes="#//Condition">
|
||||
<eOperations name="childstateChanged"/>
|
||||
<eStructuralFeatures xsi:type="ecore:EReference" name="children" eType="#//Condition"
|
||||
containment="true"/>
|
||||
</eClassifiers>
|
||||
</ecore:EPackage>
|
||||
@@ -0,0 +1,269 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<notation:Diagram xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ecore="http://www.eclipse.org/emf/2002/Ecore" xmlns:notation="http://www.eclipse.org/gmf/runtime/1.0.2/notation" xmi:id="_zcbTAPcZEeSJjd7R-1ZWOQ" type="Ecore" name="RuleEngine.ecore_diagram" measurementUnit="Pixel">
|
||||
<children xmi:type="notation:Shape" xmi:id="_9RCAoPcZEeSJjd7R-1ZWOQ" type="2001" fontName="Sans">
|
||||
<children xmi:type="notation:DecorationNode" xmi:id="_9RDOwPcZEeSJjd7R-1ZWOQ" type="5001"/>
|
||||
<children xmi:type="notation:BasicCompartment" xmi:id="_9RDOwfcZEeSJjd7R-1ZWOQ" type="7001">
|
||||
<styles xmi:type="notation:SortingStyle" xmi:id="_9RDOwvcZEeSJjd7R-1ZWOQ"/>
|
||||
<styles xmi:type="notation:FilteringStyle" xmi:id="_9RDOw_cZEeSJjd7R-1ZWOQ"/>
|
||||
</children>
|
||||
<children xmi:type="notation:BasicCompartment" xmi:id="_9RDOxPcZEeSJjd7R-1ZWOQ" type="7002">
|
||||
<styles xmi:type="notation:SortingStyle" xmi:id="_9RDOxfcZEeSJjd7R-1ZWOQ"/>
|
||||
<styles xmi:type="notation:FilteringStyle" xmi:id="_9RDOxvcZEeSJjd7R-1ZWOQ"/>
|
||||
</children>
|
||||
<children xmi:type="notation:BasicCompartment" xmi:id="_9RDOx_cZEeSJjd7R-1ZWOQ" type="7003">
|
||||
<styles xmi:type="notation:SortingStyle" xmi:id="_9RDOyPcZEeSJjd7R-1ZWOQ"/>
|
||||
<styles xmi:type="notation:FilteringStyle" xmi:id="_9RDOyfcZEeSJjd7R-1ZWOQ"/>
|
||||
</children>
|
||||
<element xmi:type="ecore:EClass" href="RuleEngine.ecore#//RuleEngine"/>
|
||||
<layoutConstraint xmi:type="notation:Bounds" xmi:id="_9RCAofcZEeSJjd7R-1ZWOQ" x="95" y="65"/>
|
||||
</children>
|
||||
<children xmi:type="notation:Shape" xmi:id="_-205wPcZEeSJjd7R-1ZWOQ" type="2001" fontName="Sans">
|
||||
<children xmi:type="notation:DecorationNode" xmi:id="_-21g0PcZEeSJjd7R-1ZWOQ" type="5001"/>
|
||||
<children xmi:type="notation:BasicCompartment" xmi:id="_-21g0fcZEeSJjd7R-1ZWOQ" type="7001">
|
||||
<styles xmi:type="notation:SortingStyle" xmi:id="_-21g0vcZEeSJjd7R-1ZWOQ"/>
|
||||
<styles xmi:type="notation:FilteringStyle" xmi:id="_-21g0_cZEeSJjd7R-1ZWOQ"/>
|
||||
</children>
|
||||
<children xmi:type="notation:BasicCompartment" xmi:id="_-21g1PcZEeSJjd7R-1ZWOQ" type="7002">
|
||||
<styles xmi:type="notation:SortingStyle" xmi:id="_-21g1fcZEeSJjd7R-1ZWOQ"/>
|
||||
<styles xmi:type="notation:FilteringStyle" xmi:id="_-21g1vcZEeSJjd7R-1ZWOQ"/>
|
||||
</children>
|
||||
<children xmi:type="notation:BasicCompartment" xmi:id="_-22H4PcZEeSJjd7R-1ZWOQ" type="7003">
|
||||
<styles xmi:type="notation:SortingStyle" xmi:id="_-22H4fcZEeSJjd7R-1ZWOQ"/>
|
||||
<styles xmi:type="notation:FilteringStyle" xmi:id="_-22H4vcZEeSJjd7R-1ZWOQ"/>
|
||||
</children>
|
||||
<element xmi:type="ecore:EClass" href="RuleEngine.ecore#//RulePkg"/>
|
||||
<layoutConstraint xmi:type="notation:Bounds" xmi:id="_-205wfcZEeSJjd7R-1ZWOQ" x="100" y="190"/>
|
||||
</children>
|
||||
<children xmi:type="notation:Shape" xmi:id="_AZp-cPcaEeSJjd7R-1ZWOQ" type="2001" fontName="Sans">
|
||||
<children xmi:type="notation:DecorationNode" xmi:id="_AZqlgPcaEeSJjd7R-1ZWOQ" type="5001"/>
|
||||
<children xmi:type="notation:BasicCompartment" xmi:id="_AZrMkPcaEeSJjd7R-1ZWOQ" type="7001">
|
||||
<styles xmi:type="notation:SortingStyle" xmi:id="_AZrMkfcaEeSJjd7R-1ZWOQ"/>
|
||||
<styles xmi:type="notation:FilteringStyle" xmi:id="_AZrMkvcaEeSJjd7R-1ZWOQ"/>
|
||||
</children>
|
||||
<children xmi:type="notation:BasicCompartment" xmi:id="_AZrMk_caEeSJjd7R-1ZWOQ" type="7002">
|
||||
<children xmi:type="notation:Node" xmi:id="__aMlMPccEeSJjd7R-1ZWOQ" type="3002">
|
||||
<element xmi:type="ecore:EOperation" href="RuleEngine.ecore#//Rule/evaluate"/>
|
||||
<layoutConstraint xmi:type="notation:Location" xmi:id="__aMlMfccEeSJjd7R-1ZWOQ"/>
|
||||
</children>
|
||||
<children xmi:type="notation:Node" xmi:id="_A9cgoPcdEeSJjd7R-1ZWOQ" type="3002">
|
||||
<element xmi:type="ecore:EOperation" href="RuleEngine.ecore#//Rule/execute"/>
|
||||
<layoutConstraint xmi:type="notation:Location" xmi:id="_A9cgofcdEeSJjd7R-1ZWOQ"/>
|
||||
</children>
|
||||
<children xmi:type="notation:Node" xmi:id="_EWs7cPcdEeSJjd7R-1ZWOQ" type="3002">
|
||||
<element xmi:type="ecore:EOperation" href="RuleEngine.ecore#//Rule/condStateChanged"/>
|
||||
<layoutConstraint xmi:type="notation:Location" xmi:id="_EWs7cfcdEeSJjd7R-1ZWOQ"/>
|
||||
</children>
|
||||
<styles xmi:type="notation:SortingStyle" xmi:id="_AZrMlPcaEeSJjd7R-1ZWOQ"/>
|
||||
<styles xmi:type="notation:FilteringStyle" xmi:id="_AZrMlfcaEeSJjd7R-1ZWOQ"/>
|
||||
</children>
|
||||
<children xmi:type="notation:BasicCompartment" xmi:id="_AZrMlvcaEeSJjd7R-1ZWOQ" type="7003">
|
||||
<styles xmi:type="notation:SortingStyle" xmi:id="_AZrMl_caEeSJjd7R-1ZWOQ"/>
|
||||
<styles xmi:type="notation:FilteringStyle" xmi:id="_AZrMmPcaEeSJjd7R-1ZWOQ"/>
|
||||
</children>
|
||||
<element xmi:type="ecore:EClass" href="RuleEngine.ecore#//Rule"/>
|
||||
<layoutConstraint xmi:type="notation:Bounds" xmi:id="_AZp-cfcaEeSJjd7R-1ZWOQ" x="305" y="180"/>
|
||||
</children>
|
||||
<children xmi:type="notation:Shape" xmi:id="_BeQPAPcaEeSJjd7R-1ZWOQ" type="2001" fontName="Sans" italic="true">
|
||||
<children xmi:type="notation:DecorationNode" xmi:id="_BeQ2EPcaEeSJjd7R-1ZWOQ" type="5001"/>
|
||||
<children xmi:type="notation:BasicCompartment" xmi:id="_BeRdIPcaEeSJjd7R-1ZWOQ" type="7001">
|
||||
<styles xmi:type="notation:SortingStyle" xmi:id="_BeRdIfcaEeSJjd7R-1ZWOQ"/>
|
||||
<styles xmi:type="notation:FilteringStyle" xmi:id="_BeRdIvcaEeSJjd7R-1ZWOQ"/>
|
||||
</children>
|
||||
<children xmi:type="notation:BasicCompartment" xmi:id="_BeRdI_caEeSJjd7R-1ZWOQ" type="7002">
|
||||
<styles xmi:type="notation:SortingStyle" xmi:id="_BeRdJPcaEeSJjd7R-1ZWOQ"/>
|
||||
<styles xmi:type="notation:FilteringStyle" xmi:id="_BeRdJfcaEeSJjd7R-1ZWOQ"/>
|
||||
</children>
|
||||
<children xmi:type="notation:BasicCompartment" xmi:id="_BeRdJvcaEeSJjd7R-1ZWOQ" type="7003">
|
||||
<styles xmi:type="notation:SortingStyle" xmi:id="_BeRdJ_caEeSJjd7R-1ZWOQ"/>
|
||||
<styles xmi:type="notation:FilteringStyle" xmi:id="_BeRdKPcaEeSJjd7R-1ZWOQ"/>
|
||||
</children>
|
||||
<element xmi:type="ecore:EClass" href="RuleEngine.ecore#//GlobalScriptContext"/>
|
||||
<layoutConstraint xmi:type="notation:Bounds" xmi:id="_BeQPAfcaEeSJjd7R-1ZWOQ" x="70" y="325"/>
|
||||
</children>
|
||||
<children xmi:type="notation:Shape" xmi:id="_CoAFUPcaEeSJjd7R-1ZWOQ" type="2001" fontName="Sans" italic="true">
|
||||
<children xmi:type="notation:DecorationNode" xmi:id="_CoAFUvcaEeSJjd7R-1ZWOQ" type="5001"/>
|
||||
<children xmi:type="notation:BasicCompartment" xmi:id="_CoAFU_caEeSJjd7R-1ZWOQ" type="7001">
|
||||
<styles xmi:type="notation:SortingStyle" xmi:id="_CoAsYPcaEeSJjd7R-1ZWOQ"/>
|
||||
<styles xmi:type="notation:FilteringStyle" xmi:id="_CoAsYfcaEeSJjd7R-1ZWOQ"/>
|
||||
</children>
|
||||
<children xmi:type="notation:BasicCompartment" xmi:id="_CoAsYvcaEeSJjd7R-1ZWOQ" type="7002">
|
||||
<styles xmi:type="notation:SortingStyle" xmi:id="_CoAsY_caEeSJjd7R-1ZWOQ"/>
|
||||
<styles xmi:type="notation:FilteringStyle" xmi:id="_CoAsZPcaEeSJjd7R-1ZWOQ"/>
|
||||
</children>
|
||||
<children xmi:type="notation:BasicCompartment" xmi:id="_CoAsZfcaEeSJjd7R-1ZWOQ" type="7003">
|
||||
<styles xmi:type="notation:SortingStyle" xmi:id="_CoAsZvcaEeSJjd7R-1ZWOQ"/>
|
||||
<styles xmi:type="notation:FilteringStyle" xmi:id="_CoAsZ_caEeSJjd7R-1ZWOQ"/>
|
||||
</children>
|
||||
<element xmi:type="ecore:EClass" href="RuleEngine.ecore#//Script"/>
|
||||
<layoutConstraint xmi:type="notation:Bounds" xmi:id="_CoAFUfcaEeSJjd7R-1ZWOQ" x="355" y="345"/>
|
||||
</children>
|
||||
<children xmi:type="notation:Shape" xmi:id="_FW8-4PcaEeSJjd7R-1ZWOQ" type="2001" fontName="Sans">
|
||||
<children xmi:type="notation:DecorationNode" xmi:id="_FW9l8PcaEeSJjd7R-1ZWOQ" type="5001"/>
|
||||
<children xmi:type="notation:BasicCompartment" xmi:id="_FW9l8fcaEeSJjd7R-1ZWOQ" type="7001">
|
||||
<children xmi:type="notation:Node" xmi:id="_XYA7IPcbEeSJjd7R-1ZWOQ" type="3001">
|
||||
<element xmi:type="ecore:EAttribute" href="RuleEngine.ecore#//Condition/status"/>
|
||||
<layoutConstraint xmi:type="notation:Location" xmi:id="_XYA7IfcbEeSJjd7R-1ZWOQ"/>
|
||||
</children>
|
||||
<children xmi:type="notation:Node" xmi:id="_tQ9kYPcbEeSJjd7R-1ZWOQ" type="3001">
|
||||
<element xmi:type="ecore:EAttribute" href="RuleEngine.ecore#//Condition/category"/>
|
||||
<layoutConstraint xmi:type="notation:Location" xmi:id="_tQ9kYfcbEeSJjd7R-1ZWOQ"/>
|
||||
</children>
|
||||
<children xmi:type="notation:Node" xmi:id="_vWxaUPcbEeSJjd7R-1ZWOQ" type="3001">
|
||||
<element xmi:type="ecore:EAttribute" href="RuleEngine.ecore#//Condition/type"/>
|
||||
<layoutConstraint xmi:type="notation:Location" xmi:id="_vWxaUfcbEeSJjd7R-1ZWOQ"/>
|
||||
</children>
|
||||
<children xmi:type="notation:Node" xmi:id="_iL0NMPchEeSJjd7R-1ZWOQ" type="3001">
|
||||
<element xmi:type="ecore:EAttribute" href="RuleEngine.ecore#//Condition/currentState"/>
|
||||
<layoutConstraint xmi:type="notation:Location" xmi:id="_iL0NMfchEeSJjd7R-1ZWOQ"/>
|
||||
</children>
|
||||
<styles xmi:type="notation:SortingStyle" xmi:id="_FW9l8vcaEeSJjd7R-1ZWOQ"/>
|
||||
<styles xmi:type="notation:FilteringStyle" xmi:id="_FW9l8_caEeSJjd7R-1ZWOQ"/>
|
||||
</children>
|
||||
<children xmi:type="notation:BasicCompartment" xmi:id="_FW-NAPcaEeSJjd7R-1ZWOQ" type="7002">
|
||||
<children xmi:type="notation:Node" xmi:id="_pogP8PcbEeSJjd7R-1ZWOQ" type="3002">
|
||||
<element xmi:type="ecore:EOperation" href="RuleEngine.ecore#//Condition/evaluate"/>
|
||||
<layoutConstraint xmi:type="notation:Location" xmi:id="_pogP8fcbEeSJjd7R-1ZWOQ"/>
|
||||
</children>
|
||||
<styles xmi:type="notation:SortingStyle" xmi:id="_FW-NAfcaEeSJjd7R-1ZWOQ"/>
|
||||
<styles xmi:type="notation:FilteringStyle" xmi:id="_FW-NAvcaEeSJjd7R-1ZWOQ"/>
|
||||
</children>
|
||||
<children xmi:type="notation:BasicCompartment" xmi:id="_FW-NA_caEeSJjd7R-1ZWOQ" type="7003">
|
||||
<styles xmi:type="notation:SortingStyle" xmi:id="_FW-NBPcaEeSJjd7R-1ZWOQ"/>
|
||||
<styles xmi:type="notation:FilteringStyle" xmi:id="_FW-NBfcaEeSJjd7R-1ZWOQ"/>
|
||||
</children>
|
||||
<element xmi:type="ecore:EClass" href="RuleEngine.ecore#//Condition"/>
|
||||
<layoutConstraint xmi:type="notation:Bounds" xmi:id="_FW8-4fcaEeSJjd7R-1ZWOQ" x="625" y="155"/>
|
||||
</children>
|
||||
<children xmi:type="notation:Shape" xmi:id="_qHwNMPcaEeSJjd7R-1ZWOQ" type="2001" fontName="Sans" italic="true">
|
||||
<children xmi:type="notation:DecorationNode" xmi:id="_qHwNMvcaEeSJjd7R-1ZWOQ" type="5001"/>
|
||||
<children xmi:type="notation:BasicCompartment" xmi:id="_qHw0QPcaEeSJjd7R-1ZWOQ" type="7001">
|
||||
<styles xmi:type="notation:SortingStyle" xmi:id="_qHw0QfcaEeSJjd7R-1ZWOQ"/>
|
||||
<styles xmi:type="notation:FilteringStyle" xmi:id="_qHw0QvcaEeSJjd7R-1ZWOQ"/>
|
||||
</children>
|
||||
<children xmi:type="notation:BasicCompartment" xmi:id="_qHw0Q_caEeSJjd7R-1ZWOQ" type="7002">
|
||||
<styles xmi:type="notation:SortingStyle" xmi:id="_qHw0RPcaEeSJjd7R-1ZWOQ"/>
|
||||
<styles xmi:type="notation:FilteringStyle" xmi:id="_qHw0RfcaEeSJjd7R-1ZWOQ"/>
|
||||
</children>
|
||||
<children xmi:type="notation:BasicCompartment" xmi:id="_qHw0RvcaEeSJjd7R-1ZWOQ" type="7003">
|
||||
<styles xmi:type="notation:SortingStyle" xmi:id="_qHw0R_caEeSJjd7R-1ZWOQ"/>
|
||||
<styles xmi:type="notation:FilteringStyle" xmi:id="_qHw0SPcaEeSJjd7R-1ZWOQ"/>
|
||||
</children>
|
||||
<element xmi:type="ecore:EClass" href="RuleEngine.ecore#//ConditionFactory"/>
|
||||
<layoutConstraint xmi:type="notation:Bounds" xmi:id="_qHwNMfcaEeSJjd7R-1ZWOQ" x="515" y="65"/>
|
||||
</children>
|
||||
<children xmi:type="notation:Shape" xmi:id="_PHsjYPceEeSJjd7R-1ZWOQ" type="2001" fontName="Sans">
|
||||
<children xmi:type="notation:DecorationNode" xmi:id="_PHtKcPceEeSJjd7R-1ZWOQ" type="5001"/>
|
||||
<children xmi:type="notation:BasicCompartment" xmi:id="_PHtKcfceEeSJjd7R-1ZWOQ" type="7001">
|
||||
<styles xmi:type="notation:SortingStyle" xmi:id="_PHtKcvceEeSJjd7R-1ZWOQ"/>
|
||||
<styles xmi:type="notation:FilteringStyle" xmi:id="_PHtKc_ceEeSJjd7R-1ZWOQ"/>
|
||||
</children>
|
||||
<children xmi:type="notation:BasicCompartment" xmi:id="_PHtKdPceEeSJjd7R-1ZWOQ" type="7002">
|
||||
<children xmi:type="notation:Node" xmi:id="_ZUcrQPceEeSJjd7R-1ZWOQ" type="3002">
|
||||
<element xmi:type="ecore:EOperation" href="RuleEngine.ecore#//CompositeCondition/childstateChanged"/>
|
||||
<layoutConstraint xmi:type="notation:Location" xmi:id="_ZUcrQfceEeSJjd7R-1ZWOQ"/>
|
||||
</children>
|
||||
<styles xmi:type="notation:SortingStyle" xmi:id="_PHtKdfceEeSJjd7R-1ZWOQ"/>
|
||||
<styles xmi:type="notation:FilteringStyle" xmi:id="_PHtKdvceEeSJjd7R-1ZWOQ"/>
|
||||
</children>
|
||||
<children xmi:type="notation:BasicCompartment" xmi:id="_PHtKd_ceEeSJjd7R-1ZWOQ" type="7003">
|
||||
<styles xmi:type="notation:SortingStyle" xmi:id="_PHtKePceEeSJjd7R-1ZWOQ"/>
|
||||
<styles xmi:type="notation:FilteringStyle" xmi:id="_PHtKefceEeSJjd7R-1ZWOQ"/>
|
||||
</children>
|
||||
<element xmi:type="ecore:EClass" href="RuleEngine.ecore#//CompositeCondition"/>
|
||||
<layoutConstraint xmi:type="notation:Bounds" xmi:id="_PHsjYfceEeSJjd7R-1ZWOQ" x="875" y="155"/>
|
||||
</children>
|
||||
<styles xmi:type="notation:DiagramStyle" xmi:id="_zcbTAfcZEeSJjd7R-1ZWOQ"/>
|
||||
<element xmi:type="ecore:EPackage" href="RuleEngine.ecore#/"/>
|
||||
<edges xmi:type="notation:Connector" xmi:id="_MnPVoPcaEeSJjd7R-1ZWOQ" type="4003" source="_9RCAoPcZEeSJjd7R-1ZWOQ" target="_-205wPcZEeSJjd7R-1ZWOQ">
|
||||
<children xmi:type="notation:DecorationNode" xmi:id="_MnPVo_caEeSJjd7R-1ZWOQ" type="6002">
|
||||
<layoutConstraint xmi:type="notation:Location" xmi:id="_MnP8sPcaEeSJjd7R-1ZWOQ" x="8" y="18"/>
|
||||
</children>
|
||||
<children xmi:type="notation:DecorationNode" xmi:id="_MnP8sfcaEeSJjd7R-1ZWOQ" type="6004">
|
||||
<layoutConstraint xmi:type="notation:Location" xmi:id="_MnP8svcaEeSJjd7R-1ZWOQ" x="-2" y="17"/>
|
||||
</children>
|
||||
<styles xmi:type="notation:FontStyle" xmi:id="_MnPVofcaEeSJjd7R-1ZWOQ" fontName="Sans"/>
|
||||
<element xmi:type="ecore:EReference" href="RuleEngine.ecore#//RuleEngine/pkgs"/>
|
||||
<bendpoints xmi:type="notation:RelativeBendpoints" xmi:id="_MnPVovcaEeSJjd7R-1ZWOQ" points="[1, 9, -10, -82]$[7, 82, -4, -9]"/>
|
||||
<sourceAnchor xmi:type="notation:IdentityAnchor" xmi:id="_MnTAAPcaEeSJjd7R-1ZWOQ" id="(0.25,0.8269230769230769)"/>
|
||||
<targetAnchor xmi:type="notation:IdentityAnchor" xmi:id="_MnTAAfcaEeSJjd7R-1ZWOQ" id="(0.4117647058823529,0.17307692307692307)"/>
|
||||
</edges>
|
||||
<edges xmi:type="notation:Connector" xmi:id="_QSKIMPcaEeSJjd7R-1ZWOQ" type="4003" source="_-205wPcZEeSJjd7R-1ZWOQ" target="_BeQPAPcaEeSJjd7R-1ZWOQ">
|
||||
<children xmi:type="notation:DecorationNode" xmi:id="_QSKvQPcaEeSJjd7R-1ZWOQ" type="6002">
|
||||
<layoutConstraint xmi:type="notation:Location" xmi:id="_QSKvQfcaEeSJjd7R-1ZWOQ" x="8" y="33"/>
|
||||
</children>
|
||||
<children xmi:type="notation:DecorationNode" xmi:id="_QSKvQvcaEeSJjd7R-1ZWOQ" type="6004">
|
||||
<layoutConstraint xmi:type="notation:Location" xmi:id="_QSKvQ_caEeSJjd7R-1ZWOQ" x="-1" y="24"/>
|
||||
</children>
|
||||
<styles xmi:type="notation:FontStyle" xmi:id="_QSKIMfcaEeSJjd7R-1ZWOQ" fontName="Sans"/>
|
||||
<element xmi:type="ecore:EReference" href="RuleEngine.ecore#//RulePkg/context"/>
|
||||
<bendpoints xmi:type="notation:RelativeBendpoints" xmi:id="_QSKIMvcaEeSJjd7R-1ZWOQ" points="[1, 26, -6, -109]$[10, 161, 3, 26]"/>
|
||||
</edges>
|
||||
<edges xmi:type="notation:Connector" xmi:id="_c6s9EPcaEeSJjd7R-1ZWOQ" type="4003" source="_-205wPcZEeSJjd7R-1ZWOQ" target="_AZp-cPcaEeSJjd7R-1ZWOQ">
|
||||
<children xmi:type="notation:DecorationNode" xmi:id="_c6tkIPcaEeSJjd7R-1ZWOQ" type="6002">
|
||||
<layoutConstraint xmi:type="notation:Location" xmi:id="_c6tkIfcaEeSJjd7R-1ZWOQ" x="48" y="-18"/>
|
||||
</children>
|
||||
<children xmi:type="notation:DecorationNode" xmi:id="_c6tkIvcaEeSJjd7R-1ZWOQ" type="6004">
|
||||
<layoutConstraint xmi:type="notation:Location" xmi:id="_c6tkI_caEeSJjd7R-1ZWOQ" x="1" y="16"/>
|
||||
</children>
|
||||
<styles xmi:type="notation:FontStyle" xmi:id="_c6s9EfcaEeSJjd7R-1ZWOQ" fontName="Sans"/>
|
||||
<element xmi:type="ecore:EReference" href="RuleEngine.ecore#//RulePkg/rules"/>
|
||||
<bendpoints xmi:type="notation:RelativeBendpoints" xmi:id="_c6s9EvcaEeSJjd7R-1ZWOQ" points="[34, 4, -238, -24]$[270, 54, -2, 26]"/>
|
||||
</edges>
|
||||
<edges xmi:type="notation:Connector" xmi:id="_iuaoIPcaEeSJjd7R-1ZWOQ" type="4003" source="_AZp-cPcaEeSJjd7R-1ZWOQ" target="_CoAFUPcaEeSJjd7R-1ZWOQ">
|
||||
<children xmi:type="notation:DecorationNode" xmi:id="_iubPMPcaEeSJjd7R-1ZWOQ" type="6002">
|
||||
<layoutConstraint xmi:type="notation:Location" xmi:id="_iubPMfcaEeSJjd7R-1ZWOQ" x="11" y="22"/>
|
||||
</children>
|
||||
<children xmi:type="notation:DecorationNode" xmi:id="_iubPMvcaEeSJjd7R-1ZWOQ" type="6004">
|
||||
<layoutConstraint xmi:type="notation:Location" xmi:id="_iubPM_caEeSJjd7R-1ZWOQ" x="2" y="22"/>
|
||||
</children>
|
||||
<styles xmi:type="notation:FontStyle" xmi:id="_iuaoIfcaEeSJjd7R-1ZWOQ" fontName="Sans"/>
|
||||
<element xmi:type="ecore:EReference" href="RuleEngine.ecore#//Rule/script"/>
|
||||
<bendpoints xmi:type="notation:RelativeBendpoints" xmi:id="_iuaoIvcaEeSJjd7R-1ZWOQ" points="[2, 26, -9, -108]$[10, 124, -1, -10]"/>
|
||||
<targetAnchor xmi:type="notation:IdentityAnchor" xmi:id="_iueSgPcaEeSJjd7R-1ZWOQ" id="(0.4827586206896552,0.19230769230769232)"/>
|
||||
</edges>
|
||||
<edges xmi:type="notation:Connector" xmi:id="_sLpXkPcaEeSJjd7R-1ZWOQ" type="4003" source="_9RCAoPcZEeSJjd7R-1ZWOQ" target="_qHwNMPcaEeSJjd7R-1ZWOQ">
|
||||
<children xmi:type="notation:DecorationNode" xmi:id="_sLp-oPcaEeSJjd7R-1ZWOQ" type="6002">
|
||||
<layoutConstraint xmi:type="notation:Location" xmi:id="_sLp-ofcaEeSJjd7R-1ZWOQ" x="128" y="-14"/>
|
||||
</children>
|
||||
<children xmi:type="notation:DecorationNode" xmi:id="_sLp-ovcaEeSJjd7R-1ZWOQ" type="6004">
|
||||
<layoutConstraint xmi:type="notation:Location" xmi:id="_sLp-o_caEeSJjd7R-1ZWOQ" x="16" y="16"/>
|
||||
</children>
|
||||
<styles xmi:type="notation:FontStyle" xmi:id="_sLpXkfcaEeSJjd7R-1ZWOQ" fontName="Sans"/>
|
||||
<element xmi:type="ecore:EReference" href="RuleEngine.ecore#//RuleEngine/factories"/>
|
||||
<bendpoints xmi:type="notation:RelativeBendpoints" xmi:id="_sLpXkvcaEeSJjd7R-1ZWOQ" points="[14, 0, -425, -17]$[469, 26, 30, 9]"/>
|
||||
<sourceAnchor xmi:type="notation:IdentityAnchor" xmi:id="_sLuQEPcaEeSJjd7R-1ZWOQ" id="(0.8409090909090909,0.5576923076923077)"/>
|
||||
<targetAnchor xmi:type="notation:IdentityAnchor" xmi:id="_sLu3IPcaEeSJjd7R-1ZWOQ" id="(0.7560975609756098,0.6923076923076923)"/>
|
||||
</edges>
|
||||
<edges xmi:type="notation:Connector" xmi:id="_BudYwPcbEeSJjd7R-1ZWOQ" type="4003" source="_AZp-cPcaEeSJjd7R-1ZWOQ" target="_FW8-4PcaEeSJjd7R-1ZWOQ">
|
||||
<children xmi:type="notation:DecorationNode" xmi:id="_BudYw_cbEeSJjd7R-1ZWOQ" type="6002">
|
||||
<layoutConstraint xmi:type="notation:Location" xmi:id="_BudYxPcbEeSJjd7R-1ZWOQ" x="14" y="-15"/>
|
||||
</children>
|
||||
<children xmi:type="notation:DecorationNode" xmi:id="_BudYxfcbEeSJjd7R-1ZWOQ" type="6004">
|
||||
<layoutConstraint xmi:type="notation:Location" xmi:id="_BudYxvcbEeSJjd7R-1ZWOQ" x="-4" y="16"/>
|
||||
</children>
|
||||
<styles xmi:type="notation:FontStyle" xmi:id="_BudYwfcbEeSJjd7R-1ZWOQ" fontName="Sans"/>
|
||||
<element xmi:type="ecore:EReference" href="RuleEngine.ecore#//Rule/condition"/>
|
||||
<bendpoints xmi:type="notation:RelativeBendpoints" xmi:id="_BudYwvcbEeSJjd7R-1ZWOQ" points="[10, 0, -164, -5]$[135, 0, -39, -5]"/>
|
||||
<sourceAnchor xmi:type="notation:IdentityAnchor" xmi:id="_BugcEPcbEeSJjd7R-1ZWOQ" id="(0.7777777777777778,0.3076923076923077)"/>
|
||||
</edges>
|
||||
<edges xmi:type="notation:Connector" xmi:id="_SSYWsPceEeSJjd7R-1ZWOQ" type="4004" source="_PHsjYPceEeSJjd7R-1ZWOQ" target="_FW8-4PcaEeSJjd7R-1ZWOQ">
|
||||
<styles xmi:type="notation:FontStyle" xmi:id="_SSYWsfceEeSJjd7R-1ZWOQ" fontName="Sans"/>
|
||||
<element xsi:nil="true"/>
|
||||
<bendpoints xmi:type="notation:RelativeBendpoints" xmi:id="_SSYWsvceEeSJjd7R-1ZWOQ" points="[-13, -5, 130, 0]$[-130, -18, 13, -13]"/>
|
||||
<sourceAnchor xmi:type="notation:IdentityAnchor" xmi:id="_SSZk0PceEeSJjd7R-1ZWOQ" id="(0.09027777777777778,0.34615384615384615)"/>
|
||||
<targetAnchor xmi:type="notation:IdentityAnchor" xmi:id="_SSZk0fceEeSJjd7R-1ZWOQ" id="(0.8108108108108109,0.11403508771929824)"/>
|
||||
</edges>
|
||||
<edges xmi:type="notation:Connector" xmi:id="_iVJA8PckEeSJjd7R-1ZWOQ" type="4003" source="_PHsjYPceEeSJjd7R-1ZWOQ" target="_FW8-4PcaEeSJjd7R-1ZWOQ">
|
||||
<children xmi:type="notation:DecorationNode" xmi:id="_iVJoAPckEeSJjd7R-1ZWOQ" type="6002">
|
||||
<layoutConstraint xmi:type="notation:Location" xmi:id="_iVJoAfckEeSJjd7R-1ZWOQ" x="-8" y="-19"/>
|
||||
</children>
|
||||
<children xmi:type="notation:DecorationNode" xmi:id="_iVJoAvckEeSJjd7R-1ZWOQ" type="6004">
|
||||
<layoutConstraint xmi:type="notation:Location" xmi:id="_iVJoA_ckEeSJjd7R-1ZWOQ" x="-38" y="12"/>
|
||||
</children>
|
||||
<styles xmi:type="notation:FontStyle" xmi:id="_iVJA8fckEeSJjd7R-1ZWOQ" fontName="Sans"/>
|
||||
<element xmi:type="ecore:EReference" href="RuleEngine.ecore#//CompositeCondition/children"/>
|
||||
<bendpoints xmi:type="notation:RelativeBendpoints" xmi:id="_iVJA8vckEeSJjd7R-1ZWOQ" points="[-23, 5, 117, -30]$[-120, 40, 20, 5]"/>
|
||||
<sourceAnchor xmi:type="notation:IdentityAnchor" xmi:id="_iVN5cPckEeSJjd7R-1ZWOQ" id="(0.1564625850340136,0.6440677966101694)"/>
|
||||
<targetAnchor xmi:type="notation:IdentityAnchor" xmi:id="_iVN5cfckEeSJjd7R-1ZWOQ" id="(0.869281045751634,0.6403508771929824)"/>
|
||||
</edges>
|
||||
</notation:Diagram>
|
||||
@@ -6,12 +6,17 @@
|
||||
*/
|
||||
|
||||
|
||||
#include "Rule.h"
|
||||
#include <hos/cosgi/service/ruleengine/rule/Rule.h>
|
||||
|
||||
#include <hos/cosgi/service/scripting/scripting_service.h>
|
||||
|
||||
#include "RuleCondition.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/devicerepository_service.h>
|
||||
#include <HosSchemaLoader/HosSchemaLoader.h>
|
||||
#include "../ruleengine_serviceimpl.h"
|
||||
|
||||
|
||||
namespace hos {
|
||||
@@ -21,40 +26,161 @@ namespace ruleengine {
|
||||
namespace rule {
|
||||
|
||||
|
||||
Rule::Rule(RulePackage* parent, std::string name, scripting::ScriptingService* executionService)
|
||||
: parent(parent), name(name), executionService(executionService) {}
|
||||
Rule::Rule(RulePackage* parent, std::string name)
|
||||
: parent(parent), name(name) {}
|
||||
|
||||
Rule::~Rule() {}
|
||||
|
||||
std::shared_ptr<Rule> Rule::createRule(RulePackage* parent, std::string name, scripting::GlobalScriptContext* gContext, Variant raw, scripting::ScriptingService* executionService) {
|
||||
std::shared_ptr<Rule> retval;
|
||||
if (raw.IsMap()) {
|
||||
retval = std::make_shared<Rule>(parent, name, executionService);
|
||||
if (raw.HasPath("conditions") && raw["conditions"].IsList()) {
|
||||
auto conditions = raw["conditions"];
|
||||
for (auto c : conditions.AsList()) {
|
||||
auto cond = RuleCondition::createRuleCondition(retval.get(), c);
|
||||
retval->conditions.push_back(cond);
|
||||
libvariant::HosSchemaLoader* Rule::getHOSSchemaLoader() {
|
||||
return parent->parent->getHOSSchemaLoader();
|
||||
}
|
||||
|
||||
std::shared_ptr<RuleCondition> Rule::createRuleCondition(Variant condition) {
|
||||
std::shared_ptr<RuleCondition> retval;
|
||||
if (condition.IsMap() && condition.HasPath("type") && condition["type"].IsString()) {
|
||||
if (condition.HasPath("$schema")) {
|
||||
SchemaResult sr = SchemaValidate(Variant(), condition, getHOSSchemaLoader());
|
||||
if (sr.Error()) {
|
||||
throw std::runtime_error(sr.PrettyPrintMessage());
|
||||
}
|
||||
if (raw.HasPath("body") && raw["body"].IsMap()) {
|
||||
retval->body = executionService->createScript(gContext, raw["body"]);
|
||||
}
|
||||
auto conditionFactory = parent->getConditionFactory(condition["type"].AsString());
|
||||
if (conditionFactory) {
|
||||
auto cond = conditionFactory->createCondition(this, condition);
|
||||
if (cond) {
|
||||
retval = cond;
|
||||
} else {
|
||||
ROS_ERROR_STREAM("Could not create rule \"" << name << "\".");
|
||||
retval.reset();
|
||||
throw std::runtime_error("Could not create condition.");
|
||||
}
|
||||
} else {
|
||||
ROS_ERROR_STREAM("Could not create rule \"" << name << "\".");
|
||||
retval.reset();
|
||||
throw std::runtime_error(std::string("Unknown condition type ").append(condition["type"].AsString()));
|
||||
}
|
||||
} else {
|
||||
throw std::runtime_error("Malformed JSON: missing property type or is not an string.");
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
void Rule::exec(scripting::GlobalScriptContext* gContext) {
|
||||
body->exec(gContext);
|
||||
std::shared_ptr<Rule> Rule::createRule(RulePackage* parent, std::string name, Variant raw) {
|
||||
std::shared_ptr<Rule> retval;
|
||||
if (parent->getRule(name)) {
|
||||
throw std::runtime_error("Rule with the same name already exists.");
|
||||
}
|
||||
if (raw.IsMap()) {
|
||||
retval = std::make_shared<Rule>(parent, name);
|
||||
if (raw.HasPath("condition") && raw["condition"].IsMap()) {
|
||||
auto condition = raw["condition"];
|
||||
auto cond = retval->createRuleCondition(condition);
|
||||
if (cond) {
|
||||
retval->condition = cond;
|
||||
} else {
|
||||
throw std::runtime_error("Could not create condition.");
|
||||
}
|
||||
auto scriptFactory = parent->getScriptingService();
|
||||
//ToDo: Create Condition
|
||||
if (raw.HasPath("script") && raw["script"].IsMap()) {
|
||||
auto script = scriptFactory->createScript(parent->getGlobalScriptContext().get(), raw["script"]);
|
||||
if (script) {
|
||||
retval->script = script;
|
||||
} else {
|
||||
throw std::runtime_error("Could not create script.");
|
||||
}
|
||||
} else {
|
||||
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"]);
|
||||
if (script) {
|
||||
retval->exitScript = script;
|
||||
} else {
|
||||
throw std::runtime_error("Could not create exitscript.");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw std::runtime_error("Malformed JSON: missing property condition.");
|
||||
}
|
||||
} else {
|
||||
throw std::runtime_error("Malformed JSON: not an object.");
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
devicerepository::DeviceRepositoryService* Rule::getDeviceRepository() {
|
||||
return parent->parent->getDeviceRepository();
|
||||
}
|
||||
|
||||
void Rule::enable() {
|
||||
if (!enabled) {
|
||||
if (condition) {
|
||||
condition->enable();
|
||||
}
|
||||
enabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
void Rule::disable() {
|
||||
if (enabled) {
|
||||
if (condition) {
|
||||
condition->disable();
|
||||
}
|
||||
enabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Rule::execute(Variant lContext) {
|
||||
if (enabled && script) {
|
||||
try {
|
||||
script->exec(parent->getGlobalScriptContext().get(), lContext);
|
||||
} catch(std::exception& e) {
|
||||
ROS_ERROR_STREAM(e.what());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Rule::executeExitScript(Variant lContext) {
|
||||
if (enabled && exitScript) {
|
||||
try {
|
||||
exitScript->exec(parent->getGlobalScriptContext().get(), lContext);
|
||||
} catch(std::exception& e) {
|
||||
ROS_ERROR_STREAM(e.what());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool Rule::evaluate() {
|
||||
if (condition) {
|
||||
bool retval = condition->evaluate();
|
||||
if (retval != currentState) {
|
||||
currentState = retval;
|
||||
if (retval) {
|
||||
execute();
|
||||
} else {
|
||||
executeExitScript();
|
||||
}
|
||||
}
|
||||
return retval;
|
||||
} else {
|
||||
currentState = false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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) {
|
||||
currentState = newState;
|
||||
if (newState) {
|
||||
execute(lcontext);
|
||||
} else {
|
||||
executeExitScript(lcontext);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} /* namespace rule */
|
||||
} /* namespace ruleengine */
|
||||
} /* namespace service */
|
||||
|
||||
@@ -1,75 +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>
|
||||
|
||||
|
||||
// Forward declarations
|
||||
namespace hos {
|
||||
namespace cosgi {
|
||||
namespace service {
|
||||
namespace ruleengine {
|
||||
namespace rule {
|
||||
class RulePackage;
|
||||
class RuleCondition;
|
||||
}
|
||||
}
|
||||
namespace scripting {
|
||||
class ScriptingService;
|
||||
class GlobalScriptContext;
|
||||
class Script;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
namespace hos {
|
||||
namespace cosgi {
|
||||
namespace service {
|
||||
namespace ruleengine {
|
||||
namespace rule {
|
||||
|
||||
using namespace libvariant;
|
||||
|
||||
class Rule {
|
||||
private:
|
||||
RulePackage* parent;
|
||||
std::string name;
|
||||
std::shared_ptr<scripting::Script> body;
|
||||
std::vector<std::shared_ptr<RuleCondition>> conditions;
|
||||
scripting::ScriptingService* executionService;
|
||||
public:
|
||||
Rule(RulePackage* parent, std::string name, scripting::ScriptingService* executionService);
|
||||
static std::shared_ptr<Rule> createRule(RulePackage* parent, std::string name, scripting::GlobalScriptContext* gContext, Variant raw, scripting::ScriptingService* executionService);
|
||||
virtual ~Rule();
|
||||
|
||||
RulePackage* getParent() { return parent; }
|
||||
|
||||
virtual void exec(scripting::GlobalScriptContext* gContext);
|
||||
};
|
||||
|
||||
class RuleBody {
|
||||
|
||||
};
|
||||
|
||||
|
||||
} /* namespace rule */
|
||||
} /* namespace ruleengine */
|
||||
} /* namespace service */
|
||||
} /* namespace cosgi */
|
||||
} /* namespace hos */
|
||||
|
||||
|
||||
#endif /* SRC_HOS_COSGI_SERVICE_RULEENGINE_RULE_RULE_H_ */
|
||||
@@ -1,27 +0,0 @@
|
||||
/*
|
||||
* RuleCondition.cpp
|
||||
*
|
||||
* Created on: 29.04.2015
|
||||
* Author: matzman
|
||||
*/
|
||||
|
||||
#include "RuleCondition.h"
|
||||
|
||||
namespace hos {
|
||||
namespace cosgi {
|
||||
namespace service {
|
||||
namespace ruleengine {
|
||||
namespace rule {
|
||||
|
||||
|
||||
std::shared_ptr<RuleCondition> RuleCondition::createRuleCondition(Rule* parent, Variant raw) {
|
||||
std::shared_ptr<RuleCondition> retval;
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
} /* namespace rule */
|
||||
} /* namespace ruleengine */
|
||||
} /* namespace service */
|
||||
} /* namespace cosgi */
|
||||
} /* namespace hos */
|
||||
@@ -1,125 +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 <Variant/Variant.h>
|
||||
#include <hos/common/observable.h>
|
||||
|
||||
|
||||
// Forward declarations
|
||||
namespace hos {
|
||||
namespace cosgi {
|
||||
namespace service {
|
||||
namespace ruleengine {
|
||||
namespace rule {
|
||||
class Rule;
|
||||
class ConditionDeviceStatusMsgEvent;
|
||||
class ConditionDeviceCommandMsgEvent;
|
||||
class ConditionDeviceStateChangedEvent;
|
||||
class ConditionSystemEvent;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
namespace hos {
|
||||
namespace cosgi {
|
||||
namespace service {
|
||||
namespace ruleengine {
|
||||
namespace rule {
|
||||
|
||||
using namespace libvariant;
|
||||
|
||||
class RuleCondition {
|
||||
public:
|
||||
enum class Type : int { DeviceStatusMsg, DeviceCommandMsg, DeviceStateChange, TimeEventCondition, SystemEventCondition};
|
||||
protected:
|
||||
Rule* parent;
|
||||
const Type type;
|
||||
public:
|
||||
static std::shared_ptr<RuleCondition> createRuleCondition(Rule* parent, libvariant::Variant raw);
|
||||
RuleCondition(Rule* parent, Type type) : parent(parent), type(type) {}
|
||||
public:
|
||||
virtual ~RuleCondition() {}
|
||||
|
||||
const Type getType() { return type; }
|
||||
};
|
||||
|
||||
|
||||
class DeviceMsgCondition : public RuleCondition {
|
||||
friend class RuleCondition;
|
||||
protected:
|
||||
Variant msgValue;
|
||||
std::string msgPath;
|
||||
std::string deviceID;
|
||||
DeviceMsgCondition(Rule* parent, Type type, const Variant msgValue, const std::string msgPath, std::string deviceID)
|
||||
: RuleCondition(parent, type) {}
|
||||
};
|
||||
|
||||
|
||||
class DeviceStatusMsgCondition : public DeviceMsgCondition, public common::IObserver<std::string, ConditionDeviceStatusMsgEvent> {
|
||||
friend class RuleCondition;
|
||||
protected:
|
||||
DeviceStatusMsgCondition(Rule* parent, const Variant msgValue, const std::string msgPath, std::string deviceID)
|
||||
: DeviceMsgCondition(parent, Type::DeviceStatusMsg, msgValue, msgPath, deviceID) {}
|
||||
public:
|
||||
virtual void notificationReceived(common::Observable<std::string, ConditionDeviceStatusMsgEvent>* o, const std::string key, const ConditionDeviceStatusMsgEvent* e) override;
|
||||
};
|
||||
|
||||
|
||||
|
||||
class DeviceCommandMsgCondition : public DeviceMsgCondition, public common::IObserver<std::string, ConditionDeviceCommandMsgEvent> {
|
||||
friend class RuleCondition;
|
||||
protected:
|
||||
public:
|
||||
DeviceCommandMsgCondition(Rule* parent, const Variant msgValue, const std::string msgPath, std::string deviceID)
|
||||
: DeviceMsgCondition(parent, Type::DeviceCommandMsg, msgValue, msgPath, deviceID) {}
|
||||
virtual void notificationReceived(common::Observable<std::string, ConditionDeviceCommandMsgEvent>* o, const std::string key, const ConditionDeviceCommandMsgEvent* e) override;
|
||||
};
|
||||
|
||||
|
||||
|
||||
class DeviceStateChangeCondition : public RuleCondition, public common::IObserver<std::string, ConditionDeviceStateChangedEvent> {
|
||||
friend class RuleCondition;
|
||||
protected:
|
||||
public:
|
||||
DeviceStateChangeCondition(Rule* parent) : RuleCondition(parent, Type::DeviceStateChange) {}
|
||||
virtual void notificationReceived(common::Observable<std::string, ConditionDeviceStateChangedEvent>* o, const std::string key, const ConditionDeviceStateChangedEvent* e) override;
|
||||
};
|
||||
|
||||
|
||||
|
||||
class TimeEventCondition : public RuleCondition {
|
||||
friend class RuleCondition;
|
||||
protected:
|
||||
public:
|
||||
TimeEventCondition(Rule* parent) : RuleCondition(parent, Type::TimeEventCondition) {}
|
||||
};
|
||||
|
||||
|
||||
|
||||
class SystemEventCondition : public RuleCondition, public common::IObserver<std::string, ConditionSystemEvent> {
|
||||
friend class RuleCondition;
|
||||
protected:
|
||||
public:
|
||||
SystemEventCondition(Rule* parent) : RuleCondition(parent, Type::SystemEventCondition) {}
|
||||
virtual void notificationReceived(common::Observable<std::string, ConditionSystemEvent>* o, const std::string key, const ConditionSystemEvent* e) override;
|
||||
};
|
||||
|
||||
|
||||
} /* namespace rule */
|
||||
} /* namespace ruleengine */
|
||||
} /* namespace service */
|
||||
} /* namespace cosgi */
|
||||
} /* namespace hos */
|
||||
|
||||
#endif /* SRC_HOS_COSGI_SERVICE_RULEENGINE_RULE_RULECONDITION_H_ */
|
||||
@@ -0,0 +1,409 @@
|
||||
/*
|
||||
* 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/devicerepository_service.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>::observable_type* o, const devicerepository::DeviceEvent* e) {
|
||||
switch (e->eventType) {
|
||||
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;
|
||||
device->addObserver(dynamic_cast<interface::Observer<devicerepository::DeviceEvent>*>(this));
|
||||
}
|
||||
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(dynamic_cast<interface::Observer<devicerepository::DeviceEvent>*>(this));
|
||||
device = nullptr;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void DeviceMsgCondition::enable() {
|
||||
auto repoService = parent->getDeviceRepository();
|
||||
auto device = repoService->getDevice(deviceID);
|
||||
if (device) {
|
||||
this->device = device.get();
|
||||
device->addObserver(dynamic_cast<interface::Observer<devicerepository::DeviceEvent>*>(this));
|
||||
} else {
|
||||
ROS_ERROR_STREAM("Rule " << parent->name << ": Could not find device " << deviceID);
|
||||
repoService->addObserver(dynamic_cast<interface::Observer<devicerepository::DeviceEvent>*>(this));
|
||||
}
|
||||
}
|
||||
|
||||
void DeviceMsgCondition::disable() {
|
||||
if (device) {
|
||||
device->deleteObserver(this);
|
||||
device = nullptr;
|
||||
}
|
||||
parent->getDeviceRepository()->deleteObserver(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>::observable_type* o, const devicerepository::DeviceEvent* e) {
|
||||
switch (e->eventType) {
|
||||
case devicerepository::DeviceEvent::EventType::STATUS_UPDATED:
|
||||
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.Get(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, 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>::observable_type* o, const devicerepository::DeviceEvent* e) {
|
||||
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.Get(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, 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.");
|
||||
}
|
||||
}
|
||||
retval = std::make_shared<DeviceStateChangeCondition>(parent, deviceID, statePointer);
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
void DeviceStateChangeCondition::notificationReceived(interface::Observer<devicerepository::DeviceEvent>::observable_type* o, const devicerepository::DeviceEvent* e) {
|
||||
switch (e->eventType) {
|
||||
case devicerepository::DeviceEvent::EventType::STATUS_UPDATED:
|
||||
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().Get(msgPointer);
|
||||
last = device->getLastState().Get(msgPointer);
|
||||
} else {
|
||||
notify = false;
|
||||
}
|
||||
if (notify && current == last) {
|
||||
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, e);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} /* namespace rule */
|
||||
} /* namespace ruleengine */
|
||||
} /* namespace service */
|
||||
} /* namespace cosgi */
|
||||
} /* namespace hos */
|
||||
@@ -0,0 +1,140 @@
|
||||
/*
|
||||
* 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>
|
||||
|
||||
|
||||
// Service Namespace
|
||||
namespace hos {
|
||||
namespace cosgi {
|
||||
namespace service {
|
||||
namespace devicerepository {
|
||||
class Device;
|
||||
class DeviceEvent;
|
||||
class DeviceRepositoryService;
|
||||
}
|
||||
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> {
|
||||
protected:
|
||||
const std::string deviceID;
|
||||
devicerepository::Device* device = nullptr;
|
||||
const std::string msgPointer;
|
||||
const Variant msgValue;
|
||||
|
||||
public:
|
||||
DeviceMsgCondition(Rule* parent, Category category, std::string type, const std::string deviceID, const std::string msgPointer, const Variant msgValue) :
|
||||
RuleCondition(parent, category, type), deviceID(deviceID), msgPointer(msgPointer), msgValue(msgValue) {}
|
||||
|
||||
virtual void notificationReceived(interface::Observer<devicerepository::DeviceEvent>::observable_type* o, const devicerepository::DeviceEvent* e) 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) {}
|
||||
|
||||
virtual void notificationReceived(interface::Observer<devicerepository::DeviceEvent>::observable_type* o, const devicerepository::DeviceEvent* e) 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) {}
|
||||
|
||||
virtual void notificationReceived(interface::Observer<devicerepository::DeviceEvent>::observable_type* o, const devicerepository::DeviceEvent* e) 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 {
|
||||
public:
|
||||
DeviceStateChangeCondition(Rule* parent, const std::string deviceID, const std::string statePointer) :
|
||||
DeviceMsgCondition(parent, Category::Event, "devicestatechange", deviceID, statePointer, Variant()) {}
|
||||
|
||||
virtual void notificationReceived(interface::Observer<devicerepository::DeviceEvent>::observable_type* o, const devicerepository::DeviceEvent* e) 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_ */
|
||||
@@ -12,7 +12,7 @@
|
||||
#include <Variant/Variant.h>
|
||||
|
||||
#include "../ruleengine_serviceimpl.h"
|
||||
#include "Rule.h"
|
||||
#include <hos/cosgi/service/ruleengine/rule/Rule.h>
|
||||
|
||||
namespace hos {
|
||||
namespace cosgi {
|
||||
@@ -20,21 +20,26 @@ namespace service {
|
||||
namespace ruleengine {
|
||||
namespace rule {
|
||||
|
||||
RulePackage::RulePackage(service::ruleengine::RuleEngineServiceImpl* parent, std::string name)
|
||||
: parent(parent), name(name) {}
|
||||
RulePackage::RulePackage(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() {
|
||||
}
|
||||
RulePackage::~RulePackage() {}
|
||||
|
||||
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::stringstream ss;
|
||||
ss << "Could not create package " << name << ": There already exists a rule package with the same name.";
|
||||
throw std::runtime_error(ss.str());
|
||||
}
|
||||
if (raw.IsMap()) {
|
||||
retval = std::make_shared<RulePackage>(parent, name);
|
||||
std::string language;
|
||||
if (raw.HasPath("language") && raw["language"].IsString()) {
|
||||
retval->language = raw["language"].AsString();
|
||||
language = raw["language"].AsString();
|
||||
} 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->executionService = parent->getExecutionBackend(retval->language);
|
||||
if (retval->executionService) {
|
||||
if (raw.HasPath("globalcontext")) {
|
||||
@@ -42,9 +47,13 @@ std::shared_ptr<RulePackage> RulePackage::createRulePackage(service::ruleengine:
|
||||
}
|
||||
if (raw.HasPath("rules") && raw["rules"].IsMap()) {
|
||||
for (auto r : raw["rules"].AsMap()) {
|
||||
std::shared_ptr<Rule> rule = Rule::createRule(retval.get(), r.first, retval->globalContext.get(), r.second, retval->executionService);
|
||||
if (rule) {
|
||||
retval->rules.insert({r.first, rule});
|
||||
try {
|
||||
std::shared_ptr<Rule> rule = Rule::createRule(retval.get(), r.first, r.second);
|
||||
if (rule) {
|
||||
retval->rules.insert({r.first, rule});
|
||||
}
|
||||
} catch (std::runtime_error& e) {
|
||||
ROS_ERROR_STREAM("Error with rule \"" << r.first << "\": " << e.what());
|
||||
}
|
||||
}
|
||||
if (retval->rules.size() == 0) {
|
||||
@@ -53,9 +62,6 @@ std::shared_ptr<RulePackage> RulePackage::createRulePackage(service::ruleengine:
|
||||
} else {
|
||||
throw std::runtime_error("Rule package JSON is missing rules parameter or parameter has wrong type.");
|
||||
}
|
||||
retval->setState(State::Ok);
|
||||
} else {
|
||||
retval->setState(State::NoExecService);
|
||||
}
|
||||
} else {
|
||||
throw std::runtime_error("Rule package JSON is not valid.");
|
||||
@@ -63,6 +69,15 @@ std::shared_ptr<RulePackage> RulePackage::createRulePackage(service::ruleengine:
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
std::shared_ptr<Rule> RulePackage::getRule(const std::string& name) {
|
||||
std::shared_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;
|
||||
for (auto item : this->rules) {
|
||||
@@ -72,6 +87,31 @@ size_t RulePackage::getRules(std::vector<std::shared_ptr<Rule>>& rules) {
|
||||
return retval;
|
||||
}
|
||||
|
||||
std::shared_ptr<RuleConditionFactory> RulePackage::getConditionFactory(std::string type) {
|
||||
return parent->getRuleConditionFactory(type);
|
||||
}
|
||||
|
||||
|
||||
void RulePackage::enable() {
|
||||
if (!enabled) {
|
||||
for (auto r : rules) {
|
||||
r.second->enable();
|
||||
}
|
||||
enabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void RulePackage::disable() {
|
||||
if (enabled) {
|
||||
for (auto r : rules) {
|
||||
r.second->enable();
|
||||
}
|
||||
enabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} /* namespace rule */
|
||||
} /* namespace ruleengine */
|
||||
} /* namespace service */
|
||||
|
||||
@@ -23,6 +23,7 @@ namespace cosgi {
|
||||
namespace service {
|
||||
namespace ruleengine {
|
||||
class RuleEngineServiceImpl;
|
||||
class RuleConditionFactory;
|
||||
namespace rule {
|
||||
class Rule;
|
||||
}
|
||||
@@ -44,38 +45,35 @@ namespace rule {
|
||||
using namespace ::libvariant;
|
||||
|
||||
class RulePackage {
|
||||
public:
|
||||
enum class State : int { Error = 0, NoExecService = 1, Ok = 2 };
|
||||
|
||||
private:
|
||||
typedef std::map<std::string, std::shared_ptr<Rule>> ruleMap_t;
|
||||
|
||||
std::string name;
|
||||
std::string language;
|
||||
State state = State::Error;
|
||||
ruleMap_t rules;
|
||||
std::shared_ptr<scripting::GlobalScriptContext> globalContext;
|
||||
service::ruleengine::RuleEngineServiceImpl* parent;
|
||||
service::scripting::ScriptingService* executionService = nullptr;
|
||||
|
||||
|
||||
bool setState(State state) {
|
||||
this->state = state;
|
||||
return true;
|
||||
}
|
||||
std::shared_ptr<scripting::GlobalScriptContext> globalContext;
|
||||
ruleMap_t rules;
|
||||
Variant rawValue;
|
||||
bool enabled = false;
|
||||
|
||||
|
||||
public:
|
||||
RulePackage(service::ruleengine::RuleEngineServiceImpl* parent, std::string name);
|
||||
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) { return rules[name]; }
|
||||
std::shared_ptr<Rule> getRule(const std::string& name);
|
||||
size_t getRules(std::vector<std::shared_ptr<Rule>>& rules);
|
||||
|
||||
service::ruleengine::RuleEngineServiceImpl* getParent() { return parent; }
|
||||
bool isEnabled() { return enabled; }
|
||||
void enable();
|
||||
void disable();
|
||||
|
||||
State getState() { return state; }
|
||||
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 */
|
||||
|
||||
+56
-17
@@ -10,12 +10,14 @@
|
||||
#include <exception>
|
||||
#include <iostream>
|
||||
#include <unordered_set>
|
||||
#include <Variant/Variant.h>
|
||||
#include <celix/bundle_context.h>
|
||||
#include <hos/cosgi/service/core/core_service.h>
|
||||
#include <hos/cosgi/service/devicerepository/devicerepository_service.h>
|
||||
#include <Variant/Variant.h>
|
||||
|
||||
#include "rule/RulePackage.h"
|
||||
#include "rule/RuleConditionImpl.h"
|
||||
|
||||
|
||||
namespace hos {
|
||||
@@ -23,6 +25,9 @@ namespace cosgi {
|
||||
namespace service {
|
||||
namespace ruleengine {
|
||||
|
||||
using namespace ::libvariant;
|
||||
|
||||
|
||||
template<> const char* implementation::ServiceInfo<RuleEngineServiceImpl>::name = service::ruleengine::RuleEngineService::SERVICE_NAME;
|
||||
template<> const int implementation::ServiceInfo<RuleEngineServiceImpl>::major = 0;
|
||||
template<> const int implementation::ServiceInfo<RuleEngineServiceImpl>::minor = 6;
|
||||
@@ -57,6 +62,11 @@ void RuleEngineServiceImpl::init() {
|
||||
XmlRpc::XmlRpcValue rawValue;
|
||||
nodehandle->getParam(ruleEnginePrefix, rawValue);
|
||||
schemaLoader.reset(new libvariant::HosSchemaLoader());
|
||||
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>());
|
||||
if (rawValue.getType() == XmlRpc::XmlRpcValue::TypeStruct) {
|
||||
for (auto pkg : rawValue) {
|
||||
std::string pname = pkg.first;
|
||||
@@ -80,26 +90,28 @@ RuleEngineServiceImpl::~RuleEngineServiceImpl() {
|
||||
|
||||
|
||||
void RuleEngineServiceImpl::notificationReceived(interface::Observer<devicerepository::DeviceEvent>::observable_type* o, const devicerepository::DeviceEvent* e) {
|
||||
switch (e->eventType) {
|
||||
/*switch (e->eventType) {
|
||||
case devicerepository::DeviceEvent::EventType::STATUS_UPDATED:
|
||||
{
|
||||
ConditionDeviceStatusMsgEvent event;
|
||||
common::Observable<std::string, ConditionDeviceStatusMsgEvent>::notifyObservers(e->device->getDeviceID(), &event);
|
||||
if (e->device->stateChanged()) {
|
||||
ConditionDeviceStateChangedEvent event;
|
||||
common::Observable<std::string, ConditionDeviceStateChangedEvent>::notifyObservers(e->device->getDeviceID(), &event);
|
||||
auto e2 = static_cast<const devicerepository::DeviceStatusMsgEvent*>(e);
|
||||
rule::DeviceStatusMsgEvent event(e2->device, e2->statusMsg, std::chrono::system_clock::now());
|
||||
this->notifyObservers(&event, (int)rule::RuleEvent::EventType::DeviceStatusMsgEvent);
|
||||
if (e2->device->getCurrentState() != e2->device->getLastState()) {
|
||||
rule::DeviceStateChangeEvent event2(e2->device, std::chrono::system_clock::now());
|
||||
this->notifyObservers(&event2, (int)rule::RuleEvent::EventType::DeviceStateChangeEvent);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case devicerepository::DeviceEvent::EventType::COMMAND_RECEIVED:
|
||||
{
|
||||
ConditionDeviceCommandMsgEvent event;
|
||||
common::Observable<std::string, ConditionDeviceCommandMsgEvent>::notifyObservers(e->device->getDeviceID(), &event);
|
||||
auto e2 = static_cast<const devicerepository::DeviceCmdMsgEvent*>(e);
|
||||
rule::DeviceCommandMsgEvent event(e2->device, e2->cmdMsg, std::chrono::system_clock::now());
|
||||
this->notifyObservers(&event, (int)rule::RuleEvent::EventType::DeviceCommandMsgEvent);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
void RuleEngineServiceImpl::addRulePkg(std::string name) {
|
||||
@@ -111,23 +123,32 @@ void RuleEngineServiceImpl::addRulePkg(std::string name) {
|
||||
}
|
||||
|
||||
void RuleEngineServiceImpl::addRulePkg(std::string name, std::string rawValue) {
|
||||
::libvariant::Variant s = schemaLoader->GetSchema(std::string("hos:schema://core/configs/rulepackage"));
|
||||
::libvariant::Variant m = ::libvariant::Deserialize(rawValue, ::libvariant::SERIALIZE_JSON);
|
||||
::libvariant::SchemaResult sr = ::libvariant::SchemaValidate(s, m, schemaLoader.get());
|
||||
Variant s = schemaLoader->GetSchema(std::string("hos:schema://core/configs/rulepackage"));
|
||||
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);
|
||||
if (pkg) {
|
||||
if (pkg->getState() == rule::RulePackage::State::Ok) {
|
||||
if (pkg->getScriptingService()) {
|
||||
rulePkgs.insert({name, pkg});
|
||||
} else if (pkg->getState() == rule::RulePackage::State::NoExecService) {
|
||||
pkg->enable();
|
||||
} else {
|
||||
noExecRulePkgs.insert({name, pkg});
|
||||
std::cout << "No execution service found for rule package \"" << name << "\"" << std::endl;
|
||||
} else {
|
||||
throw std::runtime_error("Unknown state");
|
||||
}
|
||||
} else {
|
||||
throw std::runtime_error("Unknown error during rule package creation.");
|
||||
}
|
||||
} else {
|
||||
throw std::runtime_error(sr.PrettyPrintMessage());
|
||||
std::stringstream ss;
|
||||
ss << sr.PrettyPrintMessage() << std::endl;
|
||||
for (auto e : sr.GetErrors()) {
|
||||
ss << e.PrettyPrintMessage() << std::endl;
|
||||
}
|
||||
for (auto e : sr.GetSchemaErrors()) {
|
||||
ss << e.PrettyPrintMessage() << std::endl;
|
||||
}
|
||||
throw std::runtime_error(ss.str());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -152,6 +173,24 @@ scripting::ScriptingService* RuleEngineServiceImpl::getExecutionBackend(std::str
|
||||
}
|
||||
|
||||
|
||||
void RuleEngineServiceImpl::registerRuleConditionFactory(std::string type, std::shared_ptr<RuleConditionFactory> factory) {
|
||||
condFactories.insert({type, factory});
|
||||
}
|
||||
|
||||
|
||||
std::shared_ptr<rule::RulePackage> RuleEngineServiceImpl::getRulePackage(const std::string& name) {
|
||||
std::shared_ptr<rule::RulePackage> retval;
|
||||
try {
|
||||
retval = rulePkgs.at(name);
|
||||
} catch(...) {}
|
||||
if (!retval) {
|
||||
try {
|
||||
retval = noExecRulePkgs.at(name);
|
||||
} catch(...) {}
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
} // namespace ruleengine
|
||||
} // namespace service
|
||||
} // namespace cosgi
|
||||
|
||||
+20
-34
@@ -9,21 +9,19 @@
|
||||
#define INCLUDE_HOS_COSGI_RULEENGINE_HOS_RULEENGINE_SERVICE_IMPL_H_
|
||||
|
||||
#include <hos/cosgi/service/implementation.h>
|
||||
#include <Variant/Variant.h>
|
||||
#include <mutex>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <hos_core_msgs/DeviceCommand.h>
|
||||
#include <hos_core_msgs/DeviceStatusUpdate.h>
|
||||
#include <hos_core_msgs/HosStatusUpdate.h>
|
||||
#include <HosSchemaLoader/HosSchemaLoader.h>
|
||||
#include <hos/cosgi/service/ruleengine/ruleengine_service.h>
|
||||
#include <hos/cosgi/service/scripting/scripting_service.h>
|
||||
#include <hos/cosgi/service/devicerepository/devicerepository_service.h>
|
||||
#include <HosSchemaLoader/HosSchemaLoader.h>
|
||||
#include <ros/ros.h>
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
|
||||
// Forward declarations
|
||||
@@ -36,15 +34,6 @@ namespace service {
|
||||
namespace core {
|
||||
class CoreService;
|
||||
}
|
||||
namespace ruleengine {
|
||||
namespace rule {
|
||||
class RulePackage;
|
||||
}
|
||||
}
|
||||
}
|
||||
namespace devicerepository {
|
||||
class DeviceRepositoryService;
|
||||
class DeviceEvent;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -56,35 +45,21 @@ namespace hos {
|
||||
namespace cosgi {
|
||||
namespace service {
|
||||
namespace ruleengine {
|
||||
namespace rule {
|
||||
|
||||
class RulePackage;
|
||||
}
|
||||
|
||||
class ConditionDeviceStatusMsgEvent {
|
||||
|
||||
};
|
||||
|
||||
class ConditionDeviceCommandMsgEvent {
|
||||
|
||||
};
|
||||
|
||||
class ConditionDeviceStateChangedEvent {
|
||||
|
||||
};
|
||||
|
||||
class ConditionSystemEvent {
|
||||
|
||||
};
|
||||
|
||||
class RuleEngineServiceImpl;
|
||||
class RuleEngineServiceImpl : public virtual RuleEngineService,
|
||||
class RuleEngineServiceImpl :
|
||||
public virtual RuleEngineService,
|
||||
public virtual implementation::Service<RuleEngineServiceImpl>,
|
||||
public virtual interface::Observer<devicerepository::DeviceEvent>,
|
||||
public common::Observable<std::string, ConditionDeviceStatusMsgEvent>,
|
||||
public common::Observable<std::string, ConditionDeviceCommandMsgEvent>,
|
||||
public common::Observable<std::string, ConditionDeviceStateChangedEvent>,
|
||||
public common::Observable<std::string, ConditionSystemEvent> {
|
||||
public virtual interface::Observer<devicerepository::DeviceEvent> {
|
||||
private:
|
||||
typedef std::map<std::string, std::shared_ptr<rule::RulePackage>> rulePkgMap_t;
|
||||
typedef std::map<std::string, scripting::ScriptingService*> execServiceMap_t;
|
||||
typedef std::map<std::string, std::shared_ptr<RuleConditionFactory>> condFactoryMap_t;
|
||||
|
||||
service::core::CoreService* coreService = nullptr;
|
||||
devicerepository::DeviceRepositoryService* repoService = nullptr;
|
||||
@@ -95,6 +70,7 @@ private:
|
||||
rulePkgMap_t rulePkgs;
|
||||
rulePkgMap_t noExecRulePkgs;
|
||||
execServiceMap_t execServices;
|
||||
condFactoryMap_t condFactories;
|
||||
|
||||
std::unique_ptr<libvariant::HosSchemaLoader> schemaLoader;
|
||||
|
||||
@@ -115,6 +91,8 @@ public:
|
||||
|
||||
/* Public Service Implementations */
|
||||
|
||||
virtual void registerRuleConditionFactory(std::string type, std::shared_ptr<RuleConditionFactory> factory) override;
|
||||
|
||||
/* Observer Callbacks */
|
||||
|
||||
virtual void notificationReceived(interface::Observer<devicerepository::DeviceEvent>::observable_type* o, const devicerepository::DeviceEvent* e) override;
|
||||
@@ -125,6 +103,14 @@ public:
|
||||
|
||||
void addExecutionBackend(scripting::ScriptingService* service);
|
||||
scripting::ScriptingService* getExecutionBackend(std::string language);
|
||||
|
||||
std::shared_ptr<RuleConditionFactory> getRuleConditionFactory(std::string type) { return condFactories[type]; }
|
||||
|
||||
libvariant::HosSchemaLoader* getHOSSchemaLoader() { return schemaLoader.get(); }
|
||||
|
||||
devicerepository::DeviceRepositoryService* getDeviceRepository() { return repoService; }
|
||||
|
||||
std::shared_ptr<rule::RulePackage> getRulePackage(const std::string& name);
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
{
|
||||
"language": "python",
|
||||
"globalcontext": { "code": "print \"GlobalContext: Init!\"\nvar1 = 66.6\nvar2 = 0"},
|
||||
"rules": {
|
||||
"rule01": {
|
||||
"condition": {
|
||||
"type": "logical_or",
|
||||
"$schema": "hos:schema://core/configs/rulepackage#/definitions/LogicalOrCondition",
|
||||
"conditions" : [
|
||||
{
|
||||
"type": "devicestatusmsg",
|
||||
"deviceid": "huelight3"
|
||||
},
|
||||
{
|
||||
"type": "devicestatechange",
|
||||
"deviceid": "huelight4"
|
||||
}
|
||||
]
|
||||
},
|
||||
"script": { "code": "print \"Hello World1: \" + str(var2)\nvar2 = var2 + 1\nimport __main__\nprint \"__main__.__dict__: \" + str(__main__.__dict__)\nprint \"test: \" + event_type" },
|
||||
"exitscript": { "code": "print \"Hello from exitscript1\"" }
|
||||
},
|
||||
"rule02": {
|
||||
"condition": {
|
||||
"type": "logical_or",
|
||||
"$schema": "hos:schema://core/configs/rulepackage#/definitions/LogicalOrCondition",
|
||||
"conditions" : [
|
||||
{
|
||||
"type": "devicecommandmsg",
|
||||
"deviceid": "huelight4"
|
||||
},
|
||||
{
|
||||
"type": "devicestatechange",
|
||||
"deviceid": "huelight3"
|
||||
}
|
||||
]
|
||||
},
|
||||
"script": { "code": "print \"Hello World2: \" + str(var2)\nvar2 = var2 + 1\nimport __main__\nprint \"__main__.__dict__: \" + str(__main__.__dict__)\nprint \"test: \" + event_type" },
|
||||
"exitscript": { "code": "print \"Hello from exitscript2\"" }
|
||||
},
|
||||
"rule03": {
|
||||
"condition": {
|
||||
"type": "logical_or",
|
||||
"$schema": "hos:schema://core/configs/rulepackage#/definitions/LogicalOrCondition",
|
||||
"conditions" : [
|
||||
{
|
||||
"type": "devicecommandmsg",
|
||||
"deviceid": "huelight1"
|
||||
},
|
||||
{
|
||||
"type": "devicestatechange",
|
||||
"deviceid": "huelight2"
|
||||
}
|
||||
]
|
||||
},
|
||||
"script": { "code": "print \"Hello World3: \" + str(var2)\nvar2 = var2 + 1\nimport __main__\nprint \"__main__.__dict__: \" + str(__main__.__dict__)\nprint \"test: \" + event_type" },
|
||||
"exitscript": { "code": "print \"Hello from exitscript2\"" }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,32 +1,60 @@
|
||||
{
|
||||
"language": "python",
|
||||
"globalcontext": { "code": "print \"GlobalContext: Init!\"\nvar1 = 66.6"},
|
||||
"globalcontext": { "code": "print \"GlobalContext: Init!\"\nvar1 = 66.6\nvar2 = 0"},
|
||||
"rules": {
|
||||
"rule01": {
|
||||
"conditions": [
|
||||
{
|
||||
"type": "devicemsg",
|
||||
"deviceid": "huelight1"
|
||||
},
|
||||
{
|
||||
"type": "devicestatus",
|
||||
"deviceid": "huelight1"
|
||||
}
|
||||
],
|
||||
"body": { "code": "print \"Hello World1\"" }
|
||||
"condition": {
|
||||
"type": "logical_or",
|
||||
"$schema": "hos:schema://core/configs/rulepackage#/definitions/LogicalOrCondition",
|
||||
"conditions" : [
|
||||
{
|
||||
"type": "devicestatusmsg",
|
||||
"deviceid": "huelight3"
|
||||
},
|
||||
{
|
||||
"type": "devicestatechange",
|
||||
"deviceid": "huelight4"
|
||||
}
|
||||
]
|
||||
},
|
||||
"script": { "code": "global var2\nprint \"Hello World1: \" + str(var2)\nvar2 = var2 + 1\nimport __main__\nprint \"__main__.__dict__: \" + str(__main__.__dict__)\nprint \"test: \" + event_type\ndev1 = Devices.getDevice(\"huelight1\")\nprint \"device: \" + dev1.deviceID()" },
|
||||
"exitscript": { "code": "print \"Hello from exitscript1\"" }
|
||||
},
|
||||
"rule02": {
|
||||
"conditions": [
|
||||
{
|
||||
"type": "devicemsg",
|
||||
"deviceid": "huelight2"
|
||||
},
|
||||
{
|
||||
"type": "devicestatus",
|
||||
"deviceid": "huelight2"
|
||||
}
|
||||
],
|
||||
"body": { "code": "print \"Hello World2\"" }
|
||||
"condition": {
|
||||
"type": "logical_or",
|
||||
"$schema": "hos:schema://core/configs/rulepackage#/definitions/LogicalOrCondition",
|
||||
"conditions" : [
|
||||
{
|
||||
"type": "devicecommandmsg",
|
||||
"deviceid": "huelight4"
|
||||
},
|
||||
{
|
||||
"type": "devicestatechange",
|
||||
"deviceid": "huelight3"
|
||||
}
|
||||
]
|
||||
},
|
||||
"script": { "code": "print \"Hello World2: \" + str(var2)\nvar2 = var2 + 1\nimport __main__\nprint \"__main__.__dict__: \" + str(__main__.__dict__)\nprint \"test: \" + event_type" },
|
||||
"exitscript": { "code": "print \"Hello from exitscript2\"" }
|
||||
},
|
||||
"rule03": {
|
||||
"condition": {
|
||||
"type": "logical_or",
|
||||
"$schema": "hos:schema://core/configs/rulepackage#/definitions/LogicalOrCondition",
|
||||
"conditions" : [
|
||||
{
|
||||
"type": "devicecommandmsg",
|
||||
"deviceid": "huelight1"
|
||||
},
|
||||
{
|
||||
"type": "devicestatechange",
|
||||
"deviceid": "huelight2"
|
||||
}
|
||||
]
|
||||
},
|
||||
"script": { "code": "print \"Hello World3: \" + str(var2)\nvar2 = var2 + 1\nimport __main__\nprint \"__main__.__dict__: \" + str(__main__.__dict__)\nprint \"test: \" + event_type" },
|
||||
"exitscript": { "code": "print \"Hello from exitscript2\"" }
|
||||
}
|
||||
}
|
||||
}
|
||||
+4
-3
@@ -19,6 +19,7 @@ namespace cosgi {
|
||||
namespace service {
|
||||
namespace scripting {
|
||||
|
||||
using namespace ::libvariant;
|
||||
|
||||
class GlobalScriptContext : public virtual interface::Object {
|
||||
|
||||
@@ -27,7 +28,7 @@ class GlobalScriptContext : public virtual interface::Object {
|
||||
|
||||
class Script : public virtual interface::Object {
|
||||
public:
|
||||
virtual void exec(GlobalScriptContext* gContext) = 0;
|
||||
virtual void exec(GlobalScriptContext* gContext, Variant lContext = Variant()) = 0;
|
||||
};
|
||||
|
||||
|
||||
@@ -37,8 +38,8 @@ public:
|
||||
|
||||
virtual std::string getLanguage() = 0;
|
||||
|
||||
virtual std::shared_ptr<GlobalScriptContext> createGlobalScriptContext(libvariant::Variant raw) = 0;
|
||||
virtual std::shared_ptr<Script> createScript(GlobalScriptContext* gContext, libvariant::Variant raw) = 0;
|
||||
virtual std::shared_ptr<GlobalScriptContext> createGlobalScriptContext(::libvariant::Variant raw) = 0;
|
||||
virtual std::shared_ptr<Script> createScript(GlobalScriptContext* gContext, ::libvariant::Variant raw) = 0;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ using namespace hos::cosgi;
|
||||
|
||||
struct UserData {
|
||||
apr_pool_t* memoryPool = nullptr;
|
||||
std::unique_ptr<PyRuleExecutionService> serviceImpl = nullptr;
|
||||
std::unique_ptr<PyScriptingService> serviceImpl = nullptr;
|
||||
service_registration_pt serviceReg = nullptr;
|
||||
serviceListener myServiceListener;
|
||||
bundle_context_pt bundleContext;
|
||||
@@ -56,7 +56,7 @@ celix_status_t cb_serviceChanged(void* listener, service_event_pt event) {
|
||||
}
|
||||
// Register hos-devicerepository-service
|
||||
if (checkReg && !myUserData->serviceReg && myUserData->serviceImpl->isInitialized()) {
|
||||
bundleContext_registerService(myUserData->bundleContext, (char*)PyRuleExecutionService::SERVICE_NAME, myUserData->serviceImpl.get(), nullptr, &myUserData->serviceReg);
|
||||
bundleContext_registerService(myUserData->bundleContext, (char*)PyScriptingService::SERVICE_NAME, myUserData->serviceImpl.get(), nullptr, &myUserData->serviceReg);
|
||||
}
|
||||
} else if (event->type == OSGI_FRAMEWORK_SERVICE_EVENT_UNREGISTERING) {
|
||||
}
|
||||
@@ -96,7 +96,7 @@ celix_status_t bundleActivator_start(void * userData, bundle_context_pt context)
|
||||
serviceRegistration_unregister(myUserData->serviceReg);
|
||||
myUserData->serviceReg = nullptr;
|
||||
}
|
||||
myUserData->serviceImpl.reset(new PyRuleExecutionService());
|
||||
myUserData->serviceImpl.reset(new PyScriptingService());
|
||||
// Get HOS Core Service
|
||||
service_reference_pt ref = nullptr;
|
||||
bundleContext_getServiceReference(context, (char*)CoreService::SERVICE_NAME, &ref);
|
||||
@@ -117,7 +117,7 @@ celix_status_t bundleActivator_start(void * userData, bundle_context_pt context)
|
||||
}
|
||||
// Register hos-ruleexecution-python-service
|
||||
if (myUserData->serviceImpl->isInitialized()) {
|
||||
bundleContext_registerService(context, (char*)PyRuleExecutionService::SERVICE_NAME, myUserData->serviceImpl.get(), nullptr, &myUserData->serviceReg);
|
||||
bundleContext_registerService(context, (char*)PyScriptingService::SERVICE_NAME, myUserData->serviceImpl.get(), nullptr, &myUserData->serviceReg);
|
||||
}
|
||||
// Register service listener
|
||||
myUserData->myServiceListener.handle = myUserData;
|
||||
|
||||
+93
-16
@@ -18,11 +18,11 @@ namespace service {
|
||||
namespace scripting {
|
||||
namespace python {
|
||||
|
||||
template<> const char* implementation::ServiceInfo<PyRuleExecutionService>::name = service::scripting::python::PyRuleExecutionService::SERVICE_NAME;
|
||||
template<> const int implementation::ServiceInfo<PyRuleExecutionService>::major = 0;
|
||||
template<> const int implementation::ServiceInfo<PyRuleExecutionService>::minor = 8;
|
||||
template<> const char* implementation::ServiceInfo<PyScriptingService>::name = service::scripting::python::PyScriptingService::SERVICE_NAME;
|
||||
template<> const int implementation::ServiceInfo<PyScriptingService>::major = 0;
|
||||
template<> const int implementation::ServiceInfo<PyScriptingService>::minor = 8;
|
||||
|
||||
void PyRuleExecutionService::setCoreService(service::core::CoreService* coreService) {
|
||||
void PyScriptingService::setCoreService(service::core::CoreService* coreService) {
|
||||
if (coreService && !this->coreService) {
|
||||
this->coreService = coreService;
|
||||
this->nodehandle = coreService->getPrivateNodeHandle();
|
||||
@@ -35,7 +35,7 @@ void PyRuleExecutionService::setCoreService(service::core::CoreService* coreServ
|
||||
}
|
||||
}
|
||||
|
||||
void PyRuleExecutionService::setRepoService(devicerepository::DeviceRepositoryService* repoService) {
|
||||
void PyScriptingService::setRepoService(devicerepository::DeviceRepositoryService* repoService) {
|
||||
if (repoService && !this->repoService) {
|
||||
this->repoService = repoService;
|
||||
int major = 0, minor = 0;
|
||||
@@ -47,7 +47,7 @@ void PyRuleExecutionService::setRepoService(devicerepository::DeviceRepositorySe
|
||||
}
|
||||
}
|
||||
|
||||
void PyRuleExecutionService::init() {
|
||||
void PyScriptingService::init() {
|
||||
Py_Initialize();
|
||||
boost::python::object main_module = boost::python::import("__main__");
|
||||
pyGlobalScope = main_module.attr("__dict__");// Load the sys module.
|
||||
@@ -58,10 +58,10 @@ void PyRuleExecutionService::init() {
|
||||
ROS_INFO_STREAM("PyRuleExecutionService: Initialized and Ready.");
|
||||
}
|
||||
|
||||
std::shared_ptr<service::scripting::GlobalScriptContext> PyRuleExecutionService::createGlobalScriptContext(Variant raw) {
|
||||
std::shared_ptr<PyGlobalExecutionContext> retval;
|
||||
std::shared_ptr<service::scripting::GlobalScriptContext> PyScriptingService::createGlobalScriptContext(Variant raw) {
|
||||
std::shared_ptr<PyGlobalScriptContext> retval;
|
||||
if (raw.HasPath("code") && raw["code"].IsString()) {
|
||||
retval = std::make_shared<PyGlobalExecutionContext>(this->pyGlobalScope, raw["code"].AsString());
|
||||
retval = std::make_shared<PyGlobalScriptContext>(this->pyGlobalScope, raw["code"].AsString());
|
||||
try {
|
||||
boost::python::object result = boost::python::exec(retval->getCode().c_str(), pyGlobalScope);
|
||||
} catch(boost::python::error_already_set const &) {
|
||||
@@ -72,29 +72,106 @@ std::shared_ptr<service::scripting::GlobalScriptContext> PyRuleExecutionService:
|
||||
return std::static_pointer_cast<service::scripting::GlobalScriptContext>(retval);
|
||||
}
|
||||
|
||||
std::shared_ptr<service::scripting::Script> PyRuleExecutionService::createScript(service::scripting::GlobalScriptContext* gContext, Variant raw) {
|
||||
std::shared_ptr<PyRuleBody> retval;
|
||||
std::shared_ptr<service::scripting::Script> PyScriptingService::createScript(service::scripting::GlobalScriptContext* gContext, Variant raw) {
|
||||
std::shared_ptr<PyScript> retval;
|
||||
if (raw.HasPath("code") && raw["code"].IsString()) {
|
||||
retval = std::make_shared<PyRuleBody>(raw["code"].AsString(), *this);
|
||||
retval = std::make_shared<PyScript>(raw["code"].AsString(), *this);
|
||||
}
|
||||
return std::static_pointer_cast<service::scripting::Script>(retval);
|
||||
}
|
||||
|
||||
|
||||
void PyRuleBody::exec(service::scripting::GlobalScriptContext* gContext) {
|
||||
class PyDeviceWrapper {
|
||||
private:
|
||||
devicerepository::Device* device;
|
||||
public:
|
||||
PyDeviceWrapper(devicerepository::Device* device) : device(device) {}
|
||||
|
||||
std::string deviceID() { return device->getDeviceID(); }
|
||||
};
|
||||
|
||||
|
||||
class PyDeviceRepositoryWrapper {
|
||||
private:
|
||||
devicerepository::DeviceRepositoryService* service;
|
||||
public:
|
||||
PyDeviceRepositoryWrapper(devicerepository::DeviceRepositoryService* service) : service(service) {}
|
||||
|
||||
PyDeviceWrapper getDevice(std::string name) {
|
||||
return PyDeviceWrapper(service->getDevice(name).get());
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
BOOST_PYTHON_MODULE(hos)
|
||||
{
|
||||
using namespace boost::python;
|
||||
|
||||
class_<PyDeviceWrapper>("Device", no_init)
|
||||
.def("deviceID", &PyDeviceWrapper::deviceID) ;
|
||||
|
||||
class_<PyDeviceRepositoryWrapper>("DeviceRepository", no_init)
|
||||
.def("getDevice", &PyDeviceRepositoryWrapper::getDevice) ;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void PyScript::exec(service::scripting::GlobalScriptContext* gContext, Variant lContext) {
|
||||
try {
|
||||
boost::python::object& gcont = service.getPyGlobalScope();
|
||||
if (gContext) {
|
||||
gcont = dynamic_cast<PyGlobalExecutionContext*>(gContext)->getPyGlobalContext();
|
||||
gcont = dynamic_cast<PyGlobalScriptContext*>(gContext)->getPyGlobalContext();
|
||||
}
|
||||
boost::python::object result = boost::python::exec(code.c_str(), gcont);
|
||||
boost::python::object lcont;
|
||||
if (!lContext.IsNull()) {
|
||||
lcont = convertVariantToBoostPython(lContext);
|
||||
//gcont["local"] = lcont;
|
||||
} else {
|
||||
lcont = boost::python::dict();
|
||||
}
|
||||
inithos();
|
||||
PyDeviceRepositoryWrapper deviceService(service.getDeviceRepository());
|
||||
lcont["Devices"] = boost::python::object(boost::python::ptr(&deviceService));
|
||||
gcont["Devices"] = boost::python::object(deviceService);
|
||||
boost::python::object result = boost::python::exec(code.c_str(), gcont, lcont);
|
||||
} catch(boost::python::error_already_set const &) {
|
||||
PyErr_Print();
|
||||
throw std::runtime_error("Python error during global context creation.");
|
||||
throw std::runtime_error("Python error during execution.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
boost::python::object convertVariantToBoostPython(Variant var) {
|
||||
if (var.IsBool()) {
|
||||
return boost::python::object(var.AsBool());
|
||||
} else if (var.IsFloat()) {
|
||||
return boost::python::object(var.AsDouble());
|
||||
} else if (var.IsInt()) {
|
||||
return boost::python::object(var.AsInt());
|
||||
} else if (var.IsString()) {
|
||||
return boost::python::str(var.AsString());
|
||||
} else if (var.IsUnsigned()) {
|
||||
return boost::python::object(var.AsUnsigned());
|
||||
} else if (var.IsList()) {
|
||||
boost::python::list list;
|
||||
for (auto e : var.AsList()) {
|
||||
list.append(convertVariantToBoostPython(e));
|
||||
}
|
||||
return list;
|
||||
} else if (var.IsMap()) {
|
||||
boost::python::dict map;
|
||||
for (auto e : var.AsMap()) {
|
||||
map[e.first] = convertVariantToBoostPython(e.second);
|
||||
}
|
||||
return map;
|
||||
} else {
|
||||
ROS_ERROR("Unknown Variant type encountered while converting to python");
|
||||
return boost::python::object();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
} // namespace python
|
||||
} // namespace ruleengine
|
||||
} // namespace service
|
||||
|
||||
+14
-10
@@ -35,15 +35,15 @@ namespace service {
|
||||
namespace scripting {
|
||||
namespace python {
|
||||
|
||||
class PyRuleExecutionService;
|
||||
using namespace libvariant;
|
||||
class PyScriptingService;
|
||||
using namespace ::libvariant;
|
||||
|
||||
class PyGlobalExecutionContext : public virtual service::scripting::GlobalScriptContext {
|
||||
class PyGlobalScriptContext : public virtual service::scripting::GlobalScriptContext {
|
||||
private:
|
||||
boost::python::object& gContext;
|
||||
std::string code;
|
||||
public:
|
||||
PyGlobalExecutionContext(boost::python::object& gContext, std::string code)
|
||||
PyGlobalScriptContext(boost::python::object& gContext, std::string code)
|
||||
: gContext(gContext), code(code) {}
|
||||
std::string getCode() { return code; }
|
||||
boost::python::object& getPyGlobalContext() { return gContext; }
|
||||
@@ -51,19 +51,19 @@ public:
|
||||
|
||||
|
||||
|
||||
class PyRuleBody : public virtual service::scripting::Script {
|
||||
class PyScript : public virtual service::scripting::Script {
|
||||
private:
|
||||
std::string code;
|
||||
PyRuleExecutionService& service;
|
||||
PyScriptingService& service;
|
||||
public:
|
||||
PyRuleBody(std::string code, PyRuleExecutionService& service) : code(code), service(service) {}
|
||||
PyScript(std::string code, PyScriptingService& service) : code(code), service(service) {}
|
||||
std::string getCode() { return code; }
|
||||
virtual void exec(service::scripting::GlobalScriptContext* gContext);
|
||||
virtual void exec(service::scripting::GlobalScriptContext* gContext, Variant lContext = Variant());
|
||||
};
|
||||
|
||||
|
||||
class PyRuleExecutionService;
|
||||
class PyRuleExecutionService : public virtual service::scripting::ScriptingService, public virtual implementation::Service<PyRuleExecutionService> {
|
||||
class PyScriptingService;
|
||||
class PyScriptingService : public virtual service::scripting::ScriptingService, public virtual implementation::Service<PyScriptingService> {
|
||||
private:
|
||||
service::core::CoreService* coreService = nullptr;;
|
||||
devicerepository::DeviceRepositoryService* repoService = nullptr;;
|
||||
@@ -94,9 +94,13 @@ public:
|
||||
bool isInitialized() { return initialized; }
|
||||
|
||||
boost::python::object& getPyGlobalScope() { return pyGlobalScope; }
|
||||
|
||||
devicerepository::DeviceRepositoryService* getDeviceRepository() { return repoService; }
|
||||
};
|
||||
|
||||
|
||||
boost::python::object convertVariantToBoostPython(Variant var);
|
||||
|
||||
|
||||
} // namespace python
|
||||
} // namespace scripting
|
||||
|
||||
@@ -13,44 +13,37 @@
|
||||
#include <unordered_set>
|
||||
#include <unordered_map>
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <tuple>
|
||||
|
||||
|
||||
namespace hos {
|
||||
namespace common {
|
||||
|
||||
|
||||
template <class... _Args> class Observable {};
|
||||
|
||||
template <class... _Args> class IObserver {};
|
||||
template<class _Event, class... _Args> class Observable {};
|
||||
|
||||
|
||||
template<class _Event>
|
||||
class IObserver<_Event> {
|
||||
template<class _Event, class... _Args>
|
||||
class Observer {
|
||||
public:
|
||||
typedef Observable<_Event> _observable_t;
|
||||
virtual ~IObserver() {}
|
||||
virtual void notificationReceived(_observable_t* o, const _Event* e) = 0;
|
||||
typedef Observable<_Event, _Args...> observable_type;
|
||||
virtual ~Observer() {}
|
||||
virtual void notificationReceived(observable_type* o, const _Event* e, _Args...) = 0;
|
||||
|
||||
};
|
||||
|
||||
|
||||
template<class _Key, class _Event>
|
||||
class IObserver<_Key, _Event> {
|
||||
public:
|
||||
typedef Observable<_Key, _Event> _observable_t;
|
||||
virtual ~IObserver() {}
|
||||
virtual void notificationReceived(_observable_t* o, const _Key key, const _Event* e) = 0;
|
||||
};
|
||||
|
||||
|
||||
|
||||
template<class _Event>
|
||||
class Observable<_Event> {
|
||||
public:
|
||||
typedef Observer<_Event> observer_type;
|
||||
typedef std::unordered_set<observer_type*> observers_type;
|
||||
protected:
|
||||
std::mutex _observersMutex;
|
||||
typedef IObserver<_Event> _observer_t;
|
||||
typedef std::unordered_set<_observer_t*> _observers_t;
|
||||
_observers_t _observers;
|
||||
observers_type _observers;
|
||||
public:
|
||||
void addObserver(_observer_t* o) {
|
||||
void addObserver(observer_type* o) {
|
||||
std::lock_guard<std::mutex> lock(_observersMutex);
|
||||
if (o != nullptr) {
|
||||
auto elem = _observers.find(o);
|
||||
@@ -59,7 +52,7 @@ public:
|
||||
}
|
||||
}
|
||||
}
|
||||
void deleteObserver(_observer_t* o) {
|
||||
void deleteObserver(observer_type* o) {
|
||||
std::lock_guard<std::mutex> lock(_observersMutex);
|
||||
if (o != nullptr) {
|
||||
auto elem = _observers.find(o);
|
||||
@@ -85,58 +78,64 @@ public:
|
||||
};
|
||||
|
||||
|
||||
|
||||
template<class _Key, class _Event>
|
||||
class Observable<_Key, _Event> {
|
||||
template<class _Event, class _Key, class... _Args>
|
||||
class Observable<_Event, _Key, _Args...> {
|
||||
public:
|
||||
typedef Observer<_Event, _Key, _Args...> observer_type;
|
||||
typedef std::unordered_multimap<_Key, observer_type*> observers_type;
|
||||
protected:
|
||||
std::mutex _observersMutex;
|
||||
typedef IObserver<_Key, _Event> _observer_t;
|
||||
typedef std::unordered_multimap<_Key, _observer_t*> _observers_t;
|
||||
_observers_t _observers;
|
||||
observers_type _observers;
|
||||
public:
|
||||
void addObserver(_Key k, _observer_t* o) {
|
||||
void addObserver(_Key k, observer_type* o) {
|
||||
std::lock_guard<std::mutex> lock(_observersMutex);
|
||||
if (o != nullptr) {
|
||||
bool _found = false;
|
||||
auto range = _observers.equal_range(k);
|
||||
for (auto i = range.first; i != range.second; i++) {
|
||||
if (i.second == o) {
|
||||
_found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!_found) {
|
||||
auto elems = _observers.equal_range(k);
|
||||
auto found = std::find<observers_type::value_type>(elems.first, elems.second, {k, o});
|
||||
if (found == elems.second) {
|
||||
_observers.insert({k, o});
|
||||
}
|
||||
}
|
||||
}
|
||||
void deleteObserver(_Key k, IObserver<_Event>* o) {
|
||||
void deleteObserver(_Key k, observer_type* o) {
|
||||
std::lock_guard<std::mutex> lock(_observersMutex);
|
||||
if (o != nullptr) {
|
||||
bool _found = false;
|
||||
auto range = _observers.equal_range(k);
|
||||
for (auto i = range.first; i != range.second; i++) {
|
||||
if (i.second == o) {
|
||||
_observers.erase(i);
|
||||
break;
|
||||
}
|
||||
auto elems = _observers.equal_range(k);
|
||||
auto found = std::find<observers_type::value_type>(elems.first, elems.second, {k, o});
|
||||
if ( found != elems.second) {
|
||||
_observers.remove(found);
|
||||
}
|
||||
}
|
||||
}
|
||||
void deleteObserver(observer_type* o) {
|
||||
std::lock_guard<std::mutex> lock(_observersMutex);
|
||||
if (o != nullptr) {
|
||||
// Quick and dirty, needs to be replaced with a proper implementation
|
||||
auto found = std::find<observers_type::value_type>(_observers.begin(), _observers.end(), [=](typename observers_type::value_type v)->bool {
|
||||
return v.second == o;
|
||||
});
|
||||
while ( found != _observers.end()) {
|
||||
_observers.remove(found);
|
||||
found = std::find<observers_type::value_type>(_observers.begin(), _observers.end(), [=](typename observers_type::value_type v)->bool {
|
||||
return v.second == o;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
void deleteObservers() {
|
||||
std::lock_guard<std::mutex> lock(_observersMutex);
|
||||
_observers.clear();
|
||||
}
|
||||
};
|
||||
size_t countObservers() {
|
||||
std::lock_guard<std::mutex> lock(_observersMutex);
|
||||
return _observers.size();
|
||||
}
|
||||
void notifyObservers(_Key k, const _Event* e) {
|
||||
void notifyObservers(const _Event* e, _Key k, _Args... args) {
|
||||
std::lock_guard<std::mutex> lock(_observersMutex);
|
||||
auto ol = _observers.equal_range(k);
|
||||
std::for_each(ol.first, ol.second, [&](const typename _observers_t::value_type& o) {
|
||||
o.second->notificationReceived(this, k, e);
|
||||
});
|
||||
auto elems = _observers.equal_range(k);
|
||||
for (auto i = elems.first; i != elems.second; i++) {
|
||||
i->second->notificationReceived(this, e, k, args...);
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -24,9 +24,10 @@ if __name__ == '__main__':
|
||||
msg = DeviceCommand()
|
||||
msg.seq = 1
|
||||
msg.device_id = "huelight3"
|
||||
|
||||
msg.device_type = "hue/huelight"
|
||||
msg.stamp = stamp
|
||||
|
||||
|
||||
msg_body = dict()
|
||||
msg_body["type"] = "hue/huelight/command"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user