.
This commit is contained in:
@@ -8,7 +8,6 @@ find_package(catkin REQUIRED COMPONENTS
|
||||
hos_cosgi_core
|
||||
hos_cpp_utils
|
||||
hos_deps
|
||||
cmake_modules
|
||||
roscpp
|
||||
)
|
||||
|
||||
@@ -17,7 +16,6 @@ find_package(APR REQUIRED)
|
||||
find_package(CELIX REQUIRED)
|
||||
find_package(Boost REQUIRED)
|
||||
find_package(libvariant REQUIRED)
|
||||
find_package(libhttpserver REQUIRED)
|
||||
find_package(Poco REQUIRED COMPONENTS Net NetSSL)
|
||||
|
||||
|
||||
@@ -37,6 +35,8 @@ catkin_package(
|
||||
|
||||
add_definitions(
|
||||
-std=c++11
|
||||
# We need to explicitly enable boost nanoseconds
|
||||
# -DBOOST_DATE_TIME_POSIX_TIME_STD_CONFIG
|
||||
)
|
||||
|
||||
include_directories(
|
||||
@@ -45,7 +45,6 @@ include_directories(
|
||||
${APR_INCLUDE_DIR}
|
||||
${APR_INCLUDE_DIR}/apr-1.0
|
||||
${libvariant_INCLUDE_DIRS}
|
||||
${libhttpserver_INCLUDE_DIRS}
|
||||
${Poco_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
@@ -57,7 +56,7 @@ bundle(hos_cosgi_webserver
|
||||
SOURCES
|
||||
${PROJECT_SOURCE_DIR}/src/BundleActivator
|
||||
${PROJECT_SOURCE_DIR}/src/hos/cosgi/service/webserver/WebServerServiceImpl
|
||||
# ${PROJECT_SOURCE_DIR}/src/hos/cosgi/service/webserver/resources/DevicesHTTPResource
|
||||
${PROJECT_SOURCE_DIR}/src/hos/cosgi/service/webserver/resources/DevicesResource
|
||||
${PROJECT_SOURCE_DIR}/src/hos/cosgi/service/webserver/resources/SchemaResource
|
||||
)
|
||||
|
||||
@@ -65,7 +64,6 @@ target_link_libraries(hos_cosgi_webserver
|
||||
${catkin_LIBRARIES}
|
||||
${APR_LIBRARIES}
|
||||
${libvariant_LIBRARIES}
|
||||
${libhttpserver_LIBRARIES}
|
||||
${Poco_LIBRARIES}
|
||||
)
|
||||
|
||||
|
||||
+42
-5
@@ -30,7 +30,8 @@ class WebResourceHandlerException
|
||||
protected:
|
||||
Poco::Net::HTTPResponse::HTTPStatus statusCode;
|
||||
public:
|
||||
WebResourceHandlerException(const std::string& msg, Poco::Net::HTTPResponse::HTTPStatus statusCode)
|
||||
WebResourceHandlerException( const std::string& msg,
|
||||
Poco::Net::HTTPResponse::HTTPStatus statusCode = Poco::Net::HTTPResponse::HTTPStatus::HTTP_INTERNAL_SERVER_ERROR)
|
||||
: std::runtime_error(msg), statusCode(statusCode) {}
|
||||
Poco::Net::HTTPResponse::HTTPStatus getStatusCode() const {
|
||||
return statusCode;
|
||||
@@ -142,8 +143,10 @@ protected:
|
||||
void handleResponse( const Poco::Net::HTTPServerRequest& request,
|
||||
Poco::Net::HTTPServerResponse &resp,
|
||||
const libvariant::Variant& msg,
|
||||
const std::string& mime = "text/plain",
|
||||
Poco::Net::HTTPResponse::HTTPStatus status = Poco::Net::HTTPResponse::HTTPStatus::HTTP_OK ) {
|
||||
resp.setStatusAndReason(status);
|
||||
resp.setContentType(mime);
|
||||
auto format = getResponseDataFormat(request);
|
||||
resp.send() << encodeVariant(msg, request, format);
|
||||
}
|
||||
@@ -151,24 +154,42 @@ protected:
|
||||
void handleResponse( const Poco::Net::HTTPServerRequest& request,
|
||||
Poco::Net::HTTPServerResponse &resp,
|
||||
const std::string& msg,
|
||||
const std::string& mime = "text/plain",
|
||||
Poco::Net::HTTPResponse::HTTPStatus status = Poco::Net::HTTPResponse::HTTPStatus::HTTP_OK ) {
|
||||
resp.setStatusAndReason(status);
|
||||
resp.setContentType(mime);
|
||||
resp.send() << msg;
|
||||
}
|
||||
|
||||
void handleErrorResponse( const Poco::Net::HTTPServerRequest& request,
|
||||
Poco::Net::HTTPServerResponse &resp,
|
||||
const std::string& msg = "Internal Server Error",
|
||||
const std::string& msg,
|
||||
Poco::Net::HTTPResponse::HTTPStatus status = Poco::Net::HTTPResponse::HTTPStatus::HTTP_INTERNAL_SERVER_ERROR ) {
|
||||
resp.setStatusAndReason(status);
|
||||
resp.send() << "ERROR: " << msg;
|
||||
resp.setContentType("text/plain");
|
||||
std::ostream& out = resp.send();
|
||||
out << "ERROR: " << msg;
|
||||
addDebugInfo(out, request, resp);
|
||||
}
|
||||
|
||||
void handleErrorResponse( const Poco::Net::HTTPServerRequest& request,
|
||||
Poco::Net::HTTPServerResponse &resp,
|
||||
Poco::Net::HTTPResponse::HTTPStatus status = Poco::Net::HTTPResponse::HTTPStatus::HTTP_INTERNAL_SERVER_ERROR ) {
|
||||
resp.setStatusAndReason(status);
|
||||
resp.setContentType("text/plain");
|
||||
std::ostream& out = resp.send();
|
||||
out << "ERROR: " << resp.getReason();
|
||||
addDebugInfo(out, request, resp);
|
||||
}
|
||||
|
||||
void handleErrorResponse( const Poco::Net::HTTPServerRequest& request,
|
||||
Poco::Net::HTTPServerResponse &resp,
|
||||
const WebResourceHandlerException& e ) {
|
||||
resp.setStatusAndReason(e.getStatusCode());
|
||||
resp.send() << "ERROR: " << e.what();
|
||||
resp.setContentType("text/plain");
|
||||
std::ostream& out = resp.send();
|
||||
out << "ERROR: " << e.what();
|
||||
addDebugInfo(out, request, resp);
|
||||
}
|
||||
|
||||
void handleErrorResponse( const Poco::Net::HTTPServerRequest& request,
|
||||
@@ -176,7 +197,23 @@ protected:
|
||||
const std::exception& e,
|
||||
Poco::Net::HTTPResponse::HTTPStatus status = Poco::Net::HTTPResponse::HTTPStatus::HTTP_INTERNAL_SERVER_ERROR ) {
|
||||
resp.setStatusAndReason(status);
|
||||
resp.send() << "ERROR: " << e.what();
|
||||
resp.setContentType("text/plain");
|
||||
std::ostream& out = resp.send();
|
||||
out << "ERROR: " << e.what();
|
||||
addDebugInfo(out, request, resp);
|
||||
}
|
||||
|
||||
void addDebugInfo(std::ostream& out, const Poco::Net::HTTPServerRequest& req, Poco::Net::HTTPServerResponse &resp) {
|
||||
out << std::endl << std::endl
|
||||
<<"========== DEBUG ==========" << std::endl
|
||||
<< "getURI: " << req.getURI() << std::endl
|
||||
<< "getHost: " << req.getHost() << std::endl
|
||||
<< "getMethod: " << req.getMethod() << std::endl
|
||||
<< "getTransferEncoding: " << req.getTransferEncoding() << std::endl
|
||||
<< "getVersion: " << req.getVersion() << std::endl
|
||||
<< "getContentType: " << req.getContentType() << std::endl
|
||||
<< "getContentLength: " << req.getContentLength() << std::endl
|
||||
<< "======== DEBUG END ========" << std::endl ;
|
||||
}
|
||||
|
||||
virtual void _handleRequest(Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp) {
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <ros/ros.h>
|
||||
#include "resources/SchemaResource.h"
|
||||
#include "resources/DevicesResource.h"
|
||||
|
||||
|
||||
|
||||
@@ -62,21 +63,9 @@ void WebServerServiceImpl::setRepoService(devicerepository::DeviceRepositoryServ
|
||||
|
||||
|
||||
|
||||
void WebServerServiceImpl::RequestHandlerFactory::DefaultRequestHandler::handleRequest(
|
||||
void WebServerServiceImpl::RequestHandlerFactory::DefaultRequestHandler::_handleRequest(
|
||||
Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp ) {
|
||||
resp.setStatusAndReason(Poco::Net::HTTPResponse::HTTP_FORBIDDEN);
|
||||
resp.setContentType("text/plain");
|
||||
resp.send()
|
||||
<< "ERROR: " << resp.getReason() << std::endl
|
||||
<< "========== DEBUG ==========" << std::endl
|
||||
<< "getURI: " << req.getURI() << std::endl
|
||||
<< "getHost: " << req.getHost() << std::endl
|
||||
<< "getMethod: " << req.getMethod() << std::endl
|
||||
<< "getTransferEncoding: " << req.getTransferEncoding() << std::endl
|
||||
<< "getVersion: " << req.getVersion() << std::endl
|
||||
<< "getContentType: " << req.getContentType() << std::endl
|
||||
<< "getContentLength: " << req.getContentLength() << std::endl
|
||||
<< "======== DEBUG END ========" << std::endl ;
|
||||
handleErrorResponse(req, resp, Poco::Net::HTTPResponse::HTTPStatus::HTTP_FORBIDDEN);
|
||||
}
|
||||
|
||||
|
||||
@@ -118,6 +107,9 @@ void WebServerServiceImpl::init() noexcept {
|
||||
return new resources::SchemaResourceHandler(nodehandle, &coreService->getSchemaLoader(), this);
|
||||
})
|
||||
);
|
||||
registerWebResource("/devices",
|
||||
std::make_shared<resources::DevicesResourceHandlerFactory>(this, repoService)
|
||||
);
|
||||
registerWebResource("/default",
|
||||
std::make_shared<resources::GenericWebResourceHandlerFactory>([=](const Net::HTTPServerRequest&, bool)->Net::HTTPRequestHandler* {
|
||||
return new resources::WebResourceHandlerImpl();
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
|
||||
#include <hos/cosgi/service/webserver/WebServerService.h>
|
||||
#include <hos/cosgi/service/webserver/resources/WebResourceHandlerImpl.h>
|
||||
#include <hos/cosgi/service/Implementation.h>
|
||||
#include <hos/cosgi/service/core/CoreService.h>
|
||||
#include <hos/cosgi/service/devicerepository/DeviceRepositoryService.h>
|
||||
@@ -16,15 +17,9 @@
|
||||
|
||||
|
||||
|
||||
#include <httpserver.hpp>
|
||||
//#include "resources/SchemaHTTPResource.h"
|
||||
//#include "resources/DevicesHTTPResource.h"
|
||||
|
||||
|
||||
|
||||
|
||||
#include <Poco/Net/HTTPServer.h>
|
||||
#include <Poco/Net/HTTPRequestHandlerFactory.h>
|
||||
#include <Poco/Net/HTTPRequestHandlerFactory.h>
|
||||
#include <Poco/Net/HTTPRequestHandler.h>
|
||||
#include <Poco/Net/HTTPServerRequest.h>
|
||||
#include <Poco/Net/HTTPServerResponse.h>
|
||||
@@ -44,22 +39,6 @@ namespace service {
|
||||
namespace webserver {
|
||||
|
||||
|
||||
/**
|
||||
* Used for internal error handling.
|
||||
*/
|
||||
class WebResourceHandlerException
|
||||
: public std::runtime_error {
|
||||
protected:
|
||||
Poco::Net::HTTPResponse::HTTPStatus statusCode;
|
||||
public:
|
||||
WebResourceHandlerException(const std::string& msg, Poco::Net::HTTPResponse::HTTPStatus statusCode)
|
||||
: std::runtime_error(msg), statusCode(statusCode) {}
|
||||
Poco::Net::HTTPResponse::HTTPStatus getStatusCode() {
|
||||
return statusCode;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
class WebServerServiceImpl
|
||||
: public virtual WebServerService,
|
||||
@@ -73,9 +52,9 @@ private:
|
||||
WebServerServiceImpl* parent;
|
||||
bool ssl;
|
||||
class DefaultRequestHandler
|
||||
: public Poco::Net::HTTPRequestHandler {
|
||||
: public resources::WebResourceHandlerImpl {
|
||||
public:
|
||||
virtual void handleRequest(Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp) override;
|
||||
virtual void _handleRequest(Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp) override;
|
||||
};
|
||||
public:
|
||||
RequestHandlerFactory(WebServerServiceImpl* parent, bool ssl = false)
|
||||
@@ -98,9 +77,11 @@ private:
|
||||
void init() noexcept;
|
||||
|
||||
public:
|
||||
|
||||
WebServerServiceImpl() {
|
||||
prefixPath.getPathSegments(prefixPathPieces);
|
||||
}
|
||||
|
||||
~WebServerServiceImpl();
|
||||
|
||||
|
||||
|
||||
-298
@@ -1,298 +0,0 @@
|
||||
|
||||
#include "DevicesHTTPResource.h"
|
||||
#include <boost/format.hpp>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
|
||||
|
||||
// Service Namespace
|
||||
namespace hos {
|
||||
namespace cosgi {
|
||||
namespace service {
|
||||
namespace webserver {
|
||||
namespace resources {
|
||||
|
||||
|
||||
|
||||
using namespace httpserver;
|
||||
using namespace libvariant;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Used for internal error handling.
|
||||
*/
|
||||
class ResourceException
|
||||
: public std::runtime_error {
|
||||
protected:
|
||||
int errorCode;
|
||||
public:
|
||||
ResourceException(const std::string& msg, int errorCode)
|
||||
: std::runtime_error(msg), errorCode(errorCode) {}
|
||||
int getErrorCode() {
|
||||
return errorCode;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
void DevicesHTTPResource::init( const std::vector<std::string>& prefixPath,
|
||||
const std::string& registeredPath,
|
||||
const std::string& absolutePath,
|
||||
httpserver::webserver& webserver,
|
||||
WebServerService& service ) {
|
||||
webResource.prefixPath = prefixPath;
|
||||
webResource.disallow_all();
|
||||
webResource.set_allowing(MHD_HTTP_METHOD_GET, true);
|
||||
webResource.set_allowing(MHD_HTTP_METHOD_POST, true);
|
||||
webserver.register_resource(absolutePath, &webResource, true);
|
||||
}
|
||||
|
||||
|
||||
|
||||
DevicesHTTPResource::WebResource::WebResource(devicerepository::DeviceRepositoryService* repoService)
|
||||
: repoService(repoService) {
|
||||
infoCategoryFuncs.insert({"type", [](devicerepository::Device* device, const httpserver::http_request& req, const std::string& category)->Variant {
|
||||
return device->getDeviceType();
|
||||
} });
|
||||
infoCategoryFuncs.insert({"status_topic", [](devicerepository::Device* device, const httpserver::http_request& req, const std::string& category)->Variant {
|
||||
return device->getStatusTopic();
|
||||
} });
|
||||
infoCategoryFuncs.insert({"command_topic", [](devicerepository::Device* device, const httpserver::http_request& req, const std::string& category)->Variant {
|
||||
return device->getCmdTopic();
|
||||
} });
|
||||
infoCategoryFuncs.insert({"current_state", [](devicerepository::Device* device, const httpserver::http_request& req, const std::string& category)->Variant {
|
||||
return device->getCurrentState();
|
||||
} });
|
||||
infoCategoryFuncs.insert({"last_state", [](devicerepository::Device* device, const httpserver::http_request& req, const std::string& category)->Variant {
|
||||
return device->getLastState();
|
||||
} });
|
||||
/*infoCategoryFuncs.insert({"url", [this](devicerepository::Device* device, const std::string& category, const httpserver::http_request& req)->Variant {
|
||||
std::stringstream ss;
|
||||
req.
|
||||
ss << http
|
||||
return device->getLastState();
|
||||
} });*/
|
||||
}
|
||||
|
||||
|
||||
void DevicesHTTPResource::WebResource::render_GET(const httpserver::http_request& req, httpserver::http_response** res) {
|
||||
std::cout << "DevicesHTTPResource::render_GET()" << std::endl;
|
||||
std::cout << req;
|
||||
|
||||
try {
|
||||
// Get method parameter
|
||||
DataMethod dataMethod = DataMethod::GET; // Default method is get
|
||||
std::string methodValue;
|
||||
std::string methodRaw = req.get_arg("method");
|
||||
if (!methodRaw.empty()) {
|
||||
if (methodRaw.compare("get") == 0) {
|
||||
// do nothing
|
||||
} else if (methodRaw.compare("command") == 0) {
|
||||
dataMethod = DataMethod::COMMAND;
|
||||
methodValue = req.get_arg("value");
|
||||
if (methodValue.empty()) {
|
||||
throw ResourceException((boost::format("Method parameter \"%1%\" requires a value parameter.") % methodRaw).str(), 400);
|
||||
}
|
||||
} else if (methodRaw.compare("statusupdate") == 0) {
|
||||
dataMethod = DataMethod::STATUSUPDATE;
|
||||
methodValue = req.get_arg("value");
|
||||
if (methodValue.empty()) {
|
||||
throw ResourceException((boost::format("Method parameter \"%1%\" requires a value parameter.") % methodRaw).str(), 400);
|
||||
}
|
||||
} else {
|
||||
throw ResourceException((boost::format("Unknown method parameter \"%1%\"") % methodRaw).str(), 400);
|
||||
}
|
||||
}
|
||||
|
||||
// No device given
|
||||
if (req.get_path_pieces_size() <= prefixPath.size() + 1) {
|
||||
if (dataMethod != DataMethod::GET) {
|
||||
throw ResourceException("Requested method requires a device.", 400);
|
||||
}
|
||||
Variant result;
|
||||
for (auto d : repoService->getDevices()) {
|
||||
auto dev = d.lock();
|
||||
result[dev->getDeviceID()] = getDeviceInfo(dev.get(), req);
|
||||
}
|
||||
*res = new http_response(http_response_builder(SerializeJSON(result, true), 200).string_response());
|
||||
|
||||
// Device given
|
||||
} else {
|
||||
std::string devid = req.get_path_piece(prefixPath.size() + 1);
|
||||
auto device = repoService->getDevice(devid);
|
||||
if (!device.expired()) {
|
||||
switch (dataMethod) {
|
||||
case DataMethod::GET:
|
||||
{
|
||||
std::string category = req.get_path_piece(prefixPath.size() + 2);
|
||||
std::vector<std::string> path;
|
||||
for (int i = prefixPath.size() + 3; i < req.get_path_pieces_size(); i++) {
|
||||
path.push_back(req.get_path_piece(i));
|
||||
}
|
||||
Variant result = getDeviceInfo(device.lock().get(), req, category, path);
|
||||
std::string msg;
|
||||
switch (getDataFormat(req)) {
|
||||
case DataFormat::JSON:
|
||||
msg = SerializeJSON(result, true);
|
||||
break;
|
||||
case DataFormat::JSONP:
|
||||
{
|
||||
std::string padding = req.get_arg("callback");
|
||||
if (padding.empty()) {
|
||||
padding = req.get_arg("jsonp");
|
||||
}
|
||||
if (padding.empty()) {
|
||||
throw ResourceException("No callback parameter specified for JSONP.", 500);
|
||||
}
|
||||
std::stringstream ss;
|
||||
ss << padding << "(" << SerializeJSON(result, false) << ")";
|
||||
msg = ss.str();
|
||||
}
|
||||
break;
|
||||
case DataFormat::YAML:
|
||||
msg = SerializeYAML(result);
|
||||
break;
|
||||
case DataFormat::XMLPLIST:
|
||||
msg = SerializeXMLPLIST(result, true);
|
||||
break;
|
||||
case DataFormat::BUNDLEHDR:
|
||||
msg = SerializeBundleHdr(result);
|
||||
break;
|
||||
case DataFormat::MSGPACK:
|
||||
msg = Serialize(result, SERIALIZE_MSGPACK);
|
||||
break;
|
||||
default:
|
||||
throw ResourceException("Internal server error", 500);
|
||||
}
|
||||
*res = new http_response(http_response_builder(msg, 200).string_response());
|
||||
}
|
||||
break;
|
||||
case DataMethod::COMMAND:
|
||||
{
|
||||
std::vector<std::string> path;
|
||||
for (int i = prefixPath.size() + 2; i < req.get_path_pieces_size(); i++) {
|
||||
path.push_back(req.get_path_piece(i));
|
||||
}
|
||||
sendDeviceCommand(device.lock().get(), req, methodValue, path);
|
||||
*res = new http_response(http_response_builder("OK: Command send.", 200).string_response());
|
||||
}
|
||||
break;
|
||||
case DataMethod::STATUSUPDATE:
|
||||
throw ResourceException("ToDo: Implement method.", 500);
|
||||
default:
|
||||
throw ResourceException("Internal server error", 500);
|
||||
}
|
||||
} else {
|
||||
throw ResourceException((boost::format("Could not find device \"%1%\"") % devid).str(), 404);
|
||||
}
|
||||
}
|
||||
|
||||
} catch (ResourceException& e) {
|
||||
*res = new http_response(http_response_builder((boost::format("ERROR: %1%") % e.what()).str(), e.getErrorCode()).string_response());
|
||||
} catch (std::exception& e) {
|
||||
*res = new http_response(http_response_builder((boost::format("ERROR: %1%") % e.what()).str(), 500).string_response());
|
||||
}
|
||||
|
||||
std::cout << **res;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void DevicesHTTPResource::WebResource::render_POST(const httpserver::http_request& req, httpserver::http_response** res) {
|
||||
std::cout << "DevicesHTTPResource::render_POST()" << std::endl;
|
||||
std::cout << req;
|
||||
std::cout << "content: " << req.get_content() << std::endl;
|
||||
*res = new http_response(http_response_builder("ToDo: Implement POST", 500).string_response());
|
||||
std::cout << **res;
|
||||
}
|
||||
|
||||
|
||||
|
||||
DevicesHTTPResource::WebResource::DataFormat DevicesHTTPResource::WebResource::getDataFormat(const httpserver::http_request& req) {
|
||||
DataFormat retval = DataFormat::JSON;
|
||||
std::string formatRaw = req.get_arg("format");
|
||||
if (!formatRaw.empty()) {
|
||||
if (formatRaw.compare("json") == 0) {
|
||||
// do nothing
|
||||
} else if (formatRaw.compare("jsonp") == 0) {
|
||||
retval = DataFormat::JSONP;
|
||||
} else if (formatRaw.compare("yaml") == 0) {
|
||||
retval = DataFormat::YAML;
|
||||
} else if (formatRaw.compare("xmlplist") == 0) {
|
||||
retval = DataFormat::XMLPLIST;
|
||||
} else if (formatRaw.compare("bundlehdr") == 0) {
|
||||
retval = DataFormat::BUNDLEHDR;
|
||||
} else if (formatRaw.compare("msgpack") == 0) {
|
||||
retval = DataFormat::MSGPACK;
|
||||
} else {
|
||||
throw ResourceException((boost::format("Unknown format parameter \"%1%\"") % formatRaw).str(), 400);
|
||||
}
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
Variant DevicesHTTPResource::WebResource::getDeviceInfo(devicerepository::Device* device, const httpserver::http_request& 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(device, req, category);
|
||||
}
|
||||
|
||||
// We want a single info category
|
||||
} else {
|
||||
auto c = infoCategoryFuncs.find(category);
|
||||
if (c != infoCategoryFuncs.end()) {
|
||||
retval = c->second(device, req, category);
|
||||
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 ResourceException((boost::format("Unknown path \"%1%\" for device info category \"%2%\".") % pathstr % category).str(), 404);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw ResourceException((boost::format("Unknown device info category \"%1%\"") % category).str(), 404);
|
||||
}
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
void DevicesHTTPResource::WebResource::sendDeviceCommand(devicerepository::Device* device, const httpserver::http_request& 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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
} // namespace resources
|
||||
} // namespace webserver
|
||||
} // namespace service
|
||||
} // namespace cosgi
|
||||
} // namespace hos
|
||||
|
||||
|
||||
|
||||
+240
@@ -0,0 +1,240 @@
|
||||
|
||||
#include "DevicesResource.h"
|
||||
#include <boost/format.hpp>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <Poco/URI.h>
|
||||
#include <Poco/Timestamp.h>
|
||||
#include <hos/utils/Timestamp.h>
|
||||
|
||||
|
||||
// Service Namespace
|
||||
namespace hos {
|
||||
namespace cosgi {
|
||||
namespace service {
|
||||
namespace webserver {
|
||||
namespace resources {
|
||||
|
||||
|
||||
|
||||
using namespace Poco;
|
||||
using namespace libvariant;
|
||||
|
||||
|
||||
|
||||
DevicesResourceHandlerFactory::DevicesResourceHandlerFactory(WebServerService* service, devicerepository::DeviceRepositoryService* repoService)
|
||||
: service(service), repoService(repoService) {
|
||||
infoCategoryFuncs.insert({"type", [](devicerepository::Device* device, Poco::Net::HTTPServerRequest &req, const std::string& category, bool ssl)->Variant {
|
||||
return device->getDeviceType();
|
||||
} });
|
||||
|
||||
infoCategoryFuncs.insert({"status_topic", [](devicerepository::Device* device, Poco::Net::HTTPServerRequest &req, const std::string& category, bool ssl)->Variant {
|
||||
return device->getStatusTopic();
|
||||
} });
|
||||
|
||||
infoCategoryFuncs.insert({"command_topic", [](devicerepository::Device* device, Poco::Net::HTTPServerRequest &req, const std::string& category, bool ssl)->Variant {
|
||||
return device->getCmdTopic();
|
||||
} });
|
||||
|
||||
infoCategoryFuncs.insert({"current_state", [](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", [](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", [=](devicerepository::Device* device, Poco::Net::HTTPServerRequest &req, const std::string& category, bool ssl)->Variant {
|
||||
auto& baseUri = service->getPrefixPath();
|
||||
std::stringstream ss;
|
||||
if (ssl) {
|
||||
ss << "https://";
|
||||
} else {
|
||||
ss << "http://";
|
||||
}
|
||||
ss << req.getHost() << baseUri.toString() << "/devices/" << device->getDeviceID();
|
||||
return ss.str();
|
||||
} });
|
||||
}
|
||||
|
||||
|
||||
|
||||
Net::HTTPRequestHandler* DevicesResourceHandlerFactory::createRequestHandler( const Net::HTTPServerRequest& request, bool ssl ) noexcept {
|
||||
return new DevicesResourceHandler(this, service, repoService, infoCategoryFuncs, ssl);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void DevicesResourceHandler::_handleGET(Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp) {
|
||||
// Get method parameter
|
||||
Net::HTMLForm form(req);
|
||||
DevicesAPIMethod dataMethod = DevicesAPIMethod::GET; // Default method is get
|
||||
std::string methodValue;
|
||||
if (form.has("method")) {
|
||||
std::string methodRaw = form.get("method");
|
||||
bool requireValue = false;
|
||||
if (methodRaw == "get") {
|
||||
// do nothing
|
||||
} else if (methodRaw == "command") {
|
||||
dataMethod = DevicesAPIMethod::COMMAND;
|
||||
requireValue = true;
|
||||
} else if (methodRaw.compare("statusupdate") == 0) {
|
||||
dataMethod = DevicesAPIMethod::STATUSUPDATE;
|
||||
requireValue = true;
|
||||
} else {
|
||||
throw WebResourceHandlerException((boost::format("Unknown API method \"%1%\"") % methodRaw).str(),
|
||||
Poco::Net::HTTPResponse::HTTPStatus::HTTP_BAD_REQUEST);
|
||||
}
|
||||
if (requireValue) {
|
||||
if (form.has("value")) {
|
||||
methodValue = form.get("value");
|
||||
} else {
|
||||
throw WebResourceHandlerException((boost::format("API method \"%1%\" requires a value parameter.") % methodRaw).str(),
|
||||
Poco::Net::HTTPResponse::HTTPStatus::HTTP_BAD_REQUEST);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto& prefixPath = service->getPrefixPathPieces();
|
||||
Poco::URI uri(req.getURI());
|
||||
std::vector<std::string> pathPieces;
|
||||
uri.getPathSegments(pathPieces);
|
||||
|
||||
// No device given
|
||||
if (pathPieces.size() <= prefixPath.size() + 1) {
|
||||
if (dataMethod != DevicesAPIMethod::GET) {
|
||||
throw 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);
|
||||
}
|
||||
handleResponse(req, resp, result);
|
||||
|
||||
// Device given
|
||||
} else {
|
||||
std::string devid = pathPieces[prefixPath.size() + 1];
|
||||
auto device = repoService->getDevice(devid);
|
||||
if (!device.expired()) {
|
||||
switch (dataMethod) {
|
||||
case DevicesAPIMethod::GET:
|
||||
{
|
||||
std::string category;
|
||||
if (pathPieces.size() > prefixPath.size() + 2) {
|
||||
category = pathPieces[prefixPath.size() + 2];
|
||||
}
|
||||
std::vector<std::string> path;
|
||||
for (int i = prefixPath.size() + 3; i < pathPieces.size(); i++) {
|
||||
path.push_back(pathPieces[i]);
|
||||
}
|
||||
Variant result = getDeviceInfo(device.lock().get(), req, category, path);
|
||||
handleResponse(req, resp, result);
|
||||
}
|
||||
break;
|
||||
case DevicesAPIMethod::COMMAND:
|
||||
{
|
||||
std::vector<std::string> path;
|
||||
for (int i = prefixPath.size() + 2; 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 DevicesAPIMethod::STATUSUPDATE:
|
||||
throw WebResourceHandlerException("ToDo: Implement method.");
|
||||
default:
|
||||
throw WebResourceHandlerException("Internal Server Error");
|
||||
}
|
||||
} else {
|
||||
throw WebResourceHandlerException((boost::format("Could not find device \"%1%\"") % devid).str(),
|
||||
Poco::Net::HTTPResponse::HTTPStatus::HTTP_NOT_FOUND);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void DevicesResourceHandler::_handlePOST(Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp) {
|
||||
throw WebResourceHandlerException("ToDo: Implement POST.");
|
||||
}
|
||||
|
||||
|
||||
|
||||
Variant DevicesResourceHandler::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(device, req, category, ssl);
|
||||
}
|
||||
|
||||
// We want a single info category
|
||||
} else {
|
||||
auto c = infoCategoryFuncs.find(category);
|
||||
if (c != infoCategoryFuncs.end()) {
|
||||
retval = c->second(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 WebResourceHandlerException((boost::format("Unknown path \"%1%\" for device info category \"%2%\".") % pathstr % category).str(),
|
||||
Poco::Net::HTTPResponse::HTTPStatus::HTTP_NOT_FOUND);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw WebResourceHandlerException((boost::format("Unknown device info category \"%1%\"") % category).str(),
|
||||
Poco::Net::HTTPResponse::HTTPStatus::HTTP_NOT_FOUND);
|
||||
}
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void DevicesResourceHandler::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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
} // namespace resources
|
||||
} // namespace webserver
|
||||
} // namespace service
|
||||
} // namespace cosgi
|
||||
} // namespace hos
|
||||
|
||||
|
||||
|
||||
+57
-47
@@ -3,10 +3,12 @@
|
||||
|
||||
|
||||
#include <hos/cosgi/service/webserver/WebServerService.h>
|
||||
#include <hos/cosgi/service/webserver/resources/WebResourceHandlerImpl.h>
|
||||
#include <hos/cosgi/service/devicerepository/DeviceRepositoryService.h>
|
||||
#include <Poco/Net/HTTPRequestHandler.h>
|
||||
#include <Variant/Variant.h>
|
||||
#include <ros/ros.h>
|
||||
#include <httpserver.hpp>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <functional>
|
||||
|
||||
@@ -20,6 +22,33 @@ namespace webserver {
|
||||
namespace resources {
|
||||
|
||||
|
||||
|
||||
enum class DevicesAPIMethod {
|
||||
GET,
|
||||
COMMAND,
|
||||
STATUSUPDATE
|
||||
};
|
||||
|
||||
|
||||
class DevicesResourceHandlerFactory
|
||||
: public virtual WebResourceHandlerFactory {
|
||||
public:
|
||||
typedef std::function<libvariant::Variant( devicerepository::Device* device,
|
||||
Poco::Net::HTTPServerRequest &req,
|
||||
const std::string& category,
|
||||
bool ssl )> infoCategoryFunc_t;
|
||||
private:
|
||||
WebServerService* service;
|
||||
devicerepository::DeviceRepositoryService* repoService;
|
||||
std::map<std::string, infoCategoryFunc_t> infoCategoryFuncs;
|
||||
public:
|
||||
DevicesResourceHandlerFactory(WebServerService* service, devicerepository::DeviceRepositoryService* repoService);
|
||||
virtual Poco::Net::HTTPRequestHandler* createRequestHandler( const Poco::Net::HTTPServerRequest& request, bool ssl = false ) noexcept override;
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Implements a REST API for accessing the device repository and getting info about a device.
|
||||
*
|
||||
@@ -42,66 +71,47 @@ namespace resources {
|
||||
* 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 DevicesHTTPResource
|
||||
: public virtual webserver::WebResource {
|
||||
class DevicesResourceHandler
|
||||
: public virtual WebResourceHandlerImpl {
|
||||
|
||||
private:
|
||||
DevicesResourceHandlerFactory* parent;
|
||||
WebServerService* service;
|
||||
devicerepository::DeviceRepositoryService* repoService;
|
||||
const std::map<std::string, DevicesResourceHandlerFactory::infoCategoryFunc_t>& infoCategoryFuncs;
|
||||
bool ssl;
|
||||
|
||||
/**
|
||||
* libhttpserver does a static_cast with any class registered as http resource. Unfortunately this breaks classes
|
||||
* using virtual inheritance. Therefore we register this inner class which does not use virtual inheritance.
|
||||
*/
|
||||
class WebResource
|
||||
: public httpserver::http_resource<WebResource> {
|
||||
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>() );
|
||||
|
||||
public:
|
||||
enum class DataMethod { GET, COMMAND, STATUSUPDATE };
|
||||
enum class DataFormat { JSON, JSONP, YAML, XMLPLIST, BUNDLEHDR, MSGPACK };
|
||||
|
||||
typedef std::function<libvariant::Variant( devicerepository::Device* device,
|
||||
const httpserver::http_request& req,
|
||||
const std::string& category )> infoCategoryFunc_t;
|
||||
|
||||
devicerepository::DeviceRepositoryService* repoService = nullptr;
|
||||
std::vector<std::string> prefixPath;
|
||||
std::map<std::string, infoCategoryFunc_t> infoCategoryFuncs;
|
||||
|
||||
WebResource(devicerepository::DeviceRepositoryService* repoService);
|
||||
|
||||
void render_GET(const httpserver::http_request& req, httpserver::http_response** res);
|
||||
|
||||
void render_POST(const httpserver::http_request& req, httpserver::http_response** res);
|
||||
|
||||
DataFormat getDataFormat(const httpserver::http_request& req);
|
||||
|
||||
libvariant::Variant getDeviceInfo( devicerepository::Device* device,
|
||||
const httpserver::http_request& req,
|
||||
const std::string& category = std::string(),
|
||||
const std::vector<std::string>& path = std::vector<std::string>() );
|
||||
|
||||
void sendDeviceCommand( devicerepository::Device* device,
|
||||
const httpserver::http_request& req,
|
||||
const std::string& cmd,
|
||||
const std::vector<std::string>& path = std::vector<std::string>() );
|
||||
|
||||
} webResource;
|
||||
void sendDeviceCommand( devicerepository::Device* device,
|
||||
Poco::Net::HTTPServerRequest &req,
|
||||
const std::string& cmd,
|
||||
const std::vector<std::string>& path = std::vector<std::string>() );
|
||||
|
||||
|
||||
public:
|
||||
|
||||
DevicesHTTPResource(devicerepository::DeviceRepositoryService* repoService)
|
||||
: webResource(repoService) {}
|
||||
DevicesResourceHandler( DevicesResourceHandlerFactory* parent,
|
||||
WebServerService* service,
|
||||
devicerepository::DeviceRepositoryService* repoService,
|
||||
const std::map<std::string, DevicesResourceHandlerFactory::infoCategoryFunc_t>& infoCategoryFuncs,
|
||||
bool ssl = false )
|
||||
: parent(parent), service(service), repoService(repoService), infoCategoryFuncs(infoCategoryFuncs), ssl(ssl) {}
|
||||
|
||||
virtual void init( const std::vector<std::string>& prefixPath,
|
||||
const std::string& registeredPath,
|
||||
const std::string& absolutePath,
|
||||
httpserver::webserver& webserver,
|
||||
webserver::WebServerService& service );
|
||||
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 resources
|
||||
} // namespace webserver
|
||||
} // namespace service
|
||||
} // namespace cosgi
|
||||
} // namespace hos
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
#include <chrono>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <cstdio>
|
||||
#include <ctime>
|
||||
|
||||
|
||||
namespace hos {
|
||||
namespace utils {
|
||||
|
||||
|
||||
/*
|
||||
* It took me way too long to come up with this solution (which I only consider as workaround,
|
||||
* but currently it seems to be the best solution).
|
||||
* There definitely needs to be much better support for timestamp-to-string conversion in C++,
|
||||
* the current situation is way too unsatisfying. And boost isn't much of a help either.
|
||||
*/
|
||||
template<class _CLOCK>
|
||||
std::string timestamp_to_ISO8601(const std::chrono::time_point<_CLOCK>& timestamp) {
|
||||
std::stringstream ss;
|
||||
std::tm tm_time;
|
||||
char temp[256];
|
||||
// First convert timestamp to time_t (loosing precision)
|
||||
std::time_t time = std::chrono::system_clock::to_time_t(timestamp);
|
||||
// Then convert time_t to tm_time
|
||||
localtime_r(&time, &tm_time);
|
||||
// Print first part of ISO8601 string
|
||||
std::strftime(temp, sizeof(temp), "%FT%H:%M:", &tm_time);
|
||||
ss << temp;
|
||||
// Get lost precision by converting out time_t back to a timestamp and substracting it from our given timestamp,
|
||||
// and add seconds to avoid the "0.xyz" problem
|
||||
std::chrono::duration<double> msecs = timestamp - std::chrono::system_clock::from_time_t(time) + std::chrono::seconds(tm_time.tm_sec);
|
||||
// Print seconds part of ISO8601 string
|
||||
std::snprintf(temp, sizeof(temp), "%09.6f", msecs.count()); // is more compact than C++ counterpart
|
||||
std::string secpart(temp);
|
||||
std::replace(secpart.begin(), secpart.end(), '.', ',' ); // ISO 8601 prefers ',' over '.' as decimal mark.
|
||||
ss << secpart;
|
||||
// Get and print timezone part of ISO8601 string
|
||||
std::strftime(temp, sizeof(temp), "%z", &tm_time);
|
||||
ss << temp;
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
|
||||
|
||||
} // namespace libvariant
|
||||
} // namespace hos
|
||||
|
||||
@@ -0,0 +1,207 @@
|
||||
# - Find the Poco includes and libraries.
|
||||
# The following variables are set if Poco is found. If Poco is not
|
||||
# found, Poco_FOUND is set to false.
|
||||
# Poco_FOUND - True when the Poco include directory is found.
|
||||
# Poco_INCLUDE_DIRS - the path to where the poco include files are.
|
||||
# Poco_LIBRARY_DIR - The path to where the poco library files are.
|
||||
# Poco_BINARY_DIRS - The path to where the poco dlls are.
|
||||
# Poco_LIBRARIES - list of all libs from requested components.
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# If you have installed Poco in a non-standard location.
|
||||
# Then you have three options.
|
||||
# In the following comments, it is assumed that <Your Path>
|
||||
# points to the root directory of the include directory of Poco. e.g
|
||||
# If you have put poco in C:\development\Poco then <Your Path> is
|
||||
# "C:/development/Poco" and in this directory there will be two
|
||||
# directories called "include" and "lib".
|
||||
# 1) After CMake runs, set Poco_INCLUDE_DIR to <Your Path>/poco<-version>
|
||||
# 2) Use CMAKE_INCLUDE_PATH to set a path to <Your Path>/poco<-version>. This will allow FIND_PATH()
|
||||
# to locate Poco_INCLUDE_DIR by utilizing the PATH_SUFFIXES option. e.g.
|
||||
# SET(CMAKE_INCLUDE_PATH ${CMAKE_INCLUDE_PATH} "<Your Path>/include")
|
||||
# 3) Set an environment variable called ${POCO_ROOT} that points to the root of where you have
|
||||
# installed Poco, e.g. <Your Path>. It is assumed that there is at least a subdirectory called
|
||||
# Foundation/include/Poco in this path.
|
||||
#
|
||||
# Note:
|
||||
# 1) If you are just using the poco headers, then you do not need to use
|
||||
# Poco_LIBRARY_DIR in your CMakeLists.txt file.
|
||||
# 2) If Poco has not been installed, then when setting Poco_LIBRARY_DIR
|
||||
# the script will look for /lib first and, if this fails, then for /stage/lib.
|
||||
#
|
||||
# Usage:
|
||||
# In your CMakeLists.txt file do something like this:
|
||||
# ...
|
||||
# # Poco
|
||||
# FIND_PACKAGE(Poco COMPONENTS XML Net Data...)
|
||||
# ...
|
||||
# INCLUDE_DIRECTORIES(${Poco_INCLUDE_DIRS})
|
||||
# LINK_DIRECTORIES(${Poco_LIBRARY_DIR})
|
||||
#
|
||||
# In Windows, we make the assumption that, if the Poco files are installed, the default directory
|
||||
# will be C:\poco or C:\Program Files\Poco or C:\Programme\Poco.
|
||||
|
||||
MESSAGE(STATUS "Searching for Poco library...")
|
||||
|
||||
SET(POCO_INCLUDE_PATH_DESCRIPTION "top-level directory containing the poco include directories. E.g /usr/local/include/ or c:\\poco\\include\\poco-1.3.2")
|
||||
SET(POCO_INCLUDE_DIR_MESSAGE "Set the Poco_INCLUDE_DIR cmake cache entry to the ${POCO_INCLUDE_PATH_DESCRIPTION}")
|
||||
SET(POCO_LIBRARY_PATH_DESCRIPTION "top-level directory containing the poco libraries.")
|
||||
SET(POCO_LIBRARY_DIR_MESSAGE "Set the Poco_LIBRARY_DIR cmake cache entry to the ${POCO_LIBRARY_PATH_DESCRIPTION}")
|
||||
|
||||
|
||||
SET(POCO_DIR_SEARCH $ENV{POCO_ROOT})
|
||||
IF(POCO_DIR_SEARCH)
|
||||
FILE(TO_CMAKE_PATH ${POCO_DIR_SEARCH} POCO_DIR_SEARCH)
|
||||
ENDIF(POCO_DIR_SEARCH)
|
||||
|
||||
|
||||
IF(WIN32)
|
||||
SET(POCO_DIR_SEARCH
|
||||
${POCO_DIR_SEARCH}
|
||||
C:/poco
|
||||
D:/poco
|
||||
"C:/Program Files/poco"
|
||||
"C:/Programme/poco"
|
||||
"D:/Program Files/poco"
|
||||
"D:/Programme/poco"
|
||||
)
|
||||
ENDIF(WIN32)
|
||||
|
||||
# Add in some path suffixes. These will have to be updated whenever a new Poco version comes out.
|
||||
SET(SUFFIX_FOR_INCLUDE_PATH
|
||||
poco-1.3.2
|
||||
poco-1.3.3
|
||||
poco-1.3.4
|
||||
poco-1.3.5
|
||||
poco-1.3.6
|
||||
)
|
||||
|
||||
SET(SUFFIX_FOR_LIBRARY_PATH
|
||||
poco-1.3.2/lib
|
||||
poco-1.3.2/lib/Linux/i686
|
||||
poco-1.3.2/lib/Linux/x86_64
|
||||
poco-1.3.3/lib
|
||||
poco-1.3.3/lib/Linux/i686
|
||||
poco-1.3.3/lib/Linux/x86_64
|
||||
poco-1.3.4/lib
|
||||
poco-1.3.4/lib/Linux/i686
|
||||
poco-1.3.4/lib/Linux/x86_64
|
||||
poco-1.3.5/lib
|
||||
poco-1.3.5/lib/Linux/i686
|
||||
poco-1.3.5/lib/Linux/x86_64
|
||||
poco-1.3.6/lib
|
||||
poco-1.3.6/lib/Linux/i686
|
||||
poco-1.3.6/lib/Linux/x86_64
|
||||
lib
|
||||
lib/Linux/i686
|
||||
lib/Linux/x86_64
|
||||
)
|
||||
|
||||
#
|
||||
# Look for an installation.
|
||||
#
|
||||
FIND_PATH(Poco_INCLUDE_DIR NAMES Foundation/include/Poco/SharedLibrary.h PATH_SUFFIXES ${SUFFIX_FOR_INCLUDE_PATH} PATHS
|
||||
|
||||
# Look in other places.
|
||||
${POCO_DIR_SEARCH}
|
||||
|
||||
# Help the user find it if we cannot.
|
||||
DOC "The ${POCO_INCLUDE_PATH_DESCRIPTION}"
|
||||
)
|
||||
|
||||
IF(NOT Poco_INCLUDE_DIR)
|
||||
|
||||
# Look for standard unix include paths
|
||||
FIND_PATH(Poco_INCLUDE_DIR Poco/Poco.h DOC "The ${POCO_INCLUDE_PATH_DESCRIPTION}")
|
||||
|
||||
ENDIF(NOT Poco_INCLUDE_DIR)
|
||||
|
||||
# Assume we didn't find it.
|
||||
SET(Poco_FOUND 0)
|
||||
|
||||
# Now try to get the include and library path.
|
||||
IF(Poco_INCLUDE_DIR)
|
||||
IF(EXISTS "${Poco_INCLUDE_DIR}/Foundation/include/Poco/SharedLibrary.h")
|
||||
SET(Poco_INCLUDE_DIRS
|
||||
${Poco_INCLUDE_DIR}/CppUnit/include
|
||||
${Poco_INCLUDE_DIR}/Foundation/include
|
||||
${Poco_INCLUDE_DIR}/Net/include
|
||||
${Poco_INCLUDE_DIR}/Util/include
|
||||
${Poco_INCLUDE_DIR}/XML/include
|
||||
)
|
||||
SET(Poco_FOUND 1)
|
||||
ELSEIF(EXISTS "${Poco_INCLUDE_DIR}/Poco/Poco.h")
|
||||
SET(Poco_INCLUDE_DIRS
|
||||
${Poco_INCLUDE_DIR}
|
||||
)
|
||||
SET(Poco_FOUND 1)
|
||||
ENDIF()
|
||||
|
||||
IF(NOT Poco_LIBRARY_DIR)
|
||||
|
||||
FIND_LIBRARY(Poco_FOUNDATION_LIB NAMES PocoFoundation PocoFoundationd PATH_SUFFIXES ${SUFFIX_FOR_LIBRARY_PATH} PATHS
|
||||
|
||||
# Look in other places.
|
||||
${Poco_INCLUDE_DIR}
|
||||
${POCO_DIR_SEARCH}
|
||||
|
||||
/usr/lib64
|
||||
/usr/lib
|
||||
|
||||
# Help the user find it if we cannot.
|
||||
DOC "The ${POCO_LIBRARY_PATH_DESCRIPTION}"
|
||||
)
|
||||
SET(Poco_LIBRARY_DIR "" CACHE PATH POCO_LIBARARY_PATH_DESCRIPTION)
|
||||
GET_FILENAME_COMPONENT(Poco_LIBRARY_DIR ${Poco_FOUNDATION_LIB} PATH)
|
||||
SET(Poco_LIBRARIES "")
|
||||
SET(Comp_List "")
|
||||
IF(Poco_LIBRARY_DIR AND Poco_FOUNDATION_LIB)
|
||||
# Look for the poco binary path.
|
||||
SET(Poco_BINARY_DIR ${Poco_INCLUDE_DIR})
|
||||
IF(Poco_BINARY_DIR AND EXISTS "${Poco_BINARY_DIR}/bin")
|
||||
SET(Poco_BINARY_DIRS ${Poco_BINARY_DIR}/bin)
|
||||
ENDIF(Poco_BINARY_DIR AND EXISTS "${Poco_BINARY_DIR}/bin")
|
||||
ENDIF(Poco_LIBRARY_DIR AND Poco_FOUNDATION_LIB)
|
||||
IF(Poco_FOUNDATION_LIB)
|
||||
IF ("${CMAKE_BUILD_TYPE}" STREQUAL "Debug")
|
||||
SET(DBG "d")
|
||||
ELSE ("${CMAKE_BUILD_TYPE}" STREQUAL "Debug")
|
||||
SET(DBG "")
|
||||
ENDIF ("${CMAKE_BUILD_TYPE}" STREQUAL "Debug")
|
||||
SET(Comp_List "Foundation${DBG}")
|
||||
FOREACH(COMPONENT ${Poco_FIND_COMPONENTS})
|
||||
FIND_LIBRARY(LIB${COMPONENT} "Poco${COMPONENT}${DBG}" Poco_LIBRARY_DIR)
|
||||
IF (LIB${COMPONENT})
|
||||
LIST(APPEND Poco_LIBRARIES "${LIB${COMPONENT}}")
|
||||
LIST(APPEND Comp_List "${COMPONENT}${DBG}")
|
||||
ENDIF(LIB${COMPONENT})
|
||||
ENDFOREACH(COMPONENT)
|
||||
LIST(REMOVE_DUPLICATES Comp_List)
|
||||
ENDIF(Poco_FOUNDATION_LIB)
|
||||
ENDIF(NOT Poco_LIBRARY_DIR)
|
||||
ENDIF(Poco_INCLUDE_DIR)
|
||||
|
||||
IF(NOT Poco_FOUND)
|
||||
IF(Poco_FIND_QUIETLY)
|
||||
MESSAGE(STATUS "Poco was not found. ${POCO_INCLUDE_DIR_MESSAGE}")
|
||||
ELSE(Poco_FIND_QUIETLY)
|
||||
IF(Poco_FIND_REQUIRED)
|
||||
MESSAGE(FATAL_ERROR "Poco was not found. ${POCO_INCLUDE_DIR_MESSAGE}")
|
||||
ENDIF(Poco_FIND_REQUIRED)
|
||||
ENDIF(Poco_FIND_QUIETLY)
|
||||
ELSE(NOT Poco_FOUND)
|
||||
MESSAGE(STATUS " Found Poco!")
|
||||
SET(COMPONENT_STR "components found:")
|
||||
FOREACH(comp ${Comp_List})
|
||||
SET(COMPONENT_STR "${COMPONENT_STR}, ${comp}")
|
||||
|
||||
ENDFOREACH(comp ${Comp_List})
|
||||
STRING(REPLACE ":," ":" COMPONENT_LSTR ${COMPONENT_STR})
|
||||
MESSAGE(STATUS "${COMPONENT_LSTR}.")
|
||||
ENDIF(NOT Poco_FOUND)
|
||||
|
||||
#I added this in to add "libdl" on non-Windows systems. Technically dl is only neded if the "Foundation" component is used,
|
||||
#but i doesn't hurt to add it in anyway - mas
|
||||
if(Poco_FOUND AND NOT WIN32)
|
||||
LIST(APPEND Poco_LIBRARIES "dl")
|
||||
endif(Poco_FOUND AND NOT WIN32)
|
||||
Reference in New Issue
Block a user