First working sitemap version.

This commit is contained in:
2016-03-15 02:14:05 +01:00
parent 6c35229ffe
commit 0cfd72374b
46 changed files with 2264 additions and 168 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
#!/bin/bash
PYTHON_PATH=/opt/ros/indigo/lib/python2.7/dist-packages
PYTHON_PATH=/home/matzman/tmp/ros_catkin_ws/install_isolated/lib/python2.7/dist-packages
# delete old files
rm -rf devel devel_isolated build build_isolated install install_isolated
@@ -62,7 +62,6 @@ void DeviceImpl::handleCommandMsg(const Variant& msg, const timestamp_t& stamp)
void DeviceImpl::sendCommand(const Variant& cmd) {
std::lock_guard<std::recursive_mutex> lock(objMutex);
parent->sendDeviceCommand(this, cmd);
}
+285 -34
View File
@@ -1,40 +1,291 @@
{
"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",
"icon" : "some_icon01",
"format" : "Format: %1%",
"device": {
"id": "dummySwitch0",
"pointer": "/switch/value"
}
}, {
"type": "sitemap/sitemap_template#/definitions/switch_element",
"label": "Switch0",
"icon" : "some_icon02",
"format" : "bla: %1%",
"device": {
"id": "dummySwitch0",
"pointer": "/switch/value"
}
}, {
"type": "sitemap/sitemap_template#/definitions/slider_element",
"label": "Slider0",
"icon" : "some_icon03",
"device": {
"id": "dummyDimmer0",
"pointer": "/dimmer/value"
"rootpage": "page01",
"pages": {
"page01": {
"type": "sitemap/sitemap_template#/definitions/page",
"title": "Root Page",
"elements": [
{
"type": "sitemap/sitemap_template#/definitions/frame_element",
"label": "Group 0",
"icon" : "some_icon01",
"elements" : [
{
"type": "sitemap/sitemap_template#/definitions/text_element",
"label": "Switch0",
"icon" : "some_icon01",
"format" : "Value: %1%",
"linkedPage" : "page02",
"device": {
"id": "dummySwitch0",
"pointer": "/switch/value"
}
}, {
"type": "sitemap/sitemap_template#/definitions/switch_element",
"label": "Switch0",
"icon" : "some_icon02",
"linkedPage" : "page02",
"device": {
"id": "dummySwitch0",
"pointer": "/switch/value"
},
"commandTemplate": {
"switch": {
"set": null
}
},
"commandValuePointer": "/switch/set"
}, {
"type": "sitemap/sitemap_template#/definitions/text_element",
"label": "Slider0",
"icon" : "some_icon01",
"format" : "Value: %1%",
"device": {
"id": "dummyDimmer0",
"pointer": "/dimmer/value"
}
}, {
"type": "sitemap/sitemap_template#/definitions/slider_element",
"label": "Slider0",
"icon" : "some_icon03",
"device": {
"id": "dummyDimmer0",
"pointer": "/dimmer/value"
},
"min": 0,
"max": 1,
"step": 0.1,
"commandTemplate": {
"dimmer": {
"set": null
}
},
"commandValuePointer": "/dimmer/set"
}
]
},
"min": 0,
"max": 1,
"step": 0.1
}
]
{
"type": "sitemap/sitemap_template#/definitions/frame_element",
"label": "Group 1",
"icon" : "some_icon01",
"elements" : [
{
"type": "sitemap/sitemap_template#/definitions/text_element",
"label": "Switch1",
"icon" : "some_icon01",
"format" : "Value: %1%",
"device": {
"id": "dummySwitch1",
"pointer": "/switch/value"
}
}, {
"type": "sitemap/sitemap_template#/definitions/switch_element",
"label": "Switch1",
"icon" : "some_icon02",
"device": {
"id": "dummySwitch1",
"pointer": "/switch/value"
},
"commandTemplate": {
"switch": {
"set": null
}
},
"commandValuePointer": "/switch/set"
}, {
"type": "sitemap/sitemap_template#/definitions/text_element",
"label": "Slider1",
"icon" : "some_icon01",
"format" : "Value: %1%",
"device": {
"id": "dummyDimmer1",
"pointer": "/dimmer/value"
}
}, {
"type": "sitemap/sitemap_template#/definitions/slider_element",
"label": "Slider1",
"icon" : "some_icon03",
"device": {
"id": "dummyDimmer1",
"pointer": "/dimmer/value"
},
"min": 0,
"max": 1,
"step": 0.1,
"commandTemplate": {
"dimmer": {
"set": null
}
},
"commandValuePointer": "/dimmer/set"
}
]
},
{
"type": "sitemap/sitemap_template#/definitions/frame_element",
"label": "Group 2",
"icon" : "some_icon01",
"elements" : [
{
"type": "sitemap/sitemap_template#/definitions/text_element",
"label": "Switch2",
"icon" : "some_icon01",
"format" : "Value: %1%",
"device": {
"id": "dummySwitch2",
"pointer": "/switch/value"
}
}, {
"type": "sitemap/sitemap_template#/definitions/switch_element",
"label": "Switch2",
"icon" : "some_icon02",
"device": {
"id": "dummySwitch2",
"pointer": "/switch/value"
},
"commandTemplate": {
"switch": {
"set": null
}
},
"commandValuePointer": "/switch/set"
}, {
"type": "sitemap/sitemap_template#/definitions/text_element",
"label": "Slider2",
"icon" : "some_icon01",
"format" : "Value: %1%",
"device": {
"id": "dummyDimmer2",
"pointer": "/dimmer/value"
}
}, {
"type": "sitemap/sitemap_template#/definitions/slider_element",
"label": "Slider2",
"icon" : "some_icon03",
"device": {
"id": "dummyDimmer2",
"pointer": "/dimmer/value"
},
"min": 0,
"max": 1,
"step": 0.1,
"commandTemplate": {
"dimmer": {
"set": null
}
},
"commandValuePointer": "/dimmer/set"
}
]
},
{
"type": "sitemap/sitemap_template#/definitions/frame_element",
"label": "Group 3",
"icon" : "some_icon01",
"elements" : [
{
"type": "sitemap/sitemap_template#/definitions/text_element",
"label": "Switch3",
"icon" : "some_icon01",
"format" : "Value: %1%",
"device": {
"id": "dummySwitch3",
"pointer": "/switch/value"
}
}, {
"type": "sitemap/sitemap_template#/definitions/switch_element",
"label": "Switch3",
"icon" : "some_icon02",
"device": {
"id": "dummySwitch3",
"pointer": "/switch/value"
},
"commandTemplate": {
"switch": {
"set": null
}
},
"commandValuePointer": "/switch/set"
}, {
"type": "sitemap/sitemap_template#/definitions/text_element",
"label": "Slider3",
"icon" : "some_icon01",
"format" : "Value: %1%",
"device": {
"id": "dummyDimmer3",
"pointer": "/dimmer/value"
}
}, {
"type": "sitemap/sitemap_template#/definitions/slider_element",
"label": "Slider3",
"icon" : "some_icon03",
"device": {
"id": "dummyDimmer3",
"pointer": "/dimmer/value"
},
"min": 0,
"max": 1,
"step": 0.1,
"commandTemplate": {
"dimmer": {
"set": null
}
},
"commandValuePointer": "/dimmer/set"
}
]
}
]
},
"page02": {
"type": "sitemap/sitemap_template#/definitions/page",
"title": "Page 2",
"elements": [
{
"type": "sitemap/sitemap_template#/definitions/frame_element",
"label": "Some Text",
"icon" : "some_icon01",
"elements" : [
{
"type": "sitemap/sitemap_template#/definitions/text_element",
"label": "Switch0 Text",
"icon" : "some_icon01",
"format" : "blabla: %1%",
"device": {
"id": "dummyDimmer1",
"pointer": "/dimmer/value"
},
"linkedPage" : "page03"
}
]
}
]
},
"page03": {
"type": "sitemap/sitemap_template#/definitions/page",
"title": "Page 3",
"elements": [
{
"type": "sitemap/sitemap_template#/definitions/frame_element",
"label": "Some Text 2",
"icon" : "some_icon01",
"elements" : [
{
"type": "sitemap/sitemap_template#/definitions/text_element",
"label": "Switch2 Text",
"icon" : "some_icon01",
"format" : "blabla: %1%",
"device": {
"id": "dummyDimmer3",
"pointer": "/dimmer/value"
},
"linkedPage" : "page01"
}
]
}
]
}
}
}
+166
View File
@@ -0,0 +1,166 @@
{
"type": "sitemap/sitemap_template",
"title": "Hue Lights App",
"rootpage": "page01",
"pages": {
"page01": {
"type": "sitemap/sitemap_template#/definitions/page",
"title": "My Room",
"elements": [
{
"type": "sitemap/sitemap_template#/definitions/frame_element",
"label": "Lightstrips 1",
"icon" : "some_icon01",
"elements" : [
{
"type": "sitemap/sitemap_template#/definitions/switch_element",
"label": "Lightstrips 1 Switch",
"icon" : "some_icon02",
"device": {
"id": "huelight1",
"pointer": "/switch/value"
},
"commandTemplate": {
"switch": {
"set": null
}
},
"commandValuePointer": "/switch/set"
}, {
"type": "sitemap/sitemap_template#/definitions/slider_element",
"label": "Lightstrips 1 Slider",
"icon" : "some_icon03",
"device": {
"id": "huelight1",
"pointer": "/brightness/value"
},
"min": 0,
"max": 1,
"step": 0.1,
"commandTemplate": {
"brightness": {
"set": null
}
},
"commandValuePointer": "/brightness/set"
}
]
}, {
"type": "sitemap/sitemap_template#/definitions/frame_element",
"label": "Lightstrips 2",
"icon" : "some_icon01",
"elements" : [
{
"type": "sitemap/sitemap_template#/definitions/switch_element",
"label": "Lightstrips 2 Switch",
"icon" : "some_icon02",
"device": {
"id": "huelight2",
"pointer": "/switch/value"
},
"commandTemplate": {
"switch": {
"set": null
}
},
"commandValuePointer": "/switch/set"
}, {
"type": "sitemap/sitemap_template#/definitions/slider_element",
"label": "Lightstrips 2 Slider",
"icon" : "some_icon03",
"device": {
"id": "huelight2",
"pointer": "/brightness/value"
},
"min": 0,
"max": 1,
"step": 0.1,
"commandTemplate": {
"brightness": {
"set": null
}
},
"commandValuePointer": "/brightness/set"
}
]
}, {
"type": "sitemap/sitemap_template#/definitions/frame_element",
"label": "ZimmerLampe",
"icon" : "some_icon01",
"elements" : [
{
"type": "sitemap/sitemap_template#/definitions/switch_element",
"label": "ZimmerLampe Switch",
"icon" : "some_icon02",
"device": {
"id": "huelight3",
"pointer": "/switch/value"
},
"commandTemplate": {
"switch": {
"set": null
}
},
"commandValuePointer": "/switch/set"
}, {
"type": "sitemap/sitemap_template#/definitions/slider_element",
"label": "ZimmerLampe Slider",
"icon" : "some_icon03",
"device": {
"id": "huelight3",
"pointer": "/brightness/value"
},
"min": 0,
"max": 1,
"step": 0.1,
"commandTemplate": {
"brightness": {
"set": null
}
},
"commandValuePointer": "/brightness/set"
}
]
}, {
"type": "sitemap/sitemap_template#/definitions/frame_element",
"label": "TischLampe",
"icon" : "some_icon01",
"elements" : [
{
"type": "sitemap/sitemap_template#/definitions/switch_element",
"label": "TischLampe Switch",
"icon" : "some_icon02",
"device": {
"id": "huelight4",
"pointer": "/switch/value"
},
"commandTemplate": {
"switch": {
"set": null
}
},
"commandValuePointer": "/switch/set"
}, {
"type": "sitemap/sitemap_template#/definitions/slider_element",
"label": "TischLampe Slider",
"icon" : "some_icon03",
"device": {
"id": "huelight4",
"pointer": "/brightness/value"
},
"min": 0,
"max": 1,
"step": 0.1,
"commandTemplate": {
"brightness": {
"set": null
}
},
"commandValuePointer": "/brightness/set"
}
]
}
]
}
}
}
@@ -59,6 +59,8 @@ public:
virtual std::map<std::string, std::weak_ptr<sitemaptemplate::SitemapTemplate>> getSitemaps() noexcept = 0;
virtual std::vector<std::string> getSitemapNames() noexcept = 0;
virtual std::weak_ptr<sitemaptemplate::SitemapTemplate> getDefaultSitemap() noexcept = 0;
};
@@ -20,20 +20,78 @@ class SitemapService;
namespace sitemaptemplate {
class SitemapTemplate;
class Page;
class Element;
class AtomicElement;
class CompositeElement;
class Device;
/*
* Sitemap event type.
*
**/
enum class SitemapEventType : int {
ALL_EVENTS = 1,
SITEMAP_ELEMENT_CHANGED = 2,
SITEMAP_INVALIDATED = 3
};
/**
* Base class for sitemap events.
*/
class SitemapEvent {
public:
typedef std::chrono::system_clock::time_point timestamp_t;
SitemapEventType eventType;
SitemapTemplate* const sitemap;
const timestamp_t timestamp;
SitemapEvent(SitemapTemplate* sitemap, const SitemapEventType& type, const timestamp_t& timestamp)
: eventType(type), sitemap(sitemap), timestamp(timestamp) {}
};
/**
* sitemap element changed event.
*/
class SitemapElementChangedEvent
: public SitemapEvent {
public:
std::vector<std::weak_ptr<Element>> elements;
SitemapElementChangedEvent(SitemapTemplate* sitemap, const timestamp_t& timestamp)
: SitemapEvent(sitemap,SitemapEventType::SITEMAP_ELEMENT_CHANGED, timestamp) {}
};
/**
* sitemap invalidated event.
*/
class SitemapInvalidatedEvent
: public SitemapEvent {
public:
SitemapInvalidatedEvent(SitemapTemplate* sitemap, const timestamp_t& timestamp)
: SitemapEvent(sitemap,SitemapEventType::SITEMAP_INVALIDATED, timestamp) {}
};
class SitemapTemplate
: public virtual interface::Object {
: public virtual interface::Observer<devicerepository::DeviceEventType, devicerepository::DeviceEvent>,
public virtual interface::ObservableObject<SitemapEventType, SitemapEvent> {
public:
virtual SitemapService* getParent() noexcept = 0;
virtual size_t pageCount() noexcept = 0;
virtual std::weak_ptr<Page> getPage(const std::string& name) noexcept = 0;
virtual std::vector<std::weak_ptr<Page>> getPages(const std::string& name) noexcept = 0;
virtual std::weak_ptr<Page> getRootPage() noexcept = 0;
virtual const std::string& getRootPageName() noexcept = 0;
virtual std::weak_ptr<Element> getPageElement(size_t& uid) noexcept = 0;
virtual const std::string& getName() noexcept = 0;
virtual libvariant::Variant renderSitemap() = 0;
};
@@ -44,7 +102,7 @@ class Page
: public virtual interface::Object {
public:
virtual SitemapTemplate* getSiteMap() noexcept = 0;
virtual Page* getParent() noexcept = 0;
virtual const std::string& getName() noexcept = 0;
virtual size_t elementCount() noexcept = 0;
virtual std::weak_ptr<Element> getElement(size_t index) noexcept = 0;
@@ -99,7 +157,8 @@ public:
virtual const std::string& getFormat() noexcept = 0;
virtual std::weak_ptr<Device> getDevice() noexcept = 0;
virtual const libvariant::Variant& getAdditionalParams() noexcept = 0;
virtual std::weak_ptr<Page> getLinkedToPage() noexcept = 0;
virtual std::weak_ptr<Page> getLinkedPage() noexcept = 0;
virtual const std::string& getLinkedPageName() noexcept = 0;
};
@@ -107,10 +166,12 @@ public:
class Device
: public virtual interface::Object {
public:
virtual std::weak_ptr<Element> getParent() noexcept = 0;
virtual const std::string& getDeviceID() noexcept = 0;
virtual std::weak_ptr<devicerepository::Device> getDevice() noexcept = 0;
virtual const std::string& getPointer() noexcept = 0;
virtual bool deviceExists() noexcept = 0;
virtual void sendCommand(const libvariant::Variant& cmd) = 0;
};
@@ -1,6 +1,7 @@
<launch>
<group ns="/hos/sitemap/sitemap_templates">
<param name="test01" textfile="$(find hos_cosgi_sitemap)/examples/test01.json"/>
<param name="test02" textfile="$(find hos_cosgi_sitemap)/examples/test02.json"/>
</group>
<group ns="/hos/types/repository/sitemap">
@@ -13,12 +13,24 @@
"type": "string",
"description": ""
},
"default": {
"type": "boolean",
"description": ""
},
"pages": {
"type": "object",
"patternProperties": {
"[A-Za-z0-9_-]+": {
"$ref": "#/definitions/page"
}
}
},
"rootpage": {
"$ref": "#/definitions/page"
"type": "string"
}
},
"additionalProperties": false,
"required": ["title", "rootpage"],
"required": ["title", "pages", "rootpage"],
"definitions": {
@@ -109,7 +121,7 @@
"icon": {
"type": "string"
},
"page": {
"linkedPage": {
"type": "string"
}
}
@@ -103,13 +103,38 @@ void SitemapServiceImpl::addSitemap(const std::string& name, XmlRpc::XmlRpcValue
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.");
auto rootpageRaw = value["rootpage"];
if (!rootpageRaw.IsString()) {
throw std::runtime_error("rootpage is not a string.");
}
std::string rootpage = rootpageRaw.AsString();
bool isDefault = false;
if (value.Contains("default")) {
auto isDefaultRaw = value["default"];
if (!isDefaultRaw.IsBool()) {
throw std::runtime_error("default is not a boolean.");
}
isDefault = isDefaultRaw.IsTrue();
}
auto pages = value["pages"];
if (!pages.IsMap()) {
throw std::runtime_error("pages is not a map.");
}
std::shared_ptr<sitemaptemplate::SitemapTemplateImpl> st = std::make_shared<sitemaptemplate::SitemapTemplateImpl>(*this, name, title);
for (auto page : pages.AsMap()) {
st->addPage(page.first, page.second);
}
if (!st->setRootPageName(rootpage)) {
ROS_WARN_STREAM("Sitemap " << st->getName() << " has unknown root page " << rootpage << ".");
}
std::shared_ptr<sitemaptemplate::SitemapTemplateImpl> st = std::make_shared<sitemaptemplate::SitemapTemplateImpl>(*this, title);
st->createPage(rootpage);
sitemapTemplates.insert({name, st});
if (!sitemapDefault || isDefault) {
sitemapDefault = st;
}
repoService->addObserver(devicerepository::DeviceEventType::ALL_EVENTS, st.get());
if (name.compare("default") == 0) {
ROS_WARN("There exists a sitemap with name \"default\".");
}
} else {
std::stringstream ss;
ss << "Could not validate sitemap config value: " << sr.PrettyPrintMessage();
@@ -146,6 +171,10 @@ std::vector<std::string> SitemapServiceImpl::getSitemapNames() noexcept {
}
std::weak_ptr<sitemaptemplate::SitemapTemplate> SitemapServiceImpl::getDefaultSitemap() noexcept {
return std::static_pointer_cast<sitemaptemplate::SitemapTemplate>(sitemapDefault);
}
} /* namespace sitemap */
} /* namespace service */
} /* namespace cosgi */
@@ -38,6 +38,8 @@ private:
bool initialized = false;
std::map<std::string, std::shared_ptr<sitemaptemplate::SitemapTemplateImpl>> sitemapTemplates;
std::shared_ptr<sitemaptemplate::SitemapTemplateImpl> sitemapDefault;
void init() noexcept;
void addSitemap(const std::string& name, XmlRpc::XmlRpcValue& rawValue);
@@ -72,6 +74,8 @@ public:
virtual std::vector<std::string> getSitemapNames() noexcept override;
virtual std::weak_ptr<sitemaptemplate::SitemapTemplate> getDefaultSitemap() noexcept override;
/* Internal */
@@ -1,5 +1,6 @@
#include "SitemapTemplateImpl.h"
#include <iostream>
namespace hos {
namespace cosgi {
@@ -9,54 +10,6 @@ namespace sitemaptemplate {
std::atomic<uint64_t> ElementImpl::nextID;
std::shared_ptr<PageImpl> SitemapTemplateImpl::createPage(const libvariant::Variant& param, PageImpl* parent) {
std::shared_ptr<PageImpl> newPage;
if (!parent && 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<PageImpl>(*this, title, parent);
for ( auto e : elements.AsList()) {
newPage->createElement(e);
}
if (!parent) {
rootPage = newPage;
}
return newPage;
}
std::shared_ptr<DeviceImpl> SitemapTemplateImpl::createDevice(const libvariant::Variant& param) {
auto idRaw = param["id"];
if (!idRaw.IsString()) {
throw std::runtime_error("id is not a string.");
}
std::string id = idRaw.AsString();
std::string pointer;
auto pointerRaw = param["pointer"];
if (!pointerRaw.IsString()) {
throw std::runtime_error("pointer is not a string.");
} else if (pointerRaw.IsString()) {
pointer = pointerRaw.AsString();
}
auto devRepo = parent.getDeviceRepository();
auto device = devRepo->getDevice(id);
std::shared_ptr<DeviceImpl> newDevice = std::make_shared<DeviceImpl>(id, device, pointer);
return newDevice;
}
namespace {
struct _elementTypeInfo {
@@ -75,26 +28,85 @@ _elementTypeInfo elementTypeInfo[] = {
}
std::atomic<uint64_t> ElementImpl::nextID;
std::shared_ptr<PageImpl> SitemapTemplateImpl::addPage(const std::string& name, const libvariant::Variant& pageRaw) {
auto titleRaw = pageRaw["title"];
if (!titleRaw.IsString()) {
throw std::runtime_error("title is not a string.");
}
std::string title = titleRaw.AsString();
auto elements = pageRaw["elements"];
if (!elements.IsList()) {
throw std::runtime_error("elements is not a list.");
}
auto newPage = std::make_shared<PageImpl>(*this, name, title);
for (auto element : elements.AsList()) {
newPage->addElement(element);
}
pages.insert({name, newPage});
return newPage;
}
std::vector<std::weak_ptr<Element>> PageImpl::getElements() noexcept {
std::vector<std::weak_ptr<Element>> retval;
for (auto i : elements) {
retval.push_back(std::static_pointer_cast<Element>(i));
std::weak_ptr<Page> SitemapTemplateImpl::getPage(const std::string& name) noexcept {
auto page = pages.find(name);
if (page == pages.end()) {
return std::weak_ptr<Page>();
} else {
return page->second;
}
}
std::vector<std::weak_ptr<Page>> SitemapTemplateImpl::getPages(const std::string& name) noexcept {
std::vector<std::weak_ptr<Page>> retval;
for (auto page : pages) {
retval.push_back(page.second);
}
return retval;
}
std::vector<std::weak_ptr<Element>> CompositeElementImpl::getElements() noexcept {
std::vector<std::weak_ptr<Element>> retval;
for (auto i : elements) {
retval.push_back(std::static_pointer_cast<Element>(i));
std::weak_ptr<Page> SitemapTemplateImpl::getRootPage() noexcept {
return getPage(rootPageName);
}
bool SitemapTemplateImpl::setRootPageName(const std::string& name) noexcept {
rootPageName = name;
if (pages.find(name) != pages.end()) {
return true;
}
return retval;
return false;
}
std::weak_ptr<Element> SitemapTemplateImpl::getPageElement(size_t& uid) noexcept {
auto r = pageElements.find(uid);
if (r != pageElements.end()) {
return r->second;
}
return std::weak_ptr<Element>();
}
std::shared_ptr<DeviceImpl> SitemapTemplateImpl::createDevice(const libvariant::Variant& param, const libvariant::Variant& commandTemplate, const std::string& commandValuePointer) {
auto idRaw = param["id"];
if (!idRaw.IsString()) {
throw std::runtime_error("id is not a string.");
}
std::string id = idRaw.AsString();
std::string pointer;
auto pointerRaw = param["pointer"];
if (!pointerRaw.IsString()) {
throw std::runtime_error("pointer is not a string.");
} else if (pointerRaw.IsString()) {
pointer = pointerRaw.AsString();
}
auto devRepo = parent.getDeviceRepository();
auto device = devRepo->getDevice(id);
std::shared_ptr<DeviceImpl> newDevice = std::make_shared<DeviceImpl>(id, device, pointer, commandTemplate, commandValuePointer);
devices.insert({id, newDevice});
return newDevice;
}
std::shared_ptr<ElementImpl> PageImpl::createElement(const libvariant::Variant& param, CompositeElementImpl* parent) {
std::shared_ptr<ElementImpl> SitemapTemplateImpl::createElement(PageImpl& page, const libvariant::Variant& param, CompositeElementImpl* parent) {
std::shared_ptr<ElementImpl> newElement;
libvariant::Variant addParams = param.Copy();
auto typeRaw = param["type"];
@@ -134,13 +146,23 @@ std::shared_ptr<ElementImpl> PageImpl::createElement(const libvariant::Variant&
}
}
if (info->isComposite) {
auto elements = param["elements"];
if (elements.IsNull()) {
throw std::runtime_error("elements is missing.");
} else if (!elements.IsList()) {
throw std::runtime_error("elements is not an array.");
}
addParams.Erase("elements");
switch (info->type) {
case ElementType::Frame:
newElement = std::make_shared<FrameElementImpl>(*this, info->typeString, label, icon, parent);
newElement = std::make_shared<FrameElementImpl>(page, info->typeString, label, icon, parent);
break;
default:
throw std::runtime_error((boost::format("Registered element type %1% is unknown.") % info->typeName).str());
}
for (auto element : elements.AsList()) {
std::static_pointer_cast<CompositeElementImpl>(newElement)->addElement(element);
}
} else {
std::string format;
if (param.Contains("format")) {
@@ -158,38 +180,120 @@ std::shared_ptr<ElementImpl> PageImpl::createElement(const libvariant::Variant&
if (!deviceRaw.IsMap()) {
throw std::runtime_error("device is not a map.");
} else {
device = sitemap.createDevice(deviceRaw);
libvariant::Variant commandTemplate;
std::string commandValuePointer;
if (param.Contains("commandTemplate")) {
commandTemplate = param["commandTemplate"];
addParams.Erase("commandTemplate");
}
if (param.Contains("commandValuePointer") && param["commandValuePointer"].IsString()) {
commandValuePointer = param["commandValuePointer"].AsString();
addParams.Erase("commandValuePointer");
}
device = createDevice(deviceRaw, commandTemplate, commandValuePointer);
addParams.Erase("device");
}
}
libvariant::Variant pageRaw;
if (param.Contains("page")) {
pageRaw = param["page"];
if (!pageRaw.IsMap()) {
throw std::runtime_error("page is not a map.");
libvariant::Variant linkedPageRaw;
if (param.Contains("linkedPage")) {
linkedPageRaw = param["linkedPage"];
if (!linkedPageRaw.IsString()) {
throw std::runtime_error("linkedPage is not a string.");
} else {
addParams.Erase("page");
addParams.Erase("linkedPage");
}
}
std::shared_ptr<AtomicElementImpl> newAElement;
switch (info->type) {
case ElementType::Text:
newAElement = std::make_shared<TextElementImpl>(*this, info->typeString, label, icon, format, device, addParams, parent);
newAElement = std::make_shared<TextElementImpl>(page, info->typeString, label, icon, format, device, addParams, parent);
break;
case ElementType::Slider:
newAElement = std::make_shared<SliderElementImpl>(*this, info->typeString, label, icon, format, device, addParams, parent);
newAElement = std::make_shared<SliderElementImpl>(page, info->typeString, label, icon, format, device, addParams, parent);
break;
case ElementType::Switch:
newAElement = std::make_shared<SwitchElementImpl>(*this, info->typeString, label, icon, format, device, addParams, parent);
newAElement = std::make_shared<SwitchElementImpl>(page, info->typeString, label, icon, format, device, addParams, parent);
break;
default:
throw std::runtime_error((boost::format("Registered element type %1% is unknown.") % info->typeName).str());
}
if (pageRaw.IsMap()) {
newAElement->createLinkedToPage(pageRaw);
if (device) {
device->setParent(newAElement);
}
if (linkedPageRaw.IsString()) {
newAElement->setLinkedPage(linkedPageRaw.AsString());
}
newElement = newAElement;
}
pageElements.insert({newElement->getID(), newElement});
return newElement;
}
void SitemapTemplateImpl::notificationReceived(observable_type* o, const devicerepository::DeviceEventType& key, const devicerepository::DeviceEvent* e) noexcept {
SitemapElementChangedEvent event(this, std::chrono::system_clock::now());
switch (e->eventType) {
case devicerepository::DeviceEventType::DEVICE_ADDED:
{
auto range = devices.equal_range(e->device->getDeviceID());
for (auto i = range.first; i != range.second && i != devices.end(); i++) {
auto dev = i->second.lock();
if (!dev->deviceExists()) {
dev->resolveDevice();
event.elements.push_back(dev->getParent());
}
}
}
break;
case devicerepository::DeviceEventType::DEVICE_REMOVED:
{
auto range = devices.equal_range(e->device->getDeviceID());
for (auto i = range.first; i != range.second && i != devices.end(); i++) {
auto dev = i->second.lock();
dev->resolveDevice();
event.elements.push_back(dev->getParent());
}
}
break;
case devicerepository::DeviceEventType::STATE_CHANGED:
{
auto range = devices.equal_range(e->device->getDeviceID());
for (auto i = range.first; i != range.second && i != devices.end(); i++) {
auto dev = i->second.lock();
event.elements.push_back(dev->getParent());
}
}
break;
default:
break;
}
if (event.elements.size() > 0) {
notifyObservers(SitemapEventType::ALL_EVENTS, SitemapEventType::SITEMAP_ELEMENT_CHANGED, &event);
notifyObservers(SitemapEventType::SITEMAP_ELEMENT_CHANGED, &event);
}
}
std::vector<std::weak_ptr<Element>> PageImpl::getElements() noexcept {
std::vector<std::weak_ptr<Element>> retval;
for (auto i : elements) {
retval.push_back(std::static_pointer_cast<Element>(i));
}
return retval;
}
std::vector<std::weak_ptr<Element>> CompositeElementImpl::getElements() noexcept {
std::vector<std::weak_ptr<Element>> retval;
for (auto i : elements) {
retval.push_back(std::static_pointer_cast<Element>(i));
}
return retval;
}
std::shared_ptr<ElementImpl> PageImpl::addElement(const libvariant::Variant& param, CompositeElementImpl* parent) {
std::shared_ptr<ElementImpl> newElement = sitemap.createElement(*this, param, parent);
if (!parent) {
elements.push_back(newElement);
}
@@ -198,33 +302,40 @@ std::shared_ptr<ElementImpl> PageImpl::createElement(const libvariant::Variant&
std::shared_ptr<ElementImpl> CompositeElementImpl::createElement(const libvariant::Variant& param) {
std::shared_ptr<ElementImpl> newElement = page.createElement(param, this);
std::shared_ptr<ElementImpl> CompositeElementImpl::addElement(const libvariant::Variant& param) {
std::shared_ptr<ElementImpl> newElement = page.addElement(param, this);
elements.push_back(newElement);
return newElement;
}
std::shared_ptr<PageImpl> AtomicElementImpl::createLinkedToPage(const libvariant::Variant& param) {
if (linkedToPage) {
throw std::runtime_error("There is already a linked page.");
bool AtomicElementImpl::setLinkedPage(const std::string& pagename) noexcept {
linkedPageName = pagename;
if (!this->page.getSiteMap()->getPage(linkedPageName).expired()) {
return true;
}
linkedToPage = page.sitemap.createPage(param, &page);
return linkedToPage;
return false;
}
libvariant::Variant SitemapTemplateImpl::renderSitemap() {
libvariant::Variant retval = libvariant::Variant::Map();
retval["title"] = title;
retval["rootpage"] = rootPage->renderSitemap();
retval["name"] = name;
retval["type"] = "sitemap/sitemap";
retval["rootpage"] = rootPageName;
auto tmp = libvariant::Variant::Map();
for (auto page : pages) {
tmp[page.first] = page.second->renderSitemap();
}
retval["pages"] = tmp;
return retval;
}
libvariant::Variant PageImpl::renderSitemap() {
libvariant::Variant retval = libvariant::Variant::Map();
retval["title"] = title;
retval["name"] = name;
libvariant::Variant elements = libvariant::Variant::List();
for (auto i : this->elements) {
elements.Append(i->renderSitemap());
@@ -246,20 +357,27 @@ libvariant::Variant CompositeElementImpl::renderSitemap() {
}
retval["elements"] = elements;
retval["type"] = typeString;
retval["uid"] = id;
return retval;
}
std::weak_ptr<Page> AtomicElementImpl::getLinkedPage() noexcept {
return this->page.getSiteMap()->getPage(linkedPageName);
}
libvariant::Variant AtomicElementImpl::renderSitemap() {
libvariant::Variant retval = libvariant::Variant::Map();
retval["label"] = label;
if (!icon.empty()) {
retval["icon"] = icon;
}
if (linkedToPage) {
retval["page"] = linkedToPage->renderSitemap();
if (!linkedPageName.empty()) {
retval["linkedPage"] = linkedPageName;
}
retval["value"] = _getDeviceValue();
retval["type"] = typeString;
retval["uid"] = id;
retval.Merge(additionalParams);
return retval;
}
@@ -267,7 +385,7 @@ libvariant::Variant AtomicElementImpl::renderSitemap() {
libvariant::Variant AtomicElementImpl::_getDeviceValue() {
libvariant::Variant devState;
if (device->deviceExists()) {
if (device && device->deviceExists()) {
try {
devState = device->getDevice().lock()->getCurrentState();
if (!device->getPointer().empty()) {
@@ -280,7 +398,11 @@ libvariant::Variant AtomicElementImpl::_getDeviceValue() {
devState = "NaN";
}
if (!format.empty()) {
devState = (boost::format(format) % devState.AsString()).str();
try {
devState = (boost::format(format) % devState.AsString()).str();
} catch (boost::io::format_error& e) {
devState = format;
}
}
return devState;
}
@@ -321,6 +443,21 @@ libvariant::Variant SwitchElementImpl::_getDeviceValue() {
return devState;
}
void DeviceImpl::resolveDevice() noexcept {
if (!parent.expired()) {
auto devRepo = parent.lock()->getPage()->getSiteMap()->getParent()->getDeviceRepository();
device = devRepo->getDevice(deviceID);
}
}
void DeviceImpl::sendCommand(const libvariant::Variant& cmd) {
if (!device.expired() && commandTemplate.IsMap() && !commandValuePointer.empty()) {
std::cout << "DeviceImpl::sendCommand: " << libvariant::SerializeJSON(cmd) << std::endl;
auto msg = commandTemplate.Copy();
msg.SetPath(commandValuePointer, cmd);
device.lock()->sendCommand(msg);
}
}
} // namespace sitemaptemplate
} // namespace sitemap
@@ -3,6 +3,8 @@
// hos includes
#include <hos/cosgi/service/sitemap/sitemap_template/SitemapTemplate.h>
#include <hos/cosgi/service/devicerepository/DeviceRepositoryService.h>
#include <hos/cosgi/service/Interface.h>
#include "../SitemapServiceImpl.h"
#include <hos/utils/SchemaLoader.h>
// libvariant
@@ -15,6 +17,22 @@
#include <atomic>
namespace std {
/**
* Specialization of std::hash for DeviceEventType.
*
* Required by std::map.
*/
template <>
class hash <hos::cosgi::service::sitemap::sitemaptemplate::SitemapEventType>{
public :
size_t operator()(const hos::cosgi::service::sitemap::sitemaptemplate::SitemapEventType& x) const {
return static_cast<size_t>(x);
}
};
} // namespace std
namespace hos {
namespace cosgi {
namespace service {
@@ -35,20 +53,37 @@ class DeviceImpl;
class SitemapTemplateImpl
: public virtual SitemapTemplate,
public implementation::Object {
public implementation::ObservableObject<SitemapEventType, SitemapEvent> {
private:
SitemapServiceImpl& parent;
std::string name;
std::string title;
std::shared_ptr<PageImpl> rootPage;
std::map<std::string, std::shared_ptr<PageImpl>> pages;
std::map<size_t, std::shared_ptr<ElementImpl>> pageElements;
std::string rootPageName;
std::multimap<std::string, std::weak_ptr<DeviceImpl>> devices;
public:
SitemapTemplateImpl(SitemapServiceImpl& parent, const std::string& title) : parent(parent), title(title) {}
SitemapTemplateImpl(SitemapServiceImpl& parent, const std::string& name, const std::string& title)
: parent(parent), name(name), title(title) {}
std::shared_ptr<PageImpl> createPage(const libvariant::Variant& param, PageImpl* parent = nullptr);
std::shared_ptr<DeviceImpl> createDevice(const libvariant::Variant& param);
virtual void notificationReceived(observable_type* o, const devicerepository::DeviceEventType& key, const devicerepository::DeviceEvent* e) noexcept override;
std::shared_ptr<PageImpl> addPage(const std::string& name, const libvariant::Variant& page);
bool setRootPageName(const std::string& name) noexcept;
std::shared_ptr<DeviceImpl> createDevice(const libvariant::Variant& param, const libvariant::Variant& commandTemplate, const std::string& commandValuePointer);
std::shared_ptr<ElementImpl> createElement(PageImpl& page, const libvariant::Variant& param, CompositeElementImpl* parent);
virtual SitemapService* getParent() noexcept override { return dynamic_cast<SitemapService*>(&parent); }
virtual std::weak_ptr<Page> getRootPage() noexcept override { return std::static_pointer_cast<Page>(rootPage); };
virtual size_t pageCount() noexcept override { return pages.size(); }
virtual std::weak_ptr<Page> getPage(const std::string& name) noexcept override;
virtual std::vector<std::weak_ptr<Page>> getPages(const std::string& name) noexcept override;
virtual std::weak_ptr<Page> getRootPage() noexcept override;
virtual const std::string& getRootPageName() noexcept override { return rootPageName; }
virtual std::weak_ptr<Element> getPageElement(size_t& uid) noexcept override;
virtual const std::string& getName() noexcept { return name; };
virtual libvariant::Variant renderSitemap() override;
};
@@ -61,18 +96,18 @@ class PageImpl
friend class AtomicElementImpl;
private:
SitemapTemplateImpl& sitemap;
PageImpl* parent;
std::string name;
std::string title;
std::vector<std::shared_ptr<ElementImpl>> elements;
public:
PageImpl(SitemapTemplateImpl& sitemap, const std::string& title, PageImpl* parent = nullptr)
: sitemap(sitemap), parent(parent), title(title) {}
PageImpl(SitemapTemplateImpl& sitemap, const std::string& name, const std::string& title)
: sitemap(sitemap), name(name), title(title) {}
virtual SitemapTemplate* getSiteMap() noexcept override { return static_cast<SitemapTemplate*>(&sitemap); }
virtual Page* getParent() noexcept override { return static_cast<Page*>(parent); }
virtual const std::string& getName() noexcept { return name; }
std::shared_ptr<ElementImpl> createElement(const libvariant::Variant& param, CompositeElementImpl* parent = nullptr);
std::shared_ptr<ElementImpl> addElement(const libvariant::Variant& param, CompositeElementImpl* parent = nullptr);
virtual size_t elementCount() noexcept override { return elements.size(); }
virtual std::weak_ptr<Element> getElement(size_t index) noexcept override
@@ -135,7 +170,7 @@ public:
CompositeElementImpl* parent = nullptr)
: ElementImpl(page, true, etype, typeStr, parent, label, icon) {}
std::shared_ptr<ElementImpl> createElement(const libvariant::Variant& param);
std::shared_ptr<ElementImpl> addElement(const libvariant::Variant& param);
virtual size_t elementCount() noexcept override { return elements.size(); }
virtual std::weak_ptr<Element> getElement(size_t index) noexcept override { return std::static_pointer_cast<Element>(elements.at(index)); }
@@ -153,7 +188,7 @@ protected:
std::string format;
std::shared_ptr<DeviceImpl> device;
libvariant::Variant additionalParams;
std::shared_ptr<PageImpl> linkedToPage;
std::string linkedPageName;
virtual libvariant::Variant _getDeviceValue();
@@ -163,12 +198,13 @@ public:
CompositeElementImpl* parent = nullptr)
: ElementImpl(page, false, etype, typeStr, parent, label, icon), format(format), device(device), additionalParams(additionalParams) {}
std::shared_ptr<PageImpl> createLinkedToPage(const libvariant::Variant& param);
bool setLinkedPage(const std::string& pagename) noexcept;
virtual const std::string& getFormat() noexcept override { return format; }
virtual std::weak_ptr<Device> getDevice() noexcept override;
virtual const libvariant::Variant& getAdditionalParams() noexcept override { return additionalParams; }
virtual std::weak_ptr<Page> getLinkedToPage() noexcept override { return linkedToPage; }
virtual std::weak_ptr<Page> getLinkedPage() noexcept override;
virtual const std::string& getLinkedPageName() noexcept override { return linkedPageName; }
virtual libvariant::Variant renderSitemap() override;
};
@@ -218,15 +254,24 @@ private:
std::string deviceID;
std::weak_ptr<devicerepository::Device> device;
std::string pointer;
std::weak_ptr<ElementImpl> parent;
libvariant::Variant commandTemplate;
std::string commandValuePointer;
public:
DeviceImpl(const std::string& deviceID, std::weak_ptr<devicerepository::Device> device, const std::string& pointer)
: deviceID(deviceID), device(device), pointer(pointer) {}
DeviceImpl(const std::string& deviceID, std::weak_ptr<devicerepository::Device> device, const std::string& pointer, const libvariant::Variant& commandTemplate, const std::string& commandValuePointer)
: deviceID(deviceID), device(device), pointer(pointer), commandTemplate(commandTemplate), commandValuePointer(commandValuePointer) {}
virtual std::weak_ptr<Element> getParent() noexcept override { return parent; }
void setParent(std::weak_ptr<ElementImpl> parent) noexcept { this->parent = parent; }
void resolveDevice() noexcept;
void setDevice(std::weak_ptr<devicerepository::Device> device) noexcept { this->device = device; }
virtual const std::string& getDeviceID() noexcept override { return deviceID; }
virtual std::weak_ptr<devicerepository::Device> getDevice() noexcept override { return device; }
virtual const std::string& getPointer() noexcept override { return pointer; }
virtual bool deviceExists() noexcept override { return !device.expired(); }
virtual void sendCommand(const libvariant::Variant& cmd) override;
};
@@ -85,14 +85,23 @@ void SitemapHandler::__handleInfo(Poco::Net::HTTPServerRequest &req, Poco::Net::
for (auto s : sitemaps) {
result.Append(s);
}
Variant message = Variant::Map();
message["sitemaps"] = result;
auto dsm = service->getDefaultSitemap();
if (!dsm.expired()) {
message["default"] = dsm.lock()->getName();
}
handleResponse(req, resp, form, result);
handleResponse(req, resp, form, message);
// Single sitemap mode
} else {
// Get sitemap
std::string sitemapName = pathPieces[matchPieces.size()];
auto sitemap = service->getSitemap(sitemapName);
if (sitemap.expired() && sitemapName.compare("default") == 0) {
sitemap = service->getDefaultSitemap();
}
if (!sitemap.expired()) {
// Get path from url and append pointer parameter
std::stringstream path;
@@ -108,6 +117,15 @@ void SitemapHandler::__handleInfo(Poco::Net::HTTPServerRequest &req, Poco::Net::
}
// Render sitemap
Variant result = sitemap.lock()->renderSitemap();
std::string pathStr = path.str();
if (!pathStr.empty()) {
if (result.HasPath(pathStr)) {
result = result.GetPath(pathStr);
} else {
throw webserver::webresource::WebResourceHandlerException((boost::format("Unknown path \"%1%\" for sitemap \"%2%\".") % path % sitemapName).str(),
Poco::Net::HTTPResponse::HTTPStatus::HTTP_NOT_FOUND);
}
}
// Return device info
handleResponse(req, resp, form, result);
} else {
@@ -119,14 +137,131 @@ void SitemapHandler::__handleInfo(Poco::Net::HTTPServerRequest &req, Poco::Net::
void SitemapHandler::__handleCommand(Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp, SitemapAPIMethod method, Net::HTMLForm& form) {
throw webserver::webresource::WebResourceHandlerException("ToDo. Implement that shit.",
Poco::Net::HTTPResponse::HTTPStatus::HTTP_NOT_IMPLEMENTED);
std::vector<std::string> pathPieces, matchPieces;
matchUri.getPathSegments(matchPieces);
Poco::URI uri(req.getURI());
uri.getPathSegments(pathPieces);
// No sitemap given, error
if (pathPieces.size() <= matchPieces.size()) {
throw webserver::webresource::WebResourceHandlerException("Missing sitemap.",
Poco::Net::HTTPResponse::HTTPStatus::HTTP_BAD_REQUEST);
}
// Get sitemap and do stuff
std::string sitemapName = pathPieces[matchPieces.size()];
std::weak_ptr<sitemaptemplate::SitemapTemplate> sitemap = service->getSitemap(sitemapName);
if (!sitemap.expired()) {
Variant result;
std::string msg;
if (form.has("value")) {
msg = form.get("value");
}
if (!msg.empty()) {
result = libvariant::DeserializeYAML(msg);
if (result.Contains("eventtype")) {
auto eventtype = result["eventtype"].AsString();
if (eventtype == "sitemap_element_command") {
auto uid = result["uid"].AsUnsigned();
auto value = result["value"];
auto pe = sitemap.lock()->getPageElement(uid);
if (!pe.expired()) {
auto spe = pe.lock();
if (!spe->isComposite()) {
auto ae = std::dynamic_pointer_cast<sitemaptemplate::AtomicElement>(spe);
auto dev = ae->getDevice();
if (!dev.expired()) {
dev.lock()->sendCommand(value);
handleResponse(req, resp, form, std::string("OK: Command executed"));
} else {
throw webserver::webresource::WebResourceHandlerException("No device associated with element.",
Poco::Net::HTTPResponse::HTTPStatus::HTTP_INTERNAL_SERVER_ERROR);
}
} else {
throw webserver::webresource::WebResourceHandlerException("Cannot send a command to a composite element.",
Poco::Net::HTTPResponse::HTTPStatus::HTTP_BAD_REQUEST);
}
} else {
throw webserver::webresource::WebResourceHandlerException((boost::format("Couldn't find element uid \"%1%\"") % uid).str(),
Poco::Net::HTTPResponse::HTTPStatus::HTTP_NOT_FOUND);
}
} else {
throw webserver::webresource::WebResourceHandlerException((boost::format("Unknown eventtype \"%1%\"") % eventtype).str(),
Poco::Net::HTTPResponse::HTTPStatus::HTTP_BAD_REQUEST);
}
} else {
throw webserver::webresource::WebResourceHandlerException("Missing eventtype.",
Poco::Net::HTTPResponse::HTTPStatus::HTTP_BAD_REQUEST);
}
} else {
throw webserver::webresource::WebResourceHandlerException("Missing msg.",
Poco::Net::HTTPResponse::HTTPStatus::HTTP_BAD_REQUEST);
}
} else {
throw webserver::webresource::WebResourceHandlerException((boost::format("Could not find sitemap \"%1%\"") % sitemapName).str(),
Poco::Net::HTTPResponse::HTTPStatus::HTTP_NOT_FOUND);
}
}
void SitemapHandler::__handleEvent(Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp, Net::HTMLForm& form, bool websocket) {
throw webserver::webresource::WebResourceHandlerException("ToDo. Implement that shit.",
Poco::Net::HTTPResponse::HTTPStatus::HTTP_NOT_IMPLEMENTED);
std::vector<std::string> pathPieces, matchPieces;
matchUri.getPathSegments(matchPieces);
Poco::URI uri(req.getURI());
uri.getPathSegments(pathPieces);
// No sitemap given, error
if (pathPieces.size() <= matchPieces.size()) {
throw webserver::webresource::WebResourceHandlerException("Missing sitemap.",
Poco::Net::HTTPResponse::HTTPStatus::HTTP_BAD_REQUEST);
}
// Get sitemap and do stuff
std::string sitemapName = pathPieces[matchPieces.size()];
std::weak_ptr<sitemaptemplate::SitemapTemplate> sitemap = service->getSitemap(sitemapName);
if (!sitemap.expired()) {
// Get client ID
std::string clientID;
if (form.has("clientid")) {
clientID = form.get("clientid");
} else if (req.has("Client-Id")) {
clientID = req.get("Client-Id");
} else if (req.has("X-Client-Id")) {
clientID = req.get("X-Client-Id");
}
// Get path from url and append pointer parameter
std::stringstream path;
for (int i = matchPieces.size() + 2; i < pathPieces.size(); i++) {
path << "/" << pathPieces[i];
}
std::string pointer;
if (getPointerParameter(pointer, form)) {
if (pointer.size() > 0 && pointer[0] != '/') {
path << "/";
}
path << pointer;
}
// Get event queue
auto eventQueue = parent->getEventQueue(clientID, service);
if (!eventQueue->setHandler(this, &req, &resp)) {
throw webserver::webresource::WebResourceHandlerException((boost::format("Client id \"%1%\" already in use.") % clientID).str(),
Poco::Net::HTTPResponse::HTTPStatus::HTTP_BAD_REQUEST);
}
std::cout << "Got EventQueue \"" << eventQueue->clientID << "\"" << std::endl;
eventQueue->checkRegistration(sitemaptemplate::SitemapEventType::ALL_EVENTS, sitemap.lock().get());
if (websocket) {
__handleEventWebSocket(req, resp, form, eventQueue.get(), parent);
} else {
__handleEventLongPolling(req, resp, form, eventQueue.get(), parent);
}
eventQueue->unsetHandler();
} else {
throw webserver::webresource::WebResourceHandlerException((boost::format("Could not find sitemap \"%1%\"") % sitemapName).str(),
Poco::Net::HTTPResponse::HTTPStatus::HTTP_NOT_FOUND);
}
/*throw webserver::webresource::WebResourceHandlerException("ToDo. Implement that shit.",
Poco::Net::HTTPResponse::HTTPStatus::HTTP_NOT_IMPLEMENTED);*/
}
@@ -160,6 +295,62 @@ SitemapAPIMethod SitemapHandler::getSitemapAPIMethod(Net::HTTPServerRequest &req
void SitemapEventQueue::checkRegistration(sitemaptemplate::SitemapEventType eventType, sitemaptemplate::SitemapTemplate* sitemap) {
if (!registeredToEvent || eventType != this->eventType || sitemap != this->sitemap) {
unregisterEvents();
registerEvents(eventType, sitemap);
}
}
void SitemapEventQueue::registerEvents(sitemaptemplate::SitemapEventType eventType, sitemaptemplate::SitemapTemplate* sitemap) {
std::lock_guard<std::mutex> lock(_mutex);
if (!registeredToEvent) {
std::cout << "SitemapEventQueue::registerEvents" << std::endl;
sitemap->addObserver(eventType, this);
this->sitemap = sitemap;
this->eventType = eventType;
registeredToEvent = true;
}
}
void SitemapEventQueue::unregisterEvents() {
std::lock_guard<std::mutex> lock(_mutex);
if (registeredToEvent) {
std::cout << "SitemapEventQueue::unregisterEvents" << std::endl;
if (sitemap) {
sitemap->deleteObserver(this);
}
registeredToEvent = false;
}
}
void SitemapEventQueue::notificationReceived( interface::Observer<sitemaptemplate::SitemapEventType, sitemaptemplate::SitemapEvent>::observable_type* o,
const sitemaptemplate::SitemapEventType& key,
const sitemaptemplate::SitemapEvent* e) noexcept {
Variant event;
switch (key) {
case sitemaptemplate::SitemapEventType::SITEMAP_INVALIDATED:
event["eventtype"] = "sitemap_invalidated";
break;
case sitemaptemplate::SitemapEventType::SITEMAP_ELEMENT_CHANGED:
{
const sitemaptemplate::SitemapElementChangedEvent* event2 = static_cast<const sitemaptemplate::SitemapElementChangedEvent*>(e);
event["eventtype"] = "sitemap_element_changed";
auto elements = Variant::List();
for (auto el : event2->elements) {
elements.push_back(el.lock()->renderSitemap());
}
event["elements"] = elements;
}
break;
default:
break;
}
if (!event.IsNull()) {
pushEvent(event);
}
}
} // namespace webresource
} // namespace sitemap
} // namespace service
@@ -6,6 +6,7 @@
#include <hos/cosgi/service/webserver/WebServerService.h>
#include <hos/cosgi/service/webserver/webresource/WebResourceHandlerImpl.h>
#include <hos/cosgi/service/sitemap/SitemapService.h>
#include <hos/cosgi/service/sitemap/sitemap_template/SitemapTemplate.h>
#include <hos/utils/FunctionTable.h>
#include <Poco/Net/HTTPRequestHandler.h>
#include <Variant/Variant.h>
@@ -40,8 +41,42 @@ enum class SitemapAPIMethod {
};
typedef webserver::webresource::WebResourceEventQueue<
interface::Observer<sitemaptemplate::SitemapEventType, sitemaptemplate::SitemapEvent>,
SitemapHandlerFactory,
SitemapHandler
> SitemapEventQueueBase;
class SitemapEventQueue
: public SitemapEventQueueBase {
public:
sitemaptemplate::SitemapEventType eventType = sitemaptemplate::SitemapEventType::ALL_EVENTS;
SitemapService* const sitemapService;
sitemaptemplate::SitemapTemplate* sitemap = nullptr;
SitemapEventQueue( const std::string& clientID,
SitemapHandlerFactory* handlerFactory,
SitemapService* sitemapService )
: WebResourceEventQueue(clientID, handlerFactory), sitemapService(sitemapService) {}
virtual ~SitemapEventQueue() {
unregisterEvents();
}
void checkRegistration(sitemaptemplate::SitemapEventType eventType, sitemaptemplate::SitemapTemplate* sitemap);
void registerEvents(sitemaptemplate::SitemapEventType eventType, sitemaptemplate::SitemapTemplate* sitemap = nullptr);
void unregisterEvents();
virtual void notificationReceived( interface::Observer<sitemaptemplate::SitemapEventType, sitemaptemplate::SitemapEvent>::observable_type* o,
const sitemaptemplate::SitemapEventType& key,
const sitemaptemplate::SitemapEvent* e) noexcept override;
};
class SitemapHandlerFactory
: public webserver::webresource::WebResourceHandlerFactoryImpl<SitemapHandlerFactory, false> {
: public webserver::webresource::WebResourceHandlerFactoryImpl<SitemapHandlerFactory, false, SitemapEventQueue> {
private:
SitemapService* service;
@@ -0,0 +1,68 @@
define([
"dojo/_base/config",
'dojo/_base/declare',
"dojo/Deferred",
"dojo/dom",
"dojo/dom-construct",
"dijit/layout/ContentPane",
"hos/sitemap/PageElementFactory"
], function(config, declare, Deferred, dom, domConstruct, ContentPane, PageElementFactory) {
/*
* Class representing a page.
*/
var PageClass = declare(null, {
/*
* constructor.
*
* sitemap: parent sitemap.
*
* name: page name.
*
* data: page json object received from server
*/
constructor: function(/*hos/sitemap/Sitemap*/ sitemap, /*string*/ name, /*json object*/ data) {
this.sitemap = sitemap;
this.name = name;
this.title = data["title"];
this.elements = [];
var _this = this;
data["elements"].forEach(function(data) {
var element = PageElementFactory.createPageElement(_this, data);
_this.elements.push(element);
_this.sitemap.pageElements[element.uid] = element;
});
},
/* returns string representation for debugging. */
toString: function() {
var str = "Page <b>\"" + this.name + "\"<b> {<br> \n";
this.elements.forEach(function(e) {
str += e.toString() + "<br> \n";
});
str += "} ";
return str;
},
/*
* Build the DOM.
*/
buildDom: function() {
var nodes = []
this.elements.forEach(function(e) {
nodes.push(e.domNode);
});
this.widget = new ContentPane({
title: this.title,
id: this.name,
content: nodes,
style: "height: 100%; width: 100%;"
});
this.domNode = this.widget.domNode;
}
});
return PageClass;
});
@@ -0,0 +1,56 @@
define([
"dojo/_base/config",
'dojo/_base/declare',
"dojo/Deferred",
"dojo/dom",
"dojo/dom-construct",
"hos/sitemap/pageelements/FrameElement",
"hos/sitemap/pageelements/TextElement",
"hos/sitemap/pageelements/SwitchElement",
"hos/sitemap/pageelements/SliderElement",
"hos/sitemap/pageelements/ErrorElement",
], function(
config,
declare,
Deferred,
dom,
domConstruct,
FrameElement,
TextElement,
SwitchElement,
SliderElement,
ErrorElement
) {
/*
* Class representing a page element factory.
*/
var ElementFactoryClass = declare(null, {
/*
* Creates and returns a page element.
*/
createPageElement: function(/*hos/sitemap/Page*/ page, /*json object*/ data, /*hos/sitemap/pageelements/CompositeElement*/ parent) {
if (typeof(parent)==='undefined') parent = null;
var element = null;
if (data["type"] == "sitemap/sitemap#/definitions/frame_element") {
element = new FrameElement(page, data, this, parent);
} else if (data["type"] == "sitemap/sitemap#/definitions/text_element") {
element = new TextElement(page, data, this, parent);
} else if (data["type"] == "sitemap/sitemap#/definitions/switch_element") {
element = new SwitchElement(page, data, this, parent);
} else if (data["type"] == "sitemap/sitemap#/definitions/slider_element") {
element = new SliderElement(page, data, this, parent);
} else {
element = new ErrorElement(page, data, this, parent);
}
element.buildDom();
return element;
}
});
/* Create and return factory instance. */
var factory = new ElementFactoryClass();
return factory;
});
@@ -0,0 +1,213 @@
define([
"dojo/_base/config",
'dojo/_base/declare',
'dojo/_base/lang',
"dojo/dom",
"dojo/dom-construct",
"dojo/Deferred",
"dojox/uuid/generateRandomUuid",
"hos/sitemap/Page",
"hos/sitemap/widgets/SitemapWidget",
"dojox/socket",
"dojox/socket/Reconnect",
"dojo/request/xhr"
], function(
config,
declare,
lang,
dom,
domConstruct,
Deferred,
generateRandomUuid,
Page,
SitemapWidget,
Socket,
Reconnect,
xhr
) {
/*
* Class representing a sitemap.
*/
var SitemapClass = declare(null, {
/*
* Constructor.
*
* api: parent API.
*
* name: sitemap name.
*
* data: sitemap json object received from server
*/
constructor: function(/*hos.sitemap.SitemapAPI*/ api, /*string*/ name, /*json object*/ data) {
this.api = api;
this.name = name;
// parse json data
this.rootPageName = data["rootpage"];
this.title = data["title"];
// Create pages
this.pages = {}
var _pages = data["pages"];
for (var _pageName in _pages) {
if (_pages.hasOwnProperty(_pageName)) {
var _page = new Page(this, _pageName, _pages[_pageName]);
this.pages[_pageName] = _page
}
}
this.buildDom();
},
/*
* Dictionary containing all page elements.
*/
pageElements: {},
/*
* Stack containing name of visited pages.
* Needed for bread crumbs and back button.
*/
pageNameStack: [],
/* returns string representation for debugging. */
toString: function() {
var str = "Sitemap <b>\"" + this.name + "\"<b> {<br> \n";
for (var pageName in this.pages) {
str += this.pages[pageName].toString() + "<br> \n";
}
str += "} ";
return str;
},
/*
* Build the DOM.
*/
buildDom: function() {
this.widget = new SitemapWidget({sitemap: this});
for (var pageName in this.pages) {
var page = this.pages[pageName];
page.buildDom();
this.widget.stackContainer.addChild(page.widget);
}
this.domNode = this.widget.domNode;
},
/*
* Startup function.
*/
startup: function() {
this.widget.startup();
this.goToRootPage();
this.enableUpdates();
},
/*
* Update the bread crumbs in the ui.
*/
updateBreadCrumbs: function() {
var str = "> " + this.rootPageName;
this.pageNameStack.forEach(function(e) {
str += " > " + e;
});
this.widget.setBreadCrumbs(str);
},
/*
* Go to page with given name.
*/
goToPage: function(/*string*/ name) {
this.pageNameStack.push(name);
this.widget.stackContainer.selectChild(name, true);
this.updateBreadCrumbs();
},
/*
* Go to root page.
*/
goToRootPage: function() {
this.pageNameStack = [];
this.widget.stackContainer.selectChild(this.rootPageName);
this.updateBreadCrumbs();
},
/*
* Go one page back.
*/
goBack: function() {
if (this.pageNameStack.length > 0) {
this.pageNameStack.pop();
if (this.pageNameStack.length > 0) {
this.goToPage(this.pageNameStack.pop());
} else {
this.goToRootPage();
}
return true;
} else {
return false;
}
},
/*
* Connect to server and listen for updates.
*/
enableUpdates: function() {
this._updateSocketOpened = false;
this._updateSocketClientId = generateRandomUuid();
this._updateSocket = Reconnect(new Socket({
url: config.hos.sitemapUrl + "/" + this.name + '?onevent=all&clientid=' + this._updateSocketClientId,
headers: {
"On-Event": "changes",
/*"Connection-Timeout": 60000,*/
"Client-Id": 1234
},
transport: xhr.post
}),{});
this._updateSocket.on("open", lang.hitch(this, function(event){
this._socketOpened = true;
console.log("Socket Opened ", event.message);
}));
this._updateSocket.on("close", lang.hitch(this, function(event){
this._socketOpened = false;
console.log("Socket Closed ", event.message);
}));
this._updateSocket.on("error", lang.hitch(this, function(event){
this._socketOpened = false;
console.log("Socket Error ", event.message);
}));
this._updateSocket.on("message", lang.hitch(this, function(event) {
var data = JSON.parse(event.data);
console.log("Message: " + event);
//console.log("Message: " + event.data);
if (data && data["eventtype"] == "sitemap_element_changed") {
var elements = data["elements"];
elements.forEach(lang.hitch(this, function(e) {
var uid = e["uid"];
if (uid in this.pageElements) {
var el = this.pageElements[uid];
el.updateData(e);
}
}));
}
}));
},
/*
* Send command.
*/
sendPageElementCommand: function(uid, value) {
if (this._updateSocket) {
var msg = {eventtype: "sitemap_element_command", uid: uid, value: value};
xhr.post(config.hos.sitemapUrl + "/" + this.name, {
data: {method: 'command', value: JSON.stringify(msg)}
}).then(function(data){
console.log("sendPageElementCommand ok: " + data)
}, function(err){
console.log("sendPageElementCommand error: " + err)
});
}
}
});
return SitemapClass;
});
@@ -0,0 +1,98 @@
define([
"dojo/_base/config",
'dojo/_base/declare',
"dojo/request/xhr",
"dojo/Deferred",
"hos/sitemap/Sitemap"
], function(config, declare, xhr, Deferred, Sitemap) {
/*
* Class representing the sitemap API.
*/
var ApiClass = declare(null, {
/*
* Constructor.
*/
constructor: function(/*string*/ baseUrl) {
this.baseUrl = baseUrl;
},
/*
* Returns name of default sitemap.
*
* returns: Promise(string)
*/
getDefaultSitemapName: function() {
var deferred = new Deferred();
xhr.get(this.baseUrl, {
handleAs: "json"
}).then(function(data){
deferred.resolve(data["default"]);
}, function(err){
deferred.reject(err);
});
return deferred;
},
/*
* Returns an array containing the names of all available sitemaps.
*
* returns: Promise(array(string))
*/
getSitemapNames: function() {
var deferred = new Deferred();
xhr.get(this.baseUrl, {
handleAs: "json"
}).then(function(data){
deferred.resolve(data["sitemaps"]);
}, function(err){
deferred.reject(err);
});
return deferred;
},
/*
* Returns the sitemap with given name.
*
* returns: Promise(hos.sitemap.Sitemap)
*/
getSitemap: function(/*string*/ name) {
var deferred = new Deferred();
var _this = this
xhr.get(this.baseUrl + "/" + name, {
handleAs: "json"
}).then(function(data){
var sitemap = new Sitemap(_this, name, data)
deferred.resolve(sitemap);
}, function(err){
deferred.reject(err);
});
return deferred;
},
/*
* Returns the default sitemap.
*
* returns: Promise(hos.sitemap.Sitemap)
*/
getDefaultSitemap: function() {
var deferred = new Deferred();
var _this = this
this.getDefaultSitemapName().then(function(name){
_this.getSitemap(name).then(function(sitemap){
deferred.resolve(sitemap);
}, function(err){
deferred.reject(err);
});
}, function(err){
deferred.reject(err);
});
return deferred;
}
});
/* Create and returns API instance. */
var api = new ApiClass(config.hos.sitemapUrl);
return api;
});
@@ -0,0 +1,47 @@
define([
"dojo/_base/config",
'dojo/_base/declare',
"dojo/Deferred",
"dojo/dom",
"dojo/dom-construct",
"hos/sitemap/pageelements/ElementBase"
], function(config, declare, Deferred, dom, domConstruct, ElementBase) {
return declare([ElementBase], {
/*
* constructor.
*
* page: parent page.
*
* data: page element json object received from server
*
* factory: reference to page element factory
*
* parent: parent page element
*/
constructor: function(/*hos.sitemap.Page*/ page, /*json object*/ data, /*hos.sitemap.PageElementFactory*/ factory, /*hos/sitemap/pageelements/CompositeElement*/ parent) {
this.linkedPageName = data["linkedPage"];
this.value = data["value"];
},
/*
* Go to linked page.
*/
goToLinkedPage: function() {
if (this.linkedPageName) {
this.page.sitemap.goToPage(this.linkedPageName);
}
},
/*
* Update data.
*/
updateData: function(data) {
this.inherited(arguments);
this.value = data["value"];
}
});
});
@@ -0,0 +1,34 @@
define([
"dojo/_base/config",
'dojo/_base/declare',
"dojo/Deferred",
"dojo/dom",
"dojo/dom-construct",
"hos/sitemap/pageelements/ElementBase"
], function(config, declare, Deferred, dom, domConstruct, ElementBase) {
return declare([ElementBase], {
/*
* constructor.
*
* page: parent page.
*
* data: page element json object received from server
*
* factory: reference to page element factory
*
* parent: parent page element
*/
constructor: function(/*hos.sitemap.Page*/ page, /*json object*/ data, /*hos.sitemap.PageElementFactory*/ factory, /*hos/sitemap/pageelements/CompositeElement*/ parent) {
this.elements = []
var _this = this;
data["elements"].forEach(function(data) {
var element = factory.createPageElement(page, data, _this)
_this.page.sitemap.pageElements[element.uid] = element;
_this.elements.push(element)
});
}
});
});
@@ -0,0 +1,58 @@
define([
"dojo/_base/config",
'dojo/_base/declare',
"dojo/Deferred",
"dojo/dom",
"dojo/dom-construct"
], function(config, declare, Deferred, dom, domConstruct) {
return declare(null, {
/*
* constructor.
*
* page: parent page.
*
* data: page element json object received from server
*
* factory: reference to page element factory
*
* parent: parent page element
*/
constructor: function(/*hos.sitemap.Page*/ page, /*json object*/ data, /*hos.sitemap.PageElementFactory*/ factory, /*hos/sitemap/pageelements/CompositeElement*/ parent) {
this.page = page;
this.parent = parent;
this.factory = factory;
this.uid = data["uid"];
this.type = data["type"];
this.icon = data["icon"];
this.label = data["label"];
},
/* returns string representation for debugging. */
toString: function() {
return " <b>**Unknown(" + this.uid + ")**</b> ";
},
/*
* Build the DOM.
*/
buildDom: function() {
this.domNode = domConstruct.toDom(this.toString());
},
/*
* Update data.
*/
updateData: function(data) {
this.label = data["label"];
},
/*
* Update DOM.
*/
updateDom: function() {
}
});
});
@@ -0,0 +1,20 @@
define([
"dojo/_base/config",
'dojo/_base/declare',
"dojo/Deferred",
"dojo/dom",
"dojo/dom-construct",
"hos/sitemap/pageelements/AtomicElement"
], function(config, declare, Deferred, dom, domConstruct, AtomicElement) {
return declare([AtomicElement], {
/* returns string representation for debugging. */
toString: function() {
var str = " <b>ErrorElement (" + this.uid + ")</b> ";
return str;
}
});
});
@@ -0,0 +1,40 @@
define([
"dojo/_base/config",
'dojo/_base/declare',
"dojo/Deferred",
"dojo/dom",
"dojo/dom-construct",
"hos/sitemap/pageelements/CompositeElement",
"dijit/TitlePane"
], function(config, declare, Deferred, dom, domConstruct, CompositeElement, TitlePane) {
return declare([CompositeElement], {
/* returns string representation for debugging. */
toString: function() {
var str = " <b>FrameElement (" + this.uid + ")</b>{<br> \n";
this.elements.forEach(function(e) {
str += e.toString() + "<br> \n";
});
str += "} ";
return str;
},
/*
* Build the DOM.
*/
buildDom: function() {
var nodes = []
this.elements.forEach(function(e) {
nodes.push(e.domNode);
});
this.widget = new TitlePane({
title:this.label,
content:nodes
});
this.domNode = this.widget.domNode
}
});
});
@@ -0,0 +1,100 @@
define([
"dojo/_base/config",
'dojo/_base/declare',
'dojo/_base/lang',
"dojo/Deferred",
"dojo/dom",
"dojo/dom-construct",
"hos/sitemap/pageelements/AtomicElement",
"hos/sitemap/pageelements/widgets/ElementWidget",
"dojox/mobile/Slider"
], function(config, declare, lang, Deferred, dom, domConstruct, AtomicElement, ElementWidget, Slider) {
return declare([AtomicElement], {
/*
* constructor.
*
* page: parent page.
*
* data: page element json object received from server
*
* factory: reference to page element factory
*
* parent: parent page element
*/
constructor: function(/*hos.sitemap.Page*/ page, /*json object*/ data, /*hos.sitemap.PageElementFactory*/ factory, /*hos/sitemap/pageelements/CompositeElement*/ parent) {
this.min = data["min"];
this.max = data["max"];
},
/* returns string representation for debugging. */
toString: function() {
var str = " <b>SliderElement (" + this.uid + ")</b> ";
return str;
},
/*
* Set Slider value.
*/
setSliderValue: function(value) {
this.value = value/100;
this.page.sitemap.sendPageElementCommand(this.uid, this.value);
},
/*
* Build the DOM.
*/
buildDom: function() {
this.widget = new ElementWidget({baseClass: "SliderElementWidget"});
/*this.sliderWidget = HorizontalSlider({
minimum: this.min,
maximum: this.max,
value: this.value,
onChange: lang.hitch(this, this.setSliderValue)
});*/
this.sliderWidget = Slider({
minimum: this.min*100,
maximum: this.max*100,
value: this.value*100,
style: "width: 95%;",
onChange: lang.hitch(this, this.setSliderValue)
});
this.widget.setValue(this.sliderWidget.domNode);
this.updateDom();
this.domNode = this.widget.domNode;
},
/*
* Update data.
*/
updateData: function(data) {
this.inherited(arguments);
this.updateDom();
},
/*
* Update DOM.
*/
updateDom: function() {
if (typeof(this.linkedPageName) != "undefined") {
var tmp = domConstruct.create("a",{
href: "javascript:void(0)",
innerHTML: "<b>" + this.label + "</b>",
onclick: lang.hitch(this, this.goToLinkedPage)
});
this.widget.setLabel(tmp);
var tmp2 = domConstruct.create("div",{
innerHTML: " &gt;&gt; ",
onclick: lang.hitch(this, this.goToLinkedPage)
});
this.widget.setLink(tmp2);
} else {
this.widget.setLabel(domConstruct.toDom("<b>" + this.label + "</b>"));
}
this.sliderWidget.set("value", this.value*100);
}
});
});
@@ -0,0 +1,84 @@
define([
"dojo/_base/config",
"dojo/_base/lang",
"dojo/_base/declare",
"dojo/Deferred",
"dojo/dom",
"dojo/dom-construct",
"hos/sitemap/pageelements/AtomicElement",
"hos/sitemap/pageelements/widgets/ElementWidget",
"dojox/mobile/Switch"
], function(config, lang, declare, Deferred, dom, domConstruct, AtomicElement, ElementWidget, Switch) {
return declare([AtomicElement], {
/* returns string representation for debugging. */
toString: function() {
var str = " <b>SwitchElement (" + this.uid + ")</b> ";
return str;
},
/*
* Build the DOM.
*/
buildDom: function() {
this.widget = new ElementWidget({baseClass: "SwitchElementWidget"});
this.buttonWidget = Switch({
});
this.buttonWidget.on("stateChanged", lang.hitch(this, this.setSwitch));
this.widget.setValue(this.buttonWidget.domNode);
this.updateDom();
this.domNode = this.widget.domNode;
},
/*
* Toggle switch.
*/
toggleSwitch: function() {
this.value = !this.value;
this.updateDom();
this.page.sitemap.sendPageElementCommand(this.uid, this.value);
},
/*
* Set switch.
*/
setSwitch: function(value) {
this.value = (value == "on");
this.updateDom();
this.page.sitemap.sendPageElementCommand(this.uid, this.value);
},
/*
* Update data.
*/
updateData: function(data) {
this.inherited(arguments);
this.updateDom();
},
/*
* Update DOM.
*/
updateDom: function() {
if (typeof(this.linkedPageName) != "undefined") {
var tmp = domConstruct.create("a",{
href: "javascript:void(0)",
innerHTML: "<b>" + this.label + "</b>",
onclick: lang.hitch(this, this.goToLinkedPage)
});
this.widget.setLabel(tmp);
var tmp2 = domConstruct.create("div",{
innerHTML: " &gt;&gt; ",
onclick: lang.hitch(this, this.goToLinkedPage)
});
this.widget.setLink(tmp2);
} else {
this.widget.setLabel(domConstruct.toDom("<b>" + this.label + "</b>"));
}
this.buttonWidget.set("value", this.value ? "on" : "off");
}
});
});
@@ -0,0 +1,61 @@
define([
"dojo/_base/config",
'dojo/_base/declare',
"dojo/_base/lang",
"dojo/Deferred",
"dojo/dom",
"dojo/dom-construct",
"hos/sitemap/pageelements/AtomicElement",
"hos/sitemap/pageelements/widgets/ElementWidget"
], function(config, declare, lang, Deferred, dom, domConstruct, AtomicElement, ElementWidget) {
return declare([AtomicElement], {
/* returns string representation for debugging. */
toString: function() {
var str = " <b>TextElement (" + this.uid + ")</b> ";
return str;
},
/*
* Build the DOM.
*/
buildDom: function() {
this.widget= new ElementWidget({baseClass: "TextElementWidget"});
this.updateDom();
this.domNode = this.widget.domNode;
},
/*
* Update data.
*/
updateData: function(data) {
this.inherited(arguments);
this.updateDom();
},
/*
* Update DOM.
*/
updateDom: function() {
if (typeof(this.linkedPageName) != "undefined") {
var tmp = domConstruct.create("a",{
href: "javascript:void(0)",
innerHTML: "<b>" + this.label + "</b>",
onclick: lang.hitch(this, this.goToLinkedPage)
});
this.widget.setLabel(tmp);
var tmp2 = domConstruct.create("div",{
innerHTML: " &gt;&gt; ",
onclick: lang.hitch(this, this.goToLinkedPage)
});
this.widget.setLink(tmp2);
} else {
this.widget.setLabel(domConstruct.toDom("<b>" + this.label + "</b>"));
}
this.widget.setValue(domConstruct.toDom("<b>" + this.value + "</b>"));
}
});
});
@@ -0,0 +1,31 @@
define([
"dojo/_base/declare",
"dojo/dom",
"dojo/dom-construct",
"dijit/_WidgetBase",
"dijit/_TemplatedMixin",
"dojo/text!./templates/ElementWidget.html"
], function(declare, dom, domConstruct, _WidgetBase, _TemplatedMixin, template) {
return declare([_WidgetBase, _TemplatedMixin], {
templateString: template,
setLabel: function(label) {
domConstruct.empty(this.domLabelNode);
domConstruct.place(label, this.domLabelNode);
},
setValue: function(value) {
domConstruct.empty(this.domValueNode);
domConstruct.place(value, this.domValueNode);
},
setLink: function(link) {
domConstruct.empty(this.domLinkNode);
domConstruct.place(link, this.domLinkNode);
}
});
});
@@ -0,0 +1,10 @@
<div class="${baseClass}" data-dojo-attach-point="domNode" style="background-color: #fcfcfc;">
<table class="${baseClass}Table" data-dojo-attach-point="domTableNode" style="width:100%;">
<tr>
<td class="${baseClass}Icon" data-dojo-attach-point="domIconNode"></td>
<td class="${baseClass}Label" data-dojo-attach-point="domLabelNode" style="width: 50%;"></td>
<td class="${baseClass}Value" data-dojo-attach-point="domValueNode" style="text-align: right; width: 50%;"></td>
<td class="${baseClass}Link" data-dojo-attach-point="domLinkNode"></td>
</tr>
</table>
</div>
@@ -0,0 +1,86 @@
define([
"dojo/_base/declare",
"dojo/_base/lang",
"dojo/dom",
"dojo/dom-construct",
"dijit/_WidgetBase",
"dijit/layout/BorderContainer",
"dijit/layout/StackContainer",
"dijit/layout/ContentPane",
"dijit/form/Button"
], function(declare, lang, dom, domConstruct, _WidgetBase, BorderContainer, StackContainer, ContentPane, Button) {
return declare([_WidgetBase], {
/*
* buildRendering function.
*/
buildRendering: function() {
if(!this._rendered){
// create a BorderContainer as the top widget in the hierarchy
this.bc = new BorderContainer({
style: "width: 100%; height: 100%;"
});
// create a ContentPane as the left pane in the BorderContainer
/*this.cp1 = new ContentPane({
region: "left",
splitter: true,
style: "width: 150px;",
content: "<h4>Sitemap/Navigation</h4><ul><li>ToDo</li></ul>"
});
this.bc.addChild(this.cp1);*/
this.stackContainer = StackContainer();
this.backButton = new Button({
label: "Back",
onClick: lang.hitch(this, function(){
this.sitemap.goBack();
})
});
this.homeButton = new Button({
label: "Home",
onClick: lang.hitch(this, function(){
this.sitemap.goToRootPage();
})
});
// create a ContentPane as the center pane in the BorderContainer
this.cp2 = new ContentPane({
region: "center",
content: [this.stackContainer.domNode, this.backButton.domNode, this.homeButton.domNode]
});
this.bc.addChild(this.cp2);
var contentNode = domConstruct.toDom("<b>Home Operating System:</b> <div id=\"breadCrumbNode\">bread crumbs here</div>");
this.breadCrumbNode = dom.byId("breadCrumbNode", contentNode)
// create a ContentPane as the top pane in the BorderContainer
this.cp3 = new ContentPane({
region: "top",
content: contentNode
});
this.bc.addChild(this.cp3);
this.domNode = this.bc.domNode;
}
this.inherited(arguments);
this._rendered = true;
},
/*
* startup function.
*/
startup: function() {
this.bc.startup();
},
/*
* Set bread crumbs.
*/
setBreadCrumbs: function(/*string*/ text) {
this.breadCrumbNode.innerHTML=text
}
});
});
@@ -1,6 +0,0 @@
<?php
echo 'Hello World! Running on "'.php_uname().'" and time is '.date('Y-m-d H:i:s')."\n";
echo "debug1\n";
require_once('../index.php');
echo "debug2\n";
?>
@@ -0,0 +1,33 @@
html, body {
height: 100%;
margin: 0;
overflow: hidden;
padding: 0;
}
#appLayout {
height: 100%;
}
#leftCol {
width: 14em;
}
.claro .demoLayout .edgePanel {
background-color: #d0e9fc;
}
#viewsChart {
width: 550px;
height: 550px;
}
.claro .dijitToggleButtonChecked .dijitButtonNode {
background-color: #0090ff;
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2) inset;
transition-duration: 0.1s;
}
@@ -0,0 +1,65 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>Sitemap Test 01</title>
<link rel="stylesheet" href="libs/dijit/themes/claro/claro.css" media="screen">
<link rel="stylesheet" href="libs/dojox/mobile/themes/ios7/ios7.css" media="screen">
<link rel="stylesheet" href="sitemap01.css" media="screen">
<!-- configure Dojo -->
<script>
var dojoConfig = {
async: true,
parseOnLoad: true,
isDebug: true,
deps: [
"dojo/parser",
"dijit/layout/BorderContainer",
"dijit/layout/TabContainer",
"dijit/layout/ContentPane",
"dijit/Calendar",
"dijit/Fieldset",
"dijit/TitlePane",
"dijit/form/HorizontalSlider",
"dijit/form/HorizontalRule",
"dijit/form/HorizontalRuleLabels"
],
hos: {
deviceRepoUrl: "/hos/api/devices",
sitemapUrl: "/hos/api/sitemap"
}
};
</script>
<!-- load Dojo -->
<script src="libs/dojo/dojo.js"
data-dojo-config="async: true"></script>
</head>
<body class="claro">
<script>
require([
"hos/sitemap/SitemapAPI",
"dojo/dom-construct",
"dojo/dom",
"dojo/domReady!"
], function(sitemapAPI, domConstruct, dom) {
sitemapAPI.getDefaultSitemap().then(function(sitemap) {
domConstruct.place(sitemap.domNode, "contentPane");
sitemap.startup();
}, function(error) {
var node = domConstruct.toDom("<b>Error: " + error + "</b>");
domConstruct.place(node, "contentPane");
});
});
</script>
<div id="contentPane" style="width: 100%; height: 100%;"></div>
</body>
</html>
@@ -0,0 +1,65 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>Sitemap Test 01</title>
<link rel="stylesheet" href="libs/dijit/themes/claro/claro.css" media="screen">
<link rel="stylesheet" href="libs/dojox/mobile/themes/ios7/ios7.css" media="screen">
<link rel="stylesheet" href="sitemap01.css" media="screen">
<!-- configure Dojo -->
<script>
var dojoConfig = {
async: true,
parseOnLoad: true,
isDebug: true,
deps: [
"dojo/parser",
"dijit/layout/BorderContainer",
"dijit/layout/TabContainer",
"dijit/layout/ContentPane",
"dijit/Calendar",
"dijit/Fieldset",
"dijit/TitlePane",
"dijit/form/HorizontalSlider",
"dijit/form/HorizontalRule",
"dijit/form/HorizontalRuleLabels"
],
hos: {
deviceRepoUrl: "/hos/api/devices",
sitemapUrl: "/hos/api/sitemap"
}
};
</script>
<!-- load Dojo -->
<script src="libs/dojo/dojo.js"
data-dojo-config="async: true"></script>
</head>
<body class="claro">
<script>
require([
"hos/sitemap/SitemapAPI",
"dojo/dom-construct",
"dojo/dom",
"dojo/domReady!"
], function(sitemapAPI, domConstruct, dom) {
sitemapAPI.getSitemap("test02").then(function(sitemap) {
domConstruct.place(sitemap.domNode, "contentPane");
sitemap.startup();
}, function(error) {
var node = domConstruct.toDom("<b>Error: " + error + "</b>");
domConstruct.place(node, "contentPane");
});
});
</script>
<div id="contentPane" style="width: 100%; height: 100%;"></div>
</body>
</html>
@@ -33,7 +33,7 @@
}
},
"additionalProperties": false,
"required": [ "type", "set" ]
"required": [ "set" ]
},
"effect": {
"description": "",
@@ -49,7 +49,7 @@
}
},
"additionalProperties": false,
"required": [ "type", "set" ]
"required": [ "set" ]
},
"advproperties": {
"description": "",
@@ -64,15 +64,15 @@
}
},
"additionalProperties": true,
"required": [ "type" ]
"required": []
}
},
"additionalProperties": false,
"anyOf": [
{ "required": [ "type", "switch" ] },
{ "required": [ "type", "brightness" ] },
{ "required": [ "type", "color" ] },
{ "required": [ "type", "alert" ] },
{ "required": [ "type", "effect" ] }
{ "required": [ "switch" ] },
{ "required": [ "brightness" ] },
{ "required": [ "color" ] },
{ "required": [ "alert" ] },
{ "required": [ "effect" ] }
]
}