.
This commit is contained in:
@@ -55,6 +55,7 @@ bundle(hos_cosgi_sitemap
|
||||
${PROJECT_SOURCE_DIR}/src/BundleActivator
|
||||
${PROJECT_SOURCE_DIR}/src/hos/cosgi/service/sitemap/SitemapServiceImpl
|
||||
${PROJECT_SOURCE_DIR}/src/hos/cosgi/service/sitemap/webresource/SitemapHandler
|
||||
${PROJECT_SOURCE_DIR}/src/hos/cosgi/service/sitemap/sitemap_template/SitemapTemplate
|
||||
)
|
||||
|
||||
target_link_libraries(hos_cosgi_sitemap
|
||||
@@ -85,6 +86,11 @@ install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/doc
|
||||
OPTIONAL
|
||||
)
|
||||
|
||||
foreach(dir launch schema examples)
|
||||
install(DIRECTORY ${dir}/
|
||||
DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}/${dir}
|
||||
)
|
||||
endforeach(dir)
|
||||
|
||||
|
||||
###################
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"type": "sitemap/sitemap_template",
|
||||
"title": "TestApp",
|
||||
"rootpage": {
|
||||
"type": "sitemap/sitemap_template#/definitions/page",
|
||||
"title": "Root Page",
|
||||
"elements": [
|
||||
{
|
||||
"type": "sitemap/sitemap_template#/definitions/text_element",
|
||||
"label": "Switch0 Text",
|
||||
"device": {
|
||||
"id": "dummySwitch0",
|
||||
"pointer": ""
|
||||
}
|
||||
}, {
|
||||
"type": "sitemap/sitemap_template#/definitions/switch_element",
|
||||
"label": "Switch0",
|
||||
"device": {
|
||||
"id": "dummySwitch0",
|
||||
"pointer": ""
|
||||
}
|
||||
}, {
|
||||
"type": "sitemap/sitemap_template#/definitions/slider_element",
|
||||
"label": "Slider0",
|
||||
"device": {
|
||||
"id": "dummySlider0",
|
||||
"pointer": ""
|
||||
},
|
||||
"min": 0,
|
||||
"max": 1,
|
||||
"step": 0.1
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<launch>
|
||||
<group ns="/hos/sitemap/sitemap_templates">
|
||||
<param name="test01" textfile="$(find hos_cosgi_sitemap)/examples/test01.json"/>
|
||||
</group>
|
||||
|
||||
<group ns="/hos/types/repository/sitemap">
|
||||
<param name="sitemap_template" textfile="$(find hos_cosgi_sitemap)/schema/sitemap/sitemap_template.json"/>
|
||||
</group>
|
||||
</launch>
|
||||
@@ -0,0 +1,179 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"title": "Sitemap Template Schema",
|
||||
"description": "",
|
||||
"version": "1.0.0",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"pattern": "sitemap/sitemap_template"
|
||||
},
|
||||
"title": {
|
||||
"type": "string",
|
||||
"description": ""
|
||||
},
|
||||
"rootpage": {
|
||||
"$ref": "#/definitions/page"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": ["title", "rootpage"],
|
||||
|
||||
"definitions": {
|
||||
|
||||
"page": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"pattern": "sitemap/sitemap_template#/definitions/page"
|
||||
},
|
||||
"title": {
|
||||
"type": "string"
|
||||
},
|
||||
"elements": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/element"
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": ["title", "elements"]
|
||||
},
|
||||
"element": {
|
||||
"anyOf": [
|
||||
{ "$ref": "#/definitions/composite_element" },
|
||||
{ "$ref": "#/definitions/atomic_element" }
|
||||
]
|
||||
},
|
||||
"composite_element": {
|
||||
"anyOf": [
|
||||
{ "$ref": "#/definitions/frame_element" }
|
||||
]
|
||||
},
|
||||
"atomic_element": {
|
||||
"anyOf": [
|
||||
{ "$ref": "#/definitions/text_element" },
|
||||
{ "$ref": "#/definitions/slider_element" },
|
||||
{ "$ref": "#/definitions/switch_element" }
|
||||
]
|
||||
},
|
||||
|
||||
"composite_element_base": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"label": {
|
||||
"type": "string"
|
||||
},
|
||||
"icon": {
|
||||
"type": "string"
|
||||
},
|
||||
"elements": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/element"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": ["elements"]
|
||||
},
|
||||
"frame_element": {
|
||||
"allOf": [
|
||||
{ "$ref": "#/definitions/composite_element_base" },
|
||||
{
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"pattern": "sitemap/sitemap_template#/definitions/frame_element"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"required": ["type"]
|
||||
},
|
||||
|
||||
"atomic_element_base": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"label": {
|
||||
"type": "string"
|
||||
},
|
||||
"format": {
|
||||
"type": "string"
|
||||
},
|
||||
"device": {
|
||||
"$ref": "#/definitions/device"
|
||||
},
|
||||
"icon": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"text_element": {
|
||||
"allOf": [
|
||||
{ "$ref": "#/definitions/atomic_element_base" },
|
||||
{
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"pattern": "sitemap/sitemap_template#/definitions/text_element"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"required": ["type"]
|
||||
},
|
||||
"slider_element": {
|
||||
"allOf": [
|
||||
{ "$ref": "#/definitions/atomic_element_base" },
|
||||
{
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"pattern": "sitemap/sitemap_template#/definitions/slider_element"
|
||||
},
|
||||
"min": {
|
||||
"type": "number"
|
||||
},
|
||||
"max": {
|
||||
"type": "number"
|
||||
},
|
||||
"step": {
|
||||
"type": "number"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"required": ["type"]
|
||||
},
|
||||
"switch_element": {
|
||||
"allOf": [
|
||||
{ "$ref": "#/definitions/atomic_element_base" },
|
||||
{
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"pattern": "sitemap/sitemap_template#/definitions/switch_element"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"required": ["type"]
|
||||
},
|
||||
|
||||
"device": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"pointer": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": ["id"]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,7 @@ namespace cosgi {
|
||||
namespace service {
|
||||
namespace sitemap {
|
||||
|
||||
using namespace libvariant;
|
||||
|
||||
// Service information
|
||||
HOS_SERVICE_IMPL_INFO( SitemapServiceImpl, // Implementation
|
||||
@@ -54,11 +55,68 @@ void SitemapServiceImpl::setRepoService(devicerepository::DeviceRepositoryServic
|
||||
|
||||
|
||||
void SitemapServiceImpl::init() noexcept {
|
||||
// Get public parameters
|
||||
nodehandle->getParam("sitemap_template_prefix", sitemapTemplatePrefix);
|
||||
nodehandle->getParam("hos_status_topic", hosStatusTopic);
|
||||
|
||||
// Parse sitemaps
|
||||
XmlRpc::XmlRpcValue rawValue;
|
||||
nodehandle->getParam(sitemapTemplatePrefix, rawValue);
|
||||
if (rawValue.getType() == XmlRpc::XmlRpcValue::TypeStruct) {
|
||||
for (auto dev : rawValue) {
|
||||
std::string sitemapName = dev.first;
|
||||
XmlRpc::XmlRpcValue devRaw = dev.second;
|
||||
try {
|
||||
addSitemap(sitemapName, devRaw);
|
||||
ROS_INFO_STREAM("Created sitemap " << sitemapName << ".");
|
||||
} catch (std::exception& e) {
|
||||
ROS_ERROR_STREAM("Could not create sitemap " << sitemapName << ": " << e.what());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
initialized = true;
|
||||
ROS_INFO_STREAM("RuleEngineService: Initialized and Ready.");
|
||||
}
|
||||
|
||||
|
||||
void SitemapServiceImpl::addSitemap(const std::string& name, XmlRpc::XmlRpcValue& rawValue) {
|
||||
std::lock_guard<std::recursive_mutex> lock(serviceMutex);
|
||||
if (rawValue.getType() != XmlRpc::XmlRpcValue::TypeString) {
|
||||
throw std::runtime_error("sitemap config value is not a string.");
|
||||
}
|
||||
std::string strValue = static_cast<std::string>(rawValue);
|
||||
libvariant::Variant value = libvariant::DeserializeJSON(strValue);
|
||||
Variant schema;
|
||||
try {
|
||||
schema = coreService->getSchemaLoader().GetSchema("hos:schema://sitemap/sitemap_template");
|
||||
} catch(std::exception& e) {
|
||||
std::stringstream ss;
|
||||
ss << "Could not load hos:schema://sitemap/sitemap_template: " << e.what();
|
||||
throw std::runtime_error(ss.str());
|
||||
}
|
||||
SchemaResult sr = SchemaValidate(schema, value, &coreService->getSchemaLoader());
|
||||
if (!sr.Error()) {
|
||||
auto titleRaw = value["title"];
|
||||
if (!titleRaw.IsString()) {
|
||||
throw std::runtime_error("title is not a string.");
|
||||
}
|
||||
std::string title = titleRaw.AsString();
|
||||
auto rootpage = value["rootpage"];
|
||||
if (!rootpage.IsMap()) {
|
||||
throw std::runtime_error("rootpage is not a map.");
|
||||
}
|
||||
std::shared_ptr<sitemaptemplate::SitemapTemplate> st = std::make_shared<sitemaptemplate::SitemapTemplate>(title);
|
||||
st->createPage(rootpage);
|
||||
sitemapTemplates.insert({name, st});
|
||||
} else {
|
||||
std::stringstream ss;
|
||||
ss << "Could not validate sitemap config value: " << sr.PrettyPrintMessage();
|
||||
throw std::runtime_error(ss.str());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} /* namespace sitemap */
|
||||
} /* namespace service */
|
||||
} /* namespace cosgi */
|
||||
|
||||
@@ -6,8 +6,7 @@
|
||||
#include <hos/cosgi/service/core/CoreService.h>
|
||||
#include <hos/cosgi/service/devicerepository/DeviceRepositoryService.h>
|
||||
#include <hos/cosgi/service/Implementation.h>
|
||||
//#include <hos/utils/SchemaLoader.h>
|
||||
// ROS includes
|
||||
#include "sitemap_template/SitemapTemplate.h"
|
||||
#include <ros/ros.h>
|
||||
// std includes
|
||||
#include <mutex>
|
||||
@@ -30,9 +29,16 @@ private:
|
||||
devicerepository::DeviceRepositoryService* repoService = nullptr;
|
||||
ros::NodeHandle* nodehandle = nullptr;
|
||||
|
||||
std::recursive_mutex serviceMutex;
|
||||
|
||||
std::string sitemapTemplatePrefix = "/hos/sitemap/sitemap_templates";
|
||||
std::string hosStatusTopic = "/hos/status/updates";
|
||||
bool initialized = false;
|
||||
|
||||
std::map<std::string, std::shared_ptr<sitemaptemplate::SitemapTemplate>> sitemapTemplates;
|
||||
|
||||
void init() noexcept;
|
||||
void addSitemap(const std::string& name, XmlRpc::XmlRpcValue& rawValue);
|
||||
|
||||
public:
|
||||
|
||||
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
|
||||
#include "../sitemap_template/SitemapTemplate.h"
|
||||
|
||||
namespace hos {
|
||||
namespace cosgi {
|
||||
namespace service {
|
||||
namespace sitemap {
|
||||
namespace sitemaptemplate {
|
||||
|
||||
|
||||
std::shared_ptr<Page> SitemapTemplate::createPage(const libvariant::Variant& param, std::weak_ptr<Page> parent) {
|
||||
std::shared_ptr<Page> newPage;
|
||||
if (parent.expired() && rootPage) {
|
||||
throw std::runtime_error("There is already a root page.");
|
||||
}
|
||||
auto titleRaw = param["title"];
|
||||
if (!titleRaw.IsString()) {
|
||||
throw std::runtime_error("title is not a string.");
|
||||
}
|
||||
std::string title = titleRaw.AsString();
|
||||
auto elements = param["elements"];
|
||||
if (!elements.IsList()) {
|
||||
throw std::runtime_error("elements is not a list.");
|
||||
}
|
||||
newPage = std::make_shared<Page>(*this, title, parent);
|
||||
for ( auto e : elements.AsList()) {
|
||||
newPage->createElement(e);
|
||||
}
|
||||
if (parent.expired()) {
|
||||
rootPage = newPage;
|
||||
}
|
||||
return newPage;
|
||||
}
|
||||
|
||||
|
||||
std::shared_ptr<Element> Page::createElement(const libvariant::Variant& param) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
std::shared_ptr<Element> CompositeElement::createElement(const libvariant::Variant& param) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
std::shared_ptr<Page> AtomicElement::createLinkedToPage(const libvariant::Variant& param) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
std::shared_ptr<Device> AtomicElement::createDevice(const libvariant::Variant& param) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
} // namespace sitemaptemplate
|
||||
} // namespace sitemap
|
||||
} // namespace service
|
||||
} // namespace cosgi
|
||||
} // namespace hos
|
||||
+164
@@ -0,0 +1,164 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
// hos includes
|
||||
#include <hos/utils/SchemaLoader.h>
|
||||
// libvariant
|
||||
#include <Variant/Schema.h>
|
||||
// std includes
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <atomic>
|
||||
|
||||
|
||||
namespace hos {
|
||||
namespace cosgi {
|
||||
namespace service {
|
||||
namespace sitemap {
|
||||
namespace sitemaptemplate {
|
||||
|
||||
|
||||
class Page;
|
||||
class Element;
|
||||
class AtomicElement;
|
||||
class CompositeElement;
|
||||
class Device;
|
||||
|
||||
|
||||
|
||||
class SitemapTemplate {
|
||||
private:
|
||||
std::string title;
|
||||
std::shared_ptr<Page> rootPage;
|
||||
|
||||
public:
|
||||
SitemapTemplate(const std::string& title) : title(title) {}
|
||||
|
||||
std::shared_ptr<Page> createPage(const libvariant::Variant& param, std::weak_ptr<Page> parent = std::weak_ptr<Page>());
|
||||
|
||||
std::shared_ptr<Page> getRootPage() noexcept { return rootPage; };
|
||||
};
|
||||
|
||||
|
||||
|
||||
class Page {
|
||||
private:
|
||||
SitemapTemplate& sitemap;
|
||||
std::weak_ptr<Page> parent;
|
||||
std::string title;
|
||||
std::vector<std::shared_ptr<Element>> elements;
|
||||
|
||||
public:
|
||||
Page(SitemapTemplate& sitemap, const std::string& title, std::weak_ptr<Page> parent = std::weak_ptr<Page>())
|
||||
: sitemap(sitemap), title(title) {}
|
||||
|
||||
SitemapTemplate& getSiteMap() noexcept { return sitemap; }
|
||||
std::shared_ptr<Page> getParent() noexcept { return parent.lock(); }
|
||||
|
||||
std::shared_ptr<Element> createElement(const libvariant::Variant& param);
|
||||
const std::vector<std::shared_ptr<Element>>& getElements() noexcept { return elements; }
|
||||
};
|
||||
|
||||
|
||||
enum class ElementType {
|
||||
// composite elements
|
||||
Frame,
|
||||
|
||||
// atomic elements
|
||||
Text,
|
||||
Switch,
|
||||
Slider
|
||||
};
|
||||
|
||||
class Element {
|
||||
private:
|
||||
static std::atomic<uint64_t> nextID;
|
||||
|
||||
uint64_t id;
|
||||
bool compositeFlag;
|
||||
ElementType type;
|
||||
Page& page;
|
||||
std::weak_ptr<CompositeElement> parent;
|
||||
|
||||
std::string label;
|
||||
std::string icon;
|
||||
|
||||
public:
|
||||
Element( Page& page,
|
||||
bool isComposite,
|
||||
ElementType type,
|
||||
std::weak_ptr<CompositeElement> parent,
|
||||
const std::string& label,
|
||||
const std::string& icon )
|
||||
: id(nextID.fetch_add(1)), compositeFlag(isComposite), type(type), page(page), parent(parent), label(label), icon(icon) {}
|
||||
virtual ~Element() {}
|
||||
|
||||
uint64_t getID() noexcept { return id; }
|
||||
const std::string& getLabel() noexcept { return label; }
|
||||
const std::string& getIcon() noexcept { return icon; }
|
||||
|
||||
Page& getPage() noexcept { return page; }
|
||||
std::shared_ptr<CompositeElement> getParent() noexcept { return parent.lock(); }
|
||||
|
||||
bool isComposite() noexcept { return compositeFlag; };
|
||||
ElementType getType() noexcept { return type; }
|
||||
};
|
||||
|
||||
|
||||
|
||||
class CompositeElement : public Element {
|
||||
private:
|
||||
std::vector<std::shared_ptr<Element>> elements;
|
||||
|
||||
public:
|
||||
CompositeElement(Page& page, ElementType etype, const std::string& label, const std::string& icon,
|
||||
std::weak_ptr<CompositeElement> parent = std::weak_ptr<CompositeElement>())
|
||||
: Element(page, true, etype, parent, label, icon) {}
|
||||
|
||||
std::shared_ptr<Element> createElement(const libvariant::Variant& param);
|
||||
const std::vector<std::shared_ptr<Element>>& getElements() noexcept { return elements; }
|
||||
};
|
||||
|
||||
|
||||
|
||||
class AtomicElement : public Element {
|
||||
private:
|
||||
std::string format;
|
||||
std::shared_ptr<Device> device;
|
||||
libvariant::Variant additionalParams;
|
||||
std::shared_ptr<Page> linkedToPage;
|
||||
|
||||
public:
|
||||
AtomicElement(Page& page, ElementType etype, const std::string& label, const std::string& icon,
|
||||
const std::string& format, std::shared_ptr<Device> device, libvariant::Variant additionalParams,
|
||||
std::weak_ptr<CompositeElement> parent = std::weak_ptr<CompositeElement>())
|
||||
: Element(page, false, etype, parent, label, icon), format(format), device(device), additionalParams(additionalParams) {}
|
||||
|
||||
const std::string& getFormat() { return format; }
|
||||
std::shared_ptr<Device> createDevice(const libvariant::Variant& param);
|
||||
std::shared_ptr<Device> getDevice() { return device; }
|
||||
const libvariant::Variant& getAdditionalParams() { return additionalParams; }
|
||||
|
||||
std::shared_ptr<Page> createLinkedToPage(const libvariant::Variant& param);
|
||||
std::shared_ptr<Page> getLinkedToPage() { return linkedToPage; }
|
||||
};
|
||||
|
||||
|
||||
|
||||
class Device {
|
||||
private:
|
||||
|
||||
public:
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
} // namespace sitemaptemplate
|
||||
} // namespace sitemap
|
||||
} // namespace service
|
||||
} // namespace cosgi
|
||||
} // namespace hos
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<launch>
|
||||
<include file="$(find hos_core_types)/launch/setup_core_types.launch" />
|
||||
<include file="$(find hos_philips_hue)/launch/hue_types.launch" />
|
||||
<include file="$(find hos_cosgi_sitemap)/launch/setup_sitemap.launch" />
|
||||
</launch>
|
||||
Reference in New Issue
Block a user