.
This commit is contained in:
+450
-134
@@ -21,11 +21,14 @@ using namespace Poco;
|
||||
using namespace libvariant;
|
||||
|
||||
|
||||
|
||||
HOS_SERVICE_IMPL_INFO( DeviceRepoHandlerFactory, // Implementation
|
||||
service::webserver::WebResourceHandlerFactory::SERVICE_NAME, // Service Name
|
||||
1, 0 ) // Version
|
||||
|
||||
|
||||
|
||||
|
||||
DeviceRepoHandlerFactory::DeviceRepoHandlerFactory(devicerepository::DeviceRepositoryService* repoService)
|
||||
: repoService(repoService) {
|
||||
infoCategoryFuncs.insert({"id", [](Poco::URI& matchUri, devicerepository::Device* device, Poco::Net::HTTPServerRequest &req, const std::string& category, bool ssl)->Variant {
|
||||
@@ -73,128 +76,78 @@ DeviceRepoHandlerFactory::DeviceRepoHandlerFactory(devicerepository::DeviceRepos
|
||||
}
|
||||
|
||||
|
||||
DeviceAPIMethod DeviceRepoHandlerFactory::getDeviceAPIMethod() noexcept {
|
||||
|
||||
}
|
||||
|
||||
|
||||
std::shared_ptr<DeviceEventQueue> DeviceRepoHandlerFactory::getDeviceEventQueue(std::string clientID) {
|
||||
if (clientID.empty()) {
|
||||
|
||||
} else {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Net::HTTPRequestHandler* DeviceRepoHandlerFactory::createRequestHandler( Poco::URI matchUri, const Net::HTTPServerRequest& request, bool ssl ) noexcept {
|
||||
if(request.find("Upgrade") != request.end() && Poco::icompare(request["Upgrade"], "websocket") == 0)
|
||||
return new DeviceRepoEventWebSocketHandler(matchUri, repoService, infoCategoryFuncs, ssl);
|
||||
else {
|
||||
DeviceAPIMethod method = getDeviceAPIMethod();
|
||||
if (method == DeviceAPIMethod::REGISTEREVENT) {
|
||||
return new DeviceRepoEventLongPollingHandler(matchUri, repoService, infoCategoryFuncs, ssl);
|
||||
return new DeviceRepoHandler( this, matchUri, repoService, infoCategoryFuncs, ssl);
|
||||
}
|
||||
|
||||
std::shared_ptr<DeviceEventQueue> DeviceRepoHandlerFactory::getDeviceEventQueue( const std::string& clientID ) {
|
||||
if (clientID.empty()) {
|
||||
return std::make_shared<DeviceEventQueue>(clientID, repoService);
|
||||
} else {
|
||||
auto i = deviceEventQueues.find(clientID);
|
||||
if (i == deviceEventQueues.end()) {
|
||||
auto n = std::make_shared<DeviceEventQueue>(clientID, repoService);
|
||||
deviceEventQueues.insert({clientID, n});
|
||||
return n;
|
||||
} else {
|
||||
return new DeviceRepoInfoHandler(matchUri, method, repoService, infoCategoryFuncs, ssl);
|
||||
return i->second;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Variant DeviceRepoHandlerBase::getDeviceInfo(devicerepository::Device* device, Net::HTTPServerRequest &req, const std::string& category, const std::vector<std::string>& path) {
|
||||
Variant retval;
|
||||
|
||||
// We want all info categories
|
||||
if (category.empty()) {
|
||||
for (auto c : infoCategoryFuncs) {
|
||||
retval[c.first] = c.second(matchUri, device, req, category, ssl);
|
||||
}
|
||||
|
||||
// We want a single info category
|
||||
void DeviceRepoHandler::_handleRequest(Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp) {
|
||||
if(req.find("Upgrade") != req.end() && Poco::icompare(req["Upgrade"], "websocket") == 0) {
|
||||
// WebSocket
|
||||
} else {
|
||||
auto c = infoCategoryFuncs.find(category);
|
||||
if (c != infoCategoryFuncs.end()) {
|
||||
retval = c->second(matchUri, device, req, category, ssl);
|
||||
if (path.size() > 0) {
|
||||
std::stringstream ss;
|
||||
for (auto p : path) {
|
||||
ss << "/" << p;
|
||||
}
|
||||
std::string pathstr = ss.str();
|
||||
if (retval.HasPath(pathstr)) {
|
||||
retval = retval.GetPath(pathstr);
|
||||
} else {
|
||||
throw webserver::webresource::WebResourceHandlerException((boost::format("Unknown path \"%1%\" for device info category \"%2%\".") % pathstr % category).str(),
|
||||
Poco::Net::HTTPResponse::HTTPStatus::HTTP_NOT_FOUND);
|
||||
}
|
||||
if (req.getMethod() == Net::HTTPRequest::HTTP_PUT) {
|
||||
// Put Command
|
||||
throw webserver::webresource::WebResourceHandlerException("Still a ToDo.",
|
||||
Net::HTTPResponse::HTTP_NOT_IMPLEMENTED);
|
||||
} else if (req.getMethod() == Net::HTTPRequest::HTTP_GET || req.getMethod() == Net::HTTPRequest::HTTP_POST) {
|
||||
DeviceAPIMethod method = getDeviceAPIMethod(req);
|
||||
if (method == DeviceAPIMethod::GET) {
|
||||
__handleInfo(req, resp);
|
||||
} else if (method == DeviceAPIMethod::COMMAND || method == DeviceAPIMethod::STATUSUPDATE) {
|
||||
__handleCommand(req, resp, method);
|
||||
} else if (method == DeviceAPIMethod::EVENT) {
|
||||
__handleEventLongPolling(req, resp);
|
||||
} else {
|
||||
throw webserver::webresource::WebResourceHandlerException("Could not understood request",
|
||||
Net::HTTPResponse::HTTP_BAD_REQUEST);
|
||||
}
|
||||
} else {
|
||||
throw webserver::webresource::WebResourceHandlerException((boost::format("Unknown device info category \"%1%\"") % category).str(),
|
||||
Poco::Net::HTTPResponse::HTTPStatus::HTTP_NOT_FOUND);
|
||||
throw webserver::webresource::WebResourceHandlerException("Could not understood request",
|
||||
Net::HTTPResponse::HTTP_BAD_REQUEST);
|
||||
}
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void DeviceRepoHandlerBase::sendDeviceCommand(devicerepository::Device* device, Net::HTTPServerRequest &req, const std::string& cmd, const std::vector<std::string>& path) {
|
||||
Variant cmdMsg;
|
||||
VariantRef mPart = cmdMsg.Ref();
|
||||
if (path.size() > 0) {
|
||||
for (int i = 0; i < path.size(); i++) {
|
||||
mPart[path[i]] = Variant();
|
||||
mPart.ReassignRef(mPart[path[i]]);
|
||||
}
|
||||
}
|
||||
/*
|
||||
* The libvariant json deserializer knows nothing about RFC 7159, therefore non-object or non-array values are rejected.
|
||||
* As a workaround we use the yaml deserializer (yaml can be considered a super-set of json).
|
||||
* Fun fact: the json serializer accepts non-object or non-array values.
|
||||
*/
|
||||
//mPart = DeserializeJSON(cmd);
|
||||
mPart = DeserializeYAML(cmd);
|
||||
device->sendCommand(cmdMsg);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void DeviceRepoInfoHandler::__handle(Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp, Net::HTMLForm& form) {
|
||||
|
||||
std::string methodValue;
|
||||
if (method == DeviceAPIMethod::COMMAND || method == DeviceAPIMethod::STATUSUPDATE) {
|
||||
if (form.has("value")) {
|
||||
methodValue = form.get("value");
|
||||
} else {
|
||||
throw webserver::webresource::WebResourceHandlerException("API method requires a value parameter.",
|
||||
Poco::Net::HTTPResponse::HTTPStatus::HTTP_BAD_REQUEST);
|
||||
}
|
||||
}
|
||||
void DeviceRepoHandler::__handleInfo(Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp) {
|
||||
|
||||
std::vector<std::string> pathPieces, matchPieces;
|
||||
matchUri.getPathSegments(matchPieces);
|
||||
Poco::URI uri(req.getURI());
|
||||
uri.getPathSegments(pathPieces);
|
||||
std::unique_ptr<Net::HTMLForm> form;
|
||||
if (req.getMethod() == Net::HTTPRequest::HTTP_POST) {
|
||||
form.reset(new Net::HTMLForm(req, req.stream()));
|
||||
} else {
|
||||
form.reset(new Net::HTMLForm(req));
|
||||
}
|
||||
|
||||
// No device given
|
||||
// No device given, query mode (there is no real query functionality implemented yet though)
|
||||
if (pathPieces.size() <= matchPieces.size()) {
|
||||
if (method != DeviceAPIMethod::GET) {
|
||||
throw webserver::webresource::WebResourceHandlerException("Requested API method requires a device",
|
||||
Poco::Net::HTTPResponse::HTTPStatus::HTTP_BAD_REQUEST);
|
||||
}
|
||||
|
||||
auto devices = repoService->getDevices();
|
||||
|
||||
// Range header parameter
|
||||
std::string rangeRaw;
|
||||
// Parse HTTP header "Range" parameter (to support dojo/store)
|
||||
size_t rangeStart = 0;
|
||||
size_t rangeStop = devices.size() - 1;
|
||||
std::string rangeRaw;
|
||||
if (req.has("Range")) {
|
||||
rangeRaw = req.get("Range");
|
||||
} else if (req.has("X-Range")) {
|
||||
@@ -220,19 +173,22 @@ void DeviceRepoInfoHandler::__handle(Poco::Net::HTTPServerRequest &req, Poco::Ne
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
// Iterate over devices
|
||||
Variant result = Variant::List();
|
||||
for (size_t t = rangeStart; t <= rangeStop; t++) {
|
||||
auto dev = devices[t].lock();
|
||||
result.Append(getDeviceInfo(dev.get(), req));
|
||||
}
|
||||
|
||||
// There are no devices and a range was given, probably out of range
|
||||
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);
|
||||
}
|
||||
|
||||
// Return devices array
|
||||
} else {
|
||||
std::string rangeOut = (boost::format("items %1%-%2%/%3%") % rangeStart % rangeStop % devices.size()).str();
|
||||
resp.set("Content-Range", rangeOut);
|
||||
@@ -240,41 +196,33 @@ void DeviceRepoInfoHandler::__handle(Poco::Net::HTTPServerRequest &req, Poco::Ne
|
||||
handleResponse(req, resp, result);
|
||||
}
|
||||
|
||||
// Device given
|
||||
// Single device mode
|
||||
} else {
|
||||
// Get Device
|
||||
std::string devid = pathPieces[matchPieces.size()];
|
||||
auto device = repoService->getDevice(devid);
|
||||
if (!device.expired()) {
|
||||
switch (method) {
|
||||
case DeviceAPIMethod::GET:
|
||||
{
|
||||
std::string category;
|
||||
if (pathPieces.size() > matchPieces.size() + 1) {
|
||||
category = pathPieces[matchPieces.size() + 1];
|
||||
}
|
||||
std::vector<std::string> path;
|
||||
for (int i = matchPieces.size() + 2; i < pathPieces.size(); i++) {
|
||||
path.push_back(pathPieces[i]);
|
||||
}
|
||||
Variant result = getDeviceInfo(device.lock().get(), req, category, path);
|
||||
handleResponse(req, resp, result);
|
||||
}
|
||||
break;
|
||||
case DeviceAPIMethod::COMMAND:
|
||||
{
|
||||
std::vector<std::string> path;
|
||||
for (int i = matchPieces.size()+1; i < pathPieces.size(); i++) {
|
||||
path.push_back(pathPieces[i]);
|
||||
}
|
||||
sendDeviceCommand(device.lock().get(), req, methodValue, path);
|
||||
handleResponse(req, resp, std::string("OK: Command send"));
|
||||
}
|
||||
break;
|
||||
case DeviceAPIMethod::STATUSUPDATE:
|
||||
throw webserver::webresource::WebResourceHandlerException("ToDo: Implement method.");
|
||||
default:
|
||||
throw webserver::webresource::WebResourceHandlerException("Internal Server Error");
|
||||
// Get info category
|
||||
std::string category;
|
||||
if (pathPieces.size() > matchPieces.size() + 1) {
|
||||
category = pathPieces[matchPieces.size() + 1];
|
||||
}
|
||||
// Get path from url and append pointer parameter
|
||||
std::stringstream path;
|
||||
for (int i = matchPieces.size() + 2; i < pathPieces.size(); i++) {
|
||||
path << "/" << pathPieces[i];
|
||||
}
|
||||
if (form->has("pointer")) {
|
||||
auto tmp = form->get("pointer");
|
||||
if (tmp.size() > 0 && tmp[0] != '/') {
|
||||
path << "/";
|
||||
}
|
||||
path << tmp;
|
||||
}
|
||||
// Get device info
|
||||
Variant result = getDeviceInfo(device.lock().get(), req, category, path.str());
|
||||
// Return device info
|
||||
handleResponse(req, resp, result);
|
||||
} else {
|
||||
throw webserver::webresource::WebResourceHandlerException((boost::format("Could not find device \"%1%\"") % devid).str(),
|
||||
Poco::Net::HTTPResponse::HTTPStatus::HTTP_NOT_FOUND);
|
||||
@@ -283,24 +231,392 @@ void DeviceRepoInfoHandler::__handle(Poco::Net::HTTPServerRequest &req, Poco::Ne
|
||||
}
|
||||
|
||||
|
||||
void DeviceRepoHandler::__handleCommand(Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp, DeviceAPIMethod method) {
|
||||
std::vector<std::string> pathPieces, matchPieces;
|
||||
matchUri.getPathSegments(matchPieces);
|
||||
Poco::URI uri(req.getURI());
|
||||
uri.getPathSegments(pathPieces);
|
||||
std::unique_ptr<Net::HTMLForm> form;
|
||||
if (req.getMethod() == Net::HTTPRequest::HTTP_POST) {
|
||||
form.reset(new Net::HTMLForm(req, req.stream()));
|
||||
} else {
|
||||
form.reset(new Net::HTMLForm(req));
|
||||
}
|
||||
|
||||
void DeviceRepoInfoHandler::_handleGET(Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp) {
|
||||
Net::HTMLForm form(req);
|
||||
__handle(req, resp, form);
|
||||
}
|
||||
// No device given
|
||||
if (pathPieces.size() <= matchPieces.size()) {
|
||||
throw webserver::webresource::WebResourceHandlerException("API method requires a device",
|
||||
Poco::Net::HTTPResponse::HTTPStatus::HTTP_BAD_REQUEST);
|
||||
}
|
||||
|
||||
void DeviceRepoInfoHandler::_handlePOST(Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp) {
|
||||
Net::HTMLForm form(req, req.stream());
|
||||
__handle(req, resp, form);
|
||||
// Get message
|
||||
std::string msg;
|
||||
if (form->has("value")) {
|
||||
msg = form->get("value");
|
||||
} else {
|
||||
throw webserver::webresource::WebResourceHandlerException("API method requires a value.",
|
||||
Poco::Net::HTTPResponse::HTTPStatus::HTTP_BAD_REQUEST);
|
||||
}
|
||||
|
||||
// Get path from url and append pointer parameter
|
||||
std::vector<std::string> path;
|
||||
for (int i = matchPieces.size()+1; i < pathPieces.size(); i++) {
|
||||
path.push_back(pathPieces[i]);
|
||||
}
|
||||
if (form->has("pointer")) {
|
||||
URI pointer(form->get("pointer"));
|
||||
pointer.getPathSegments(path);
|
||||
}
|
||||
|
||||
// Get device and do stuff
|
||||
std::string devid = pathPieces[matchPieces.size()];
|
||||
auto device = repoService->getDevice(devid);
|
||||
if (!device.expired()) {
|
||||
switch (method) {
|
||||
case DeviceAPIMethod::COMMAND:
|
||||
sendDeviceCommand(device.lock().get(), req, msg, path);
|
||||
handleResponse(req, resp, std::string("OK: Command send"));
|
||||
break;
|
||||
case DeviceAPIMethod::STATUSUPDATE:
|
||||
throw webserver::webresource::WebResourceHandlerException("ToDo: Implement method.");
|
||||
default:
|
||||
throw webserver::webresource::WebResourceHandlerException("Internal Server Error");
|
||||
}
|
||||
} else {
|
||||
throw webserver::webresource::WebResourceHandlerException((boost::format("Could not find device \"%1%\"") % devid).str(),
|
||||
Poco::Net::HTTPResponse::HTTPStatus::HTTP_NOT_FOUND);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void DeviceRepoHandler::__handleEventLongPolling(Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp) {
|
||||
std::vector<std::string> pathPieces, matchPieces;
|
||||
matchUri.getPathSegments(matchPieces);
|
||||
Poco::URI uri(req.getURI());
|
||||
uri.getPathSegments(pathPieces);
|
||||
std::unique_ptr<Net::HTMLForm> form;
|
||||
if (req.getMethod() == Net::HTTPRequest::HTTP_POST) {
|
||||
form.reset(new Net::HTMLForm(req, req.stream()));
|
||||
} else {
|
||||
form.reset(new Net::HTMLForm(req));
|
||||
}
|
||||
bool singleDeviceMode = true;
|
||||
|
||||
// No device given
|
||||
if (pathPieces.size() <= matchPieces.size()) {
|
||||
singleDeviceMode = false;
|
||||
}
|
||||
|
||||
// Get device and do stuff
|
||||
std::weak_ptr<Device> device;
|
||||
std::string devid;
|
||||
if (singleDeviceMode) {
|
||||
devid = pathPieces[matchPieces.size()];
|
||||
device = repoService->getDevice(devid);
|
||||
}
|
||||
if (!singleDeviceMode || !device.expired()) {
|
||||
// Get event type
|
||||
std::string eventRaw;
|
||||
DeviceEventType event = DeviceEventType::STATE_CHANGED;
|
||||
if (form->has("onevent")) {
|
||||
eventRaw = form->get("onevent");
|
||||
} else if (req.has("ON-EVENT")) {
|
||||
eventRaw = req.get("ON-EVENT");
|
||||
} else if (req.has("X-ON-EVENT")) {
|
||||
eventRaw = req.get("X-ON-EVENT");
|
||||
}
|
||||
if (!eventRaw.empty()) {
|
||||
if (eventRaw == "all") {
|
||||
event = DeviceEventType::ALL_EVENTS;
|
||||
} else if (eventRaw == "command") {
|
||||
event = DeviceEventType::COMMAND_RECEIVED;
|
||||
} else if (eventRaw == "statusupdate") {
|
||||
event = DeviceEventType::STATUS_UPDATE_RECEIVED;
|
||||
} else if (eventRaw == "statechanged") {
|
||||
event = DeviceEventType::STATE_CHANGED;
|
||||
} else if (eventRaw == "added") {
|
||||
event = DeviceEventType::DEVICE_ADDED;
|
||||
} else if (eventRaw == "removed") {
|
||||
event = DeviceEventType::DEVICE_REMOVED;
|
||||
} else {
|
||||
throw webserver::webresource::WebResourceHandlerException((boost::format("Unknown event type \"%1%\"") % eventRaw).str(),
|
||||
Poco::Net::HTTPResponse::HTTPStatus::HTTP_BAD_REQUEST);
|
||||
}
|
||||
}
|
||||
// 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];
|
||||
}
|
||||
if (form->has("pointer")) {
|
||||
auto tmp = form->get("pointer");
|
||||
if (tmp.size() > 0 && tmp[0] != '/') {
|
||||
path << "/";
|
||||
}
|
||||
path << tmp;
|
||||
}
|
||||
// Get event queue
|
||||
auto eventQueue = parent->getDeviceEventQueue(clientID);
|
||||
devicerepository::Device* devptr = nullptr;
|
||||
if (singleDeviceMode) {
|
||||
devptr = device.lock().get();
|
||||
}
|
||||
eventQueue->checkRegistration(event, devptr, path.str());
|
||||
auto msg = eventQueue->popEvent();
|
||||
handleResponse(req, resp, msg);
|
||||
} else {
|
||||
throw webserver::webresource::WebResourceHandlerException((boost::format("Could not find device \"%1%\"") % devid).str(),
|
||||
Poco::Net::HTTPResponse::HTTPStatus::HTTP_NOT_FOUND);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void DeviceEventQueue::notificationReceived( interface::Observer<devicerepository::DeviceEventType, devicerepository::DeviceEvent>::observable_type* o,
|
||||
const devicerepository::DeviceEventType& key,
|
||||
const devicerepository::DeviceEvent* e) noexcept {
|
||||
pushEvent(Variant("Test"));
|
||||
}
|
||||
|
||||
|
||||
Variant DeviceRepoHandler::getDeviceInfo(devicerepository::Device* device, Net::HTTPServerRequest &req, const std::string& category, const std::string& path) {
|
||||
Variant retval;
|
||||
|
||||
// We want all info categories
|
||||
if (category.empty()) {
|
||||
for (auto c : infoCategoryFuncs) {
|
||||
retval[c.first] = c.second(matchUri, device, req, category, ssl);
|
||||
}
|
||||
|
||||
// We want a single info category
|
||||
} else {
|
||||
auto c = infoCategoryFuncs.find(category);
|
||||
if (c != infoCategoryFuncs.end()) {
|
||||
retval = c->second(matchUri, device, req, category, ssl);
|
||||
if (!path.empty()) {
|
||||
if (retval.HasPath(path)) {
|
||||
retval = retval.GetPath(path);
|
||||
} else {
|
||||
throw webserver::webresource::WebResourceHandlerException((boost::format("Unknown path \"%1%\" for device info category \"%2%\".") % path % category).str(),
|
||||
Poco::Net::HTTPResponse::HTTPStatus::HTTP_NOT_FOUND);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw webserver::webresource::WebResourceHandlerException((boost::format("Unknown device info category \"%1%\"") % category).str(),
|
||||
Poco::Net::HTTPResponse::HTTPStatus::HTTP_NOT_FOUND);
|
||||
}
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void DeviceRepoHandler::sendDeviceCommand(devicerepository::Device* device, Net::HTTPServerRequest &req, const std::string& cmd, const std::vector<std::string>& path) {
|
||||
Variant cmdMsg;
|
||||
VariantRef mPart = cmdMsg.Ref();
|
||||
if (path.size() > 0) {
|
||||
for (int i = 0; i < path.size(); i++) {
|
||||
mPart[path[i]] = Variant();
|
||||
mPart.ReassignRef(mPart[path[i]]);
|
||||
}
|
||||
}
|
||||
/*
|
||||
* The libvariant json deserializer knows nothing about RFC 7159, therefore non-object or non-array values are rejected.
|
||||
* As a workaround we use the yaml deserializer (yaml can be considered a super-set of json).
|
||||
* Fun fact: the json serializer accepts non-object or non-array values.
|
||||
*/
|
||||
//mPart = DeserializeJSON(cmd);
|
||||
mPart = DeserializeYAML(cmd);
|
||||
device->sendCommand(cmdMsg);
|
||||
}
|
||||
|
||||
|
||||
DeviceAPIMethod DeviceRepoHandler::getDeviceAPIMethod(Net::HTTPServerRequest &req) noexcept {
|
||||
std::unique_ptr<Net::HTMLForm> form;
|
||||
DeviceAPIMethod retval = DeviceAPIMethod::GET; // default
|
||||
if (req.getMethod() == Net::HTTPRequest::HTTP_POST && req.has("Content-type")
|
||||
&& req.get("Content-type") == Net::HTMLForm::ENCODING_URL) {
|
||||
form.reset(new Net::HTMLForm(req, req.stream()));
|
||||
} else {
|
||||
form.reset(new Net::HTMLForm(req));
|
||||
}
|
||||
std::string methodRaw;
|
||||
if (form->has("method")) {
|
||||
methodRaw = form->get("method");
|
||||
} else if (req.has("API-Method")) {
|
||||
methodRaw = req.get("API-Method");
|
||||
} else if (req.has("X-API-Method")) {
|
||||
methodRaw = req.get("X-API-Method");
|
||||
}
|
||||
if (!methodRaw.empty()) {
|
||||
if (methodRaw == "command") {
|
||||
retval = DeviceAPIMethod::COMMAND;
|
||||
} else if (methodRaw == "statusupdate") {
|
||||
retval = DeviceAPIMethod::STATUSUPDATE;
|
||||
} else if (methodRaw == "event") {
|
||||
retval = DeviceAPIMethod::EVENT;
|
||||
} else {
|
||||
throw webserver::webresource::WebResourceHandlerException(
|
||||
(boost::format("Unknown device api method \"%1%\"") % methodRaw).str(),
|
||||
Net::HTTPResponse::HTTPStatus::HTTP_BAD_REQUEST );
|
||||
}
|
||||
} else if (form->has("onevent")) {
|
||||
retval = DeviceAPIMethod::EVENT;
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
|
||||
void DeviceEventQueue::notificationReceived( interface::Observer<devicerepository::DeviceEventType,
|
||||
devicerepository::DeviceEvent>::observable_type* o,
|
||||
const devicerepository::DeviceEventType& key,
|
||||
const devicerepository::DeviceEvent* e) noexcept {
|
||||
if (eventType == DeviceEventType::ALL_EVENTS || eventType == key) {
|
||||
switch (key) {
|
||||
case devicerepository::DeviceEventType::COMMAND_RECEIVED:
|
||||
break;
|
||||
case devicerepository::DeviceEventType::STATUS_UPDATE_RECEIVED:
|
||||
break;
|
||||
case devicerepository::DeviceEventType::STATE_CHANGED:
|
||||
break;
|
||||
case devicerepository::DeviceEventType::DEVICE_ADDED:
|
||||
break;
|
||||
case devicerepository::DeviceEventType::DEVICE_REMOVED:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Net::HTTPRequestHandler* DeviceRepoHandlerFactory::createRequestHandler( Poco::URI matchUri, const Net::HTTPServerRequest& request, bool ssl ) noexcept {
|
||||
if(request.find("Upgrade") != request.end() && Poco::icompare(request["Upgrade"], "websocket") == 0) {
|
||||
return new DeviceRepoEventHandler( matchUri, DeviceAPIMethod::EVENT, repoService, ssl, true);
|
||||
} else {
|
||||
if (request.getMethod() == Net::HTTPRequest::HTTP_PUT) {
|
||||
return new DeviceRepoCommandHandler(matchUri, DeviceAPIMethod::UNKNOWN, repoService, ssl);
|
||||
} else if (request.getMethod() == Net::HTTPRequest::HTTP_GET || request.getMethod() == Net::HTTPRequest::HTTP_POST) {
|
||||
DeviceAPIMethod method = getDeviceAPIMethod(request);
|
||||
if (method == DeviceAPIMethod::GET) {
|
||||
return new DeviceRepoInfoHandler( matchUri, method, repoService, infoCategoryFuncs, ssl );
|
||||
} else if (method == DeviceAPIMethod::COMMAND || method == DeviceAPIMethod::COMMAND) {
|
||||
return new DeviceRepoCommandHandler(matchUri, method, repoService, infoCategoryFuncs, ssl);
|
||||
} else if (method == DeviceAPIMethod::EVENT) {
|
||||
return new DeviceRepoEventHandler( matchUri, method, repoService, infoCategoryFuncs, ssl, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
return service->createErrorHandler(request, ssl, "", Net::HTTPResponse::HTTP_BAD_REQUEST);
|
||||
}
|
||||
|
||||
|
||||
|
||||
DeviceRepoHandlerFactory::DeviceRepoHandlerFactory(devicerepository::DeviceRepositoryService* 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();
|
||||
} });
|
||||
|
||||
infoCategoryFuncs.insert({"status_topic", [](Poco::URI& matchUri, devicerepository::Device* device, Poco::Net::HTTPServerRequest &req, const std::string& category, bool ssl)->Variant {
|
||||
return device->getStatusTopic();
|
||||
} });
|
||||
|
||||
infoCategoryFuncs.insert({"command_topic", [](Poco::URI& matchUri, devicerepository::Device* device, Poco::Net::HTTPServerRequest &req, const std::string& category, bool ssl)->Variant {
|
||||
return device->getCmdTopic();
|
||||
} });
|
||||
|
||||
infoCategoryFuncs.insert({"current_state", [](Poco::URI& matchUri, devicerepository::Device* device, Poco::Net::HTTPServerRequest &req, const std::string& category, bool ssl)->Variant {
|
||||
auto retval = device->getCurrentState();
|
||||
if (retval.IsMap()) {
|
||||
retval["timestamp"] = utils::timestamp_to_ISO8601(device->getCurrentStateTimestamp());
|
||||
}
|
||||
return retval;
|
||||
} });
|
||||
|
||||
infoCategoryFuncs.insert({"last_state", [](Poco::URI& matchUri, devicerepository::Device* device, Poco::Net::HTTPServerRequest &req, const std::string& category, bool ssl)->Variant {
|
||||
auto retval = device->getLastState();
|
||||
if (retval.IsMap()) {
|
||||
retval["timestamp"] = utils::timestamp_to_ISO8601(device->getLastStateTimestamp());
|
||||
}
|
||||
return retval;
|
||||
} });
|
||||
|
||||
infoCategoryFuncs.insert({"uri", [=](Poco::URI& matchUri, devicerepository::Device* device, Poco::Net::HTTPServerRequest &req, const std::string& category, bool ssl)->Variant {
|
||||
std::stringstream ss;
|
||||
if (ssl) {
|
||||
ss << "https://";
|
||||
} else {
|
||||
ss << "http://";
|
||||
}
|
||||
ss << req.getHost() << matchUri.toString() << device->getDeviceID();
|
||||
return ss.str();
|
||||
} });
|
||||
}
|
||||
|
||||
|
||||
|
||||
DeviceAPIMethod DeviceRepoHandlerFactory::getDeviceAPIMethod(Net::HTTPServerRequest &req) noexcept {
|
||||
Net::HTMLForm form;
|
||||
DeviceAPIMethod retval = DeviceAPIMethod::GET; // default
|
||||
if (req.getMethod() == Net::HTTPRequest::HTTP_POST && req.has("Content-type")
|
||||
&& req.get("Content-type") == Net::HTMLForm::ENCODING_URL) {
|
||||
form = Net::HTMLForm(req, req.stream());
|
||||
} else {
|
||||
form = Net::HTMLForm(req);
|
||||
}
|
||||
std::string methodRaw;
|
||||
if (form.has("method")) {
|
||||
methodRaw = form.has("method");
|
||||
} else if (req.has("API-Method")) {
|
||||
methodRaw = req.has("API-Method");
|
||||
} else if (req.has("X-API-Method")) {
|
||||
methodRaw = req.has("X-API-Method");
|
||||
}
|
||||
if (!methodRaw.empty()) {
|
||||
if (methodRaw == "command") {
|
||||
retval = DeviceAPIMethod::COMMAND;
|
||||
} else if (methodRaw == "statusupdate") {
|
||||
retval = DeviceAPIMethod::STATUSUPDATE;
|
||||
} else if (methodRaw == "event") {
|
||||
retval = DeviceAPIMethod::EVENT;
|
||||
} else {
|
||||
throw webserver::webresource::WebResourceHandlerException(
|
||||
(boost::format("Unknown device api method \"%1%\"") % methodRaw).str(),
|
||||
Net::HTTPResponse::HTTPStatus::HTTP_BAD_REQUEST );
|
||||
}
|
||||
} else if (form.has("event")) {
|
||||
retval = DeviceAPIMethod::EVENT;
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
} // namespace resources
|
||||
} // namespace webserver
|
||||
} // namespace service
|
||||
} // namespace cosgi
|
||||
} // namespace hos
|
||||
|
||||
|
||||
|
||||
|
||||
+125
-109
@@ -9,9 +9,19 @@
|
||||
#include <Poco/Net/HTTPRequestHandler.h>
|
||||
#include <Variant/Variant.h>
|
||||
#include <ros/ros.h>
|
||||
#pragma once
|
||||
|
||||
|
||||
#include <hos/cosgi/service/Implementation.h>
|
||||
#include <hos/cosgi/service/webserver/WebServerService.h>
|
||||
#include <hos/cosgi/service/webserver/webresource/WebResourceHandlerImpl.h>
|
||||
#include <hos/cosgi/service/devicerepository/DeviceRepositoryService.h>
|
||||
#include <Poco/Net/HTTPRequestHandler.h>
|
||||
#include <Variant/Variant.h>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <queue>
|
||||
#include <condition_variable>
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
|
||||
@@ -30,54 +40,90 @@ enum class DeviceAPIMethod {
|
||||
GET,
|
||||
COMMAND,
|
||||
STATUSUPDATE,
|
||||
REGISTEREVENT
|
||||
EVENT,
|
||||
UNKNOWN
|
||||
};
|
||||
|
||||
|
||||
|
||||
class DeviceEventQueue
|
||||
: public virtual interface::Observer<devicerepository::DeviceEventType, devicerepository::DeviceEvent> {
|
||||
public:
|
||||
const std::string clientID;
|
||||
DeviceEventType eventType = DeviceEventType::ALL_EVENTS;
|
||||
DeviceRepositoryService* const repoService;
|
||||
DeviceEventType eventType;
|
||||
Device* device;
|
||||
Device* device = nullptr;
|
||||
std::string pointer;
|
||||
std::queue<libvariant::Variant> eventQueue;
|
||||
bool registeredToEvent = false;
|
||||
size_t openConnectionCount = 0;
|
||||
std::queue<libvariant::Variant> _eventQueue;
|
||||
std::mutex _mutex;
|
||||
std::condition_variable _cv;
|
||||
|
||||
DeviceEventQueue( const std::string& clientID,
|
||||
const DeviceEventType eventType,
|
||||
DeviceRepositoryService* repoService,
|
||||
Device* device,
|
||||
std::string pointer )
|
||||
: clientID(clientID), eventType(eventType), repoService(repoService), device(device), pointer(pointer) {}
|
||||
|
||||
virtual void notificationReceived( interface::Observer<devicerepository::DeviceEventType,
|
||||
devicerepository::DeviceEvent>::observable_type* o,
|
||||
const devicerepository::DeviceEventType& key,
|
||||
const devicerepository::DeviceEvent* e) noexcept override {
|
||||
if (eventType == DeviceEventType::ALL_EVENTS || eventType == key) {
|
||||
switch (key) {
|
||||
case devicerepository::DeviceEventType::COMMAND_RECEIVED:
|
||||
break;
|
||||
case devicerepository::DeviceEventType::STATUS_UPDATE_RECEIVED:
|
||||
break;
|
||||
case devicerepository::DeviceEventType::STATE_CHANGED:
|
||||
break;
|
||||
case devicerepository::DeviceEventType::DEVICE_ADDED:
|
||||
break;
|
||||
case devicerepository::DeviceEventType::DEVICE_REMOVED:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
DeviceRepositoryService* repoService )
|
||||
: clientID(clientID), repoService(repoService) {}
|
||||
void pushEvent(const libvariant::Variant& event) {
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_mutex);
|
||||
_eventQueue.push(event);
|
||||
}
|
||||
_cv.notify_one();
|
||||
}
|
||||
libvariant::Variant popEvent() {
|
||||
std::unique_lock<std::mutex> lock(_mutex);
|
||||
libvariant::Variant retval;
|
||||
while (_eventQueue.empty()) {
|
||||
_cv.wait(lock);
|
||||
}
|
||||
retval = _eventQueue.front();
|
||||
_eventQueue.pop();
|
||||
return retval;
|
||||
}
|
||||
void checkRegistration(DeviceEventType eventType, Device* device, const std::string& pointer) {
|
||||
if (!registeredToEvent || eventType != this->eventType || device != this->device) {
|
||||
unregisterEvent();
|
||||
registerEvent(eventType, device);
|
||||
}
|
||||
if (this->pointer != pointer) {
|
||||
this->pointer = pointer;
|
||||
}
|
||||
}
|
||||
|
||||
void registerEvent(DeviceEventType eventType, Device* device = nullptr) {
|
||||
std::lock_guard<std::mutex> lock(_mutex);
|
||||
if (!registeredToEvent) {
|
||||
if (device) {
|
||||
device->addObserver(eventType, this);
|
||||
} else {
|
||||
repoService->addObserver(eventType, this);
|
||||
}
|
||||
this->device = device;
|
||||
this->eventType = eventType;
|
||||
registeredToEvent = true;
|
||||
}
|
||||
}
|
||||
void unregisterEvent() {
|
||||
std::lock_guard<std::mutex> lock(_mutex);
|
||||
if (registeredToEvent) {
|
||||
if (device) {
|
||||
device->deleteObserver(this);
|
||||
} else {
|
||||
repoService->deleteObserver(this);
|
||||
}
|
||||
registeredToEvent = false;
|
||||
}
|
||||
}
|
||||
~DeviceEventQueue() {
|
||||
unregisterEvent();
|
||||
}
|
||||
virtual void notificationReceived( interface::Observer<devicerepository::DeviceEventType, devicerepository::DeviceEvent>::observable_type* o,
|
||||
const devicerepository::DeviceEventType& key,
|
||||
const devicerepository::DeviceEvent* e) noexcept override;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
class DeviceRepoHandlerFactory
|
||||
: public virtual webserver::WebResourceHandlerFactory,
|
||||
public implementation::Service<DeviceRepoHandlerFactory> {
|
||||
@@ -106,140 +152,110 @@ public:
|
||||
return false;
|
||||
}
|
||||
|
||||
static DeviceAPIMethod getDeviceAPIMethod() noexcept;
|
||||
|
||||
std::shared_ptr<DeviceEventQueue> getDeviceEventQueue( const std::string& clientID,
|
||||
const DeviceEventType eventType,
|
||||
DeviceRepositoryService* repoService,
|
||||
Device* device,
|
||||
std::string pointer );
|
||||
std::shared_ptr<DeviceEventQueue> getDeviceEventQueue( const std::string& clientID );
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Implements a REST API for accessing the device repository and getting info about a device.
|
||||
*
|
||||
* HTTP GET REQUESTS:
|
||||
* 1. http://[prefixPath]/devices - Returns all known devices
|
||||
* 2. http://[prefixPath]/devices/[deviceID] - Returns complete info about the given device as JSON object.
|
||||
* 3. http://[prefixPath]/devices/[deviceID]/[infoCategory]/[infoPath] - Returns selected info about the given device as JSON object ([infoCategory]/[infoPath] is treated as JSON Path selecting parts of the complete JSON info object from 1).
|
||||
* 4. http://[prefixPath]/devices/[deviceID]/[cmdPath]?method=command&value=[cmdValue] - Sends the command [cmdPath]/[cmdValue] to the given device.
|
||||
* 5. http://[prefixPath]/devices/[deviceID]/[statusPath]?method=statusupdate&value=[statusValue] - Sends the status update [statusPath]/[statusValue] to the given device (Only useful for virtual devices).
|
||||
*
|
||||
* HTTP POST REQUESTS (content-type: application/x-www-form-urlencoded):
|
||||
* - Basically the same as 4 and 5 from the HTTP GET REQUESTS list, but parameters are transmitted in the request body instead of the URL.
|
||||
*
|
||||
* HTTP POST REQUESTS (content-type: application/json):
|
||||
* - http://[prefixPath]/devices/[deviceID]/[method]/[methodPath] - Semantically the same as 4 and 5 from the HTTP GET REQUESTS list, but the method parameter is part of the URL and the request body only contains a single JSON text.
|
||||
*
|
||||
*
|
||||
* A note about JSON object returned by the above URLs: According to RFC 4627 a JSON text is a serialized object or array.
|
||||
* This was later relaxed by RFC 7159 which defines that a JSON text is a serialized value. Our API adheres to RFC 7159 and may return
|
||||
* 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
|
||||
class DeviceRepoHandler
|
||||
: public webserver::webresource::WebResourceHandlerImpl {
|
||||
|
||||
protected:
|
||||
DeviceRepoHandlerFactory* parent;
|
||||
Poco::URI matchUri;
|
||||
devicerepository::DeviceRepositoryService* repoService;
|
||||
const std::map<std::string, DeviceRepoHandlerFactory::infoCategoryFunc_t>& infoCategoryFuncs;
|
||||
bool ssl;
|
||||
|
||||
|
||||
public:
|
||||
|
||||
DeviceRepoHandler( DeviceRepoHandlerFactory* parent,
|
||||
Poco::URI matchUri,
|
||||
devicerepository::DeviceRepositoryService* repoService,
|
||||
const std::map<std::string, DeviceRepoHandlerFactory::infoCategoryFunc_t>& infoCategoryFuncs,
|
||||
bool ssl = false )
|
||||
: parent(parent), matchUri(matchUri), repoService(repoService), infoCategoryFuncs(infoCategoryFuncs), ssl(ssl) {}
|
||||
|
||||
virtual void _handleRequest(Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp) override;
|
||||
|
||||
void __handleInfo(Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp);
|
||||
|
||||
void __handleCommand(Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp, DeviceAPIMethod method);
|
||||
|
||||
void __handleEventLongPolling(Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp);
|
||||
|
||||
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>() );
|
||||
const std::string& path = 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) {}
|
||||
|
||||
static DeviceAPIMethod getDeviceAPIMethod(Poco::Net::HTTPServerRequest &req) noexcept;
|
||||
};
|
||||
|
||||
|
||||
|
||||
/*
|
||||
class DeviceRepoInfoHandler
|
||||
: public DeviceRepoHandlerBase {
|
||||
|
||||
protected:
|
||||
DeviceAPIMethod method;
|
||||
|
||||
const std::map<std::string, DeviceRepoHandlerFactory::infoCategoryFunc_t>& infoCategoryFuncs;
|
||||
public:
|
||||
|
||||
DeviceRepoInfoHandler( Poco::URI matchUri,
|
||||
DeviceAPIMethod method,
|
||||
devicerepository::DeviceRepositoryService* repoService,
|
||||
const std::map<std::string, DeviceRepoHandlerFactory::infoCategoryFunc_t>& infoCategoryFuncs,
|
||||
bool ssl = false )
|
||||
: DeviceRepoHandlerBase(matchUri, repoService, infoCategoryFuncs, ssl), method(method) {}
|
||||
: DeviceRepoHandlerBase(matchUri, method, repoService, ssl), infoCategoryFuncs(infoCategoryFuncs) {}
|
||||
|
||||
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 __handle(Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp, Poco::Net::HTMLForm& form);
|
||||
virtual void _handleRequest(Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp) override;
|
||||
|
||||
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>() );
|
||||
};
|
||||
|
||||
|
||||
|
||||
class DeviceRepoEventLongPollingHandler
|
||||
class DeviceRepoCommandHandler
|
||||
: public DeviceRepoHandlerBase {
|
||||
|
||||
protected:
|
||||
|
||||
|
||||
public:
|
||||
|
||||
DeviceRepoEventLongPollingHandler( Poco::URI matchUri,
|
||||
DeviceRepoCommandHandler( Poco::URI matchUri,
|
||||
DeviceAPIMethod method,
|
||||
devicerepository::DeviceRepositoryService* repoService,
|
||||
const std::map<std::string, DeviceRepoHandlerFactory::infoCategoryFunc_t>& infoCategoryFuncs,
|
||||
bool ssl = false )
|
||||
: DeviceRepoHandlerBase(matchUri, repoService, infoCategoryFuncs, ssl) {}
|
||||
: DeviceRepoHandlerBase(matchUri, method, repoService, 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 _handleRequest(Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp) override;
|
||||
|
||||
void sendDeviceCommand( devicerepository::Device* device,
|
||||
Poco::Net::HTTPServerRequest &req,
|
||||
const std::string& cmd,
|
||||
const std::vector<std::string>& path = std::vector<std::string>() );
|
||||
};
|
||||
|
||||
|
||||
|
||||
class DeviceRepoEventWebSocketHandler
|
||||
class DeviceRepoEventHandler
|
||||
: public DeviceRepoHandlerBase {
|
||||
|
||||
protected:
|
||||
|
||||
|
||||
bool webSocketConnection;
|
||||
public:
|
||||
|
||||
DeviceRepoEventWebSocketHandler( Poco::URI matchUri,
|
||||
DeviceRepoEventHandler( Poco::URI matchUri,
|
||||
DeviceAPIMethod method,
|
||||
devicerepository::DeviceRepositoryService* repoService,
|
||||
const std::map<std::string, DeviceRepoHandlerFactory::infoCategoryFunc_t>& infoCategoryFuncs,
|
||||
bool ssl = false )
|
||||
: DeviceRepoHandlerBase(matchUri, repoService, infoCategoryFuncs, 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;
|
||||
bool ssl = false,
|
||||
bool webSocketConnection = false)
|
||||
: DeviceRepoHandlerBase(matchUri, method, repoService, ssl), webSocketConnection(webSocketConnection) {}
|
||||
|
||||
virtual void _handleRequest(Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp) override;
|
||||
};
|
||||
|
||||
*/
|
||||
|
||||
|
||||
} // namespace webresource
|
||||
|
||||
+306
@@ -0,0 +1,306 @@
|
||||
|
||||
#include "DeviceRepoHandler.h"
|
||||
#include <hos/utils/Timestamp.h>
|
||||
#include <boost/format.hpp>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <Poco/URI.h>
|
||||
#include <Poco/Timestamp.h>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
|
||||
|
||||
// Service Namespace
|
||||
namespace hos {
|
||||
namespace cosgi {
|
||||
namespace service {
|
||||
namespace devicerepository {
|
||||
namespace webresource {
|
||||
|
||||
|
||||
|
||||
using namespace Poco;
|
||||
using namespace libvariant;
|
||||
|
||||
|
||||
HOS_SERVICE_IMPL_INFO( DeviceRepoHandlerFactory, // Implementation
|
||||
service::webserver::WebResourceHandlerFactory::SERVICE_NAME, // Service Name
|
||||
1, 0 ) // Version
|
||||
|
||||
|
||||
DeviceRepoHandlerFactory::DeviceRepoHandlerFactory(devicerepository::DeviceRepositoryService* 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();
|
||||
} });
|
||||
|
||||
infoCategoryFuncs.insert({"status_topic", [](Poco::URI& matchUri, devicerepository::Device* device, Poco::Net::HTTPServerRequest &req, const std::string& category, bool ssl)->Variant {
|
||||
return device->getStatusTopic();
|
||||
} });
|
||||
|
||||
infoCategoryFuncs.insert({"command_topic", [](Poco::URI& matchUri, devicerepository::Device* device, Poco::Net::HTTPServerRequest &req, const std::string& category, bool ssl)->Variant {
|
||||
return device->getCmdTopic();
|
||||
} });
|
||||
|
||||
infoCategoryFuncs.insert({"current_state", [](Poco::URI& matchUri, devicerepository::Device* device, Poco::Net::HTTPServerRequest &req, const std::string& category, bool ssl)->Variant {
|
||||
auto retval = device->getCurrentState();
|
||||
if (retval.IsMap()) {
|
||||
retval["timestamp"] = utils::timestamp_to_ISO8601(device->getCurrentStateTimestamp());
|
||||
}
|
||||
return retval;
|
||||
} });
|
||||
|
||||
infoCategoryFuncs.insert({"last_state", [](Poco::URI& matchUri, devicerepository::Device* device, Poco::Net::HTTPServerRequest &req, const std::string& category, bool ssl)->Variant {
|
||||
auto retval = device->getLastState();
|
||||
if (retval.IsMap()) {
|
||||
retval["timestamp"] = utils::timestamp_to_ISO8601(device->getLastStateTimestamp());
|
||||
}
|
||||
return retval;
|
||||
} });
|
||||
|
||||
infoCategoryFuncs.insert({"uri", [=](Poco::URI& matchUri, devicerepository::Device* device, Poco::Net::HTTPServerRequest &req, const std::string& category, bool ssl)->Variant {
|
||||
std::stringstream ss;
|
||||
if (ssl) {
|
||||
ss << "https://";
|
||||
} else {
|
||||
ss << "http://";
|
||||
}
|
||||
ss << req.getHost() << matchUri.toString() << device->getDeviceID();
|
||||
return ss.str();
|
||||
} });
|
||||
}
|
||||
|
||||
|
||||
DeviceAPIMethod DeviceRepoHandlerFactory::getDeviceAPIMethod() noexcept {
|
||||
|
||||
}
|
||||
|
||||
|
||||
/*std::shared_ptr<DeviceEventQueue> DeviceRepoHandlerFactory::getDeviceEventQueue(std::string clientID) {
|
||||
if (clientID.empty()) {
|
||||
|
||||
} else {
|
||||
|
||||
}
|
||||
}*/
|
||||
|
||||
|
||||
Net::HTTPRequestHandler* DeviceRepoHandlerFactory::createRequestHandler( Poco::URI matchUri, const Net::HTTPServerRequest& request, bool ssl ) noexcept {
|
||||
if(request.find("Upgrade") != request.end() && Poco::icompare(request["Upgrade"], "websocket") == 0)
|
||||
return new DeviceRepoEventWebSocketHandler(matchUri, repoService, infoCategoryFuncs, ssl);
|
||||
else {
|
||||
DeviceAPIMethod method = getDeviceAPIMethod();
|
||||
if (method == DeviceAPIMethod::EVENT) {
|
||||
return new DeviceRepoEventLongPollingHandler(matchUri, repoService, infoCategoryFuncs, ssl);
|
||||
} else {
|
||||
return new DeviceRepoInfoHandler(matchUri, method, repoService, infoCategoryFuncs, ssl);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Variant DeviceRepoHandlerBase::getDeviceInfo(devicerepository::Device* device, Net::HTTPServerRequest &req, const std::string& category, const std::vector<std::string>& path) {
|
||||
Variant retval;
|
||||
|
||||
// We want all info categories
|
||||
if (category.empty()) {
|
||||
for (auto c : infoCategoryFuncs) {
|
||||
retval[c.first] = c.second(matchUri, device, req, category, ssl);
|
||||
}
|
||||
|
||||
// We want a single info category
|
||||
} else {
|
||||
auto c = infoCategoryFuncs.find(category);
|
||||
if (c != infoCategoryFuncs.end()) {
|
||||
retval = c->second(matchUri, device, req, category, ssl);
|
||||
if (path.size() > 0) {
|
||||
std::stringstream ss;
|
||||
for (auto p : path) {
|
||||
ss << "/" << p;
|
||||
}
|
||||
std::string pathstr = ss.str();
|
||||
if (retval.HasPath(pathstr)) {
|
||||
retval = retval.GetPath(pathstr);
|
||||
} else {
|
||||
throw webserver::webresource::WebResourceHandlerException((boost::format("Unknown path \"%1%\" for device info category \"%2%\".") % pathstr % category).str(),
|
||||
Poco::Net::HTTPResponse::HTTPStatus::HTTP_NOT_FOUND);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw webserver::webresource::WebResourceHandlerException((boost::format("Unknown device info category \"%1%\"") % category).str(),
|
||||
Poco::Net::HTTPResponse::HTTPStatus::HTTP_NOT_FOUND);
|
||||
}
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void DeviceRepoHandlerBase::sendDeviceCommand(devicerepository::Device* device, Net::HTTPServerRequest &req, const std::string& cmd, const std::vector<std::string>& path) {
|
||||
Variant cmdMsg;
|
||||
VariantRef mPart = cmdMsg.Ref();
|
||||
if (path.size() > 0) {
|
||||
for (int i = 0; i < path.size(); i++) {
|
||||
mPart[path[i]] = Variant();
|
||||
mPart.ReassignRef(mPart[path[i]]);
|
||||
}
|
||||
}
|
||||
/*
|
||||
* The libvariant json deserializer knows nothing about RFC 7159, therefore non-object or non-array values are rejected.
|
||||
* As a workaround we use the yaml deserializer (yaml can be considered a super-set of json).
|
||||
* Fun fact: the json serializer accepts non-object or non-array values.
|
||||
*/
|
||||
//mPart = DeserializeJSON(cmd);
|
||||
mPart = DeserializeYAML(cmd);
|
||||
device->sendCommand(cmdMsg);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void DeviceRepoInfoHandler::__handle(Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp, Net::HTMLForm& form) {
|
||||
|
||||
std::string methodValue;
|
||||
if (method == DeviceAPIMethod::COMMAND || method == DeviceAPIMethod::STATUSUPDATE) {
|
||||
if (form.has("value")) {
|
||||
methodValue = form.get("value");
|
||||
} else {
|
||||
throw webserver::webresource::WebResourceHandlerException("API method requires a value parameter.",
|
||||
Poco::Net::HTTPResponse::HTTPStatus::HTTP_BAD_REQUEST);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> pathPieces, matchPieces;
|
||||
matchUri.getPathSegments(matchPieces);
|
||||
Poco::URI uri(req.getURI());
|
||||
uri.getPathSegments(pathPieces);
|
||||
|
||||
// No device given
|
||||
if (pathPieces.size() <= matchPieces.size()) {
|
||||
if (method != DeviceAPIMethod::GET) {
|
||||
throw webserver::webresource::WebResourceHandlerException("Requested API method requires a device",
|
||||
Poco::Net::HTTPResponse::HTTPStatus::HTTP_BAD_REQUEST);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
// Device given
|
||||
} else {
|
||||
std::string devid = pathPieces[matchPieces.size()];
|
||||
auto device = repoService->getDevice(devid);
|
||||
if (!device.expired()) {
|
||||
switch (method) {
|
||||
case DeviceAPIMethod::GET:
|
||||
{
|
||||
std::string category;
|
||||
if (pathPieces.size() > matchPieces.size() + 1) {
|
||||
category = pathPieces[matchPieces.size() + 1];
|
||||
}
|
||||
std::vector<std::string> path;
|
||||
for (int i = matchPieces.size() + 2; i < pathPieces.size(); i++) {
|
||||
path.push_back(pathPieces[i]);
|
||||
}
|
||||
Variant result = getDeviceInfo(device.lock().get(), req, category, path);
|
||||
handleResponse(req, resp, result);
|
||||
}
|
||||
break;
|
||||
case DeviceAPIMethod::COMMAND:
|
||||
{
|
||||
std::vector<std::string> path;
|
||||
for (int i = matchPieces.size()+1; i < pathPieces.size(); i++) {
|
||||
path.push_back(pathPieces[i]);
|
||||
}
|
||||
sendDeviceCommand(device.lock().get(), req, methodValue, path);
|
||||
handleResponse(req, resp, std::string("OK: Command send"));
|
||||
}
|
||||
break;
|
||||
case DeviceAPIMethod::STATUSUPDATE:
|
||||
throw webserver::webresource::WebResourceHandlerException("ToDo: Implement method.");
|
||||
default:
|
||||
throw webserver::webresource::WebResourceHandlerException("Internal Server Error");
|
||||
}
|
||||
} else {
|
||||
throw webserver::webresource::WebResourceHandlerException((boost::format("Could not find device \"%1%\"") % devid).str(),
|
||||
Poco::Net::HTTPResponse::HTTPStatus::HTTP_NOT_FOUND);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void DeviceRepoInfoHandler::_handleGET(Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp) {
|
||||
Net::HTMLForm form(req);
|
||||
__handle(req, resp, form);
|
||||
}
|
||||
|
||||
void DeviceRepoInfoHandler::_handlePOST(Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp) {
|
||||
Net::HTMLForm form(req, req.stream());
|
||||
__handle(req, resp, form);
|
||||
}
|
||||
|
||||
|
||||
|
||||
} // namespace resources
|
||||
} // namespace webserver
|
||||
} // namespace service
|
||||
} // namespace cosgi
|
||||
} // namespace hos
|
||||
|
||||
|
||||
|
||||
+250
@@ -0,0 +1,250 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
#include <hos/cosgi/service/Implementation.h>
|
||||
#include <hos/cosgi/service/webserver/WebServerService.h>
|
||||
#include <hos/cosgi/service/webserver/webresource/WebResourceHandlerImpl.h>
|
||||
#include <hos/cosgi/service/devicerepository/DeviceRepositoryService.h>
|
||||
#include <Poco/Net/HTTPRequestHandler.h>
|
||||
#include <Variant/Variant.h>
|
||||
#include <ros/ros.h>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <queue>
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
|
||||
|
||||
|
||||
// Service Namespace
|
||||
namespace hos {
|
||||
namespace cosgi {
|
||||
namespace service {
|
||||
namespace devicerepository {
|
||||
namespace webresource {
|
||||
|
||||
|
||||
|
||||
enum class DeviceAPIMethod {
|
||||
GET,
|
||||
COMMAND,
|
||||
STATUSUPDATE,
|
||||
EVENT
|
||||
};
|
||||
|
||||
|
||||
class DeviceEventQueue
|
||||
: public virtual interface::Observer<devicerepository::DeviceEventType, devicerepository::DeviceEvent> {
|
||||
public:
|
||||
const std::string clientID;
|
||||
DeviceRepositoryService* const repoService;
|
||||
DeviceEventType eventType;
|
||||
Device* device;
|
||||
std::string pointer;
|
||||
std::queue<libvariant::Variant> eventQueue;
|
||||
size_t openConnectionCount = 0;
|
||||
|
||||
DeviceEventQueue( const std::string& clientID,
|
||||
const DeviceEventType eventType,
|
||||
DeviceRepositoryService* repoService,
|
||||
Device* device,
|
||||
std::string pointer )
|
||||
: clientID(clientID), eventType(eventType), repoService(repoService), device(device), pointer(pointer) {}
|
||||
|
||||
virtual void notificationReceived( interface::Observer<devicerepository::DeviceEventType,
|
||||
devicerepository::DeviceEvent>::observable_type* o,
|
||||
const devicerepository::DeviceEventType& key,
|
||||
const devicerepository::DeviceEvent* e) noexcept override {
|
||||
if (eventType == DeviceEventType::ALL_EVENTS || eventType == key) {
|
||||
switch (key) {
|
||||
case devicerepository::DeviceEventType::COMMAND_RECEIVED:
|
||||
break;
|
||||
case devicerepository::DeviceEventType::STATUS_UPDATE_RECEIVED:
|
||||
break;
|
||||
case devicerepository::DeviceEventType::STATE_CHANGED:
|
||||
break;
|
||||
case devicerepository::DeviceEventType::DEVICE_ADDED:
|
||||
break;
|
||||
case devicerepository::DeviceEventType::DEVICE_REMOVED:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
class DeviceRepoHandlerFactory
|
||||
: public virtual webserver::WebResourceHandlerFactory,
|
||||
public implementation::Service<DeviceRepoHandlerFactory> {
|
||||
public:
|
||||
typedef std::function<libvariant::Variant( Poco::URI& matchUri,
|
||||
devicerepository::Device* device,
|
||||
Poco::Net::HTTPServerRequest &req,
|
||||
const std::string& category,
|
||||
bool ssl )> infoCategoryFunc_t;
|
||||
private:
|
||||
webserver::WebServerService* service = nullptr;
|
||||
DeviceRepositoryService* repoService;
|
||||
std::map<std::string, infoCategoryFunc_t> infoCategoryFuncs;
|
||||
std::map<std::string, std::shared_ptr<DeviceEventQueue>> deviceEventQueues;
|
||||
|
||||
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 {
|
||||
this->service = service;
|
||||
}
|
||||
|
||||
virtual bool excludeFromAuthentication() noexcept override {
|
||||
return false;
|
||||
}
|
||||
|
||||
static DeviceAPIMethod getDeviceAPIMethod() noexcept;
|
||||
|
||||
std::shared_ptr<DeviceEventQueue> getDeviceEventQueue( const std::string& clientID,
|
||||
const DeviceEventType eventType,
|
||||
DeviceRepositoryService* repoService,
|
||||
Device* device,
|
||||
std::string pointer );
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Implements a REST API for accessing the device repository and getting info about a device.
|
||||
*
|
||||
* HTTP GET REQUESTS:
|
||||
* 1. http://[prefixPath]/devices - Returns all known devices
|
||||
* 2. http://[prefixPath]/devices/[deviceID] - Returns complete info about the given device as JSON object.
|
||||
* 3. http://[prefixPath]/devices/[deviceID]/[infoCategory]/[infoPath] - Returns selected info about the given device as JSON object ([infoCategory]/[infoPath] is treated as JSON Path selecting parts of the complete JSON info object from 1).
|
||||
* 4. http://[prefixPath]/devices/[deviceID]/[cmdPath]?method=command&value=[cmdValue] - Sends the command [cmdPath]/[cmdValue] to the given device.
|
||||
* 5. http://[prefixPath]/devices/[deviceID]/[statusPath]?method=statusupdate&value=[statusValue] - Sends the status update [statusPath]/[statusValue] to the given device (Only useful for virtual devices).
|
||||
*
|
||||
* HTTP POST REQUESTS (content-type: application/x-www-form-urlencoded):
|
||||
* - Basically the same as 4 and 5 from the HTTP GET REQUESTS list, but parameters are transmitted in the request body instead of the URL.
|
||||
*
|
||||
* HTTP POST REQUESTS (content-type: application/json):
|
||||
* - http://[prefixPath]/devices/[deviceID]/[method]/[methodPath] - Semantically the same as 4 and 5 from the HTTP GET REQUESTS list, but the method parameter is part of the URL and the request body only contains a single JSON text.
|
||||
*
|
||||
*
|
||||
* A note about JSON object returned by the above URLs: According to RFC 4627 a JSON text is a serialized object or array.
|
||||
* This was later relaxed by RFC 7159 which defines that a JSON text is a serialized value. Our API adheres to RFC 7159 and may return
|
||||
* 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 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) {}
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
class DeviceRepoInfoHandler
|
||||
: public DeviceRepoHandlerBase {
|
||||
|
||||
protected:
|
||||
DeviceAPIMethod method;
|
||||
|
||||
public:
|
||||
|
||||
DeviceRepoInfoHandler( Poco::URI matchUri,
|
||||
DeviceAPIMethod method,
|
||||
devicerepository::DeviceRepositoryService* repoService,
|
||||
const std::map<std::string, DeviceRepoHandlerFactory::infoCategoryFunc_t>& infoCategoryFuncs,
|
||||
bool ssl = false )
|
||||
: DeviceRepoHandlerBase(matchUri, repoService, infoCategoryFuncs, ssl), method(method) {}
|
||||
|
||||
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 __handle(Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp, Poco::Net::HTMLForm& form);
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
class DeviceRepoEventLongPollingHandler
|
||||
: public DeviceRepoHandlerBase {
|
||||
|
||||
protected:
|
||||
|
||||
|
||||
public:
|
||||
|
||||
DeviceRepoEventLongPollingHandler( Poco::URI matchUri,
|
||||
devicerepository::DeviceRepositoryService* repoService,
|
||||
const std::map<std::string, DeviceRepoHandlerFactory::infoCategoryFunc_t>& infoCategoryFuncs,
|
||||
bool ssl = false )
|
||||
: DeviceRepoHandlerBase(matchUri, repoService, infoCategoryFuncs, 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;
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
class DeviceRepoEventWebSocketHandler
|
||||
: public DeviceRepoHandlerBase {
|
||||
|
||||
protected:
|
||||
|
||||
|
||||
public:
|
||||
|
||||
DeviceRepoEventWebSocketHandler( Poco::URI matchUri,
|
||||
devicerepository::DeviceRepositoryService* repoService,
|
||||
const std::map<std::string, DeviceRepoHandlerFactory::infoCategoryFunc_t>& infoCategoryFuncs,
|
||||
bool ssl = false )
|
||||
: DeviceRepoHandlerBase(matchUri, repoService, infoCategoryFuncs, 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;
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
} // namespace webresource
|
||||
} // namespace devicerepository
|
||||
} // namespace service
|
||||
} // namespace cosgi
|
||||
} // namespace hos
|
||||
|
||||
@@ -56,3 +56,24 @@ EVENT Parameters:
|
||||
==============================================================
|
||||
|
||||
|
||||
GET/POST Info Handler [Condition: GET/POST + AP::GET]
|
||||
|
||||
GET/POST/PUT Command Handler [Condition: GET/POST/PUT + API::COMMAND/API::STATUSUPDATE]
|
||||
|
||||
GET/POST Event Handler [Condition: GET/POST + API::EVENT]
|
||||
|
||||
WebSocket Event Handler [Condition: WebSocket]
|
||||
|
||||
|
||||
|
||||
IF GET/POST:
|
||||
Get API Method
|
||||
Get other parameters
|
||||
Do stuff
|
||||
ELSEIF PUT:
|
||||
Get other parameters
|
||||
Do stuff
|
||||
ELSEIF WebSocket:
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -9,9 +9,8 @@ define([
|
||||
});
|
||||
var _updateSocketOpened = false;
|
||||
var _updateSocket = new Socket({
|
||||
url: config.hos.deviceRepoUrl,
|
||||
interval: 5000,
|
||||
transport: xhr.get });
|
||||
url: config.hos.deviceRepoUrl + "?onevent=statechanged",
|
||||
transport: xhr.post });
|
||||
_updateSocket.on("open", function(event){
|
||||
_socketOpened = true;
|
||||
console.log("Socket Opened ", event.message);
|
||||
|
||||
Reference in New Issue
Block a user