This commit is contained in:
2015-05-24 01:08:07 +02:00
parent 3626b45dfc
commit 13c182c82b
49 changed files with 2675 additions and 1546 deletions
@@ -9,4 +9,9 @@
extern "C" {
#include "celix/framework.h"
#include "celix/linked_list_iterator.h"
#include "celix/bundle_activator.h"
#include "celix/bundle_context.h"
#include "celix/service_listener.h"
#include "celix/service_reference.h"
#include "celix/constants.h"
}
@@ -22,6 +22,11 @@ namespace core {
enum class CoreServiceEventType : int {
STARTUP_FINISHED = 1,
SHUTDOWN = 2
};
/**
* Base class for events emitted by the CoreService.
*/
@@ -29,12 +34,10 @@ class CoreServiceEvent {
public:
typedef std::chrono::system_clock::time_point timestamp_t;
enum class EventType : int { STARTUP_FINISHED = 1, SHUTDOWN = 2 };
const EventType eventType;
CoreServiceEventType eventType;
const timestamp_t timestamp;
CoreServiceEvent(EventType type, const timestamp_t timestamp = std::chrono::system_clock::now())
CoreServiceEvent(CoreServiceEventType type, const timestamp_t timestamp = std::chrono::system_clock::now())
: eventType(type), timestamp(timestamp) {}
};
@@ -48,7 +51,7 @@ public:
class StartupFinishedEvent
: public CoreServiceEvent {
public:
StartupFinishedEvent() : CoreServiceEvent(EventType::STARTUP_FINISHED) {}
StartupFinishedEvent() : CoreServiceEvent(CoreServiceEventType::STARTUP_FINISHED) {}
};
@@ -61,7 +64,7 @@ public:
class ShutdownEvent
: public CoreServiceEvent {
public:
ShutdownEvent() : CoreServiceEvent(EventType::SHUTDOWN) {}
ShutdownEvent() : CoreServiceEvent(CoreServiceEventType::SHUTDOWN) {}
};
@@ -22,7 +22,9 @@ template<> const int implementation::ServiceInfo<CoreServiceImpl>::minor = 0;
CoreServiceImpl::CoreServiceImpl(ros::NodeHandle& publicNodeHandle, ros::NodeHandle& privateNodeHandle)
: publicNodeHandle(publicNodeHandle), privateNodeHandle(privateNodeHandle) {}
: publicNodeHandle(publicNodeHandle), privateNodeHandle(privateNodeHandle) {
schemaLoader.reset(new utils::SchemaLoader());
}
@@ -33,43 +33,20 @@ private:
public:
/**
* Constructor.
*/
CoreServiceImpl(ros::NodeHandle& publicNodeHandle, ros::NodeHandle& privateNodeHandle);
////////////////////////////////////////////////////////////
////////////// Public Service Implementations //////////////
////////////////////////////////////////////////////////////
/* Public Service Implementations */
/**
* Returns a reference to a node handle with public namespace.
*
* \return public node handle
*/
virtual ros::NodeHandle& getPublicNodeHandle() override {
return publicNodeHandle;
}
/**
* Returns a reference to a node handle with private namespace.
*
* \return private node handle
*/
virtual ros::NodeHandle& getPrivateNodeHandle() override {
return privateNodeHandle;
}
/**
* Request an application shutdown
*/
virtual void requestShutdown() override;
/**
* Returns a reference to the schema loader.
*
* \return schema loader.
*/
virtual utils::SchemaLoader& getSchemaLoader() override {
return *schemaLoader;
}
+1 -1
View File
@@ -9,7 +9,7 @@
// Boost
#include <boost/format.hpp>
// ROS
#include "ros/ros.h"
#include <ros/ros.h>
// APR
#include <apr_general.h>
#include <apr_strings.h>
@@ -1,5 +1,6 @@
#ifndef _HOS_DEVICEREPOSITORY_SERVICE_H_
#define _HOS_DEVICEREPOSITORY_SERVICE_H_
#pragma once
#include <hos/cosgi/service/Interface.h>
@@ -26,6 +27,22 @@ using namespace libvariant;
/*
* Device event type.
*
* DeviceRepositoryService emits DEVICE_ADDED, DEVICE_REMOVED.
* Device emits STATUS_UPDATE_RECEIVED, COMMAND_RECEIVED, STATE_CHANGED, DEVICE_REMOVED.
**/
enum class DeviceEventType : int {
DEVICE_ADDED = 1,
DEVICE_REMOVED = 2,
STATUS_UPDATE_RECEIVED = 3,
COMMAND_RECEIVED = 4,
STATE_CHANGED = 5
};
/**
* Base class for device events.
*/
@@ -33,20 +50,15 @@ class DeviceEvent {
public:
typedef std::chrono::system_clock::time_point timestamp_t;
enum class EventType : int {
DEVICE_ADDED = 1,
DEVICE_REMOVED = 2,
STATUS_UPDATE_RECEIVED = 3,
COMMAND_RECEIVED = 4,
STATE_CHANGED = 5
};
DeviceEventType eventType;
Device* const device;
const timestamp_t timestamp;
const EventType eventType;
DeviceEvent(Device* device, const EventType& type, const timestamp_t& timestamp)
: device(device), timestamp(timestamp), eventType(type) {}
const timestamp_t timestamp;
DeviceEvent(Device* device, const DeviceEventType& type, const timestamp_t& timestamp)
: eventType(type), device(device), timestamp(timestamp) {}
};
@@ -58,7 +70,7 @@ class DeviceAddedEvent
: public DeviceEvent {
public:
DeviceAddedEvent(Device* device, const timestamp_t& timestamp)
: DeviceEvent(device, DeviceEvent::EventType::DEVICE_ADDED, timestamp) {}
: DeviceEvent(device,DeviceEventType::DEVICE_ADDED, timestamp) {}
};
@@ -70,7 +82,7 @@ class DeviceRemovedEvent
: public DeviceEvent {
public:
DeviceRemovedEvent(Device* device, const timestamp_t& timestamp)
: DeviceEvent(device, DeviceEvent::EventType::DEVICE_REMOVED, timestamp) {}
: DeviceEvent(device, DeviceEventType::DEVICE_REMOVED, timestamp) {}
};
@@ -84,7 +96,7 @@ public:
const Variant statusMsg;
DeviceStatusMsgEvent(Device* device, const Variant& statusMsg, const timestamp_t& timestamp)
: DeviceEvent(device, DeviceEvent::EventType::STATUS_UPDATE_RECEIVED, timestamp), statusMsg(statusMsg) {}
: DeviceEvent(device, DeviceEventType::STATUS_UPDATE_RECEIVED, timestamp), statusMsg(statusMsg) {}
};
@@ -98,7 +110,7 @@ public:
const Variant cmdMsg;
DeviceCmdMsgEvent(Device* device, const Variant& cmdMsg, const timestamp_t& timestamp)
: DeviceEvent(device, DeviceEvent::EventType::COMMAND_RECEIVED, timestamp), cmdMsg(cmdMsg) {}
: DeviceEvent(device, DeviceEventType::COMMAND_RECEIVED, timestamp), cmdMsg(cmdMsg) {}
};
@@ -112,33 +124,89 @@ public:
const Variant statusMsg;
DeviceStateChangeEvent(Device* device, const Variant& statusMsg, const timestamp_t& timestamp)
: DeviceEvent(device, DeviceEvent::EventType::STATE_CHANGED, timestamp), statusMsg(statusMsg) {}
: DeviceEvent(device, DeviceEventType::STATE_CHANGED, timestamp), statusMsg(statusMsg) {}
};
/**
* Public COSGI interface of an device object.
*
* It offers following functionality:
* - Get device informations.
* - Events indicating device state changes.
* - Send commands to the device.
*/
class Device
: public virtual interface::ObservableObject<DeviceEvent::EventType, DeviceEvent> {
: public virtual interface::ObservableObject<DeviceEventType, DeviceEvent> {
public:
typedef std::chrono::system_clock::time_point timestamp_t;
/**
* Returns the device identifier.
*
* \return unique device identifier.
*/
virtual const std::string& getDeviceID() noexcept = 0;
/**
* Returns the device type.
*
* \return device type.
*/
virtual const std::string& getDeviceType() noexcept = 0;
/**
* Returns the ROS topic the device uses for commands.
*
* \return command topic.
*/
virtual const std::string& getCmdTopic() noexcept = 0;
/**
* Returns the ROS topic the device uses for status updates.
*
* \return status updates topic.
*/
virtual const std::string& getStatusTopic() noexcept = 0;
/**
* Returns the current state of the device.
*
* \return current state.
*/
virtual const Variant& getCurrentState() noexcept = 0;
/**
* Returns the timestamp of the current state.
*
* \return timestamp.
*/
virtual const timestamp_t& getCurrentStateTimestamp() noexcept = 0;
/**
* Returns the last state of the device.
*
* \return last state.
*/
virtual const Variant& getLastState() noexcept = 0;
/**
* Returns the timestamp of the last state.
*
* \return timestamp.
*/
virtual const timestamp_t& getLastStateTimestamp() noexcept = 0;
/**
* Sends a command to the device.
*
* May return exceptions derived from std::exception.
*
* \param[in] cmd command.
*/
virtual void sendCommand(const Variant& cmd) = 0;
};
@@ -146,18 +214,49 @@ public:
/**
* Public COSGI interface of the device repository service
**/
* Public COSGI interface of the device repository service.
*
* It offers following functionality:
* - Retrieve devices.
* - Events indicating new/removed devices.
*/
class DeviceRepositoryService
: public virtual interface::ObservableService<DeviceEvent::EventType, DeviceEvent> {
: public virtual interface::ObservableService<DeviceEventType, DeviceEvent> {
public:
/**
* The service name.
*/
static constexpr const char* SERVICE_NAME = "hos_devicerepository_service";
/**
* Retrieves the device with the given identifier.
*
* \param[in] device_id device identifier.
* \return device pointer, or expired pointer when no device was found.
*/
virtual std::weak_ptr<Device> getDevice(const std::string& device_id) noexcept = 0;
/**
* Returns all known devices.
*
* \return vector of device pointers.
*/
virtual std::vector<std::weak_ptr<Device>> getDevices() noexcept = 0;
/**
* Returns the number of known devices.
*
* \return device count.
*/
virtual size_t deviceCount() noexcept = 0;
/**
* Returns the device ids of known devices.
*
* \return vector of device pointers.
*/
virtual std::vector<std::string> getDeviceIDs() noexcept = 0;
};
@@ -169,6 +268,3 @@ public:
} // namespace cosgi
} // namespace hos
#endif /* _HOS_DEVICEREPOSITORY_SERVICE_H_ */
@@ -1,41 +1,42 @@
// std
#include <cstdlib>
#include <cstdio>
#include <string>
#include <memory>
// ROS
#include <ros/ros.h>
// APR
#include <apr_general.h>
#include "ros/ros.h"
extern "C" {
#include "celix/bundle_activator.h"
#include "celix/bundle_context.h"
}
// Celix
#include <hos/cosgi/celix.h>
// CoreService
#include <hos/cosgi/service/core/CoreService.h>
#include "hos/cosgi/service/devicerepository/DeviceRepositoryServiceImpl.h"
#include <iostream>
#include <memory>
using namespace hos::cosgi;
using namespace hos::cosgi::service::devicerepository;
struct UserData {
apr_pool_t* memoryPool = nullptr;
std::unique_ptr<DeviceRepositoryServiceImpl> serviceImpl;
service_registration_pt serviceReg = nullptr;
};
celix_status_t bundleActivator_create(bundle_context_pt context, void **userData) {
celix_status_t status = CELIX_START_ERROR;
apr_pool_t* memoryPool = nullptr;
// Create APR memory pool
apr_status_t astatus = apr_pool_create(&memoryPool, NULL);
if (astatus != APR_SUCCESS) {
ROS_ERROR("DeviceRepositoryService: Could not create APR memory pool (%i).", astatus);
} else {
// Create user data
UserData* myUserData = (UserData*)apr_palloc(memoryPool, sizeof(UserData));
if (myUserData != nullptr) {
@@ -47,20 +48,24 @@ celix_status_t bundleActivator_create(bundle_context_pt context, void **userData
ROS_ERROR("DeviceRepositoryService: Could not create user data struct");
apr_pool_destroy(memoryPool);
}
}
return status;
}
celix_status_t bundleActivator_start(void * userData, bundle_context_pt context) {
celix_status_t status = CELIX_SUCCESS;
if (userData != nullptr) {
UserData* myUserData = (UserData*)userData;
// Create hos-devicerepository-service
// Create device repository service
if (myUserData->serviceReg != nullptr) {
serviceRegistration_unregister(myUserData->serviceReg);
}
myUserData->serviceImpl.reset(new DeviceRepositoryServiceImpl());
// Get HOS Core Service
// Get core service
service_reference_pt ref = nullptr;
bundleContext_getServiceReference(context, (char*)service::core::CoreService::SERVICE_NAME, &ref);
service::core::CoreService* coreService = nullptr;
@@ -70,10 +75,12 @@ celix_status_t bundleActivator_start(void * userData, bundle_context_pt context)
} else {
myUserData->serviceImpl->setCoreService(coreService);
}
// Register hos-devicerepository-service
// Register device repository service
if (myUserData->serviceImpl->isInitialized()) {
bundleContext_registerService(context, (char*)DeviceRepositoryService::SERVICE_NAME, myUserData->serviceImpl.get(), nullptr, &myUserData->serviceReg);
}
ROS_INFO("DeviceRepositoryService: Bundle successfully started");
} else {
ROS_ERROR("DeviceRepositoryService: Could not start bundle, user data struct is null");
@@ -81,6 +88,7 @@ celix_status_t bundleActivator_start(void * userData, bundle_context_pt context)
return status;
}
celix_status_t bundleActivator_stop(void * userData, bundle_context_pt context) {
celix_status_t status = CELIX_ILLEGAL_STATE;
if (userData != nullptr) {
@@ -100,6 +108,7 @@ celix_status_t bundleActivator_stop(void * userData, bundle_context_pt context)
return status;
}
celix_status_t bundleActivator_destroy(void * userData, bundle_context_pt context) {
celix_status_t status = CELIX_ILLEGAL_STATE;
if (userData != nullptr) {
@@ -116,3 +125,4 @@ celix_status_t bundleActivator_destroy(void * userData, bundle_context_pt contex
}
return status;
}
@@ -1,12 +1,5 @@
/*
* hos_itemrepository_service_impl.cpp
*
* Created on: 05.04.2015
* Author: matzman
*/
#include "DeviceRepositoryServiceImpl.h"
#include <hos/cosgi/service/core/CoreService.h>
#include <ros/ros.h>
#include <exception>
@@ -15,51 +8,65 @@
// Service Namespace
namespace hos {
namespace cosgi {
namespace service {
namespace devicerepository {
// Service information
template<> const char* implementation::ServiceInfo<DeviceRepositoryServiceImpl>::name = service::devicerepository::DeviceRepositoryService::SERVICE_NAME;
template<> const int implementation::ServiceInfo<DeviceRepositoryServiceImpl>::major = 1;
template<> const int implementation::ServiceInfo<DeviceRepositoryServiceImpl>::minor = 0;
bool DeviceImpl::handleStatusMsg(const Variant& msg, const timestamp_t& stamp) {
bool DeviceImpl::handleStatusMsg(const Variant& msg, const timestamp_t& stamp) noexcept {
std::lock_guard<std::recursive_mutex> lock(objMutex);
Variant tmp = currentState.Copy();
tmp.Merge(msg);
bool stateChanged = false;
if (tmp != currentState) { // state has changed
lastState = currentState.Copy();
lastStamp = currentStamp;
currentState = std::move(tmp);
Variant newstate = currentState.Copy();
newstate.Merge(msg);
if (newstate != currentState) {
lastState = std::move(currentState);
lastStamp = std::move(currentStamp);
currentState = std::move(newstate);
currentStamp = stamp;
stateChanged = true;
}
DeviceStatusMsgEvent event(this, msg, stamp);
notifyObservers(DeviceEvent::EventType::STATUS_UPDATE_RECEIVED, &event);
notifyObservers(DeviceEventType::STATUS_UPDATE_RECEIVED, &event);
if (stateChanged){
DeviceStateChangeEvent event(this, msg, stamp);
notifyObservers(DeviceEvent::EventType::STATE_CHANGED, &event);
notifyObservers(DeviceEventType::STATE_CHANGED, &event);
}
return stateChanged;
}
void DeviceImpl::handleCommandMsg(const Variant& msg, const timestamp_t& stamp) {
void DeviceImpl::handleCommandMsg(const Variant& msg, const timestamp_t& stamp) noexcept {
std::lock_guard<std::recursive_mutex> lock(objMutex);
DeviceCmdMsgEvent event(this, msg, stamp);
notifyObservers(DeviceEvent::EventType::COMMAND_RECEIVED, &event);
notifyObservers(DeviceEventType::COMMAND_RECEIVED, &event);
}
void DeviceImpl::sendCommand(const Variant& cmd) {
std::lock_guard<std::recursive_mutex> lock(objMutex);
parent->sendDeviceCommand(this, cmd);
}
void DeviceRepositoryServiceImpl::setCoreService(service::core::CoreService* coreService) {
void DeviceRepositoryServiceImpl::setCoreService(service::core::CoreService* coreService) noexcept {
if (coreService && !this->coreService) {
this->coreService = coreService;
this->nodehandle = &coreService->getPrivateNodeHandle();
@@ -70,11 +77,15 @@ void DeviceRepositoryServiceImpl::setCoreService(service::core::CoreService* cor
}
}
void DeviceRepositoryServiceImpl::init() {
void DeviceRepositoryServiceImpl::init() noexcept {
// Get public parameters
nodehandle->getParam("device_repository_prefix", deviceRepositoryPrefix);
nodehandle->getParam("hos_status_topic", hosStatusTopic);
XmlRpc::XmlRpcValue rawValue;
nodehandle->getParam(deviceRepositoryPrefix, rawValue);
// create devices
if (rawValue.getType() == XmlRpc::XmlRpcValue::TypeStruct) {
for (auto dev : rawValue) {
std::string devname = dev.first;
@@ -87,11 +98,13 @@ void DeviceRepositoryServiceImpl::init() {
}
}
}
hosStatusSub = nodehandle->subscribe(hosStatusTopic, 1000, &DeviceRepositoryServiceImpl::hosStatusMsgCallback, this);
initialized = true;
ROS_INFO_STREAM("DeviceRepositoryService: Initialized and Ready.");
}
void DeviceRepositoryServiceImpl::addDevice(const std::string& name) {
std::stringstream ss;
ss << deviceRepositoryPrefix << "/" << name;
@@ -100,28 +113,37 @@ void DeviceRepositoryServiceImpl::addDevice(const std::string& name) {
addDevice(name, rawValue);
}
void DeviceRepositoryServiceImpl::addDevice(const std::string& name, XmlRpc::XmlRpcValue& rawValue) {
std::lock_guard<std::recursive_mutex> lock(serviceMutex);
std::string type, statusTopic, cmdTopic;
// device type
XmlRpc::XmlRpcValue& typeRaw = rawValue["type"];
if (typeRaw.getType() != XmlRpc::XmlRpcValue::TypeString) {
throw std::runtime_error("type is not a string.");
}
type = static_cast<std::string>(typeRaw);
type = static_cast<std::string>(typeRaw);
// device status topic
XmlRpc::XmlRpcValue& statusTopicRaw = rawValue["status_topic"];
if (statusTopicRaw.getType() != XmlRpc::XmlRpcValue::TypeString) {
throw std::runtime_error("statusTopic is not a string.");
}
statusTopic = static_cast<std::string>(statusTopicRaw);
// device command topic
XmlRpc::XmlRpcValue& cmdTopicRaw = rawValue["cmd_topic"];
if (cmdTopicRaw.getType() != XmlRpc::XmlRpcValue::TypeString) {
throw std::runtime_error("cmdTopic is not a string.");
}
cmdTopic = static_cast<std::string>(cmdTopicRaw);
// create device
std::shared_ptr<DeviceImpl> dev = std::make_shared<DeviceImpl>(this, name, type, statusTopic, cmdTopic);
devices.insert({name, dev});
// status topic
// status topic subscriber
auto& entry = subscripedTopics[dev->getStatusTopic()];
if (entry.first <= 0) {
entry.second = nodehandle->subscribe(dev->getStatusTopic(), 1000, &DeviceRepositoryServiceImpl::statusMsgCallback, this);
@@ -130,7 +152,7 @@ void DeviceRepositoryServiceImpl::addDevice(const std::string& name, XmlRpc::Xml
subscripedTopics[dev->getStatusTopic()] = entry;
}
// cmd topic
// cmd topic subscriber
auto& entry2 = subscripedTopics[dev->getCmdTopic()];
if (entry2.first <= 0) {
entry2.second = nodehandle->subscribe(dev->getCmdTopic(), 1000, &DeviceRepositoryServiceImpl::cmdMsgCallback, this);
@@ -139,7 +161,7 @@ void DeviceRepositoryServiceImpl::addDevice(const std::string& name, XmlRpc::Xml
subscripedTopics[dev->getCmdTopic()] = entry2;
}
// cmd publisher
// cmd topic publisher
auto& entry3 = publishedTopics[dev->getCmdTopic()];
if (entry3.first <= 0) {
entry3.second = nodehandle->advertise<hos_core_msgs::DeviceCommand>(dev->getCmdTopic(), 1000);
@@ -148,13 +170,9 @@ void DeviceRepositoryServiceImpl::addDevice(const std::string& name, XmlRpc::Xml
publishedTopics[dev->getCmdTopic()] = entry3;
}
entry2.first= entry2.first + 1;
subscripedTopics[dev->getCmdTopic()] = entry2;
// send device added event
DeviceAddedEvent event(dev.get(), std::chrono::system_clock::now());
notifyObservers(DeviceEvent::EventType::DEVICE_ADDED, &event);
notifyObservers(DeviceEventType::DEVICE_ADDED, &event);
}
@@ -163,8 +181,8 @@ void DeviceRepositoryServiceImpl::removeDevice(const std::string& name) {
auto dev = devices.find(name);
if (dev != devices.end()) {
DeviceRemovedEvent event(dev->second.get(), std::chrono::system_clock::now());
notifyObservers(DeviceEvent::EventType::DEVICE_REMOVED, &event);
dev->second->notifyObservers(DeviceEvent::EventType::DEVICE_REMOVED, &event);
notifyObservers(DeviceEventType::DEVICE_REMOVED, &event);
dev->second->notifyObservers(DeviceEventType::DEVICE_REMOVED, &event);
devices.erase(name);
}
}
@@ -178,6 +196,7 @@ std::weak_ptr<Device> DeviceRepositoryServiceImpl::getDevice(const std::string&
}
}
std::vector<std::weak_ptr<Device>> DeviceRepositoryServiceImpl::getDevices() noexcept {
std::lock_guard<std::recursive_mutex> lock(serviceMutex);
std::vector<std::weak_ptr<Device>> retval;
@@ -188,11 +207,6 @@ std::vector<std::weak_ptr<Device>> DeviceRepositoryServiceImpl::getDevices() noe
}
size_t DeviceRepositoryServiceImpl::deviceCount() noexcept {
return devices.size();
}
std::vector<std::string> DeviceRepositoryServiceImpl::getDeviceIDs() noexcept {
std::lock_guard<std::recursive_mutex> lock(serviceMutex);
std::vector<std::string> retval;
@@ -203,90 +217,117 @@ std::vector<std::string> DeviceRepositoryServiceImpl::getDeviceIDs() noexcept {
}
void DeviceRepositoryServiceImpl::statusMsgCallback(const hos_core_msgs::DeviceStatusUpdate::ConstPtr& msg) {
void DeviceRepositoryServiceImpl::statusMsgCallback(const hos_core_msgs::DeviceStatusUpdate::ConstPtr& msg) noexcept {
std::lock_guard<std::recursive_mutex> lock(serviceMutex);
std::cout << "HOSDeviceRepositoryServiceImpl::statusMsgCallback" << std::endl;
Variant s = coreService->getSchemaLoader().GetSchema(std::string("hos:schema://").append(msg->device_type).append("/status"));
Variant m = Deserialize(msg->msg_body, SERIALIZE_JSON);
SchemaResult sr = SchemaValidate(s, m, &coreService->getSchemaLoader());
if (!sr.Error()) {
auto dev = devices.find(msg->device_id);
if (dev != devices.end()) {
auto stamp = std::chrono::system_clock::now();
bool stateChanged = dev->second->handleStatusMsg(m, stamp);
DeviceStatusMsgEvent event(dev->second.get(), m, stamp);
notifyObservers(DeviceEvent::EventType::STATUS_UPDATE_RECEIVED, &event);
if (stateChanged) {
DeviceStateChangeEvent event(dev->second.get(), m, stamp);
notifyObservers(DeviceEvent::EventType::STATE_CHANGED, &event);
try {
// Verify message using appropriate json schema.
auto schemaUrl = (boost::format("hos:schema://%1%/status") % msg->device_type).str();
Variant s = coreService->getSchemaLoader().GetSchema(schemaUrl);
Variant m = Deserialize(msg->msg_body, SERIALIZE_JSON);
SchemaResult sr = SchemaValidate(s, m, &coreService->getSchemaLoader());
if (!sr.Error()) {
// Get recipient and pass on message
auto dev = devices.find(msg->device_id);
if (dev != devices.end()) {
auto stamp = std::chrono::system_clock::now();
dev->second->handleStatusMsg(m, stamp);
}
} else {
ROS_ERROR_STREAM("Could not validate status message: " << sr.PrettyPrintMessage());
}
} else {
ROS_ERROR_STREAM("Could not validate status message: " << sr.PrettyPrintMessage());
} catch (std::exception& e) {
ROS_ERROR_STREAM("Exception while handling device status message: " << e.what());
}
}
void DeviceRepositoryServiceImpl::cmdMsgCallback(const hos_core_msgs::DeviceCommand::ConstPtr& msg) {
void DeviceRepositoryServiceImpl::cmdMsgCallback(const hos_core_msgs::DeviceCommand::ConstPtr& msg) noexcept {
std::lock_guard<std::recursive_mutex> lock(serviceMutex);
std::cout << "HOSDeviceRepositoryServiceImpl::cmdMsgCallback" << std::endl;
Variant s = coreService->getSchemaLoader().GetSchema(std::string("hos:schema://").append(msg->device_type).append("/command"));
Variant m = Deserialize(msg->msg_body, SERIALIZE_JSON);
auto test = msg->stamp;
SchemaResult sr = SchemaValidate(s, m, &coreService->getSchemaLoader());
if (!sr.Error()) {
auto dev = devices.find(msg->device_id);
if (dev != devices.end()) {
auto stamp = std::chrono::system_clock::now();
dev->second->handleCommandMsg(m, stamp);
DeviceCmdMsgEvent event(dev->second.get(), m, stamp);
notifyObservers(DeviceEvent::EventType::STATUS_UPDATE_RECEIVED, &event);
try {
// Verify message using appropriate json schema.
auto schemaUrl = (boost::format("hos:schema://%1%/command") % msg->device_type).str();
Variant s = coreService->getSchemaLoader().GetSchema(schemaUrl);
Variant m = Deserialize(msg->msg_body, SERIALIZE_JSON);
auto test = msg->stamp;
SchemaResult sr = SchemaValidate(s, m, &coreService->getSchemaLoader());
if (!sr.Error()) {
// Get recipient and pass on message
auto dev = devices.find(msg->device_id);
if (dev != devices.end()) {
auto stamp = std::chrono::system_clock::now();
dev->second->handleCommandMsg(m, stamp);
}
} else {
ROS_ERROR_STREAM("Could not validate command message: " << sr.PrettyPrintMessage());
}
} else {
ROS_ERROR_STREAM("Could not validate command message: " << sr.PrettyPrintMessage());
} catch (std::exception& e) {
ROS_ERROR_STREAM("Exception while handling device command message: " << e.what());
}
}
void DeviceRepositoryServiceImpl::hosStatusMsgCallback(const hos_core_msgs::HosStatusUpdate::ConstPtr& msg) {
void DeviceRepositoryServiceImpl::hosStatusMsgCallback(const hos_core_msgs::HosStatusUpdate::ConstPtr& msg) noexcept {
std::lock_guard<std::recursive_mutex> lock(serviceMutex);
std::cout << "HOSDeviceRepositoryServiceImpl::hosStatusMsgCallback" << std::endl;
Variant s = coreService->getSchemaLoader().GetSchema(std::string("hos:schema://core/msgs/hosstatusupdate"));
Variant m = Deserialize(msg->msg_body, SERIALIZE_JSON);
SchemaResult sr = SchemaValidate(s, m, &coreService->getSchemaLoader());
if (!sr.Error()) {
if (m.Contains("devices")) {
auto d = m["devices"];
if (d.Contains("added") && d["added"].IsList()) {
for (auto dev : d["added"].AsList()) {
if (dev.IsString()) {
std::string devname = dev.AsString();
if (devices.find(devname) == devices.end()) {
addDevice(devname);
try {
// Verify message using appropriate json schema.
Variant s = coreService->getSchemaLoader().GetSchema(std::string("hos:schema://core/msgs/hosstatusupdate"));
Variant m = Deserialize(msg->msg_body, SERIALIZE_JSON);
SchemaResult sr = SchemaValidate(s, m, &coreService->getSchemaLoader());
if (!sr.Error()) {
// we are only interested in the device part.
if (m.Contains("devices")) {
auto d = m["devices"];
// devices added
if (d.Contains("added") && d["added"].IsList()) {
for (auto dev : d["added"].AsList()) {
if (dev.IsString()) {
std::string devname = dev.AsString();
if (devices.find(devname) == devices.end()) {
addDevice(devname);
}
}
}
}
}
if (d.Contains("removed") && d["removed"].IsList()) {
for (auto dev : d["removed"].AsList()) {
if (dev.IsString()) {
std::string devname = dev.AsString();
if (devices.find(devname) != devices.end()) {
removeDevice(devname);
// devices removed
if (d.Contains("removed") && d["removed"].IsList()) {
for (auto dev : d["removed"].AsList()) {
if (dev.IsString()) {
std::string devname = dev.AsString();
if (devices.find(devname) != devices.end()) {
removeDevice(devname);
}
}
}
}
}
} else {
ROS_ERROR_STREAM("Could not validate hos status message: " << sr.PrettyPrintMessage());
}
} else {
ROS_ERROR_STREAM("Could not validate hos status message: " << sr.PrettyPrintMessage());
} catch (std::exception& e) {
ROS_ERROR_STREAM("Exception while handling hos status message: " << e.what());
}
}
void DeviceRepositoryServiceImpl::sendDeviceCommand(Device* device, const Variant& cmd) {
void DeviceRepositoryServiceImpl::sendDeviceCommand(DeviceImpl* device, const Variant& cmd) {
std::lock_guard<std::recursive_mutex> lock(serviceMutex);
// Verify message using appropriate json schema.
auto schemaUrl = (boost::format("hos:schema://%1%/command") % device->getDeviceType()).str();
Variant s = coreService->getSchemaLoader().GetSchema(schemaUrl);
SchemaResult sr = SchemaValidate(s, cmd, &coreService->getSchemaLoader());
if (!sr.Error()) {
// Retrieve appropriate publisher and send message
auto entry = publishedTopics[device->getCmdTopic()];
if (entry.first > 0) {
hos_core_msgs::DeviceCommand command;
@@ -296,11 +337,14 @@ void DeviceRepositoryServiceImpl::sendDeviceCommand(Device* device, const Varian
command.msg_body = Serialize(cmd, SERIALIZE_JSON);
command.stamp = ros::Time::now();
entry.second.publish(command);
} else {
ROS_ERROR_STREAM("Could not find device command publisher for topic " << device->getCmdTopic());
auto msg = (boost::format("Could not find device command publisher for topic %1%") % device->getCmdTopic()).str();
throw std::runtime_error(msg);
}
} else {
ROS_ERROR_STREAM("Could not validate device command: " << sr.PrettyPrintMessage());
auto msg = (boost::format("Could not validate device command: %1%") % sr.PrettyPrintMessage()).str();
throw std::runtime_error(msg);
}
}
@@ -1,27 +1,25 @@
/*
* hos_itemrepository_service_impl.h
*
* Created on: 05.04.2015
* Author: matzman
*/
#ifndef SRC_HOS_COSGI_SERVICE_DEVICEREPOSITORY_DEVICEREPOSITORY_SERVICEIMPL_H_
#define SRC_HOS_COSGI_SERVICE_DEVICEREPOSITORY_DEVICEREPOSITORY_SERVICEIMPL_H_
#pragma once
#include <Variant/Variant.h>
#include <hos/cosgi/service/devicerepository/DeviceRepositoryService.h>
// hos includes
#include <hos/cosgi/service/core/CoreService.h>
#include <hos/cosgi/service/Implementation.h>
#include <hos_core_msgs/DeviceCommand.h>
#include <hos_core_msgs/DeviceStatusUpdate.h>
#include <hos_core_msgs/HosStatusUpdate.h>
#include <hos/utils/SchemaLoader.h>
// libvariant includes
#include <Variant/Variant.h>
// ROS includes
#include <ros/ros.h>
// std includes
#include <mutex>
#include <map>
#include <set>
#include <memory>
#include <functional>
#include <hos_core_msgs/DeviceCommand.h>
#include <hos_core_msgs/DeviceStatusUpdate.h>
#include <hos_core_msgs/HosStatusUpdate.h>
#include <hos/cosgi/service/devicerepository/DeviceRepositoryService.h>
#include <hos/cosgi/service/core/CoreService.h>
#include <hos/cosgi/service/Implementation.h>
#include <ros/ros.h>
using namespace libvariant;
@@ -29,10 +27,15 @@ using namespace libvariant;
namespace std {
/**
* Specialization of std::hash for DeviceEventType.
*
* Required by std::map.
*/
template <>
class hash <hos::cosgi::service::devicerepository::DeviceEvent::EventType>{
class hash <hos::cosgi::service::devicerepository::DeviceEventType>{
public :
size_t operator()(const hos::cosgi::service::devicerepository::DeviceEvent::EventType& x) const {
size_t operator()(const hos::cosgi::service::devicerepository::DeviceEventType& x) const {
return static_cast<size_t>(x);
}
};
@@ -52,22 +55,26 @@ class DeviceRepositoryServiceImpl;
/**
* Device implementation
* Implementation of Device.
**/
class DeviceImpl
: public virtual Device,
public virtual implementation::ObservableObject<DeviceEvent::EventType, DeviceEvent> {
public virtual implementation::ObservableObject<DeviceEventType, DeviceEvent> {
private:
DeviceRepositoryServiceImpl* parent;
std::recursive_mutex objMutex;
std::string deviceID;
std::string deviceType;
std::string statusTopic;
std::string cmdTopic;
Variant currentState;
timestamp_t currentStamp;
Variant lastState;
timestamp_t lastStamp;
@@ -77,26 +84,46 @@ public:
const std::string& deviceType,
const std::string& statusTopic,
const std::string& cmdTopic )
: parent(parent), deviceID(deviceID), deviceType(deviceType), statusTopic(statusTopic), cmdTopic(cmdTopic) {}
: parent(parent), deviceID(deviceID), deviceType(deviceType),
statusTopic(statusTopic), cmdTopic(cmdTopic) {}
/* Public Service Implementations */
virtual const std::string& getDeviceID() noexcept override { return deviceID; }
virtual const std::string& getDeviceType() noexcept override { return deviceType; }
virtual const std::string& getCmdTopic() noexcept override { return cmdTopic; }
virtual const std::string& getStatusTopic() noexcept override { return statusTopic; }
virtual const std::string& getDeviceID() noexcept override {
return deviceID;
}
virtual const std::string& getDeviceType() noexcept override {
return deviceType;
}
virtual const std::string& getCmdTopic() noexcept override {
return cmdTopic;
}
virtual const std::string& getStatusTopic() noexcept override {
return statusTopic;
}
virtual const Variant& getCurrentState() noexcept override { return currentState; }
virtual const timestamp_t& getCurrentStateTimestamp() noexcept override { return currentStamp; }
virtual const Variant& getLastState() noexcept override { return lastState; }
virtual const timestamp_t& getLastStateTimestamp() noexcept override { return lastStamp; }
virtual const Variant& getCurrentState() noexcept override {
return currentState;
}
virtual const timestamp_t& getCurrentStateTimestamp() noexcept override {
return currentStamp;
}
virtual const Variant& getLastState() noexcept override {
return lastState;
}
virtual const timestamp_t& getLastStateTimestamp() noexcept override {
return lastStamp;
}
virtual void sendCommand(const Variant& cmd) override;
/* Internal Stuff */
bool handleStatusMsg(const Variant& msg, const timestamp_t& stamp);
void handleCommandMsg(const Variant& msg, const timestamp_t& stamp);
/* Internal */
bool handleStatusMsg(const Variant& msg, const timestamp_t& stamp) noexcept;
void handleCommandMsg(const Variant& msg, const timestamp_t& stamp) noexcept;
};
@@ -107,9 +134,12 @@ public:
**/
class DeviceRepositoryServiceImpl
: public virtual DeviceRepositoryService,
public virtual implementation::ObservableService<DeviceRepositoryServiceImpl, DeviceEvent::EventType, DeviceEvent> {
public virtual implementation::ObservableService<DeviceRepositoryServiceImpl, DeviceEventType, DeviceEvent> {
private:
typedef std::map<std::string, std::shared_ptr<DeviceImpl>> devicesMap_t;
typedef std::map<std::string, std::pair<int, ros::Subscriber>> subscripedTopicMap_t;
typedef std::map<std::string, std::pair<int, ros::Publisher>> publishedTopicMap_t;
std::recursive_mutex serviceMutex;
@@ -119,52 +149,68 @@ private:
std::string deviceRepositoryPrefix = "/hos/devices/repository";
std::string hosStatusTopic = "/hos/status/updates";
typedef std::map<std::string, std::shared_ptr<DeviceImpl>> devicesMap_t;
devicesMap_t devices;
typedef std::map<std::string, std::pair<int, ros::Subscriber>> subscripedTopicMap_t;
typedef std::map<std::string, std::pair<int, ros::Publisher>> publishedTopicMap_t;
subscripedTopicMap_t subscripedTopics;
publishedTopicMap_t publishedTopics;
long msgSeqNumber = 0;
ros::Subscriber hosStatusSub;
long msgSeqNumber = 0;
bool initialized = false;
void init();
void init() noexcept;
void addDevice(const std::string& name);
void addDevice(const std::string& name, XmlRpc::XmlRpcValue& rawValue);
void removeDevice(const std::string& name);
public:
/* Initialization */
void setCoreService(service::core::CoreService* coreService);
void setCoreService(service::core::CoreService* coreService) noexcept;
/* Public Service Implementations */
virtual std::weak_ptr<Device> getDevice(const std::string& device_id) noexcept override;
virtual std::vector<std::weak_ptr<Device>> getDevices() noexcept override;
virtual size_t deviceCount() noexcept override;
virtual size_t deviceCount() noexcept override {
return devices.size();
}
virtual std::vector<std::string> getDeviceIDs() noexcept override;
/* Message Callbacks */
void statusMsgCallback(const hos_core_msgs::DeviceStatusUpdate::ConstPtr& msg);
void cmdMsgCallback(const hos_core_msgs::DeviceCommand::ConstPtr& msg);
void hosStatusMsgCallback(const hos_core_msgs::HosStatusUpdate::ConstPtr& msg);
/** ROS subscriber callback for device status messages. */
void statusMsgCallback(const hos_core_msgs::DeviceStatusUpdate::ConstPtr& msg) noexcept;
/* Internal Stuff */
/** ROS subscriber callback for device command messages. */
void cmdMsgCallback(const hos_core_msgs::DeviceCommand::ConstPtr& msg) noexcept;
/** ROS subscriber callback for hos status messages. */
void hosStatusMsgCallback(const hos_core_msgs::HosStatusUpdate::ConstPtr& msg) noexcept;
/* Internal */
bool isInitialized() noexcept {
return initialized;
}
void sendDeviceCommand(DeviceImpl* device, const Variant& cmd);
bool isInitialized() noexcept { return initialized; }
void sendDeviceCommand(Device* device, const Variant& cmd);
};
} // namespace devicerepository
} // namespace service
} // namespace cosgi
} // namespace hos
#endif /* SRC_HOS_COSGI_SERVICE_DEVICEREPOSITORY_DEVICEREPOSITORY_SERVICEIMPL_H_ */
+8 -5
View File
@@ -120,11 +120,14 @@ set(BUNDLE_NAME "hos_ruleengine")
bundle(hos_cosgi_ruleengine
SOURCES
${PROJECT_SOURCE_DIR}/src/bundle_activator
${PROJECT_SOURCE_DIR}/src/hos/cosgi/service/ruleengine/ruleengine_serviceimpl
${PROJECT_SOURCE_DIR}/src/hos/cosgi/service/ruleengine/rule/RulePackage
${PROJECT_SOURCE_DIR}/src/hos/cosgi/service/ruleengine/rule/Rule
${PROJECT_SOURCE_DIR}/src/hos/cosgi/service/ruleengine/rule/RuleConditionImpl
${PROJECT_SOURCE_DIR}/src/BundleActivator
${PROJECT_SOURCE_DIR}/src/hos/cosgi/service/ruleengine/RuleEngineServiceImpl
${PROJECT_SOURCE_DIR}/src/hos/cosgi/service/ruleengine/rules/RulePackageImpl
${PROJECT_SOURCE_DIR}/src/hos/cosgi/service/ruleengine/rules/RuleImpl
${PROJECT_SOURCE_DIR}/src/hos/cosgi/service/ruleengine/rules/conditions/RuleConditionImpl
${PROJECT_SOURCE_DIR}/src/hos/cosgi/service/ruleengine/rules/conditions/LogicalOrCondition
${PROJECT_SOURCE_DIR}/src/hos/cosgi/service/ruleengine/rules/conditions/LogicalAndCondition
${PROJECT_SOURCE_DIR}/src/hos/cosgi/service/ruleengine/rules/conditions/DeviceMsgCondition
)
target_link_libraries(hos_cosgi_ruleengine
${catkin_LIBRARIES}
@@ -0,0 +1,104 @@
#pragma once
#include <hos/cosgi/service/Interface.h>
#include <string>
#include <vector>
#include <memory>
// forward declarations
namespace libvariant { class Variant; }
namespace hos {
namespace cosgi {
namespace service {
namespace ruleengine {
// forward declarations
namespace rules {
class RulePackage;
class Rule;
namespace conditions { class RuleCondition; }
}
/**
* Public abstract COSGI interface of a rule condition factory.
*/
class RuleConditionFactory
: public virtual interface::Object {
public:
/**
* Creates and returns a condition from the given json config.
*
* \param[in] parent parent rule
* \param[in] json config
* \return new rule condition
*/
virtual std::shared_ptr<rules::conditions::RuleCondition> createCondition(rules::Rule* parent, const libvariant::Variant& raw) = 0;
};
/**
* Public COSGI interface of the rule engine service.
*/
class RuleEngineService
: public virtual interface::Service {
public:
/**
* The service name.
*/
static constexpr const char* SERVICE_NAME = "hos_ruleengine_service";
/**
* Returns the rulepackage with the given name.
*
* \param[in] name name
* \return weak reference to rule package (an expired reference is returned if the wanted rule package does not exists).
*/
virtual std::weak_ptr<rules::RulePackage> getRulePackage(const std::string& name) noexcept = 0;
/**
* Returns a vector containing all known rulepackages.
*
* \return vector of weak references.
*/
virtual std::vector<std::weak_ptr<rules::RulePackage>> getRulePackages() noexcept = 0;
/**
* Returns the number of known rule packages.
*
* \return rule package count.
*/
virtual size_t getRulePackageCount() noexcept = 0;
/**
* Registers a rule condition factory under the given type identifier.
*
* \param[in] type condition type identifier.
* \param[in] factory rule condition factory.
*/
virtual void registerRuleConditionFactory(const std::string& type, std::shared_ptr<RuleConditionFactory> factory) noexcept = 0;
};
} // namespace ruleengine
} // namespace service
} // namespace cosgi
} // namespace hos
@@ -1,94 +0,0 @@
/*
* Rule.h
*
* Created on: 29.04.2015
* Author: matzman
*/
#ifndef SRC_HOS_COSGI_SERVICE_RULEENGINE_RULE_RULE_H_
#define SRC_HOS_COSGI_SERVICE_RULEENGINE_RULE_RULE_H_
#include <string>
#include <memory>
#include <vector>
#include <Variant/Variant.h>
#include "RuleCondition.h"
// Forward declarations
namespace hos {
namespace cosgi {
namespace service {
namespace devicerepository {
class Device;
class DeviceEvent;
class DeviceRepositoryService;
}
namespace ruleengine {
namespace rule {
class RulePackage;
}
}
namespace scripting {
class ScriptingService;
class GlobalScriptContext;
class Script;
}
}
}
namespace utils {
class SchemaLoader;
}
}
namespace hos {
namespace cosgi {
namespace service {
namespace ruleengine {
namespace rule {
using namespace ::libvariant;
class Rule {
private:
std::shared_ptr<scripting::Script> script;
std::shared_ptr<scripting::Script> exitScript;
std::shared_ptr<RuleCondition> condition;
bool enabled = false;
bool currentState = false;
public:
RulePackage* const parent;
const std::string name;
Rule(RulePackage* parent, const std::string name);
static std::shared_ptr<Rule> createRule(RulePackage* parent, const std::string name, Variant raw);
bool getCurrentState() { return currentState; }
bool evaluate();
void execute(Variant lContext = Variant());
void executeExitScript(Variant lContext = Variant());
bool isEnabled() { return enabled; }
void enable();
void disable();
void conditionStateChanged(bool newState, RuleCondition::Category category, Variant lcontext = Variant());
std::shared_ptr<RuleCondition> createRuleCondition(Variant raw);
utils::SchemaLoader* getHOSSchemaLoader();
devicerepository::DeviceRepositoryService* getDeviceRepository();
};
} /* namespace rule */
} /* namespace ruleengine */
} /* namespace service */
} /* namespace cosgi */
} /* namespace hos */
#endif /* SRC_HOS_COSGI_SERVICE_RULEENGINE_RULE_RULE_H_ */
@@ -1,105 +0,0 @@
/*
* RuleCondition.h
*
* Created on: 29.04.2015
* Author: matzman
*/
#ifndef SRC_HOS_COSGI_SERVICE_RULEENGINE_RULE_RULECONDITION_H_
#define SRC_HOS_COSGI_SERVICE_RULEENGINE_RULE_RULECONDITION_H_
#include <memory>
#include <vector>
#include <Variant/Variant.h>
namespace hos {
namespace cosgi {
namespace service {
namespace ruleengine {
namespace rule {
class Rule;
class RuleCompositeCondition;
using namespace ::libvariant;
class RuleCondition {
public:
enum class Category : int { Event, State, Mixed };
protected:
bool currentState = false;
Category category;
RuleCompositeCondition* compositeParent = nullptr;
bool enabled = false;
void notifyStateChangeToParent(bool newState, Category category, Variant lcontext = Variant());
public:
Rule* const parent;
const std::string type;
RuleCondition(Rule* parent, Category category, const std::string type) :
parent(parent), category(category), type(type) {}
virtual ~RuleCondition() {}
void setCompositeParent(RuleCompositeCondition* compositeParent) {
this->compositeParent = compositeParent;
}
virtual Category getCategory() { return category; }
bool getCurrentState() { return currentState; }
virtual bool evaluate() { return currentState; }
bool isEnabled() { return enabled; }
virtual void enable() { enabled = true; }
virtual void disable() { enabled = false; }
};
class RuleCompositeCondition : public RuleCondition {
protected:
std::vector<std::shared_ptr<RuleCondition>> children;
public:
RuleCompositeCondition(Rule* parent, Category category, std::string type) :
RuleCondition(parent, category, type) {}
virtual void childStateChanged(RuleCondition* child, bool newState, Category category, Variant lcontext = Variant()) = 0;
virtual void addChild(std::shared_ptr<RuleCondition> child) {
if (child) {
child->setCompositeParent(this);
children.push_back(child);
}
};
virtual void enable() {
if (!enabled) {
for (auto c : children) {
c->enable();
}
enabled = true;
}
}
virtual void disable() {
if (enabled) {
for (auto c : children) {
c->disable();
}
enabled = false;
}
}
};
} /* namespace rule */
} /* namespace ruleengine */
} /* namespace service */
} /* namespace cosgi */
} /* namespace hos */
#endif /* SRC_HOS_COSGI_SERVICE_RULEENGINE_RULE_RULECONDITION_H_ */
@@ -1,49 +0,0 @@
/*
* hos_ruleengine_service.h
*
* Created on: 26.04.2015
* Author: matzman
*/
#ifndef INCLUDE_HOS_COSGI_RULEENGINE_HOS_COSGI_SERVICE_RULEENGINE_RULEENGINE_SERVICE_H_
#define INCLUDE_HOS_COSGI_RULEENGINE_HOS_COSGI_SERVICE_RULEENGINE_RULEENGINE_SERVICE_H_
#include <hos/cosgi/service/Interface.h>
#include <memory>
#include <Variant/Variant.h>
namespace hos {
namespace cosgi {
namespace service {
namespace ruleengine {
namespace rule {
class RuleCondition;
class Rule;
}
class RuleConditionFactory : public virtual interface::Object {
public:
virtual std::shared_ptr<rule::RuleCondition> createCondition(rule::Rule* parent, ::libvariant::Variant raw) = 0;
};
class RuleEngineService : public virtual interface::Service {
public:
static constexpr const char* SERVICE_NAME = "hos_ruleengine_service";
virtual void registerRuleConditionFactory(std::string type, std::shared_ptr<RuleConditionFactory> factory) = 0;
};
} // namespace ruleengine
} // namespace service
} // namespace cosgi
} // namespace hos
#endif /* INCLUDE_HOS_COSGI_RULEENGINE_HOS_COSGI_SERVICE_RULEENGINE_RULEENGINE_SERVICE_H_ */
@@ -0,0 +1,145 @@
#pragma once
#include <hos/cosgi/service/Interface.h>
#include <Variant/Variant.h>
namespace hos {
namespace cosgi {
namespace service {
// forward declarations
namespace devicerepository { class DeviceRepositoryService; }
// main namespace
namespace ruleengine {
namespace rules {
// Forward declarations
namespace conditions {
class RuleCondition;
enum class ConditionTriggerType : int;
}
class RulePackage;
/**
* Public COSGI interface of a rule.
*/
class Rule
: public virtual interface::Object {
public:
/**
* Returns a reference to the parent rule package.
*
* \return rule package.
*/
virtual RulePackage* getParent() noexcept = 0;
/**
* Returns the name of the rule.
*
* \return name.
*/
virtual const std::string& getName() noexcept = 0;
/**
* Returns whether the rule is currently active.
*
* Only conditions of trigger type "state" can activate a rule.
*
* \return whether rule is active.
*/
virtual bool isActive() noexcept = 0;
/**
* Evaluates the conditions of the rule and may execute associated scripts.
*
* Only active conditions of trigger type "state" can return true here.
*
* May return exceptions derived from std::exception.
*
* \return evaluation result.
*/
virtual bool evaluate() = 0;
/**
* Executes the associated script.
*
* May return exceptions derived from std::exception.
*
* \param[in] local context.
*/
virtual void executeScript(const libvariant::Variant& lContext = libvariant::Variant()) = 0;
/**
* Executes the associated exit script.
*
* May return exceptions derived from std::exception.
*
* \param[in] local context.
*/
virtual void executeExitScript(const libvariant::Variant& lContext = libvariant::Variant()) = 0;
/**
* Returns whether this rule is enabled.
*
* \return enabled
*/
virtual bool isEnabled() noexcept = 0;
/**
* Enable/Disable this rule package.
*
* \param[in] enabled.
*/
virtual void setEnabled(bool enabled) noexcept = 0;
/* Interface for third-party conditions. */
/**
* Creates and returns a new condition based on the given json config.
*
* \param[in] json config.
* \return new rule condition.
*/
virtual std::shared_ptr<conditions::RuleCondition> createRuleCondition(const libvariant::Variant& raw) = 0;
/**
* Indicates that one of the conditions of the rule changed state.
*
* \param[in] newState new state.
* \param[in] trigger trigger type.
* \param[in] lcontext local context that should be passed to script.
*/
virtual void conditionStateChanged(bool newState, const conditions::ConditionTriggerType& trigger,
const libvariant::Variant& lcontext = libvariant::Variant()) noexcept = 0;
/**
* Returns a reference to the device repository service.
*
* \return device repository service.
*/
virtual devicerepository::DeviceRepositoryService* getDeviceRepository() noexcept = 0;
};
} /* namespace rules */
} /* namespace ruleengine */
} /* namespace service */
} /* namespace cosgi */
} /* namespace hos */
@@ -0,0 +1,127 @@
#pragma once
#include <hos/cosgi/service/Interface.h>
#include <vector>
#include <memory>
#include <string>
// Forward declarations
namespace hos { namespace cosgi { namespace service {
namespace scripting { class ScriptingService; }
namespace scripting { class GlobalScriptContext; }
} } }
namespace hos {
namespace cosgi {
namespace service {
namespace ruleengine {
// Forward declarations
class RuleEngineService;
// main namespace
namespace rules {
// Forward declarations
class Rule;
/**
* Public COSGI interface of a rule package.
*/
class RulePackage
: public virtual interface::Object {
public:
/**
* Returns a reference to the rule engine service.
*
* \return rule engine service.
*/
virtual service::ruleengine::RuleEngineService* getParent() noexcept = 0;
/**
* Returns the name of the rule package.
*
* \return name.
*/
virtual const std::string& getName() noexcept = 0;
/**
* Returns an identifier indicating the scripting language.
*
* \return scripting language identifier.
*/
virtual const std::string& getLanguage() noexcept = 0;
/**
* Returns the rule with the given name.
*
* \param[in] name rule name.
* \return weak reference to rule (expired reference in case of rule not found).
*/
virtual std::weak_ptr<Rule> getRule(const std::string& name) noexcept = 0;
/**
* Returns all rules contained within the rule package
*
* \return vector of weak references.
*/
virtual std::vector<std::weak_ptr<Rule>> getRules() noexcept = 0;
/**
* Returns the number of contained rules.
*
* \return rule count.
*/
virtual size_t getRuleCount() noexcept = 0;
/**
* Returns whether this rule package is enabled.
*
* \return enabled
*/
virtual bool isEnabled() noexcept = 0;
/**
* Enable/Disable this rule package.
*
* \param[in] enabled.
*/
virtual void setEnabled(bool enabled) noexcept = 0;
/**
* Returns a reference to the associated scripting service.
*
* \return scripting service.
*/
virtual service::scripting::ScriptingService* getScriptingService() noexcept = 0;
/**
* Returns a reference to the associated global script context.
*
* \return global script context.
*/
virtual std::weak_ptr<scripting::GlobalScriptContext> getGlobalScriptContext() noexcept = 0;
};
} /* namespace rules */
} /* namespace ruleengine */
} /* namespace service */
} /* namespace cosgi */
} /* namespace hos */
@@ -0,0 +1,163 @@
#pragma once
#include <hos/cosgi/service/Interface.h>
#include <memory>
#include <vector>
#include <Variant/Variant.h>
namespace hos {
namespace cosgi {
namespace service {
namespace ruleengine {
namespace rules {
// forward declarations
class Rule;
// main namespace
namespace conditions {
// forward declarations
class RuleCompositeCondition;
/*
* Condition trigger type.
*
* Conditions can have two different trigger types:
* - Event trigger: Conditions are triggered by events happening.
* - State trigger: Condition are triggered by device state.
*
* "Both" indicates that an condition has both trigger types.
**/
enum class ConditionTriggerType : int {
Event = 1,
State = 2,
Both = 3
};
/**
* Public COSGI interface of a rule condition.
*/
class RuleCondition
: public virtual interface::Object {
public:
/**
* Returns the type of the condition.
*
* \return condition type.
*/
virtual const std::string& getType() noexcept = 0;
/**
* Returns the trigger type of the condition.
*
* \return trigger type.
*/
virtual ConditionTriggerType getTriggerType() noexcept = 0;
/**
* Returns the current state of the condition.
*
* This function returns a cached value.
*
* \return current state.
*/
virtual bool getCurrentState() noexcept = 0;
/**
* Evaluates the condition and returns the result.
*
* \return evaluation result.
*/
virtual bool evaluate() noexcept = 0;
/**
* Returns a reference to the parent rule.
*
* \return parent rule.
*/
virtual rules::Rule* getParent() noexcept = 0;
/**
* Returns whether this rule condition is enabled.
*
* \return enabled.
*/
virtual bool isEnabled() noexcept = 0;
/**
* Enable/Disable this rule condition.
*
* \param[in] enabled.
*/
virtual void setEnabled(bool enabled) noexcept = 0;
/* Interface for third-party conditions. */
/**
* Sets the composite condition this condition is contained within.
*
* \param[in] parent composite condition.
*/
virtual void setCompositeParent(RuleCompositeCondition* compositeParent) noexcept = 0;
};
/**
* Public COSGI interface of a composite condition.
*/
class RuleCompositeCondition
: public virtual RuleCondition {
public:
/* Interface for third-party conditions. */
/**
* Indicates that a contained condition has changed its state.
*
* \param[in] child condition whose state has changed.
* \param[in] newState new state.
* \param[in] trigger trigger type.
* \param[in] lcontext local context that should be passed to the script.
*/
virtual void childStateChanged( RuleCondition* child,
bool newState,
ConditionTriggerType trigger,
const libvariant::Variant& lcontext = libvariant::Variant() ) noexcept = 0;
/**
* Adds a new condition.
*
* \param[in] child child condition.
*/
virtual void addChild(std::shared_ptr<RuleCondition> child) = 0;
};
} /* namespace conditions */
} /* namespace rules */
} /* namespace ruleengine */
} /* namespace service */
} /* namespace cosgi */
} /* namespace hos */
@@ -0,0 +1,123 @@
#pragma once
#include "RuleCondition.h"
// main namespace
namespace hos {
namespace cosgi {
namespace service {
namespace ruleengine {
namespace rules {
namespace conditions {
/**
* Base class for condition implementations.
*/
class RuleConditionImpl
: public virtual RuleCondition {
protected:
rules::Rule* parent;
ConditionTriggerType triggerType;
std::string type;
bool currentState = false;
RuleCompositeCondition* compositeParent = nullptr;
bool enabled = false;
void notifyStateChangeToParent(bool newState, ConditionTriggerType category, const libvariant::Variant& lcontext = libvariant::Variant());
public:
RuleConditionImpl(rules::Rule* parent, ConditionTriggerType triggerType, const std::string& type) :
parent(parent), triggerType(triggerType), type(type) {}
virtual rules::Rule* getParent() noexcept override {
return parent;
}
virtual void setCompositeParent(RuleCompositeCondition* compositeParent) noexcept override {
this->compositeParent = compositeParent;
}
virtual const std::string& getType() noexcept override {
return type;
}
virtual ConditionTriggerType getTriggerType() noexcept override {
return triggerType;
}
virtual bool getCurrentState() noexcept override {
return currentState;
}
virtual bool evaluate() noexcept override {
return currentState;
}
virtual bool isEnabled() noexcept override {
return enabled;
}
virtual void setEnabled(bool enabled) noexcept override {
this->enabled;
}
};
/**
* Base class for composite condition implementations.
*/
class RuleCompositeConditionImpl
: public virtual RuleCompositeCondition,
public RuleConditionImpl {
protected:
std::vector<std::shared_ptr<RuleCondition>> children;
public:
RuleCompositeConditionImpl(Rule* parent, ConditionTriggerType category, std::string type) :
RuleConditionImpl(parent, category, type) {}
virtual void childStateChanged( RuleCondition* child,
bool newState,
ConditionTriggerType category,
const libvariant::Variant& lcontext = libvariant::Variant() ) noexcept override = 0;
virtual void addChild(std::shared_ptr<RuleCondition> child) override {
if (child) {
child->setCompositeParent(this);
children.push_back(child);
}
};
virtual void setEnabled(bool enabled) noexcept override {
if (this->enabled != enabled) {
for (auto c : children) {
c->setEnabled(enabled);
}
this->enabled = enabled;
}
}
};
} /* namespace conditions */
} /* namespace rules */
} /* namespace ruleengine */
} /* namespace service */
} /* namespace cosgi */
} /* namespace hos */
@@ -21,7 +21,7 @@ extern "C" {
}
#include <hos/cosgi/service/core/CoreService.h>
#include "hos/cosgi/service/ruleengine/ruleengine_serviceimpl.h"
#include "hos/cosgi/service/ruleengine/RuleEngineServiceImpl.h"
#include <memory>
#include <mutex>
@@ -5,7 +5,6 @@
* Author: matzman
*/
#include "ruleengine_serviceimpl.h"
#include <ros/ros.h>
#include <exception>
#include <iostream>
@@ -16,8 +15,12 @@
#include <hos/cosgi/service/devicerepository/DeviceRepositoryService.h>
#include <Variant/Variant.h>
#include "rule/RulePackage.h"
#include "rule/RuleConditionImpl.h"
#include "RuleEngineServiceImpl.h"
#include "rules/conditions/LogicalAndCondition.h"
#include "rules/conditions/LogicalOrCondition.h"
#include "rules/conditions/DeviceMsgCondition.h"
#include "rules/RulePackageImpl.h"
namespace hos {
@@ -62,11 +65,11 @@ void RuleEngineServiceImpl::init() {
XmlRpc::XmlRpcValue rawValue;
nodehandle->getParam(ruleEnginePrefix, rawValue);
schemaLoader.reset(new utils::SchemaLoader());
registerRuleConditionFactory("logical_or", std::make_shared<rule::LogicalOrConditionFactory>());
registerRuleConditionFactory("logical_and", std::make_shared<rule::LogicalAndConditionFactory>());
registerRuleConditionFactory("devicestatusmsg", std::make_shared<rule::DeviceStatusMsgConditionFactory>());
registerRuleConditionFactory("devicecommandmsg", std::make_shared<rule::DeviceCommandMsgConditionFactory>());
registerRuleConditionFactory("devicestatechange", std::make_shared<rule::DeviceStateChangeConditionFactory>());
registerRuleConditionFactory("logical_or", std::make_shared<rules::conditions::LogicalOrConditionFactory>());
registerRuleConditionFactory("logical_and", std::make_shared<rules::conditions::LogicalAndConditionFactory>());
registerRuleConditionFactory("devicestatusmsg", std::make_shared<rules::conditions::DeviceStatusMsgConditionFactory>());
registerRuleConditionFactory("devicecommandmsg", std::make_shared<rules::conditions::DeviceCommandMsgConditionFactory>());
registerRuleConditionFactory("devicestatechange", std::make_shared<rules::conditions::DeviceStateChangeConditionFactory>());
if (rawValue.getType() == XmlRpc::XmlRpcValue::TypeStruct) {
for (auto pkg : rawValue) {
std::string pname = pkg.first;
@@ -96,11 +99,11 @@ void RuleEngineServiceImpl::addRulePkg(std::string name, std::string rawValue) {
Variant m = Deserialize(rawValue, SERIALIZE_JSON);
SchemaResult sr = SchemaValidate(s, m, schemaLoader.get());
if (!sr.Error()) {
std::shared_ptr<rule::RulePackage> pkg = rule::RulePackage::createRulePackage(this, name, m);
std::shared_ptr<rules::RulePackageImpl> pkg = rules::RulePackageImpl::createRulePackage(this, name, m);
if (pkg) {
if (pkg->getScriptingService()) {
rulePkgs.insert({name, pkg});
pkg->enable();
pkg->setEnabled(true);
} else {
noExecRulePkgs.insert({name, pkg});
std::cout << "No execution service found for rule package \"" << name << "\"" << std::endl;
@@ -142,17 +145,17 @@ scripting::ScriptingService* RuleEngineServiceImpl::getExecutionBackend(std::str
}
void RuleEngineServiceImpl::registerRuleConditionFactory(std::string type, std::shared_ptr<RuleConditionFactory> factory) {
void RuleEngineServiceImpl::registerRuleConditionFactory(const std::string& type, std::shared_ptr<RuleConditionFactory> factory) noexcept {
condFactories.insert({type, factory});
}
std::shared_ptr<rule::RulePackage> RuleEngineServiceImpl::getRulePackage(const std::string& name) {
std::shared_ptr<rule::RulePackage> retval;
std::weak_ptr<rules::RulePackage> RuleEngineServiceImpl::getRulePackage(const std::string& name) noexcept {
std::weak_ptr<rules::RulePackage> retval;
try {
retval = rulePkgs.at(name);
} catch(...) {}
if (!retval) {
if (retval.expired()) {
try {
retval = noExecRulePkgs.at(name);
} catch(...) {}
@@ -160,6 +163,18 @@ std::shared_ptr<rule::RulePackage> RuleEngineServiceImpl::getRulePackage(const s
return retval;
}
std::vector<std::weak_ptr<rules::RulePackage>> RuleEngineServiceImpl::getRulePackages() noexcept {
}
size_t RuleEngineServiceImpl::getRulePackageCount() noexcept {
}
} // namespace ruleengine
} // namespace service
} // namespace cosgi
@@ -1,12 +1,7 @@
/*
* hos_ruleengine_service_impl.h
*
* Created on: 26.04.2015
* Author: matzman
*/
#ifndef INCLUDE_HOS_COSGI_RULEENGINE_HOS_RULEENGINE_SERVICE_IMPL_H_
#define INCLUDE_HOS_COSGI_RULEENGINE_HOS_RULEENGINE_SERVICE_IMPL_H_
#pragma once
#include <mutex>
#include <map>
@@ -15,10 +10,10 @@
#include <hos_core_msgs/DeviceStatusUpdate.h>
#include <hos_core_msgs/HosStatusUpdate.h>
#include <hos/utils/SchemaLoader.h>
#include <hos/cosgi/service/ruleengine/ruleengine_service.h>
#include <hos/cosgi/service/scripting/scripting_service.h>
#include <hos/cosgi/service/devicerepository/DeviceRepositoryService.h>
#include <hos/cosgi/service/Implementation.h>
#include <hos/cosgi/service/ruleengine/RuleEngineService.h>
#include <hos/cosgi/service/scripting/ScriptingService.h>
#include <ros/ros.h>
#include <memory>
#include <string>
@@ -45,18 +40,19 @@ namespace hos {
namespace cosgi {
namespace service {
namespace ruleengine {
namespace rule {
class RulePackage;
namespace rules {
class RulePackageImpl;
}
class RuleEngineServiceImpl;
class RuleEngineServiceImpl :
public virtual RuleEngineService,
public virtual implementation::Service<RuleEngineServiceImpl> {
private:
typedef std::map<std::string, std::shared_ptr<rule::RulePackage>> rulePkgMap_t;
typedef std::map<std::string, std::shared_ptr<rules::RulePackageImpl>> rulePkgMap_t;
typedef std::map<std::string, scripting::ScriptingService*> execServiceMap_t;
typedef std::map<std::string, std::shared_ptr<RuleConditionFactory>> condFactoryMap_t;
@@ -82,39 +78,52 @@ private:
void init();
public:
/* Initialization */
void setCoreService(service::core::CoreService* coreService);
void setRepoService(devicerepository::DeviceRepositoryService* repoService);
/* Public Service Implementations */
virtual void registerRuleConditionFactory(std::string type, std::shared_ptr<RuleConditionFactory> factory) override;
virtual void registerRuleConditionFactory(const std::string& type, std::shared_ptr<RuleConditionFactory> factory) noexcept override;
virtual std::weak_ptr<rules::RulePackage> getRulePackage(const std::string& name) noexcept override;
virtual std::vector<std::weak_ptr<rules::RulePackage>> getRulePackages() noexcept override;
virtual size_t getRulePackageCount() noexcept override;
/* Internal Stuff */
bool isInitialized() { return initialized; }
void addExecutionBackend(scripting::ScriptingService* service);
scripting::ScriptingService* getExecutionBackend(std::string language);
std::shared_ptr<RuleConditionFactory> getRuleConditionFactory(std::string type) { return condFactories[type]; }
std::shared_ptr<RuleConditionFactory> getRuleConditionFactory(std::string type) {
return condFactories[type];
}
utils::SchemaLoader* getHOSSchemaLoader() { return schemaLoader.get(); }
utils::SchemaLoader* getHOSSchemaLoader() {
return schemaLoader.get();
}
devicerepository::DeviceRepositoryService* getDeviceRepository() { return repoService; }
devicerepository::DeviceRepositoryService* getDeviceRepository() {
return repoService;
}
std::shared_ptr<rule::RulePackage> getRulePackage(const std::string& name);
};
} // namespace ruleengine
} // namespace service
} // namespace cosgi
} // namespace hos
#endif /* INCLUDE_HOS_COSGI_RULEENGINE_HOS_RULEENGINE_SERVICE_IMPL_H_ */
@@ -1,432 +0,0 @@
/*
* RuleCondition.cpp
*
* Created on: 29.04.2015
* Author: matzman
*/
#include "RuleConditionImpl.h"
#include <hos/cosgi/service/ruleengine/rule/Rule.h>
#include <hos/cosgi/service/devicerepository/DeviceRepositoryService.h>
#include <ros/ros.h>
namespace hos {
namespace cosgi {
namespace service {
namespace ruleengine {
namespace rule {
std::shared_ptr<rule::RuleCondition> LogicalOrConditionFactory::createCondition(rule::Rule* parent, Variant raw) {
std::shared_ptr<rule::LogicalOrCondition> retval;
if (raw.HasPath("conditions") && raw["conditions"].IsList() && raw["conditions"].Size() > 0) {
retval = std::make_shared<rule::LogicalOrCondition>(parent);
for (auto c : raw["conditions"].AsList()) {
auto cond = parent->createRuleCondition(c);
if (cond) {
retval->addChild(cond);
} else {
throw std::runtime_error("Could not create condition.");
}
}
} else {
throw std::runtime_error("Malformed JSON: missing property condition or is not a list.");
}
return std::static_pointer_cast<rule::RuleCondition>(retval);
}
std::shared_ptr<rule::RuleCondition> LogicalAndConditionFactory::createCondition(rule::Rule* parent, Variant raw) {
std::shared_ptr<rule::LogicalAndCondition> retval;
if (raw.HasPath("conditions") && raw["conditions"].IsList() && raw["conditions"].Size() > 0) {
retval = std::make_shared<rule::LogicalAndCondition>(parent);
for (auto c : raw["conditions"].AsList()) {
auto cond = parent->createRuleCondition(c);
if (cond) {
retval->addChild(cond);
} else {
throw std::runtime_error("Could not create condition.");
}
}
} else {
throw std::runtime_error("Malformed JSON: missing property condition or is not a list.");
}
return std::static_pointer_cast<rule::RuleCondition>(retval);
}
void RuleCondition::notifyStateChangeToParent(bool newState, Category category, Variant lcontext) {
if (compositeParent) {
compositeParent->childStateChanged(this, newState, category, lcontext);
} else {
parent->conditionStateChanged(newState, category, lcontext);
}
}
void LogicalOrCondition::addChild(std::shared_ptr<RuleCondition> child) {
if (child->getCategory() == Category::Event) {
eventChildrenCount++;
} else if (child->getCategory() == Category::State) {
stateChildrenCount++;
} else {
mixedChildrenCount++;
}
if (mixedChildrenCount > 0 || (eventChildrenCount > 0 && stateChildrenCount > 0)) {
category = Category::Mixed;
} else if (eventChildrenCount > 0 && stateChildrenCount == 0) {
category = Category::Event;
} else {
category = Category::State;
}
RuleCompositeCondition::addChild(child);
}
void LogicalOrCondition::childStateChanged(RuleCondition* child, bool newState, Category category, Variant lcontext) {
if (newState && category == Category::Event && !currentState) {
notifyStateChangeToParent(true, category, lcontext);
} else if (category == Category::State) {
if (newState) {
trueChildrenCount++;
if (!currentState) {
currentState = true;
notifyStateChangeToParent(true, category, lcontext);
}
} else {
trueChildrenCount--;
if (currentState && trueChildrenCount <= 0) {
currentState = false;
notifyStateChangeToParent(false, category, lcontext);
}
}
}
}
bool LogicalOrCondition::evaluate() {
// Event conditions always evaluate to false
if (category == Category::Event) {
return false;
}
trueChildrenCount = 0;
for (auto c : children) {
if (c->evaluate()) {
trueChildrenCount++;
}
}
currentState = trueChildrenCount > 0;
return currentState;
}
void LogicalAndCondition::addChild(std::shared_ptr<RuleCondition> child) {
if (child->getCategory() == Category::Event) {
if (eventChildrenCount > 0) {
throw new std::runtime_error("Cannot have an and condition with more than two event conditions.");
}
eventChildrenCount++;
} else if (child->getCategory() == Category::State) {
stateChildrenCount++;
} else {
mixedChildrenCount++;
}
if (mixedChildrenCount > 0 || (eventChildrenCount > 0 && stateChildrenCount > 0)) {
category = Category::Mixed;
} else if (eventChildrenCount > 0 && stateChildrenCount == 0) {
category = Category::Event;
} else {
category = Category::State;
}
RuleCompositeCondition::addChild(child);
}
void LogicalAndCondition::childStateChanged(RuleCondition* child, bool newState, Category category, Variant lcontext) {
if (newState && category == Category::Event && !currentState && trueChildrenCount >= children.size()-1) {
notifyStateChangeToParent(true, category);
} else if (category == Category::State) {
if (newState) {
trueChildrenCount++;
if (!currentState && trueChildrenCount >= children.size()) {
currentState = true;
notifyStateChangeToParent(true, category, lcontext);
}
} else {
trueChildrenCount--;
if (currentState) {
currentState = false;
notifyStateChangeToParent(false, category, lcontext);
}
}
}
}
bool LogicalAndCondition::evaluate() {
// Event conditions always evaluate to false
if (category == Category::Event) {
return false;
}
trueChildrenCount = 0;
for (auto c : children) {
if (c->evaluate()) {
trueChildrenCount++;
}
}
currentState = trueChildrenCount == children.size();
return currentState;
}
void DeviceMsgCondition::notificationReceived(interface::Observer<devicerepository::DeviceEvent::EventType, devicerepository::DeviceEvent>::observable_type* o, const devicerepository::DeviceEvent::EventType& key, const devicerepository::DeviceEvent* e) noexcept {
switch (key) {
case devicerepository::DeviceEvent::EventType::DEVICE_ADDED:
if (!device && this->deviceID.compare(e->device->getDeviceID()) == 0) {
ROS_INFO_STREAM("Rule " << parent->name << ": Device " << deviceID << " appeared.");
device = e->device;
parent->getDeviceRepository()->deleteObserver(devicerepository::DeviceEvent::EventType::DEVICE_ADDED, this);
_registerAsListener();
}
break;
case devicerepository::DeviceEvent::EventType::DEVICE_REMOVED:
if (device && device->getDeviceID().compare(e->device->getDeviceID()) == 0) {
ROS_ERROR_STREAM("Rule " << parent->name << ": Device " << deviceID << " disappeared.");
device->deleteObserver(this);
device = nullptr;
parent->getDeviceRepository()->addObserver(devicerepository::DeviceEvent::EventType::DEVICE_ADDED, this);
}
break;
default:
break;
}
}
void DeviceMsgCondition::enable() {
auto repoService = parent->getDeviceRepository();
auto device = repoService->getDevice(deviceID);
if (!device.expired()) {
this->device = device.lock().get();
_registerAsListener();
} else {
ROS_ERROR_STREAM("Rule " << parent->name << ": Could not find device " << deviceID);
parent->getDeviceRepository()->addObserver(devicerepository::DeviceEvent::EventType::DEVICE_ADDED, this);
}
}
void DeviceMsgCondition::disable() {
if (device) {
device->deleteObserver(this);
device = nullptr;
}
parent->getDeviceRepository()->deleteObserver(this);
}
void DeviceMsgCondition::_registerAsListener() {
if (device) {
for (auto k : _listenerKeys) {
device->addObserver(k, this);
}
}
}
std::shared_ptr<rule::RuleCondition> DeviceStatusMsgConditionFactory::createCondition(rule::Rule* parent, Variant raw) {
std::shared_ptr<rule::DeviceStatusMsgCondition> retval;
std::string deviceID;
if (raw.HasPath("deviceid") && raw["deviceid"].IsString()) {
deviceID = raw["deviceid"].AsString();
} else {
throw std::runtime_error("Malformed JSON: missing property deviceid or is not an string.");
}
std::string msgPointer;
if (raw.HasPath("msgpointer")) {
if (raw["msgpointer"].IsString()) {
msgPointer = raw["msgpointer"].AsString();
} else {
throw std::runtime_error("Malformed JSON: property msgpointer is not an string.");
}
}
Variant msgValue;
if (raw.HasPath("msgvalue")) {
msgValue = raw["msgvalue"];
}
retval = std::make_shared<rule::DeviceStatusMsgCondition>(parent, deviceID, msgPointer, msgValue);
return retval;
}
void DeviceStatusMsgCondition::notificationReceived(interface::Observer<devicerepository::DeviceEvent::EventType, devicerepository::DeviceEvent>::observable_type* o, const devicerepository::DeviceEvent::EventType& key, const devicerepository::DeviceEvent* e) noexcept {
switch (e->eventType) {
case devicerepository::DeviceEvent::EventType::STATUS_UPDATE_RECEIVED:
if (device && device == e->device) {
auto e2 = static_cast<const devicerepository::DeviceStatusMsgEvent*>(e);
bool notify = true;
Variant value;
if (msgPointer.empty()) {
value = e2->statusMsg;
} else if (e2->statusMsg.HasPath(msgPointer)) {
value = e2->statusMsg.GetPath(msgPointer);
} else {
notify = false;
}
if (notify && !msgValue.IsNull()) {
if (msgValue != value) {
notify = false;
}
}
if (notify) {
Variant lcontext;
lcontext["event_deviceid"] = Variant(e->device->getDeviceID());
lcontext["event_type"] = Variant("statusmsg");
lcontext["event_msg"] = e2->statusMsg;
notifyStateChangeToParent(true, Category::Event, lcontext);
}
}
break;
default:
DeviceMsgCondition::notificationReceived(o, key, e);
break;
}
}
std::shared_ptr<rule::RuleCondition> DeviceCommandMsgConditionFactory::createCondition(rule::Rule* parent, Variant raw) {
std::shared_ptr<rule::DeviceCommandMsgCondition> retval;
std::string deviceID;
if (raw.HasPath("deviceid") && raw["deviceid"].IsString()) {
deviceID = raw["deviceid"].AsString();
} else {
throw std::runtime_error("Malformed JSON: missing property deviceid or is not an string.");
}
std::string msgPointer;
if (raw.HasPath("msgpointer")) {
if (raw["msgpointer"].IsString()) {
msgPointer = raw["msgpointer"].AsString();
} else {
throw std::runtime_error("Malformed JSON: property msgpointer is not an string.");
}
}
Variant msgValue;
if (raw.HasPath("msgvalue")) {
msgValue = raw["msgvalue"];
}
retval = std::make_shared<rule::DeviceCommandMsgCondition>(parent, deviceID, msgPointer, msgValue);
return retval;
}
void DeviceCommandMsgCondition::notificationReceived(interface::Observer<devicerepository::DeviceEvent::EventType, devicerepository::DeviceEvent>::observable_type* o, const devicerepository::DeviceEvent::EventType& key, const devicerepository::DeviceEvent* e) noexcept {
switch (e->eventType) {
case devicerepository::DeviceEvent::EventType::COMMAND_RECEIVED:
if (device && device == e->device) {
auto e2 = static_cast<const devicerepository::DeviceCmdMsgEvent*>(e);
bool notify = true;
Variant value;
if (msgPointer.empty()) {
value = e2->cmdMsg;
} else if (e2->cmdMsg.HasPath(msgPointer)) {
value = e2->cmdMsg.GetPath(msgPointer);
} else {
notify = false;
}
if (notify && !msgValue.IsNull()) {
if (msgValue != value) {
notify = false;
}
}
if (notify) {
Variant lcontext;
lcontext["event_deviceid"] = Variant(e->device->getDeviceID());
lcontext["event_type"] = Variant("commandmsg");
lcontext["event_msg"] = e2->cmdMsg;
notifyStateChangeToParent(true, Category::Event, lcontext);
}
}
break;
default:
DeviceMsgCondition::notificationReceived(o, key, e);
break;
}
}
std::shared_ptr<rule::RuleCondition> DeviceStateChangeConditionFactory::createCondition(rule::Rule* parent, Variant raw) {
std::shared_ptr<rule::DeviceStateChangeCondition> retval;
std::string deviceID;
if (raw.HasPath("deviceid") && raw["deviceid"].IsString()) {
deviceID = raw["deviceid"].AsString();
} else {
throw std::runtime_error("Malformed JSON: missing property deviceid or is not an string.");
}
std::string statePointer;
if (raw.HasPath("msgpointer")) {
if (raw["msgpointer"].IsString()) {
statePointer = raw["msgpointer"].AsString();
} else {
throw std::runtime_error("Malformed JSON: property msgpointer is not an string.");
}
}
Variant fromValue;
if (raw.HasPath("fromvalue")) {
fromValue = raw["fromvalue"];
}
Variant toValue;
if (raw.HasPath("tovalue")) {
toValue = raw["tovalue"];
}
retval = std::make_shared<DeviceStateChangeCondition>(parent, deviceID, statePointer, fromValue, toValue);
return retval;
}
void DeviceStateChangeCondition::notificationReceived(interface::Observer<devicerepository::DeviceEvent::EventType, devicerepository::DeviceEvent>::observable_type* o, const devicerepository::DeviceEvent::EventType& key, const devicerepository::DeviceEvent* e) noexcept {
switch (e->eventType) {
case devicerepository::DeviceEvent::EventType::STATE_CHANGED:
if (device && device == e->device) {
auto e2 = static_cast<const devicerepository::DeviceStatusMsgEvent*>(e);
bool notify = true;
Variant current, last;
if (msgPointer.empty()) {
current = device->getCurrentState();
last = device->getLastState();
} else if (device->getCurrentState().HasPath(msgPointer) && device->getLastState().HasPath(msgPointer)) {
current = device->getCurrentState().GetPath(msgPointer);
last = device->getLastState().GetPath(msgPointer);
} else {
notify = false;
}
if (notify) {
if (current == last) {
notify = false;
} else if (!fromValue.IsNull() && fromValue != last) {
notify = false;
} else if (!msgValue.IsNull() && msgValue != current) {
notify = false;
}
}
if (notify) {
Variant lcontext;
lcontext["event_deviceid"] = Variant(e->device->getDeviceID());
lcontext["event_type"] = Variant("statechange");
lcontext["event_msg"] = e2->statusMsg;
notifyStateChangeToParent(true, Category::Event, lcontext);
}
}
break;
default:
DeviceMsgCondition::notificationReceived(o, key, e);
break;
}
}
} /* namespace rule */
} /* namespace ruleengine */
} /* namespace service */
} /* namespace cosgi */
} /* namespace hos */
@@ -1,142 +0,0 @@
/*
* RuleConditionImpl.h
*
* Created on: 11.05.2015
* Author: matzman
*/
#ifndef SRC_HOS_COSGI_SERVICE_RULEENGINE_RULE_RULECONDITIONIMPL_H_
#define SRC_HOS_COSGI_SERVICE_RULEENGINE_RULE_RULECONDITIONIMPL_H_
#include <hos/cosgi/service/ruleengine/rule/RuleCondition.h>
#include <hos/cosgi/service/ruleengine/ruleengine_service.h>
#include <hos/cosgi/service/devicerepository/DeviceRepositoryService.h>
#include <set>
// Service Namespace
namespace hos {
namespace cosgi {
namespace service {
namespace ruleengine {
namespace rule {
using namespace ::libvariant;
class LogicalOrCondition : public RuleCompositeCondition {
protected:
unsigned eventChildrenCount = 0;
unsigned stateChildrenCount = 0;
unsigned mixedChildrenCount = 0;
unsigned trueChildrenCount = 0;
public:
LogicalOrCondition(Rule* parent) :
RuleCompositeCondition(parent, Category::Mixed, "logical_or") {}
virtual void addChild(std::shared_ptr<RuleCondition> child) override;
virtual void childStateChanged(RuleCondition* child, bool newState, Category category, Variant lcontext = Variant()) override;
virtual bool evaluate() override;
};
class LogicalOrConditionFactory : public virtual RuleConditionFactory {
public:
virtual std::shared_ptr<rule::RuleCondition> createCondition(rule::Rule* parent, ::libvariant::Variant raw) override;
};
class LogicalAndCondition : public RuleCompositeCondition {
protected:
unsigned eventChildrenCount = 0;
unsigned stateChildrenCount = 0;
unsigned mixedChildrenCount = 0;
unsigned trueChildrenCount = 0;
public:
LogicalAndCondition(Rule* parent) :
RuleCompositeCondition(parent, Category::Mixed, "logical_and") {}
virtual void addChild(std::shared_ptr<RuleCondition> child) override;
virtual void childStateChanged(RuleCondition* child, bool newState, Category category, Variant lcontext = Variant()) override;
virtual bool evaluate() override;
};
class LogicalAndConditionFactory : public virtual RuleConditionFactory {
public:
virtual std::shared_ptr<rule::RuleCondition> createCondition(rule::Rule* parent, ::libvariant::Variant raw) override;
};
class DeviceMsgCondition : public RuleCondition, public virtual interface::Observer<devicerepository::DeviceEvent::EventType, devicerepository::DeviceEvent> {
protected:
const std::string deviceID;
devicerepository::Device* device = nullptr;
const std::string msgPointer;
const Variant msgValue;
std::set<devicerepository::DeviceEvent::EventType> _listenerKeys;
virtual void _registerAsListener();
public:
DeviceMsgCondition(Rule* parent, Category category, std::string type, const std::string deviceID, const std::string msgPointer, const Variant msgValue, std::set<devicerepository::DeviceEvent::EventType> _listenerKeys) :
RuleCondition(parent, category, type), deviceID(deviceID), msgPointer(msgPointer), msgValue(msgValue), _listenerKeys(_listenerKeys) {}
virtual void notificationReceived(interface::Observer<devicerepository::DeviceEvent::EventType, devicerepository::DeviceEvent>::observable_type* o, const devicerepository::DeviceEvent::EventType& key, const devicerepository::DeviceEvent* e) noexcept override;
virtual void enable() override;
virtual void disable() override;
};
class DeviceStatusMsgCondition : public DeviceMsgCondition {
public:
DeviceStatusMsgCondition(Rule* parent, const std::string deviceID, const std::string msgPointer, const Variant msgValue) :
DeviceMsgCondition(parent, Category::Event, "devicestatusmsg", deviceID, msgPointer, msgValue, {devicerepository::DeviceEvent::EventType::STATUS_UPDATE_RECEIVED}) {}
virtual void notificationReceived(interface::Observer<devicerepository::DeviceEvent::EventType, devicerepository::DeviceEvent>::observable_type* o, const devicerepository::DeviceEvent::EventType& key, const devicerepository::DeviceEvent* e) noexcept override;
};
class DeviceStatusMsgConditionFactory : public virtual RuleConditionFactory {
public:
virtual std::shared_ptr<rule::RuleCondition> createCondition(rule::Rule* parent, Variant raw) override;
};
class DeviceCommandMsgCondition : public DeviceMsgCondition {
public:
DeviceCommandMsgCondition(Rule* parent, const std::string deviceID, const std::string msgPointer, const Variant msgValue) :
DeviceMsgCondition(parent, Category::Event, "devicecommandmsg", deviceID, msgPointer, msgValue, {devicerepository::DeviceEvent::EventType::COMMAND_RECEIVED}) {}
virtual void notificationReceived(interface::Observer<devicerepository::DeviceEvent::EventType, devicerepository::DeviceEvent>::observable_type* o, const devicerepository::DeviceEvent::EventType& key, const devicerepository::DeviceEvent* e) noexcept override;
};
class DeviceCommandMsgConditionFactory : public virtual RuleConditionFactory {
public:
virtual std::shared_ptr<rule::RuleCondition> createCondition(rule::Rule* parent, ::libvariant::Variant raw) override;
};
class DeviceStateChangeCondition : public DeviceMsgCondition {
protected:
const Variant fromValue;
public:
DeviceStateChangeCondition(Rule* parent, const std::string deviceID, const std::string statePointer, const Variant fromValue, const Variant toValue) :
DeviceMsgCondition(parent, Category::Event, "devicestatechange", deviceID, statePointer, toValue, {devicerepository::DeviceEvent::EventType::STATE_CHANGED}), fromValue(fromValue) {}
virtual void notificationReceived(interface::Observer<devicerepository::DeviceEvent::EventType, devicerepository::DeviceEvent>::observable_type* o, const devicerepository::DeviceEvent::EventType& key, const devicerepository::DeviceEvent* e) noexcept override;
};
class DeviceStateChangeConditionFactory : public virtual RuleConditionFactory {
public:
virtual std::shared_ptr<rule::RuleCondition> createCondition(rule::Rule* parent, Variant raw) override;
};
} /* namespace rule */
} /* namespace ruleengine */
} /* namespace service */
} /* namespace cosgi */
} /* namespace hos */
#endif /* SRC_HOS_COSGI_SERVICE_RULEENGINE_RULE_RULECONDITIONIMPL_H_ */
@@ -1,85 +0,0 @@
/*
* RulePackage.h
*
* Created on: 29.04.2015
* Author: matzman
*/
#ifndef SRC_HOS_COSGI_SERVICE_RULEENGINE_RULE_RULEPACKAGE_H_
#define SRC_HOS_COSGI_SERVICE_RULEENGINE_RULE_RULEPACKAGE_H_
#include <hos/cosgi/service/scripting/scripting_service.h>
#include <vector>
#include <map>
#include <memory>
#include <string>
#include <Variant/Variant.h>
// Forward declarations
namespace hos {
namespace cosgi {
namespace service {
namespace ruleengine {
class RuleEngineServiceImpl;
class RuleConditionFactory;
namespace rule {
class Rule;
}
}
namespace scripting {
class GlobalScriptContext;
}
}
}
}
namespace hos {
namespace cosgi {
namespace service {
namespace ruleengine {
namespace rule {
using namespace ::libvariant;
class RulePackage {
private:
typedef std::map<std::string, std::shared_ptr<Rule>> ruleMap_t;
service::scripting::ScriptingService* executionService = nullptr;
std::shared_ptr<scripting::GlobalScriptContext> globalContext;
ruleMap_t rules;
Variant rawValue;
bool enabled = false;
public:
service::ruleengine::RuleEngineServiceImpl* const parent;
const std::string name;
const std::string language;
static std::shared_ptr<RulePackage> createRulePackage(service::ruleengine::RuleEngineServiceImpl* parent, std::string name, Variant raw);
RulePackage(service::ruleengine::RuleEngineServiceImpl* parent, const std::string name, const std::string language, Variant raw);
virtual ~RulePackage();
std::shared_ptr<Rule> getRule(const std::string& name);
size_t getRules(std::vector<std::shared_ptr<Rule>>& rules);
bool isEnabled() { return enabled; }
void enable();
void disable();
service::scripting::ScriptingService* getScriptingService() { return executionService; }
std::shared_ptr<scripting::GlobalScriptContext> getGlobalScriptContext() { return globalContext; }
std::shared_ptr<service::ruleengine::RuleConditionFactory> getConditionFactory(std::string type);
};
} /* namespace rule */
} /* namespace ruleengine */
} /* namespace service */
} /* namespace cosgi */
} /* namespace hos */
#endif /* SRC_HOS_COSGI_SERVICE_RULEENGINE_RULE_RULEPACKAGE_H_ */
@@ -6,39 +6,36 @@
*/
#include <hos/cosgi/service/ruleengine/rule/Rule.h>
#include "../rules/RuleImpl.h"
#include <hos/cosgi/service/scripting/scripting_service.h>
#include <hos/cosgi/service/ruleengine/rule/RuleCondition.h>
#include <ros/ros.h>
#include "RulePackage.h"
#include <hos/cosgi/service/ruleengine/ruleengine_service.h>
#include <hos/cosgi/service/devicerepository/DeviceRepositoryService.h>
#include <hos/cosgi/service/ruleengine/rules/conditions/RuleCondition.h>
#include <hos/cosgi/service/ruleengine/RuleEngineService.h>
#include <hos/cosgi/service/scripting/ScriptingService.h>
#include <hos/utils/SchemaLoader.h>
#include "../ruleengine_serviceimpl.h"
#include <Variant/Variant.h>
#include "../rules/RulePackageImpl.h"
#include "../RuleEngineServiceImpl.h"
namespace hos {
namespace cosgi {
namespace service {
namespace ruleengine {
namespace rule {
namespace rules {
Rule::Rule(RulePackage* parent, std::string name)
RuleImpl::RuleImpl(RulePackageImpl* parent, std::string name)
: parent(parent), name(name) {}
utils::SchemaLoader* Rule::getHOSSchemaLoader() {
return parent->parent->getHOSSchemaLoader();
}
std::shared_ptr<RuleCondition> Rule::createRuleCondition(Variant condition) {
std::shared_ptr<RuleCondition> retval;
std::shared_ptr<conditions::RuleCondition> RuleImpl::createRuleCondition(const Variant& condition) {
std::shared_ptr<conditions::RuleCondition> retval;
if (condition.IsMap() && condition.HasPath("type") && condition["type"].IsString()) {
if (condition.HasPath("$schema")) {
SchemaResult sr = SchemaValidate(Variant(), condition, getHOSSchemaLoader());
SchemaResult sr = SchemaValidate(Variant(), condition, parent->parent->getHOSSchemaLoader());
if (sr.Error()) {
throw std::runtime_error(sr.PrettyPrintMessage());
}
@@ -60,13 +57,13 @@ std::shared_ptr<RuleCondition> Rule::createRuleCondition(Variant condition) {
return retval;
}
std::shared_ptr<Rule> Rule::createRule(RulePackage* parent, std::string name, Variant raw) {
std::shared_ptr<Rule> retval;
if (parent->getRule(name)) {
std::shared_ptr<RuleImpl> RuleImpl::createRule(RulePackageImpl* parent, std::string name, Variant raw) {
std::shared_ptr<RuleImpl> retval;
if (!parent->getRule(name).expired()) {
throw std::runtime_error("Rule with the same name already exists.");
}
if (raw.IsMap()) {
retval = std::make_shared<Rule>(parent, name);
retval = std::make_shared<RuleImpl>(parent, name);
if (raw.HasPath("condition") && raw["condition"].IsMap()) {
auto condition = raw["condition"];
auto cond = retval->createRuleCondition(condition);
@@ -78,7 +75,7 @@ std::shared_ptr<Rule> Rule::createRule(RulePackage* parent, std::string name, Va
auto scriptFactory = parent->getScriptingService();
//ToDo: Create Condition
if (raw.HasPath("script") && raw["script"].IsMap()) {
auto script = scriptFactory->createScript(parent->getGlobalScriptContext().get(), raw["script"]);
auto script = scriptFactory->createScript(raw["script"]);
if (script) {
retval->script = script;
} else {
@@ -88,7 +85,7 @@ std::shared_ptr<Rule> Rule::createRule(RulePackage* parent, std::string name, Va
throw std::runtime_error("Malformed JSON: missing property script or is not an object.");
}
if (raw.HasPath("exitscript") && raw["exitscript"].IsMap()) {
auto script = scriptFactory->createScript(parent->getGlobalScriptContext().get(), raw["exitscript"]);
auto script = scriptFactory->createScript(raw["exitscript"]);
if (script) {
retval->exitScript = script;
} else {
@@ -105,43 +102,34 @@ std::shared_ptr<Rule> Rule::createRule(RulePackage* parent, std::string name, Va
}
devicerepository::DeviceRepositoryService* Rule::getDeviceRepository() {
devicerepository::DeviceRepositoryService* RuleImpl::getDeviceRepository() noexcept {
return parent->parent->getDeviceRepository();
}
void Rule::enable() {
if (!enabled) {
void RuleImpl::setEnabled(bool enabled) noexcept {
if (this->enabled != enabled) {
if (condition) {
condition->enable();
condition->setEnabled(enabled);
}
enabled = true;
}
}
void Rule::disable() {
if (enabled) {
if (condition) {
condition->disable();
}
enabled = false;
this->enabled = enabled;
}
}
void Rule::execute(Variant lContext) {
void RuleImpl::executeScript(const libvariant::Variant& lContext) {
if (enabled && script) {
try {
script->exec(parent->getGlobalScriptContext().get(), lContext);
script->execute(parent->getGlobalScriptContext().lock(), lContext);
} catch(std::exception& e) {
ROS_ERROR_STREAM(e.what());
}
}
}
void Rule::executeExitScript(Variant lContext) {
void RuleImpl::executeExitScript(const libvariant::Variant& lContext) {
if (enabled && exitScript) {
try {
exitScript->exec(parent->getGlobalScriptContext().get(), lContext);
exitScript->execute(parent->getGlobalScriptContext().lock(), lContext);
} catch(std::exception& e) {
ROS_ERROR_STREAM(e.what());
}
@@ -149,13 +137,13 @@ void Rule::executeExitScript(Variant lContext) {
}
bool Rule::evaluate() {
bool RuleImpl::evaluate() {
if (condition) {
bool retval = condition->evaluate();
if (retval != currentState) {
currentState = retval;
if (retval) {
execute();
executeScript();
} else {
executeExitScript();
}
@@ -168,13 +156,13 @@ bool Rule::evaluate() {
}
void Rule::conditionStateChanged(bool newState, RuleCondition::Category category, Variant lcontext) {
if (newState && !currentState && category == RuleCondition::Category::Event) {
execute(lcontext);
} else if (category == RuleCondition::Category::State && newState != currentState) {
void RuleImpl::conditionStateChanged(bool newState, const conditions::ConditionTriggerType& category, const Variant& lcontext) noexcept {
if (newState && !currentState && category == conditions::ConditionTriggerType::Event) {
executeScript(lcontext);
} else if (category == conditions::ConditionTriggerType::State && newState != currentState) {
currentState = newState;
if (newState) {
execute(lcontext);
executeScript(lcontext);
} else {
executeExitScript(lcontext);
}
@@ -0,0 +1,100 @@
#pragma once
#include <hos/cosgi/service/ruleengine/rules/conditions/RuleCondition.h>
#include <hos/cosgi/service/ruleengine/rules/Rule.h>
#include <hos/cosgi/service/ruleengine/rules/RulePackage.h>
#include "RulePackageImpl.h"
#include <string>
#include <memory>
#include <vector>
#include <Variant/Variant.h>
// Forward declarations
namespace hos {
namespace cosgi {
namespace service {
namespace devicerepository {
class Device;
class DeviceEvent;
class DeviceRepositoryService;
}
namespace ruleengine {
namespace rules {
class RulePackageImpl;
}
}
namespace scripting {
class ScriptingService;
class GlobalScriptContext;
class Script;
}
}
}
namespace utils {
class SchemaLoader;
}
}
namespace hos {
namespace cosgi {
namespace service {
namespace ruleengine {
namespace rules {
using namespace ::libvariant;
class RuleImpl : public virtual Rule {
private:
std::shared_ptr<scripting::Script> script;
std::shared_ptr<scripting::Script> exitScript;
std::shared_ptr<conditions::RuleCondition> condition;
bool enabled = false;
bool currentState = false;
public:
RulePackageImpl* const parent;
const std::string name;
RuleImpl(RulePackageImpl* parent, const std::string name);
static std::shared_ptr<RuleImpl> createRule(RulePackageImpl* parent, const std::string name, Variant raw);
virtual RulePackage* getParent() noexcept {
return dynamic_cast<RulePackage*>(parent);
}
virtual const std::string& getName() noexcept {
return name;
}
virtual bool isActive() noexcept override {
return currentState;
}
virtual bool evaluate() override;
virtual void executeScript(const libvariant::Variant& lContext = libvariant::Variant()) override;
virtual void executeExitScript(const libvariant::Variant& lContext = libvariant::Variant()) override;
virtual bool isEnabled() noexcept override {
return enabled;
}
virtual void setEnabled(bool enabled) noexcept override;
void conditionStateChanged(bool newState, const conditions::ConditionTriggerType& category, const Variant& lcontext = Variant()) noexcept override;
std::shared_ptr<conditions::RuleCondition> createRuleCondition(const Variant& raw) override;
devicerepository::DeviceRepositoryService* getDeviceRepository() noexcept override;
};
} /* namespace rules */
} /* namespace ruleengine */
} /* namespace service */
} /* namespace cosgi */
} /* namespace hos */
@@ -5,29 +5,29 @@
* Author: matzman
*/
#include "RulePackage.h"
#include "RuleImpl.h"
#include <ros/ros.h>
#include <exception>
#include <Variant/Variant.h>
#include "../ruleengine_serviceimpl.h"
#include <hos/cosgi/service/ruleengine/rule/Rule.h>
#include "../RuleEngineServiceImpl.h"
#include "RulePackageImpl.h"
namespace hos {
namespace cosgi {
namespace service {
namespace ruleengine {
namespace rule {
namespace rules {
RulePackage::RulePackage(service::ruleengine::RuleEngineServiceImpl* parent, const std::string name, const std::string language, const Variant raw)
RulePackageImpl::RulePackageImpl(service::ruleengine::RuleEngineServiceImpl* parent, const std::string name, const std::string language, const Variant raw)
: parent(parent), name(name), language(language), rawValue(raw) {}
RulePackage::~RulePackage() {}
RulePackageImpl::~RulePackageImpl() {}
std::shared_ptr<RulePackage> RulePackage::createRulePackage(service::ruleengine::RuleEngineServiceImpl* parent, std::string name, Variant raw) {
std::shared_ptr<RulePackage> retval;
if (parent->getRulePackage(name)) {
std::shared_ptr<RulePackageImpl> RulePackageImpl::createRulePackage(service::ruleengine::RuleEngineServiceImpl* parent, std::string name, Variant raw) {
std::shared_ptr<RulePackageImpl> retval;
if (!parent->getRulePackage(name).expired()) {
std::stringstream ss;
ss << "Could not create package " << name << ": There already exists a rule package with the same name.";
throw std::runtime_error(ss.str());
@@ -39,7 +39,7 @@ std::shared_ptr<RulePackage> RulePackage::createRulePackage(service::ruleengine:
} else {
throw std::runtime_error("Rule package JSON is missing language parameter or parameter has wrong type.");
}
retval = std::make_shared<RulePackage>(parent, name, language, raw);
retval = std::make_shared<RulePackageImpl>(parent, name, language, raw);
retval->executionService = parent->getExecutionBackend(retval->language);
if (retval->executionService) {
if (raw.HasPath("globalcontext")) {
@@ -48,7 +48,7 @@ std::shared_ptr<RulePackage> RulePackage::createRulePackage(service::ruleengine:
if (raw.HasPath("rules") && raw["rules"].IsMap()) {
for (auto r : raw["rules"].AsMap()) {
try {
std::shared_ptr<Rule> rule = Rule::createRule(retval.get(), r.first, r.second);
std::shared_ptr<RuleImpl> rule = RuleImpl::createRule(retval.get(), r.first, r.second);
if (rule) {
retval->rules.insert({r.first, rule});
}
@@ -70,49 +70,39 @@ std::shared_ptr<RulePackage> RulePackage::createRulePackage(service::ruleengine:
}
std::shared_ptr<Rule> RulePackage::getRule(const std::string& name) {
std::shared_ptr<Rule> retval;
std::weak_ptr<Rule> RulePackageImpl::getRule(const std::string& name) noexcept {
std::weak_ptr<Rule> retval;
try {
retval = rules.at(name);
} catch (...) {}
return retval;
}
size_t RulePackage::getRules(std::vector<std::shared_ptr<Rule>>& rules) {
size_t retval = 0;
std::vector<std::weak_ptr<Rule>> RulePackageImpl::getRules() noexcept {
std::vector<std::weak_ptr<Rule>> retval;
for (auto item : this->rules) {
rules.push_back(item.second);
retval++;
retval.push_back(dynamic_pointer_cast<Rule>(item.second));
}
return retval;
}
std::shared_ptr<RuleConditionFactory> RulePackage::getConditionFactory(std::string type) {
std::shared_ptr<RuleConditionFactory> RulePackageImpl::getConditionFactory(std::string type) {
return parent->getRuleConditionFactory(type);
}
void RulePackage::enable() {
if (!enabled) {
void RulePackageImpl::setEnabled(bool enabled) noexcept {
if (this->enabled != enabled) {
for (auto r : rules) {
r.second->enable();
r.second->setEnabled(enabled);
}
enabled = true;
this->enabled = enabled;
}
}
void RulePackage::disable() {
if (enabled) {
for (auto r : rules) {
r.second->enable();
}
enabled = false;
}
}
} /* namespace rule */
} /* namespace rules */
} /* namespace ruleengine */
} /* namespace service */
} /* namespace cosgi */
@@ -0,0 +1,97 @@
#pragma once
#include <hos/cosgi/service/ruleengine/rules/RulePackage.h>
#include <hos/cosgi/service/scripting/ScriptingService.h>
#include "../RuleEngineServiceImpl.h"
#include <vector>
#include <map>
#include <memory>
#include <string>
#include <Variant/Variant.h>
// Forward declarations
namespace hos {
namespace cosgi {
namespace service {
namespace ruleengine {
class RuleEngineServiceImpl;
class RuleConditionFactory;
namespace rules {
class RuleImpl;
}
}
namespace scripting {
class GlobalScriptContext;
}
}
}
}
namespace hos {
namespace cosgi {
namespace service {
namespace ruleengine {
namespace rules {
using namespace ::libvariant;
class RulePackageImpl
: public virtual RulePackage {
private:
typedef std::map<std::string, std::shared_ptr<RuleImpl>> ruleMap_t;
service::scripting::ScriptingService* executionService = nullptr;
std::shared_ptr<scripting::GlobalScriptContext> globalContext;
ruleMap_t rules;
Variant rawValue;
bool enabled = false;
public:
static std::shared_ptr<RulePackageImpl> createRulePackage(service::ruleengine::RuleEngineServiceImpl* parent, std::string name, Variant raw);
RulePackageImpl(service::ruleengine::RuleEngineServiceImpl* parent, const std::string name, const std::string language, Variant raw);
virtual ~RulePackageImpl();
service::ruleengine::RuleEngineServiceImpl* const parent;
const std::string name;
const std::string language;
virtual service::ruleengine::RuleEngineService* getParent() noexcept override {
return dynamic_cast<service::ruleengine::RuleEngineService*>(parent);
}
virtual const std::string& getName() noexcept override {
return name;
};
virtual const std::string& getLanguage() noexcept override {
return language;
}
virtual std::weak_ptr<Rule> getRule(const std::string& name) noexcept override;
virtual std::vector<std::weak_ptr<Rule>> getRules() noexcept override;
virtual size_t getRuleCount() noexcept override { return rules.size(); }
virtual bool isEnabled() noexcept override {
return enabled;
}
virtual void setEnabled(bool enabled) noexcept override;
virtual service::scripting::ScriptingService* getScriptingService() noexcept override { return executionService; }
virtual std::weak_ptr<scripting::GlobalScriptContext> getGlobalScriptContext() noexcept override { return globalContext; }
std::shared_ptr<service::ruleengine::RuleConditionFactory> getConditionFactory(std::string type);
};
} /* namespace rules */
} /* namespace ruleengine */
} /* namespace service */
} /* namespace cosgi */
} /* namespace hos */
@@ -0,0 +1,271 @@
#include "DeviceMsgCondition.h"
#include <hos/cosgi/service/devicerepository/DeviceRepositoryService.h>
#include "../RuleImpl.h"
#include <ros/ros.h>
namespace hos {
namespace cosgi {
namespace service {
namespace ruleengine {
namespace rules {
namespace conditions {
void DeviceMsgCondition::notificationReceived(interface::Observer<devicerepository::DeviceEventType, devicerepository::DeviceEvent>::observable_type* o, const devicerepository::DeviceEventType& key, const devicerepository::DeviceEvent* e) noexcept {
switch (key) {
case devicerepository::DeviceEventType::DEVICE_ADDED:
if (!device && this->deviceID.compare(e->device->getDeviceID()) == 0) {
ROS_INFO_STREAM("Rule " << parent->getName() << ": Device " << deviceID << " appeared.");
device = e->device;
parent->getDeviceRepository()->deleteObserver(devicerepository::DeviceEventType::DEVICE_ADDED, this);
_registerAsListener();
}
break;
case devicerepository::DeviceEventType::DEVICE_REMOVED:
if (device && device->getDeviceID().compare(e->device->getDeviceID()) == 0) {
ROS_ERROR_STREAM("Rule " << parent->getName() << ": Device " << deviceID << " disappeared.");
device->deleteObserver(this);
device = nullptr;
parent->getDeviceRepository()->addObserver(devicerepository::DeviceEventType::DEVICE_ADDED, this);
}
break;
default:
break;
}
}
void DeviceMsgCondition::setEnabled(bool enabled) noexcept {
if (this->enabled != enabled) {
if (enabled) {
auto repoService = parent->getDeviceRepository();
auto device = repoService->getDevice(deviceID);
if (!device.expired()) {
this->device = device.lock().get();
_registerAsListener();
} else {
ROS_ERROR_STREAM("Rule " << parent->getName() << ": Could not find device " << deviceID);
parent->getDeviceRepository()->addObserver(devicerepository::DeviceEventType::DEVICE_ADDED, this);
}
} else {
if (device) {
device->deleteObserver(this);
device = nullptr;
}
parent->getDeviceRepository()->deleteObserver(this);
}
this->enabled = enabled;
}
}
void DeviceMsgCondition::_registerAsListener() {
if (device) {
for (auto k : _listenerKeys) {
device->addObserver(k, this);
}
}
}
std::shared_ptr<RuleCondition> DeviceStatusMsgConditionFactory::createCondition(rules::Rule* parent, const Variant& raw) {
std::shared_ptr<DeviceStatusMsgCondition> retval;
std::string deviceID;
if (raw.HasPath("deviceid") && raw["deviceid"].IsString()) {
deviceID = raw["deviceid"].AsString();
} else {
throw std::runtime_error("Malformed JSON: missing property deviceid or is not an string.");
}
std::string msgPointer;
if (raw.HasPath("msgpointer")) {
if (raw["msgpointer"].IsString()) {
msgPointer = raw["msgpointer"].AsString();
} else {
throw std::runtime_error("Malformed JSON: property msgpointer is not an string.");
}
}
Variant msgValue;
if (raw.HasPath("msgvalue")) {
msgValue = raw["msgvalue"];
}
retval = std::make_shared<DeviceStatusMsgCondition>(parent, deviceID, msgPointer, msgValue);
return retval;
}
void DeviceStatusMsgCondition::notificationReceived(interface::Observer<devicerepository::DeviceEventType, devicerepository::DeviceEvent>::observable_type* o, const devicerepository::DeviceEventType& key, const devicerepository::DeviceEvent* e) noexcept {
switch (e->eventType) {
case devicerepository::DeviceEventType::STATUS_UPDATE_RECEIVED:
if (device && device == e->device) {
auto e2 = static_cast<const devicerepository::DeviceStatusMsgEvent*>(e);
bool notify = true;
Variant value;
if (msgPointer.empty()) {
value = e2->statusMsg;
} else if (e2->statusMsg.HasPath(msgPointer)) {
value = e2->statusMsg.GetPath(msgPointer);
} else {
notify = false;
}
if (notify && !msgValue.IsNull()) {
if (msgValue != value) {
notify = false;
}
}
if (notify) {
Variant lcontext;
lcontext["event_deviceid"] = Variant(e->device->getDeviceID());
lcontext["event_type"] = Variant("statusmsg");
lcontext["event_msg"] = e2->statusMsg;
notifyStateChangeToParent(true, ConditionTriggerType::Event, lcontext);
}
}
break;
default:
DeviceMsgCondition::notificationReceived(o, key, e);
break;
}
}
std::shared_ptr<RuleCondition> DeviceCommandMsgConditionFactory::createCondition(rules::Rule* parent, const Variant& raw) {
std::shared_ptr<DeviceCommandMsgCondition> retval;
std::string deviceID;
if (raw.HasPath("deviceid") && raw["deviceid"].IsString()) {
deviceID = raw["deviceid"].AsString();
} else {
throw std::runtime_error("Malformed JSON: missing property deviceid or is not an string.");
}
std::string msgPointer;
if (raw.HasPath("msgpointer")) {
if (raw["msgpointer"].IsString()) {
msgPointer = raw["msgpointer"].AsString();
} else {
throw std::runtime_error("Malformed JSON: property msgpointer is not an string.");
}
}
Variant msgValue;
if (raw.HasPath("msgvalue")) {
msgValue = raw["msgvalue"];
}
retval = std::make_shared<DeviceCommandMsgCondition>(parent, deviceID, msgPointer, msgValue);
return retval;
}
void DeviceCommandMsgCondition::notificationReceived(interface::Observer<devicerepository::DeviceEventType, devicerepository::DeviceEvent>::observable_type* o, const devicerepository::DeviceEventType& key, const devicerepository::DeviceEvent* e) noexcept {
switch (e->eventType) {
case devicerepository::DeviceEventType::COMMAND_RECEIVED:
if (device && device == e->device) {
auto e2 = static_cast<const devicerepository::DeviceCmdMsgEvent*>(e);
bool notify = true;
Variant value;
if (msgPointer.empty()) {
value = e2->cmdMsg;
} else if (e2->cmdMsg.HasPath(msgPointer)) {
value = e2->cmdMsg.GetPath(msgPointer);
} else {
notify = false;
}
if (notify && !msgValue.IsNull()) {
if (msgValue != value) {
notify = false;
}
}
if (notify) {
Variant lcontext;
lcontext["event_deviceid"] = Variant(e->device->getDeviceID());
lcontext["event_type"] = Variant("commandmsg");
lcontext["event_msg"] = e2->cmdMsg;
notifyStateChangeToParent(true, ConditionTriggerType::Event, lcontext);
}
}
break;
default:
DeviceMsgCondition::notificationReceived(o, key, e);
break;
}
}
std::shared_ptr<RuleCondition> DeviceStateChangeConditionFactory::createCondition(rules::Rule* parent, const Variant& raw) {
std::shared_ptr<DeviceStateChangeCondition> retval;
std::string deviceID;
if (raw.HasPath("deviceid") && raw["deviceid"].IsString()) {
deviceID = raw["deviceid"].AsString();
} else {
throw std::runtime_error("Malformed JSON: missing property deviceid or is not an string.");
}
std::string statePointer;
if (raw.HasPath("msgpointer")) {
if (raw["msgpointer"].IsString()) {
statePointer = raw["msgpointer"].AsString();
} else {
throw std::runtime_error("Malformed JSON: property msgpointer is not an string.");
}
}
Variant fromValue;
if (raw.HasPath("fromvalue")) {
fromValue = raw["fromvalue"];
}
Variant toValue;
if (raw.HasPath("tovalue")) {
toValue = raw["tovalue"];
}
retval = std::make_shared<DeviceStateChangeCondition>(parent, deviceID, statePointer, fromValue, toValue);
return retval;
}
void DeviceStateChangeCondition::notificationReceived(interface::Observer<devicerepository::DeviceEventType, devicerepository::DeviceEvent>::observable_type* o, const devicerepository::DeviceEventType& key, const devicerepository::DeviceEvent* e) noexcept {
switch (e->eventType) {
case devicerepository::DeviceEventType::STATE_CHANGED:
if (device && device == e->device) {
auto e2 = static_cast<const devicerepository::DeviceStatusMsgEvent*>(e);
bool notify = true;
Variant current, last;
if (msgPointer.empty()) {
current = device->getCurrentState();
last = device->getLastState();
} else if (device->getCurrentState().HasPath(msgPointer) && device->getLastState().HasPath(msgPointer)) {
current = device->getCurrentState().GetPath(msgPointer);
last = device->getLastState().GetPath(msgPointer);
} else {
notify = false;
}
if (notify) {
if (current == last) {
notify = false;
} else if (!fromValue.IsNull() && fromValue != last) {
notify = false;
} else if (!msgValue.IsNull() && msgValue != current) {
notify = false;
}
}
if (notify) {
Variant lcontext;
lcontext["event_deviceid"] = Variant(e->device->getDeviceID());
lcontext["event_type"] = Variant("statechange");
lcontext["event_msg"] = e2->statusMsg;
notifyStateChangeToParent(true, ConditionTriggerType::Event, lcontext);
}
}
break;
default:
DeviceMsgCondition::notificationReceived(o, key, e);
break;
}
}
} /* namespace conditions */
} /* namespace rules */
} /* namespace ruleengine */
} /* namespace service */
} /* namespace cosgi */
} /* namespace hos */
@@ -0,0 +1,140 @@
#pragma once
#include <hos/cosgi/service/ruleengine/rules/conditions/RuleConditionImpl.h>
#include <hos/cosgi/service/ruleengine/RuleEngineService.h>
#include <hos/cosgi/service/devicerepository/DeviceRepositoryService.h>
#include <set>
// main Namespace
namespace hos {
namespace cosgi {
namespace service {
namespace ruleengine {
namespace rules {
namespace conditions {
using namespace libvariant;
class DeviceMsgCondition
: public RuleConditionImpl,
public virtual interface::Observer<devicerepository::DeviceEventType, devicerepository::DeviceEvent> {
protected:
const std::string deviceID;
devicerepository::Device* device = nullptr;
const std::string msgPointer;
const Variant msgValue;
std::set<devicerepository::DeviceEventType> _listenerKeys;
virtual void _registerAsListener();
public:
DeviceMsgCondition( Rule* parent,
ConditionTriggerType category,
const std::string& type,
const std::string& deviceID,
const std::string& msgPointer,
const Variant& msgValue,
const std::set<devicerepository::DeviceEventType> _listenerKeys )
: RuleConditionImpl(parent, category, type), deviceID(deviceID),
msgPointer(msgPointer), msgValue(msgValue), _listenerKeys(_listenerKeys) {}
virtual void notificationReceived( interface::Observer<devicerepository::DeviceEventType,
devicerepository::DeviceEvent>::observable_type* o,
const devicerepository::DeviceEventType& key,
const devicerepository::DeviceEvent* e) noexcept override;
virtual void setEnabled(bool enabled) noexcept override;
};
class DeviceStatusMsgCondition
: public DeviceMsgCondition {
public:
DeviceStatusMsgCondition( Rule* parent,
const std::string& deviceID,
const std::string& msgPointer,
const Variant& msgValue)
: DeviceMsgCondition(parent, ConditionTriggerType::Event, "devicestatusmsg", deviceID,
msgPointer, msgValue, {devicerepository::DeviceEventType::STATUS_UPDATE_RECEIVED}) {}
virtual void notificationReceived( interface::Observer<devicerepository::DeviceEventType,
devicerepository::DeviceEvent>::observable_type* o,
const devicerepository::DeviceEventType& key,
const devicerepository::DeviceEvent* e ) noexcept override;
};
class DeviceStatusMsgConditionFactory
: public RuleConditionFactory {
public:
virtual std::shared_ptr<RuleCondition> createCondition(rules::Rule* parent, const Variant& raw) override;
};
class DeviceCommandMsgCondition
: public DeviceMsgCondition {
public:
DeviceCommandMsgCondition(Rule* parent, const std::string deviceID, const std::string msgPointer, const Variant msgValue)
: DeviceMsgCondition(parent, ConditionTriggerType::Event, "devicecommandmsg", deviceID,
msgPointer, msgValue, {devicerepository::DeviceEventType::COMMAND_RECEIVED}) {}
virtual void notificationReceived( interface::Observer<devicerepository::DeviceEventType,
devicerepository::DeviceEvent>::observable_type* o,
const devicerepository::DeviceEventType& key,
const devicerepository::DeviceEvent* e ) noexcept override;
};
class DeviceCommandMsgConditionFactory
: public RuleConditionFactory {
public:
virtual std::shared_ptr<RuleCondition> createCondition(rules::Rule* parent, const Variant& raw) override;
};
class DeviceStateChangeCondition
: public DeviceMsgCondition {
protected:
const Variant fromValue;
public:
DeviceStateChangeCondition( Rule* parent,
const std::string deviceID,
const std::string statePointer,
const Variant fromValue,
const Variant toValue )
: DeviceMsgCondition(parent, ConditionTriggerType::Event, "devicestatechange", deviceID, statePointer,
toValue, {devicerepository::DeviceEventType::STATE_CHANGED}), fromValue(fromValue) {}
virtual void notificationReceived(interface::Observer<devicerepository::DeviceEventType, devicerepository::DeviceEvent>::observable_type* o, const devicerepository::DeviceEventType& key, const devicerepository::DeviceEvent* e) noexcept override;
};
class DeviceStateChangeConditionFactory
: public RuleConditionFactory {
public:
virtual std::shared_ptr<RuleCondition> createCondition(rules::Rule* parent, const Variant& raw) override;
};
} /* namespace conditions */
} /* namespace rules */
} /* namespace ruleengine */
} /* namespace service */
} /* namespace cosgi */
} /* namespace hos */
@@ -0,0 +1,104 @@
#include "LogicalAndCondition.h"
#include <hos/cosgi/service/ruleengine/rules/Rule.h>
namespace hos {
namespace cosgi {
namespace service {
namespace ruleengine {
namespace rules {
namespace conditions {
std::shared_ptr<RuleCondition> LogicalAndConditionFactory::createCondition(rules::Rule* parent, const Variant& raw) {
std::shared_ptr<LogicalAndCondition> retval;
if (raw.HasPath("conditions") && raw["conditions"].IsList() && raw["conditions"].Size() > 0) {
retval = std::make_shared<LogicalAndCondition>(parent);
for (auto c : raw["conditions"].AsList()) {
auto cond = parent->createRuleCondition(c);
if (cond) {
retval->addChild(cond);
} else {
throw std::runtime_error("Could not create condition.");
}
}
} else {
throw std::runtime_error("Malformed JSON: missing property condition or is not a list.");
}
return std::static_pointer_cast<RuleConditionImpl>(retval);
}
void LogicalAndCondition::addChild(std::shared_ptr<RuleCondition> child) {
if (child->getTriggerType() == ConditionTriggerType::Event) {
if (eventChildrenCount > 0) {
throw new std::runtime_error("Cannot have an and condition with more than two event conditions.");
}
eventChildrenCount++;
} else if (child->getTriggerType() == ConditionTriggerType::State) {
stateChildrenCount++;
} else {
mixedChildrenCount++;
}
if (mixedChildrenCount > 0 || (eventChildrenCount > 0 && stateChildrenCount > 0)) {
triggerType = ConditionTriggerType::Both;
} else if (eventChildrenCount > 0 && stateChildrenCount == 0) {
triggerType = ConditionTriggerType::Event;
} else {
triggerType = ConditionTriggerType::State;
}
RuleCompositeConditionImpl::addChild(child);
}
void LogicalAndCondition::childStateChanged(RuleCondition* child, bool newState, ConditionTriggerType category, const Variant& lcontext) noexcept {
if (newState && category == ConditionTriggerType::Event && !currentState && trueChildrenCount >= children.size()-1) {
notifyStateChangeToParent(true, category);
} else if (category == ConditionTriggerType::State) {
if (newState) {
trueChildrenCount++;
if (!currentState && trueChildrenCount >= children.size()) {
currentState = true;
notifyStateChangeToParent(true, category, lcontext);
}
} else {
trueChildrenCount--;
if (currentState) {
currentState = false;
notifyStateChangeToParent(false, category, lcontext);
}
}
}
}
bool LogicalAndCondition::evaluate() noexcept {
// Event conditions always evaluate to false
if (triggerType == ConditionTriggerType::Event) {
return false;
}
trueChildrenCount = 0;
for (auto c : children) {
if (c->evaluate()) {
trueChildrenCount++;
}
}
currentState = trueChildrenCount == children.size();
return currentState;
}
} /* namespace conditions */
} /* namespace rules */
} /* namespace ruleengine */
} /* namespace service */
} /* namespace cosgi */
} /* namespace hos */
@@ -0,0 +1,57 @@
#pragma once
#include <hos/cosgi/service/ruleengine/rules/conditions/RuleConditionImpl.h>
#include <hos/cosgi/service/ruleengine/RuleEngineService.h>
// Service Namespace
namespace hos {
namespace cosgi {
namespace service {
namespace ruleengine {
namespace rules {
namespace conditions {
using namespace libvariant;
class LogicalAndCondition
: public RuleCompositeConditionImpl {
protected:
unsigned eventChildrenCount = 0;
unsigned stateChildrenCount = 0;
unsigned mixedChildrenCount = 0;
unsigned trueChildrenCount = 0;
public:
LogicalAndCondition(Rule* parent) :
RuleCompositeConditionImpl(parent, ConditionTriggerType::Both, "logical_and") {}
virtual void addChild(std::shared_ptr<RuleCondition> child) override;
virtual void childStateChanged(RuleCondition* child, bool newState, ConditionTriggerType category,
const Variant& lcontext = Variant()) noexcept override;
virtual bool evaluate() noexcept override;
};
class LogicalAndConditionFactory
: public RuleConditionFactory {
public:
virtual std::shared_ptr<RuleCondition> createCondition(rules::Rule* parent, const Variant& raw) override;
};
} /* namespace conditions */
} /* namespace rules */
} /* namespace ruleengine */
} /* namespace service */
} /* namespace cosgi */
} /* namespace hos */
@@ -0,0 +1,101 @@
#include "LogicalOrCondition.h"
#include <hos/cosgi/service/ruleengine/rules/Rule.h>
namespace hos {
namespace cosgi {
namespace service {
namespace ruleengine {
namespace rules {
namespace conditions {
std::shared_ptr<RuleCondition> LogicalOrConditionFactory::createCondition(rules::Rule* parent, const Variant& raw) {
std::shared_ptr<LogicalOrCondition> retval;
if (raw.HasPath("conditions") && raw["conditions"].IsList() && raw["conditions"].Size() > 0) {
retval = std::make_shared<LogicalOrCondition>(parent);
for (auto c : raw["conditions"].AsList()) {
auto cond = parent->createRuleCondition(c);
if (cond) {
retval->addChild(cond);
} else {
throw std::runtime_error("Could not create condition.");
}
}
} else {
throw std::runtime_error("Malformed JSON: missing property condition or is not a list.");
}
return std::static_pointer_cast<RuleConditionImpl>(retval);
}
void LogicalOrCondition::addChild(std::shared_ptr<RuleCondition> child) {
if (child->getTriggerType() == ConditionTriggerType::Event) {
eventChildrenCount++;
} else if (child->getTriggerType() == ConditionTriggerType::State) {
stateChildrenCount++;
} else {
mixedChildrenCount++;
}
if (mixedChildrenCount > 0 || (eventChildrenCount > 0 && stateChildrenCount > 0)) {
triggerType = ConditionTriggerType::Both;
} else if (eventChildrenCount > 0 && stateChildrenCount == 0) {
triggerType = ConditionTriggerType::Event;
} else {
triggerType = ConditionTriggerType::State;
}
RuleCompositeConditionImpl::addChild(child);
}
void LogicalOrCondition::childStateChanged(RuleCondition* child, bool newState,
ConditionTriggerType category, const Variant& lcontext) noexcept {
if (newState && category == ConditionTriggerType::Event && !currentState) {
notifyStateChangeToParent(true, category, lcontext);
} else if (category == ConditionTriggerType::State) {
if (newState) {
trueChildrenCount++;
if (!currentState) {
currentState = true;
notifyStateChangeToParent(true, category, lcontext);
}
} else {
trueChildrenCount--;
if (currentState && trueChildrenCount <= 0) {
currentState = false;
notifyStateChangeToParent(false, category, lcontext);
}
}
}
}
bool LogicalOrCondition::evaluate() noexcept {
// Event conditions always evaluate to false
if (triggerType == ConditionTriggerType::Event) {
return false;
}
trueChildrenCount = 0;
for (auto c : children) {
if (c->evaluate()) {
trueChildrenCount++;
}
}
currentState = trueChildrenCount > 0;
return currentState;
}
} /* namespace conditions */
} /* namespace rules */
} /* namespace ruleengine */
} /* namespace service */
} /* namespace cosgi */
} /* namespace hos */
@@ -0,0 +1,57 @@
#pragma once
#include <hos/cosgi/service/ruleengine/rules/conditions/RuleConditionImpl.h>
#include <hos/cosgi/service/ruleengine/RuleEngineService.h>
// Service Namespace
namespace hos {
namespace cosgi {
namespace service {
namespace ruleengine {
namespace rules {
namespace conditions {
using namespace libvariant;
class LogicalOrCondition
: public RuleCompositeConditionImpl {
protected:
unsigned eventChildrenCount = 0;
unsigned stateChildrenCount = 0;
unsigned mixedChildrenCount = 0;
unsigned trueChildrenCount = 0;
public:
LogicalOrCondition(Rule* parent) :
RuleCompositeConditionImpl(parent, ConditionTriggerType::Both, "logical_or") {}
virtual void addChild(std::shared_ptr<RuleCondition> child) override;
virtual void childStateChanged(RuleCondition* child, bool newState, ConditionTriggerType category,
const libvariant::Variant& lcontext = libvariant::Variant()) noexcept override;
virtual bool evaluate() noexcept override;
};
class LogicalOrConditionFactory
: public RuleConditionFactory {
public:
virtual std::shared_ptr<RuleCondition> createCondition(rules::Rule* parent, const Variant& raw) override;
};
} /* namespace conditions */
} /* namespace rules */
} /* namespace ruleengine */
} /* namespace service */
} /* namespace cosgi */
} /* namespace hos */
@@ -0,0 +1,32 @@
#include <hos/cosgi/service/ruleengine/rules/conditions/RuleConditionImpl.h>
#include "../RuleImpl.h"
namespace hos {
namespace cosgi {
namespace service {
namespace ruleengine {
namespace rules {
namespace conditions {
void RuleConditionImpl::notifyStateChangeToParent(bool newState, ConditionTriggerType category, const libvariant::Variant& lcontext) {
if (compositeParent) {
compositeParent->childStateChanged(this, newState, category, lcontext);
} else {
parent->conditionStateChanged(newState, category, lcontext);
}
}
} /* namespace conditions */
} /* namespace rules */
} /* namespace ruleengine */
} /* namespace service */
} /* namespace cosgi */
} /* namespace hos */
+6
View File
@@ -93,6 +93,12 @@ catkin_package(
## Build ##
###########
include_directories(
include/${PROJECT_NAME}/
${catkin_INCLUDE_DIRS}
${libvariant_INCLUDE_DIR}
)
## Specify additional locations of header files
## Your package locations should be listed before other locations
# include_directories(include)
@@ -0,0 +1,107 @@
#pragma once
#include <hos/cosgi/service/Interface.h>
#include <Variant/Variant.h>
#include <memory>
// Service Namespace
namespace hos {
namespace cosgi {
namespace service {
namespace scripting {
/**
* Public COSGI interface of an abstract global script context.
*
* It persists the global context across script executions (to implement global variables).
*
* Currently it is just an opaque handle that is passed to the script execution handle.
*/
class GlobalScriptContext
: public virtual interface::Object {};
/**
* Public COSGI interface of an abstract script object.
*
* This object is supposed to be stateless, every state is carried in the global script context.
*/
class Script
: public virtual interface::Object {
public:
/**
* Execute the script object with the given contexts.
*
* May return exceptions derived from std::exception.
*
* \param[in] gContext reference to global context.
* \param[in] lContext map with local variables.
*/
virtual void execute( std::shared_ptr<GlobalScriptContext> gContext = std::shared_ptr<GlobalScriptContext>(),
const libvariant::Variant& lContext = libvariant::Variant() ) = 0;
};
/**
* Public COSGI interface of a scripting service.
*
* It offers following functionality:
* - Create scripts and related objects.
*/
class ScriptingService
: public virtual interface::Service {
public:
/**
* Common name for every scripting service implementation.
*/
static constexpr const char* SERVICE_NAME = "hos_scripting_service";
/**
* Returns an unique identifier indicating the scripting language.
*
* \return unique language identifier.
*/
virtual const char* getLanguage() noexcept = 0;
/**
* Creates and returns a global context object from the given json global context config.
*
* May return exceptions derived from std::exception.
*
* \param raw json config.
* \return global context object.
*/
virtual std::shared_ptr<GlobalScriptContext> createGlobalScriptContext(const libvariant::Variant& raw) = 0;
/**
* Creates and returns a script object from the given json script config.
*
* May return exceptions derived from std::exception.
*
* \param raw json config.
* \return global context object.
*/
virtual std::shared_ptr<Script> createScript(const libvariant::Variant& raw) = 0;
};
} // namespace scripting
} // namespace service
} // namespace cosgi
} // namespace hos
@@ -1,54 +0,0 @@
/*
* hos_ruleexecution_service.h
*
* Created on: 01.05.2015
* Author: matzman
*/
#ifndef INCLUDE_HOS_COSGI_SCRIPTING_HOS_COSGI_SERVICE_SCRIPTING_SCRIPTING_SERVICE_H_
#define INCLUDE_HOS_COSGI_SCRIPTING_HOS_COSGI_SERVICE_SCRIPTING_SCRIPTING_SERVICE_H_
#include <hos/cosgi/service/Interface.h>
#include <Variant/Variant.h>
#include <memory>
namespace hos {
namespace cosgi {
namespace service {
namespace scripting {
using namespace ::libvariant;
class GlobalScriptContext : public virtual interface::Object {
};
class Script : public virtual interface::Object {
public:
virtual void exec(GlobalScriptContext* gContext, Variant lContext = Variant()) = 0;
};
class ScriptingService : public virtual interface::Service {
public:
static constexpr const char* SERVICE_NAME = "hos_scripting_service";
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;
};
} // namespace scripting
} // namespace service
} // namespace cosgi
} // namespace hos
#endif /* INCLUDE_HOS_COSGI_SCRIPTING_HOS_COSGI_SERVICE_SCRIPTING_SCRIPTING_SERVICE_H_ */
@@ -121,9 +121,10 @@ set(BUNDLE_NAME "hos_scripting_python")
bundle(hos_cosgi_scripting_python
SOURCES
${PROJECT_SOURCE_DIR}/src/bundle_activator
${PROJECT_SOURCE_DIR}/src/hos/cosgi/service/scripting/python/scripting_serviceimpl
${PROJECT_SOURCE_DIR}/src/hos/cosgi/service/scripting/python/PyHOSAPI
${PROJECT_SOURCE_DIR}/src/BundleActivator
${PROJECT_SOURCE_DIR}/src/hos/cosgi/service/scripting/python/PyScriptingServiceImpl
${PROJECT_SOURCE_DIR}/src/hos/cosgi/service/scripting/python/utils/utils
${PROJECT_SOURCE_DIR}/src/hos/cosgi/service/scripting/python/hosapi/PyHOSAPI
)
target_link_libraries(hos_cosgi_scripting_python
${catkin_LIBRARIES}
@@ -1,28 +1,19 @@
/*
* bundle_activator.cpp
*
* Created on: 26.04.2015
* Author: matzman
*/
// std
#include <cstdlib>
#include <cstdio>
#include <string>
#include <memory>
// ROS
#include <ros/ros.h>
// APR
#include <apr_general.h>
#include "ros/ros.h"
extern "C" {
#include "celix/bundle_activator.h"
#include "celix/bundle_context.h"
#include "celix/service_reference.h"
#include "celix/constants.h"
}
// Celix
#include <hos/cosgi/celix.h>
// CoreService
#include "hos/cosgi/service/scripting/python/PyScriptingServiceImpl.h"
#include <hos/cosgi/service/core/CoreService.h>
#include <hos/cosgi/service/devicerepository/DeviceRepositoryService.h>
#include "hos/cosgi/service/scripting/python/scripting_serviceimpl.h"
#include <memory>
using namespace hos::cosgi::service::scripting;
@@ -42,6 +33,7 @@ struct UserData {
};
celix_status_t cb_serviceChanged(void* listener, service_event_pt event) {
UserData* myUserData = (UserData*) ((serviceListener*)listener)->handle;
if (event->type == OSGI_FRAMEWORK_SERVICE_EVENT_REGISTERED) {
@@ -63,6 +55,7 @@ celix_status_t cb_serviceChanged(void* listener, service_event_pt event) {
return CELIX_SUCCESS;
}
celix_status_t bundleActivator_create(bundle_context_pt context, void **userData) {
celix_status_t status = CELIX_SUCCESS;
apr_pool_t* memoryPool = nullptr;
@@ -87,6 +80,7 @@ celix_status_t bundleActivator_create(bundle_context_pt context, void **userData
return status;
}
celix_status_t bundleActivator_start(void * userData, bundle_context_pt context) {
celix_status_t status = CELIX_SUCCESS;
if (userData != nullptr) {
@@ -132,6 +126,7 @@ celix_status_t bundleActivator_start(void * userData, bundle_context_pt context)
return status;
}
celix_status_t bundleActivator_stop(void * userData, bundle_context_pt context) {
celix_status_t status = CELIX_ILLEGAL_STATE;
if (userData != nullptr) {
@@ -152,6 +147,7 @@ celix_status_t bundleActivator_stop(void * userData, bundle_context_pt context)
return status;
}
celix_status_t bundleActivator_destroy(void * userData, bundle_context_pt context) {
celix_status_t status = CELIX_ILLEGAL_STATE;
if (userData != nullptr) {
@@ -168,3 +164,4 @@ celix_status_t bundleActivator_destroy(void * userData, bundle_context_pt contex
}
return status;
}
@@ -1,17 +1,8 @@
/*
* hos_cosgi_ruleexecution_python_impl.cpp
*
* Created on: 01.05.2015
* Author: matzman
*/
#include "scripting_serviceimpl.h"
#include "PyScriptingServiceImpl.h"
#include <hos/cosgi/service/core/CoreService.h>
#include "PyHOSAPI.h"
#include <hos/cosgi/service/devicerepository/DeviceRepositoryService.h>
#include "hosapi/PyHOSAPI.h"
@@ -21,52 +12,77 @@ namespace service {
namespace scripting {
namespace python {
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 PyScriptingService::setCoreService(service::core::CoreService* coreService) {
// Service information
template<> const char* implementation::ServiceInfo<PyScriptingService>::name = service::scripting::python::PyScriptingService::SERVICE_NAME;
template<> const int implementation::ServiceInfo<PyScriptingService>::major = 1;
template<> const int implementation::ServiceInfo<PyScriptingService>::minor = 0;
void PyScriptingService::setCoreService(service::core::CoreService* coreService) noexcept {
if (coreService && !this->coreService) {
this->coreService = coreService;
this->nodehandle = &coreService->getPrivateNodeHandle();
int major = 0, minor = 0;
coreService->getServiceVersion(major, minor);
ROS_INFO_STREAM("PyRuleExecutionService: Found HOSCoreService Version " << major << "." << minor);
ROS_INFO_STREAM("PyScriptingService: Found CoreService Version " << major << "." << minor);
if (repoService) {
init();
}
}
}
void PyScriptingService::setRepoService(devicerepository::DeviceRepositoryService* repoService) {
void PyScriptingService::setRepoService(devicerepository::DeviceRepositoryService* repoService) noexcept {
if (repoService && !this->repoService) {
this->repoService = repoService;
int major = 0, minor = 0;
repoService->getServiceVersion(major, minor);
ROS_INFO_STREAM("PyRuleExecutionService: Found HOSDeviceRepositoryService Version " << major << "." << minor);
ROS_INFO_STREAM("PyScriptingService: Found DeviceRepositoryService Version " << major << "." << minor);
if (coreService) {
init();
}
}
}
void PyScriptingService::init() {
void PyScriptingService::init() noexcept {
std::lock_guard<std::recursive_mutex> lock(serviceMutex);
// Initialize Python
Py_Initialize();
// Import __main__
boost::python::object main_module = boost::python::import("__main__");
pyGlobalScope = main_module.attr("__dict__");// Load the sys module.
// Create default global scope
pyGlobalScope = main_module.attr("__dict__");
// import sys and get version
boost::python::object sys = boost::python::import("sys");
std::string version = boost::python::extract<std::string>(sys.attr("version"));
ROS_INFO_STREAM("PyRuleExecutionService: Python Version: " << version);
ROS_INFO_STREAM("PyScriptingService: Python Version: " << version);
// Initialize HOS Python API
inithos();
deviceService.reset(new hosapi::PyDeviceRepositoryWrapper(repoService));
initialized = true;
ROS_INFO_STREAM("PyRuleExecutionService: Initialized and Ready.");
}
std::shared_ptr<service::scripting::GlobalScriptContext> PyScriptingService::createGlobalScriptContext(Variant raw) {
std::shared_ptr<service::scripting::GlobalScriptContext> PyScriptingService::createGlobalScriptContext(const Variant& raw) {
std::lock_guard<std::recursive_mutex> lock(serviceMutex);
std::shared_ptr<PyGlobalScriptContext> retval;
// Retrieve script code and execute it
if (raw.HasPath("code") && raw["code"].IsString()) {
retval = std::make_shared<PyGlobalScriptContext>(this->pyGlobalScope, raw["code"].AsString());
retval = std::make_shared<PyGlobalScriptContext>(this, this->pyGlobalScope, raw["code"].AsString());
try {
boost::python::object result = boost::python::exec(retval->getCode().c_str(), pyGlobalScope);
// When no local context is given, all variable created during execution comes into the global scope
boost::python::object result = boost::python::exec(retval->getScriptCode().c_str(), pyGlobalScope);
} catch(boost::python::error_already_set const &) {
PyErr_Print();
throw std::runtime_error("Python error during global context creation.");
@@ -75,10 +91,13 @@ std::shared_ptr<service::scripting::GlobalScriptContext> PyScriptingService::cre
return std::static_pointer_cast<service::scripting::GlobalScriptContext>(retval);
}
std::shared_ptr<service::scripting::Script> PyScriptingService::createScript(service::scripting::GlobalScriptContext* gContext, Variant raw) {
std::shared_ptr<service::scripting::Script> PyScriptingService::createScript(const Variant& raw) {
std::lock_guard<std::recursive_mutex> lock(serviceMutex);
std::shared_ptr<PyScript> retval;
// Retrieve script code and create script object
if (raw.HasPath("code") && raw["code"].IsString()) {
retval = std::make_shared<PyScript>(raw["code"].AsString(), *this);
retval = std::make_shared<PyScript>(this, raw["code"].AsString());
}
return std::static_pointer_cast<service::scripting::Script>(retval);
}
@@ -86,24 +105,33 @@ std::shared_ptr<service::scripting::Script> PyScriptingService::createScript(ser
void PyScript::exec(service::scripting::GlobalScriptContext* gContext, Variant lContext) {
void PyScript::execute( std::shared_ptr<GlobalScriptContext> gContext, const libvariant::Variant& lContext ) {
try {
boost::python::object& gcont = service.getPyGlobalScope();
auto gCont = dynamic_pointer_cast<PyGlobalScriptContext>(gContext);
// Get global scope
boost::python::object* gscope;
if (gContext) {
gcont = dynamic_cast<PyGlobalScriptContext*>(gContext)->getPyGlobalContext();
}
boost::python::object lcont;
if (!lContext.IsNull()) {
lcont = convertVariantToBoostPython(lContext);
//gcont["local"] = lcont;
gscope = &dynamic_pointer_cast<PyGlobalScriptContext>(gContext)->getPyObject();
} else {
lcont = boost::python::dict();
gscope = &service->getPyDefaultScrope();
}
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);
// Create local scope
boost::python::object lscope;
if (!lContext.IsNull() && lContext.IsMap()) {
lscope = utils::convertVariantToBoostPython(lContext);
//gscope["local"] = lscope;
} else {
lscope = boost::python::dict();
}
lscope["Devices"] = boost::python::object(boost::python::ptr(service->getDeviceService()));
// Execute script scope
boost::python::object result = boost::python::exec(code.c_str(), *gscope, lscope);
} catch(boost::python::error_already_set const &) {
PyErr_Print();
throw std::runtime_error("Python error during execution.");
@@ -0,0 +1,171 @@
#pragma once
#include <hos/cosgi/service/scripting/ScriptingService.h>
// hos includes
#include <hos/cosgi/service/Implementation.h>
#include <hos/cosgi/service/core/CoreService.h>
#include <hos/cosgi/service/devicerepository/DeviceRepositoryService.h>
#include "hosapi/PyHOSAPI.h"
// libvariant includes
#include <Variant/Variant.h>
// ROS includes
#include <ros/ros.h>
// std includes
#include <memory>
// boost includes
#include <boost/python.hpp>
// Service Namespace
namespace hos {
namespace cosgi {
namespace service {
namespace scripting {
namespace python {
// forward declarations
class PyScriptingService;
using namespace libvariant;
/**
* Python implementation of GlobalScriptContext.
*/
class PyGlobalScriptContext
: public virtual service::scripting::GlobalScriptContext {
private:
PyScriptingService* service;
boost::python::object gContext;
std::string code;
public:
PyGlobalScriptContext(PyScriptingService* service, const boost::python::object& gContext, const std::string& code)
: service(service), gContext(gContext), code(code) {}
/* Internal */
const std::string& getScriptCode() noexcept {
return code;
}
boost::python::object& getPyObject() noexcept {
return gContext;
}
};
/**
* Python implementation of Script.
*/
class PyScript
: public virtual service::scripting::Script {
private:
PyScriptingService* service;
std::string code;
public:
PyScript(PyScriptingService* service, const std::string& code)
: code(code), service(service) {}
/* Public Service Implementations */
virtual void execute( std::shared_ptr<GlobalScriptContext> gContext = std::shared_ptr<GlobalScriptContext>(),
const libvariant::Variant& lContext = libvariant::Variant() ) override;
/* Internal */
const std::string& getScriptCode() noexcept {
return code;
}
};
/**
* Implementation of device repository service
**/
class PyScriptingService
: public virtual service::scripting::ScriptingService,
public virtual implementation::Service<PyScriptingService> {
private:
std::recursive_mutex serviceMutex;
service::core::CoreService* coreService = nullptr;;
devicerepository::DeviceRepositoryService* repoService = nullptr;;
ros::NodeHandle* nodehandle = nullptr;
boost::python::object pyGlobalScope;
std::unique_ptr<hosapi::PyDeviceRepositoryWrapper> deviceService;
bool initialized = false;
void init() noexcept;
public:
/** Language identifier. */
static constexpr const char* LANGUAGE = "python";
/* Initialization */
void setCoreService(service::core::CoreService* coreService) noexcept;
void setRepoService(devicerepository::DeviceRepositoryService* repoService) noexcept;
/* Public Service Implementations */
virtual const char* getLanguage() noexcept override {
return LANGUAGE;
}
virtual std::shared_ptr<scripting::GlobalScriptContext> createGlobalScriptContext(const Variant& raw) override;
virtual std::shared_ptr<scripting::Script> createScript(const Variant& raw) override;
/* Internal */
bool isInitialized() noexcept {
return initialized;
}
boost::python::object& getPyDefaultScrope() noexcept {
return pyGlobalScope;
}
hosapi::PyDeviceRepositoryWrapper* getDeviceService() noexcept {
return deviceService.get();
}
};
} // namespace python
} // namespace scripting
} // namespace service
} // namespace cosgi
} // namespace hos
@@ -0,0 +1,27 @@
#include "PyHOSAPI.h"
/**
* Python HOS API declaration.
*/
BOOST_PYTHON_MODULE(hos)
{
using namespace boost::python;
using namespace hos::cosgi::service::scripting::python::hosapi;
// Device class
class_<PyDeviceWrapper>("Device", no_init)
.def("deviceID", &PyDeviceWrapper::deviceID)
.def("sendCommand", &PyDeviceWrapper::sendCommand);
// Device Repository class.
class_<PyDeviceRepositoryWrapper>("DeviceRepository", no_init)
.def("getDevice", &PyDeviceRepositoryWrapper::getDevice)
.def("__getitem__", &PyDeviceRepositoryWrapper::getDevice)
.def("__len__", &PyDeviceRepositoryWrapper::deviceCount)
.def("keys", &PyDeviceRepositoryWrapper::deviceIDs);
};
@@ -1,53 +1,72 @@
/*
* PyHOSAPI.h
*
* Created on: 19.05.2015
* Author: matzman
*/
#ifndef SRC_HOS_COSGI_SERVICE_SCRIPTING_PYTHON_PYHOSAPI_H_
#define SRC_HOS_COSGI_SERVICE_SCRIPTING_PYTHON_PYHOSAPI_H_
#pragma once
#include <boost/python.hpp>
#include <hos/cosgi/service/scripting/scripting_service.h>
#include <hos/cosgi/service/scripting/ScriptingService.h>
#include <hos/cosgi/service/devicerepository/DeviceRepositoryService.h>
#include <boost/python.hpp>
#include "../utils/utils.h"
/**
* This function declares the python hos module.
*/
extern "C" void inithos();
// main namespace
namespace hos {
namespace cosgi {
namespace service {
namespace scripting {
namespace python {
class PyScriptingService;
namespace hosapi {
boost::python::object convertVariantToBoostPython(Variant var);
Variant convertBoostPythonTovariant(boost::python::object obj);
/**
* Python HOS API wrapper for devicerepository::Device.
*/
class PyDeviceWrapper {
private:
devicerepository::Device* device;
public:
PyDeviceWrapper(devicerepository::Device* device) : device(device) {}
std::string deviceID() { return device->getDeviceID(); }
PyDeviceWrapper(devicerepository::Device* device)
: device(device) {}
void sendCommand(boost::python::object cmd) {
device->sendCommand(convertBoostPythonTovariant(cmd));
const std::string deviceID() noexcept {
return device->getDeviceID();
}
void sendCommand(const boost::python::object& cmd) {
device->sendCommand(utils::convertBoostPythonTovariant(cmd));
}
};
class PyDeviceRepositoryWrapper {
private:
devicerepository::DeviceRepositoryService* service;
public:
PyDeviceRepositoryWrapper(devicerepository::DeviceRepositoryService* service) : service(service) {}
PyDeviceWrapper getDevice(std::string name) {
/**
* Python HOS API wrapper for devicerepository::DeviceRepositoryService.
*/
class PyDeviceRepositoryWrapper {
private:
devicerepository::DeviceRepositoryService* service;
public:
PyDeviceRepositoryWrapper(devicerepository::DeviceRepositoryService* service)
: service(service) {}
PyDeviceWrapper getDevice(const std::string& name) {
return PyDeviceWrapper(service->getDevice(name).lock().get());
}
@@ -62,17 +81,15 @@ public:
}
return retval;
}
};
extern "C" void inithos();
} // namespace hosapi
} // namespace python
} // namespace scripting
} // namespace service
} // namespace cosgi
} // namespace hos
#endif /* SRC_HOS_COSGI_SERVICE_SCRIPTING_PYTHON_PYHOSAPI_H_ */
@@ -1,111 +0,0 @@
/*
* hos_cosgi_ruleexecution_python_impl.h
*
* Created on: 01.05.2015
* Author: matzman
*/
#ifndef SRC_HOS_COSGI_SERVICE_SCRIPTING_PYTHON_SCRIPTING_SERVICEIMPL_H_
#define SRC_HOS_COSGI_SERVICE_SCRIPTING_PYTHON_SCRIPTING_SERVICEIMPL_H_
#include <ros/ros.h>
#include <Variant/Variant.h>
#include <boost/python.hpp>
#include <hos/cosgi/service/scripting/scripting_service.h>
#include <hos/cosgi/service/devicerepository/DeviceRepositoryService.h>
#include <hos/cosgi/service/Implementation.h>
#include <memory>
// Forward declarations
namespace hos {
namespace cosgi {
namespace service {
namespace core {
class CoreService;
}
}
}
}
namespace hos {
namespace cosgi {
namespace service {
namespace scripting {
namespace python {
class PyScriptingService;
using namespace ::libvariant;
class PyGlobalScriptContext : public virtual service::scripting::GlobalScriptContext {
private:
boost::python::object& gContext;
std::string code;
public:
PyGlobalScriptContext(boost::python::object& gContext, std::string code)
: gContext(gContext), code(code) {}
std::string getCode() { return code; }
boost::python::object& getPyGlobalContext() { return gContext; }
};
class PyScript : public virtual service::scripting::Script {
private:
std::string code;
PyScriptingService& service;
public:
PyScript(std::string code, PyScriptingService& service) : code(code), service(service) {}
std::string getCode() { return code; }
virtual void exec(service::scripting::GlobalScriptContext* gContext, Variant lContext = Variant());
};
class PyScriptingService;
class PyScriptingService : public virtual service::scripting::ScriptingService, public virtual implementation::Service<PyScriptingService> {
private:
service::core::CoreService* coreService = nullptr;;
devicerepository::DeviceRepositoryService* repoService = nullptr;;
ros::NodeHandle* nodehandle = nullptr;
boost::python::object pyGlobalScope;
bool initialized = false;
void init();
public:
static constexpr const char* LANGUAGE = "python";
/* Initialization */
void setCoreService(service::core::CoreService* coreService);
void setRepoService(devicerepository::DeviceRepositoryService* repoService);
/* Public Service Implementations */
virtual std::string getLanguage() { return LANGUAGE; }
virtual std::shared_ptr<scripting::GlobalScriptContext> createGlobalScriptContext(Variant raw);
virtual std::shared_ptr<scripting::Script> createScript(scripting::GlobalScriptContext* gContext, Variant raw);
/* Internal Stuff */
bool isInitialized() { return initialized; }
boost::python::object& getPyGlobalScope() { return pyGlobalScope; }
devicerepository::DeviceRepositoryService* getDeviceRepository() { return repoService; }
};
} // namespace python
} // namespace scripting
} // namespace service
} // namespace cosgi
} // namespace hos
#endif /* SRC_HOS_COSGI_SERVICE_SCRIPTING_PYTHON_SCRIPTING_SERVICEIMPL_H_ */
@@ -1,39 +1,21 @@
/*
* PyHOSAPI.cpp
*
* Created on: 19.05.2015
* Author: matzman
*/
#include "utils.h"
#include "ros/ros.h"
#include "PyHOSAPI.h"
#include <ros/ros.h>
// main namespace
namespace hos {
namespace cosgi {
namespace service {
namespace scripting {
namespace python {
namespace utils {
BOOST_PYTHON_MODULE(hos)
{
using namespace boost::python;
class_<PyDeviceWrapper>("Device", no_init)
.def("deviceID", &PyDeviceWrapper::deviceID)
.def("sendCommand", &PyDeviceWrapper::sendCommand) ;
class_<PyDeviceRepositoryWrapper>("DeviceRepository", no_init)
.def("getDevice", &PyDeviceRepositoryWrapper::getDevice)
.def("__getitem__", &PyDeviceRepositoryWrapper::getDevice)
.def("__len__", &PyDeviceRepositoryWrapper::deviceCount)
.def("keys", &PyDeviceRepositoryWrapper::deviceIDs) ;
}
boost::python::object convertVariantToBoostPython(Variant var) {
boost::python::object convertVariantToBoostPython(const libvariant::Variant& var) noexcept {
if (var.IsBool()) {
return boost::python::object(var.AsBool());
} else if (var.IsFloat()) {
@@ -63,45 +45,46 @@ boost::python::object convertVariantToBoostPython(Variant var) {
}
Variant convertBoostPythonTovariant(boost::python::object obj) {
Variant retval;
libvariant::Variant convertBoostPythonTovariant(const boost::python::object& obj) noexcept {
libvariant::Variant retval;
if (!obj.is_none()) {
if (PyObject_TypeCheck(obj.ptr(), &PyString_Type)) {
retval = Variant(boost::python::extract<std::string>(obj));
retval = libvariant::Variant(boost::python::extract<std::string>(obj));
} else if (PyObject_TypeCheck(obj.ptr(), &PyBool_Type)) {
retval = Variant(boost::python::extract<bool>(obj));
retval = libvariant::Variant(boost::python::extract<bool>(obj));
} else if (PyObject_TypeCheck(obj.ptr(), &PyFloat_Type)) {
retval = Variant(boost::python::extract<double>(obj));
retval = libvariant::Variant(boost::python::extract<double>(obj));
} else if (PyObject_TypeCheck(obj.ptr(), &PyInt_Type)) {
retval = Variant(boost::python::extract<int>(obj));
retval = libvariant::Variant(boost::python::extract<int>(obj));
} else if (PyObject_TypeCheck(obj.ptr(), &PyDict_Type)) {
boost::python::dict dict = boost::python::extract<boost::python::dict>(obj);
for (int i = 0; i < boost::python::len(dict); i++) {
auto k = dict.keys()[i];
Variant key = convertBoostPythonTovariant(k);
Variant value = convertBoostPythonTovariant(dict[k]);
libvariant::Variant key = convertBoostPythonTovariant(k);
libvariant::Variant value = convertBoostPythonTovariant(dict[k]);
retval[key.AsString()] = value;
}
} else if (PyObject_TypeCheck(obj.ptr(), &PyList_Type)) {
boost::python::list list = boost::python::extract<boost::python::list>(obj);
for (int i = 0; i < boost::python::len(list); i++) {
auto v = list[i];
Variant value = convertBoostPythonTovariant(v);
libvariant::Variant value = convertBoostPythonTovariant(v);
retval.Append(value);
}
} else {
ROS_ERROR("Unknown PyObject type encountered while converting to Variant");
return Variant();
return libvariant::Variant();
}
}
return retval;
}
} // namespace utils
} // namespace python
} // namespace scripting
} // namespace ruleengine
} // namespace service
} // namespace cosgi
} // namespace hos
@@ -0,0 +1,44 @@
#pragma once
#include <boost/python.hpp>
#include <Variant/Variant.h>
// main namespace
namespace hos {
namespace cosgi {
namespace service {
namespace scripting {
namespace python {
namespace utils {
/**
* Converts a Variant into a boost::python::object.
*
* \param[in] variant object.
* \return boost python object.
*/
boost::python::object convertVariantToBoostPython(const libvariant::Variant& var) noexcept;
/**
* Converts a boost::python::object into a Variant.
*
* \param[in] boost python object.
* \return variant object.
*/
libvariant::Variant convertBoostPythonTovariant(const boost::python::object& obj) noexcept;
} // namespace utils
} // namespace python
} // namespace ruleengine
} // namespace service
} // namespace cosgi
} // namespace hos
@@ -80,8 +80,13 @@ void DevicesHTTPResource::render_GET(const httpserver::http_request& req, httpse
for (int i = prefixPathPieces+1; i < req.get_path_pieces_size(); i++) {
path.push_back(req.get_path_piece(i));
}
setDeviceState(device.lock().get(), setValue, path);
*res = new http_response(http_response_builder("OK", 200).string_response());
try {
setDeviceState(device.lock().get(), setValue, path);
*res = new http_response(http_response_builder("OK", 200).string_response());
} catch (std::exception& e) {
auto msg = (boost::format("ERROR: %1%") % e.what()).str();
*res = new http_response(http_response_builder(msg , 400).string_response());
}
} else {
std::string info;
if (req.get_path_pieces_size() > prefixPathPieces+1) {
@@ -14,16 +14,22 @@ namespace utils {
class SchemaLoader: public libvariant::SchemaLoader {
class SchemaLoader
: public libvariant::SchemaLoader {
private:
std::string ros_param_ns;
std::string prefix;
ros::NodeHandle nodeHandle;
bool loadTypeSchema(std::string schema);
public:
SchemaLoader(std::string ns = "/hos/types/repository", std::string prefix = "hos:schema://")
SchemaLoader(const std::string& ns = "/hos/types/repository", const std::string& prefix = "hos:schema://")
: ros_param_ns(ns), prefix(prefix), nodeHandle("~") {}
virtual bool HandleMissing(const std::string &uri, LoaderError &error);
virtual bool HandleMissing(const std::string &uri, LoaderError &error) override;
};