.
This commit is contained in:
@@ -55,7 +55,7 @@ bundle(hos_cosgi_devicerepository
|
||||
SOURCES
|
||||
${PROJECT_SOURCE_DIR}/src/BundleActivator
|
||||
${PROJECT_SOURCE_DIR}/src/hos/cosgi/service/devicerepository/DeviceRepositoryServiceImpl
|
||||
${PROJECT_SOURCE_DIR}/src/hos/cosgi/service/webresource/DeviceRepoHandler
|
||||
${PROJECT_SOURCE_DIR}/src/hos/cosgi/service/devicerepository/webresource/DeviceRepoHandler
|
||||
)
|
||||
|
||||
target_link_libraries(hos_cosgi_devicerepository
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
#include <hos/cosgi/service/core/CoreService.h>
|
||||
// Services
|
||||
#include "hos/cosgi/service/devicerepository/DeviceRepositoryServiceImpl.h"
|
||||
#include "hos/cosgi/service/webresource/DeviceRepoHandler.h"
|
||||
#include "hos/cosgi/service/devicerepository/webresource/DeviceRepoHandler.h"
|
||||
|
||||
|
||||
using namespace hos::cosgi;
|
||||
|
||||
+83
-14
@@ -5,6 +5,7 @@
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <Poco/URI.h>
|
||||
#include <Poco/Timestamp.h>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
|
||||
|
||||
// Service Namespace
|
||||
@@ -26,7 +27,11 @@ HOS_SERVICE_IMPL_INFO( DeviceRepoHandlerFactory, // Implementation
|
||||
|
||||
|
||||
DeviceRepoHandlerFactory::DeviceRepoHandlerFactory(devicerepository::DeviceRepositoryService* repoService)
|
||||
: repoService(repoService) {
|
||||
: repoService(repoService) {
|
||||
infoCategoryFuncs.insert({"id", [](Poco::URI& matchUri, devicerepository::Device* device, Poco::Net::HTTPServerRequest &req, const std::string& category, bool ssl)->Variant {
|
||||
return device->getDeviceID();
|
||||
} });
|
||||
|
||||
infoCategoryFuncs.insert({"type", [](Poco::URI& matchUri, devicerepository::Device* device, Poco::Net::HTTPServerRequest &req, const std::string& category, bool ssl)->Variant {
|
||||
return device->getDeviceType();
|
||||
} });
|
||||
@@ -68,6 +73,9 @@ DeviceRepoHandlerFactory::DeviceRepoHandlerFactory(devicerepository::DeviceRepos
|
||||
}
|
||||
|
||||
|
||||
DeviceAPIMethod DeviceRepoHandlerFactory::getDeviceAPIMethod() noexcept {
|
||||
|
||||
}
|
||||
|
||||
Net::HTTPRequestHandler* DeviceRepoHandlerFactory::createRequestHandler( Poco::URI matchUri, const Net::HTTPServerRequest& request, bool ssl ) noexcept {
|
||||
return new DeviceRepoHandler(matchUri, repoService, infoCategoryFuncs, ssl);
|
||||
@@ -76,9 +84,9 @@ Net::HTTPRequestHandler* DeviceRepoHandlerFactory::createRequestHandler( Poco::U
|
||||
|
||||
|
||||
void DeviceRepoHandler::_handleGET(Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp) {
|
||||
// Get method parameter
|
||||
// Get API method parameter
|
||||
Net::HTMLForm form(req);
|
||||
DevicesAPIMethod dataMethod = DevicesAPIMethod::GET; // Default method is get
|
||||
DeviceAPIMethod dataMethod = DeviceAPIMethod::GET; // Default method is get
|
||||
std::string methodValue;
|
||||
if (form.has("method")) {
|
||||
std::string methodRaw = form.get("method");
|
||||
@@ -86,10 +94,10 @@ void DeviceRepoHandler::_handleGET(Poco::Net::HTTPServerRequest &req, Poco::Net:
|
||||
if (methodRaw == "get") {
|
||||
// do nothing
|
||||
} else if (methodRaw == "command") {
|
||||
dataMethod = DevicesAPIMethod::COMMAND;
|
||||
dataMethod = DeviceAPIMethod::COMMAND;
|
||||
requireValue = true;
|
||||
} else if (methodRaw.compare("statusupdate") == 0) {
|
||||
dataMethod = DevicesAPIMethod::STATUSUPDATE;
|
||||
dataMethod = DeviceAPIMethod::STATUSUPDATE;
|
||||
requireValue = true;
|
||||
} else {
|
||||
throw webserver::webresource::WebResourceHandlerException((boost::format("Unknown API method \"%1%\"") % methodRaw).str(),
|
||||
@@ -112,16 +120,61 @@ void DeviceRepoHandler::_handleGET(Poco::Net::HTTPServerRequest &req, Poco::Net:
|
||||
|
||||
// No device given
|
||||
if (pathPieces.size() <= matchPieces.size()) {
|
||||
if (dataMethod != DevicesAPIMethod::GET) {
|
||||
if (dataMethod != DeviceAPIMethod::GET) {
|
||||
throw webserver::webresource::WebResourceHandlerException("Requested API method requires a device",
|
||||
Poco::Net::HTTPResponse::HTTPStatus::HTTP_BAD_REQUEST);
|
||||
}
|
||||
Variant result;
|
||||
for (auto d : repoService->getDevices()) {
|
||||
auto dev = d.lock();
|
||||
result[dev->getDeviceID()] = getDeviceInfo(dev.get(), req);
|
||||
|
||||
auto devices = repoService->getDevices();
|
||||
|
||||
// Range header parameter
|
||||
std::string rangeRaw;
|
||||
size_t rangeStart = 0;
|
||||
size_t rangeStop = devices.size() - 1;
|
||||
if (req.has("Range")) {
|
||||
rangeRaw = req.get("Range");
|
||||
} else if (req.has("X-Range")) {
|
||||
rangeRaw = req.get("X-Range");
|
||||
}
|
||||
if (!rangeRaw.empty()) {
|
||||
if (boost::algorithm::starts_with(rangeRaw, "items=")) {
|
||||
auto range = rangeRaw.substr(6);
|
||||
std::vector<std::string> splitParts;
|
||||
boost::algorithm::split(splitParts, range, boost::algorithm::is_any_of("-"));
|
||||
if (splitParts.size() == 2) {
|
||||
try {
|
||||
size_t r1 = std::stol(splitParts[0]);
|
||||
size_t r2 = std::stol(splitParts[1]);
|
||||
if (r1 <= r2) {
|
||||
rangeStart = r1;
|
||||
rangeStop = std::min(r2, rangeStop);
|
||||
}
|
||||
} catch (...) {
|
||||
ROS_ERROR_STREAM("Could not parse Range parameter \"" << rangeRaw << "\".");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
Variant result = Variant::List();
|
||||
for (size_t t = rangeStart; t <= rangeStop; t++) {
|
||||
auto dev = devices[t].lock();
|
||||
result.Append(getDeviceInfo(dev.get(), req));
|
||||
}
|
||||
if (result.Size() == 0 && rangeStart > 0) {
|
||||
if (rangeStart > 0) {
|
||||
handleErrorResponse(req, resp, "Out of range", Poco::Net::HTTPResponse::HTTP_NOT_FOUND);
|
||||
} else {
|
||||
handleResponse(req, resp, result);
|
||||
}
|
||||
} else {
|
||||
std::string rangeOut = (boost::format("items %1%-%2%/%3%") % rangeStart % rangeStop % devices.size()).str();
|
||||
resp.set("Content-Range", rangeOut);
|
||||
resp.set("X-Content-Range", rangeOut);
|
||||
handleResponse(req, resp, result);
|
||||
}
|
||||
handleResponse(req, resp, result);
|
||||
|
||||
// Device given
|
||||
} else {
|
||||
@@ -129,7 +182,7 @@ void DeviceRepoHandler::_handleGET(Poco::Net::HTTPServerRequest &req, Poco::Net:
|
||||
auto device = repoService->getDevice(devid);
|
||||
if (!device.expired()) {
|
||||
switch (dataMethod) {
|
||||
case DevicesAPIMethod::GET:
|
||||
case DeviceAPIMethod::GET:
|
||||
{
|
||||
std::string category;
|
||||
if (pathPieces.size() > matchPieces.size() + 1) {
|
||||
@@ -143,7 +196,7 @@ void DeviceRepoHandler::_handleGET(Poco::Net::HTTPServerRequest &req, Poco::Net:
|
||||
handleResponse(req, resp, result);
|
||||
}
|
||||
break;
|
||||
case DevicesAPIMethod::COMMAND:
|
||||
case DeviceAPIMethod::COMMAND:
|
||||
{
|
||||
std::vector<std::string> path;
|
||||
for (int i = matchPieces.size()+1; i < pathPieces.size(); i++) {
|
||||
@@ -153,7 +206,7 @@ void DeviceRepoHandler::_handleGET(Poco::Net::HTTPServerRequest &req, Poco::Net:
|
||||
handleResponse(req, resp, std::string("OK: Command send"));
|
||||
}
|
||||
break;
|
||||
case DevicesAPIMethod::STATUSUPDATE:
|
||||
case DeviceAPIMethod::STATUSUPDATE:
|
||||
throw webserver::webresource::WebResourceHandlerException("ToDo: Implement method.");
|
||||
default:
|
||||
throw webserver::webresource::WebResourceHandlerException("Internal Server Error");
|
||||
@@ -230,6 +283,22 @@ void DeviceRepoHandler::sendDeviceCommand(devicerepository::Device* device, Net:
|
||||
device->sendCommand(cmdMsg);
|
||||
}
|
||||
|
||||
void DeviceRepoHandler::notificationReceived(interface::Observer<devicerepository::DeviceEventType, devicerepository::DeviceEvent>::observable_type* o, const devicerepository::DeviceEventType& key, const devicerepository::DeviceEvent* e) noexcept {
|
||||
switch (key) {
|
||||
case devicerepository::DeviceEventType::DEVICE_ADDED:
|
||||
break;
|
||||
case devicerepository::DeviceEventType::DEVICE_REMOVED:
|
||||
break;
|
||||
case devicerepository::DeviceEventType::COMMAND_RECEIVED:
|
||||
break;
|
||||
case devicerepository::DeviceEventType::STATUS_UPDATE_RECEIVED:
|
||||
break;
|
||||
case devicerepository::DeviceEventType::STATE_CHANGED:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
+54
-2
@@ -24,7 +24,7 @@ namespace webresource {
|
||||
|
||||
|
||||
|
||||
enum class DevicesAPIMethod {
|
||||
enum class DeviceAPIMethod {
|
||||
GET,
|
||||
COMMAND,
|
||||
STATUSUPDATE
|
||||
@@ -47,6 +47,7 @@ private:
|
||||
std::map<std::string, infoCategoryFunc_t> infoCategoryFuncs;
|
||||
public:
|
||||
DeviceRepoHandlerFactory(devicerepository::DeviceRepositoryService* repoService);
|
||||
|
||||
virtual Poco::Net::HTTPRequestHandler* createRequestHandler(Poco::URI matchUri, const Poco::Net::HTTPServerRequest& request, bool ssl ) noexcept override;
|
||||
|
||||
virtual void setWebServerService(webserver::WebServerService* service) noexcept override {
|
||||
@@ -57,6 +58,8 @@ public:
|
||||
return false;
|
||||
}
|
||||
|
||||
static DeviceAPIMethod getDeviceAPIMethod() noexcept;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -83,8 +86,52 @@ public:
|
||||
* JSON texts (namely single strings, numbers or boolean values) that are not valid JSON texts according to RFC 4627.
|
||||
* Keep in mind that there are still a lot of JSON libraries and validators out there which still no nothing about RFC 7159.
|
||||
*/
|
||||
|
||||
class DeviceRepoHandlerBase
|
||||
: public interface::Observer<devicerepository::DeviceEventType, devicerepository::DeviceEvent>,
|
||||
public webserver::webresource::WebResourceHandlerImpl {
|
||||
|
||||
protected:
|
||||
Poco::URI matchUri;
|
||||
devicerepository::DeviceRepositoryService* repoService;
|
||||
const std::map<std::string, DeviceRepoHandlerFactory::infoCategoryFunc_t>& infoCategoryFuncs;
|
||||
bool ssl;
|
||||
|
||||
libvariant::Variant getDeviceInfo( devicerepository::Device* device,
|
||||
Poco::Net::HTTPServerRequest &req,
|
||||
const std::string& category = std::string(),
|
||||
const std::vector<std::string>& path = std::vector<std::string>() );
|
||||
|
||||
void sendDeviceCommand( devicerepository::Device* device,
|
||||
Poco::Net::HTTPServerRequest &req,
|
||||
const std::string& cmd,
|
||||
const std::vector<std::string>& path = std::vector<std::string>() );
|
||||
|
||||
|
||||
public:
|
||||
|
||||
DeviceRepoHandlerBase( Poco::URI matchUri,
|
||||
devicerepository::DeviceRepositoryService* repoService,
|
||||
const std::map<std::string, DeviceRepoHandlerFactory::infoCategoryFunc_t>& infoCategoryFuncs,
|
||||
bool ssl = false )
|
||||
: matchUri(matchUri), repoService(repoService), infoCategoryFuncs(infoCategoryFuncs), ssl(ssl) {}
|
||||
|
||||
virtual void _handleGET(Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp) override;
|
||||
|
||||
virtual void _handlePOST(Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp) override;
|
||||
|
||||
virtual void notificationReceived( interface::Observer<devicerepository::DeviceEventType,
|
||||
devicerepository::DeviceEvent>::observable_type* o,
|
||||
const devicerepository::DeviceEventType& key,
|
||||
const devicerepository::DeviceEvent* e) noexcept override;
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
class DeviceRepoHandler
|
||||
: public virtual webserver::webresource::WebResourceHandlerImpl {
|
||||
: public interface::Observer<devicerepository::DeviceEventType, devicerepository::DeviceEvent>,
|
||||
public webserver::webresource::WebResourceHandlerImpl {
|
||||
|
||||
private:
|
||||
Poco::URI matchUri;
|
||||
@@ -115,6 +162,11 @@ public:
|
||||
|
||||
virtual void _handlePOST(Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp) override;
|
||||
|
||||
virtual void notificationReceived( interface::Observer<devicerepository::DeviceEventType,
|
||||
devicerepository::DeviceEvent>::observable_type* o,
|
||||
const devicerepository::DeviceEventType& key,
|
||||
const devicerepository::DeviceEvent* e) noexcept override;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
|
||||
HTTP - GET / HTTP - POST (application/x-www-form-urlencoded):
|
||||
==============================================================
|
||||
x) [OUT] DeviceAPIMethod::GET [Args: format, pointer, query?]
|
||||
x) [IN] DeviceAPIMethod::COMMAND [Args: format, pointer, msg]
|
||||
x) [IN] DeviceAPIMethod::STATUS_UPDATE [Args: format, pointer, msg]
|
||||
x) [EVENT] DeviceAPIMethod::REGISTER_EVENT [Args: format, pointer, event]
|
||||
- Long-Polling
|
||||
- WebSocket
|
||||
|
||||
|
||||
API Method Parameter:
|
||||
-- Query: method = get | command | statusupdate | event
|
||||
-- Header: API-METHOD | X-API-METHOD
|
||||
|
||||
Common Parameters:
|
||||
|
||||
x) Format-Parameters:
|
||||
-- Query: format = json | jsonp | xmlplist | ...
|
||||
-- Query: json | jsonp [= <callback>] | xmlplist | ...
|
||||
-- Query: callback = callback - in conjunction with jsonp or format=jsonp, defaults to callback
|
||||
-- Header: application/[x-]javascript = jsonp | application/json = json | application/yaml = yaml | application/xml = xmlplist
|
||||
|
||||
x) Pointer Parameter:
|
||||
-- Query: pointer = <selector in JSON Pointer format>
|
||||
-- URL: .../devices/<id>/[.../]<pointer>
|
||||
|
||||
COMMAND/STATUS_UPDATE Parameters:
|
||||
-- Query: body = <command | status-update>
|
||||
|
||||
EVENT Parameters:
|
||||
-- Query: event = command | statusupdate | statechange | added | removed | all
|
||||
@@ -0,0 +1,32 @@
|
||||
define([
|
||||
"dojo/_base/config",
|
||||
"dojo/store/JsonRest",
|
||||
"dojox/socket",
|
||||
"dojo/_base/xhr"
|
||||
], function(config, JsonRest, Socket, xhr){
|
||||
var _store = new JsonRest({
|
||||
target: config.hos.deviceRepoUrl
|
||||
});
|
||||
var _updateSocketOpened = false;
|
||||
var _updateSocket = new Socket({
|
||||
url: config.hos.deviceRepoUrl,
|
||||
interval: 5000,
|
||||
transport: xhr.get });
|
||||
_updateSocket.on("open", function(event){
|
||||
_socketOpened = true;
|
||||
console.log("Socket Opened ", event.message);
|
||||
});
|
||||
_updateSocket.on("close", function(event){
|
||||
_socketOpened = false;
|
||||
console.log("Socket Closed ", event.message);
|
||||
});
|
||||
_updateSocket.on("error", function(event){
|
||||
_socketOpened = false;
|
||||
console.log("Socket Error ", event.message);
|
||||
});
|
||||
_updateSocket.on("message", function(event) {
|
||||
var data = event.data;
|
||||
console.log("Message: " /*+ data*/);
|
||||
});
|
||||
return _store;
|
||||
});
|
||||
@@ -0,0 +1,52 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Tutorial: Hello Dojo!</title>
|
||||
|
||||
<link rel="stylesheet" href="dojo/dijit/themes/claro/claro.css" media="screen">
|
||||
|
||||
<!-- configure Dojo -->
|
||||
<script>
|
||||
var dojoConfig = {
|
||||
async: true,
|
||||
parseOnLoad: true,
|
||||
isDebug: true,
|
||||
deps: [
|
||||
"dojo/parser"
|
||||
],
|
||||
hos: {
|
||||
deviceRepoUrl: "/hos/api/devices/"
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<!-- load Dojo -->
|
||||
<script src="dojo/dojo/dojo.js"
|
||||
data-dojo-config="async: true"></script>
|
||||
|
||||
</head>
|
||||
<body class="claro">
|
||||
|
||||
|
||||
<script>
|
||||
require(["hos/DeviceRepo"], function(DeviceRepo){
|
||||
|
||||
var self = this;
|
||||
|
||||
var devices = DeviceRepo.query()
|
||||
.then(function(data){
|
||||
console.log("Data: ", data);
|
||||
}, function(err){
|
||||
console.log("Error: ", err);
|
||||
}, function(update){
|
||||
console.log("Update: ", update);
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
+4
-4
@@ -273,13 +273,13 @@ protected:
|
||||
_handlePOST(req, resp);
|
||||
} else {
|
||||
std::cout << "Unsupported HTTP Request Method" << std::endl;
|
||||
handleErrorResponse(req, resp, (boost::format("Unsupported HTTP Request Method %1%") % req.getMethod()).str(),
|
||||
Poco::Net::HTTPResponse::HTTPStatus::HTTP_BAD_REQUEST);
|
||||
handleErrorResponse(req, resp, (boost::format("HTTP method %1% not implemented") % req.getMethod()).str(),
|
||||
Poco::Net::HTTPResponse::HTTPStatus::HTTP_NOT_IMPLEMENTED);
|
||||
}
|
||||
} catch (NotImplementedException& e) {
|
||||
std::cout << "Unsupported HTTP Request Method" << std::endl;
|
||||
handleErrorResponse(req, resp, (boost::format("Unsupported HTTP Request Method %1%") % req.getMethod()).str(),
|
||||
Poco::Net::HTTPResponse::HTTPStatus::HTTP_BAD_REQUEST);
|
||||
handleErrorResponse(req, resp, (boost::format("HTTP method %1% not implemented") % req.getMethod()).str(),
|
||||
Poco::Net::HTTPResponse::HTTPStatus::HTTP_NOT_IMPLEMENTED);
|
||||
}
|
||||
}
|
||||
} catch (WebResourceHandlerException& e) {
|
||||
|
||||
@@ -57,11 +57,12 @@ void WebServerServiceImpl::setCoreService(service::core::CoreService* coreServic
|
||||
Poco::Net::HTTPRequestHandler* WebServerServiceImpl::RequestHandlerFactory::createRequestHandler( const Poco::Net::HTTPServerRequest& request) {
|
||||
WebResourceHandlerFactory* factory = nullptr;
|
||||
std::string matchUri;
|
||||
Poco::URI uri(request.getURI());
|
||||
// We want a longest prefix match.
|
||||
// Since we have an ordered map, we just need to iterate it in reverse order and return the first match.
|
||||
for (auto i = parent->webResourceFactories.rbegin(); i != parent->webResourceFactories.rend(); i++) {
|
||||
if ( boost::algorithm::starts_with(request.getURI(), i->first)
|
||||
&& ( request.getURI().size() == i->first.size() || request.getURI()[i->first.size()] == '/' ) ) {
|
||||
if ( boost::algorithm::starts_with(uri.getPath(), i->first)
|
||||
&& ( uri.getPath().size() == i->first.size() || uri.getPath()[i->first.size()] == '/' ) ) {
|
||||
factory = i->second;
|
||||
matchUri = i->first;
|
||||
break;
|
||||
|
||||
Reference in New Issue
Block a user