From c1ccbef7a59df33e6ff0c9a4609caab7e668ba77 Mon Sep 17 00:00:00 2001 From: Stefan Radomski Date: Thu, 28 Feb 2013 15:00:53 +0100 Subject: Prepared everything for FE-Design demo --- apps/mmi-browser.cpp | 33 ++- apps/samples/vrml-server.scxml | 221 +++++++++++++++++---- config.h.in | 1 + contrib/cmake/HeaderExists.cmake | 1 + docs/EXTENSIONS.md | 156 ++++++++++++++- src/uscxml/Interpreter.cpp | 128 ++++++++++-- .../datamodel/ecmascript/v8/V8DataModel.cpp | 4 + .../plugins/element/postpone/PostponeElement.cpp | 22 +- .../plugins/element/response/ResponseElement.cpp | 81 +++++++- .../invoker/filesystem/dirmon/DirMonInvoker.cpp | 109 +++++++--- .../invoker/filesystem/dirmon/DirMonInvoker.h | 3 +- .../graphics/openscenegraph/OSGConverter.cpp | 106 +++++++--- .../invoker/graphics/openscenegraph/OSGConverter.h | 1 + .../plugins/invoker/heartbeat/HeartbeatInvoker.cpp | 1 + .../plugins/invoker/http/HTTPServletInvoker.cpp | 2 + src/uscxml/server/HTTPServer.cpp | 36 +++- src/uscxml/server/HTTPServer.h | 4 +- 17 files changed, 774 insertions(+), 135 deletions(-) diff --git a/apps/mmi-browser.cpp b/apps/mmi-browser.cpp index 4090135..1cb82e3 100644 --- a/apps/mmi-browser.cpp +++ b/apps/mmi-browser.cpp @@ -2,10 +2,20 @@ #include "uscxml/Interpreter.h" #include +#ifdef HAS_SIGNAL_H +#include +#endif + #ifdef _WIN32 #include "XGetopt.h" #endif +#ifdef HAS_SIGNAL_H +void handler(int s) { + printf("Caught SIGPIPE ############\n"); +} +#endif + void printUsageAndExit() { printf("mmi-browser version " USCXML_VERSION " (" CMAKE_BUILD_TYPE " build - " CMAKE_COMPILER_STRING ")\n"); printf("Usage\n"); @@ -23,6 +33,27 @@ void printUsageAndExit() { int main(int argc, char** argv) { using namespace uscxml; +#ifdef HAS_SIGNAL_H + // disable SIGPIPE +// struct sigaction act; +// act.sa_handler=SIG_IGN; +// sigemptyset(&act.sa_mask); +// act.sa_flags=0; +// sigaction(SIGPIPE, &act, NULL); + + // signal(SIGPIPE, handler); + + signal(SIGPIPE, SIG_IGN); + + // struct sigaction act; + // int r; + // memset(&act, 0, sizeof(act)); + // act.sa_handler = SIG_IGN; + // act.sa_flags = SA_RESTART; + // r = sigaction(SIGPIPE, &act, NULL); + +#endif + if (argc < 2) { printUsageAndExit(); } @@ -51,12 +82,12 @@ int main(int argc, char** argv) { // std::cout << argv[i] << std::endl; // std::cout << optind << std::endl; - Interpreter* interpreter = Interpreter::fromURI(argv[optind]); if (interpreter) { interpreter->setCmdLineOptions(argc, argv); interpreter->start(); while(interpreter->runOnMainThread(25)); + // interpreter->interpret(); delete interpreter; } diff --git a/apps/samples/vrml-server.scxml b/apps/samples/vrml-server.scxml index f48008c..1d356be 100644 --- a/apps/samples/vrml-server.scxml +++ b/apps/samples/vrml-server.scxml @@ -1,68 +1,201 @@ + + + + + - + - - + + + + + + + - + - + - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - + + + - + - @@ -70,18 +203,38 @@ + + + + + + + +
+ + + + +
+ + + + + + + + _event.data.pathComponent.length == 1"> - +
+ - + diff --git a/config.h.in b/config.h.in index ca2996f..5637057 100644 --- a/config.h.in +++ b/config.h.in @@ -62,5 +62,6 @@ /** Header files we found */ #cmakedefine HAS_UNISTD_H #cmakedefine HAS_STRING_H +#cmakedefine HAS_SIGNAL_H #endif \ No newline at end of file diff --git a/contrib/cmake/HeaderExists.cmake b/contrib/cmake/HeaderExists.cmake index dc0004a..29f801d 100644 --- a/contrib/cmake/HeaderExists.cmake +++ b/contrib/cmake/HeaderExists.cmake @@ -1,3 +1,4 @@ include(CheckIncludeFile) CHECK_INCLUDE_FILE(unistd.h HAS_UNISTD_H) CHECK_INCLUDE_FILE(string.h HAS_STRING_H) +CHECK_INCLUDE_FILE(signal.h HAS_SIGNAL_H) diff --git a/docs/EXTENSIONS.md b/docs/EXTENSIONS.md index ba77206..9eb1726 100644 --- a/docs/EXTENSIONS.md +++ b/docs/EXTENSIONS.md @@ -4,8 +4,9 @@ Most platform specific extensions are realized via special invokers. These are components that you can load via: - + + @@ -13,25 +14,172 @@ Most platform specific extensions are realized via special invokers. These are c -### FFMPEG +When invoked, you can send them events via: + + + + + +To get an idea which parameters can be passed for invoke and send, the source as linked below is the ultimate reference. +Note: Every expr attribute is subject to evaluation by the datamodel. If you want to pass a literal string +with the ecmascript datamodel, you will have to 'quote' it. + +### FFMPEG + * [FFMPEGInvoker.cpp](https://github.com/tklab-tud/uscxml/blob/master/src/uscxml/plugins/invoker/ffmpeg/FFMPEGInvoker.cpp) ### Directory Monitor + * [DirMonInvoker.cpp](https://github.com/tklab-tud/uscxml/blob/master/src/uscxml/plugins/invoker/filesystem/dirmon/DirMonInvoker.cpp) + +Monitors a directory for modifications. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Invoke
type + dirmon
+ DirectoryMonitor
+ http://uscxml.tk.informatik.tu-darmstadt.de/#dirmon +
paramexprdefaultcomment
dirA valid system directorynoneAnything that evaluates to a valid directory with the given datamodel
recurse"true" or "false"falseWhether or not to monitor subdirectories as well.
suffix
suffixes
filename suffixesallA single or space-seperated list of suffixes of files that will get reported

Send
Will not accept anything yet.

Emitted Events
Example
+'invokeid' => "dirmon.vrml"
+'origintype' => "dirmon"
+'origin' => "#_dirmon.vrml"
+'name' => "file.existing"
+'data' ...
+    'file' ...
+        'atime' => "1361746741"
+        'ctime' => "1350452789"
+        'dir' => "/Users/sradomski/data"
+        'extension' => "wrl"
+        'mtime' => "1328012634"
+        'name' => "HARD_MP_VAL_035.wrl"
+        'path' => "/Users/sradomski/data/HARD_MP_VAL_035.wrl"
+        'relPath' => "/HARD_MP_VAL_035.wrl"
+        'size' => "1509110"
+
FieldDetails
nameOne of file.[existing|added|deleted|modified]
data.file.dirThe directory as passed per dir param to invoke.
data.file.pathThe full path to the file we found.
data.file.relPathThe relative path starting from data.file.dir to the file we found.
data.file.sizeFile size in bytes.
### 3D Scenegraph + * [OSGInvoker.cpp](https://github.com/tklab-tud/uscxml/blob/master/src/uscxml/plugins/invoker/graphics/openscenegraph/OSGInvoker.cpp) + +Accepts a simplified scenegraph XML notation as content and opens a set of windows with scenes. This invoker registers +as an event listener on the XML in the content and will allow changes to the scenegraph per ecmascript. + + * [OSGConverter.cpp](https://github.com/tklab-tud/uscxml/blob/master/src/uscxml/plugins/invoker/graphics/openscenegraph/OSGConverter.cpp) + +Transfer model files into other representations and make screenshots of models. ### Heartbeat + * [HeartbeatInvoker.cpp](https://github.com/tklab-tud/uscxml/blob/master/src/uscxml/plugins/invoker/heartbeat/HeartbeatInvoker.cpp) + +Continuously sends events. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Invoke
type + heartbeat
+ http://uscxml.tk.informatik.tu-darmstadt.de/#heartbeat +
paramexprdefaultcomment
intervale.g. '3s' or '1200ms'noneA time designation as defined in CSS2
eventnameThe name of the event to send in the given intervalDefaults to heartbeat.<interval>

Send
Will not accept anything yet.

Emitted Events
Example
+'invokeid' => "heartbeat"
+'origintype' => "heartbeat"
+'origin' => "#_heartbeat"
+'name' => "heartbeat.100ms"
+'data' => "undefined"
+
FieldDetails
nameOne of file.[existing|added|deleted|modified]
data.file.dirThe directory as passed per dir param to invoke.
data.file.pathThe full path to the file we found.
data.file.relPathThe relative path starting from data.file.dir to the file we found.
data.file.sizeFile size in bytes.
### HTTP Servlet + * [HTTPServletInvoker.cpp](https://github.com/tklab-tud/uscxml/blob/master/src/uscxml/plugins/invoker/http/HTTPServletInvoker.cpp) ### SCXML + * [HTTPServletInvoker.cpp](https://github.com/tklab-tud/uscxml/blob/master/src/uscxml/plugins/invoker/scxml/USCXMLInvoker.cpp) ### uMundo Publish / Subscribe - + * [UmundoInvoker.cpp](https://github.com/tklab-tud/uscxml/blob/master/src/uscxml/plugins/invoker/umundo/UmundoInvoker.cpp) ## Elements +### Fetch + * [FetchElement.cpp](https://github.com/tklab-tud/uscxml/blob/master/src/uscxml/plugins/element/fetch/FetchElement.cpp) + +### Postpone + * [PostponeElement.cpp](https://github.com/tklab-tud/uscxml/blob/master/src/uscxml/plugins/element/postpone/PostponeElement.cpp) + +### Response + * [ResponseElement.cpp](https://github.com/tklab-tud/uscxml/blob/master/src/uscxml/plugins/element/response/ResponseElement.cpp) +## IO Processors -## IO Processors \ No newline at end of file +### BasicHTTP + * [EventIOProcessor.cpp](https://github.com/tklab-tud/uscxml/blob/master/src/uscxml/plugins/ioprocessor/basichttp/libevent/EventIOProcessor.cpp) diff --git a/src/uscxml/Interpreter.cpp b/src/uscxml/Interpreter.cpp index 1e7c84e..eeeff0c 100644 --- a/src/uscxml/Interpreter.cpp +++ b/src/uscxml/Interpreter.cpp @@ -456,7 +456,13 @@ void Interpreter::mainEventLoop() { #endif monIter = _monitors.begin(); while(monIter != _monitors.end()) { - (*monIter)->beforeMicroStep(this); + try { + (*monIter)->beforeMicroStep(this); + } catch (Event e) { + LOG(ERROR) << "Syntax error when calling beforeMicroStep on monitors: " << std::endl << e << std::endl; + } catch (...) { + LOG(ERROR) << "An exception occured when calling beforeMicroStep on monitors"; + } monIter++; } @@ -478,7 +484,13 @@ void Interpreter::mainEventLoop() { if (!enabledTransitions.empty()) { monIter = _monitors.begin(); while(monIter != _monitors.end()) { - (*monIter)->beforeTakingTransitions(this, enabledTransitions); + try { + (*monIter)->beforeTakingTransitions(this, enabledTransitions); + } catch (Event e) { + LOG(ERROR) << "Syntax error when calling beforeTakingTransitions on monitors: " << std::endl << e << std::endl; + } catch (...) { + LOG(ERROR) << "An exception occured when calling beforeTakingTransitions on monitors"; + } monIter++; } microstep(enabledTransitions); @@ -502,7 +514,13 @@ void Interpreter::mainEventLoop() { monIter = _monitors.begin(); // if (!_sendQueue || _sendQueue->isEmpty()) { while(monIter != _monitors.end()) { - (*monIter)->onStableConfiguration(this); + try { + (*monIter)->onStableConfiguration(this); + } catch (Event e) { + LOG(ERROR) << "Syntax error when calling onStableConfiguration on monitors: " << std::endl << e << std::endl; + } catch (...) { + LOG(ERROR) << "An exception occured when calling onStableConfiguration on monitors"; + } monIter++; } // } @@ -564,7 +582,13 @@ void Interpreter::mainEventLoop() { } monIter = _monitors.begin(); while(monIter != _monitors.end()) { - (*monIter)->beforeCompletion(this); + try { + (*monIter)->beforeCompletion(this); + } catch (Event e) { + LOG(ERROR) << "Syntax error when calling beforeCompletion on monitors: " << std::endl << e << std::endl; + } catch (...) { + LOG(ERROR) << "An exception occured when calling beforeCompletion on monitors"; + } monIter++; } @@ -572,7 +596,13 @@ void Interpreter::mainEventLoop() { monIter = _monitors.begin(); while(monIter != _monitors.end()) { - (*monIter)->afterCompletion(this); + try { + (*monIter)->afterCompletion(this); + } catch (Event e) { + LOG(ERROR) << "Syntax error when calling afterCompletion on monitors: " << std::endl << e << std::endl; + } catch (...) { + LOG(ERROR) << "An exception occured when calling afterCompletion on monitors"; + } monIter++; } @@ -642,12 +672,22 @@ void Interpreter::send(const Arabica::DOM::Node& element) { } else if (HAS_ATTR(element, "event")) { sendReq.name = ATTR(element, "event"); } + } catch (Event e) { + LOG(ERROR) << "Syntax error in send element eventexpr:" << std::endl << e << std::endl; + return; + } + try { // target if (HAS_ATTR(element, "targetexpr") && _dataModel) { sendReq.target = _dataModel.evalAsString(ATTR(element, "targetexpr")); } else if (HAS_ATTR(element, "target")) { sendReq.target = ATTR(element, "target"); } + } catch (Event e) { + LOG(ERROR) << "Syntax error in send element targetexpr:" << std::endl << e << std::endl; + return; + } + try { // type if (HAS_ATTR(element, "typeexpr") && _dataModel) { sendReq.type = _dataModel.evalAsString(ATTR(element, "typeexpr")); @@ -656,6 +696,11 @@ void Interpreter::send(const Arabica::DOM::Node& element) { } else { sendReq.type = "http://www.w3.org/TR/scxml/#SCXMLEventProcessor"; } + } catch (Event e) { + LOG(ERROR) << "Syntax error in send element typeexpr:" << std::endl << e << std::endl; + return; + } + try { // id if (HAS_ATTR(element, "idlocation") && _dataModel) { sendReq.sendid = _dataModel.evalAsString(ATTR(element, "idlocation")); @@ -682,7 +727,12 @@ void Interpreter::send(const Arabica::DOM::Node& element) { * See 3.14 IDs for details. * */ + } catch (Event e) { + LOG(ERROR) << "Syntax error in send element idlocation:" << std::endl << e << std::endl; + return; + } + try { // delay std::string delay; sendReq.delayMs = 0; @@ -705,6 +755,12 @@ void Interpreter::send(const Arabica::DOM::Node& element) { LOG(ERROR) << "Cannot make sense of delay value " << delay << ": does not end in 's' or 'ms'"; } } + } catch (Event e) { + LOG(ERROR) << "Syntax error in send element delayexpr:" << std::endl << e << std::endl; + return; + } + + try { // namelist if (HAS_ATTR(element, "namelist")) { if (_dataModel) { @@ -718,7 +774,12 @@ void Interpreter::send(const Arabica::DOM::Node& element) { LOG(ERROR) << "Namelist attribute at send requires datamodel to be defined"; } } + } catch (Event e) { + LOG(ERROR) << "Syntax error in send element namelist:" << std::endl << e << std::endl; + return; + } + try { // params NodeSet params = filterChildElements(_xmlNSPrefix + "param", element); for (int i = 0; i < params.size(); i++) { @@ -740,6 +801,11 @@ void Interpreter::send(const Arabica::DOM::Node& element) { sendReq.params.insert(std::make_pair(paramKey, paramValue)); sendReq.data.compound[paramKey] = Data(paramValue, Data::VERBATIM); } + } catch (Event e) { + LOG(ERROR) << "Syntax error in send element param expr:" << std::endl << e << std::endl; + return; + } + try { // content NodeSet contents = filterChildElements(_xmlNSPrefix + "content", element); @@ -763,18 +829,18 @@ void Interpreter::send(const Arabica::DOM::Node& element) { LOG(ERROR) << "content element does not specify any content."; } } - - assert(_sendIds.find(sendReq.sendid) == _sendIds.end()); - _sendIds[sendReq.sendid] = std::make_pair(this, sendReq); - if (sendReq.delayMs > 0) { - _sendQueue->addEvent(sendReq.sendid, Interpreter::delayedSend, sendReq.delayMs, &_sendIds[sendReq.sendid]); - } else { - delayedSend(&_sendIds[sendReq.sendid], sendReq.name); - } - } catch (Event e) { - LOG(ERROR) << "Syntax error in send element:" << std::endl << e << std::endl; + LOG(ERROR) << "Syntax error in send element content:" << std::endl << e << std::endl; + return; } + + assert(_sendIds.find(sendReq.sendid) == _sendIds.end()); + _sendIds[sendReq.sendid] = std::make_pair(this, sendReq); + if (sendReq.delayMs > 0) { + _sendQueue->addEvent(sendReq.sendid, Interpreter::delayedSend, sendReq.delayMs, &_sendIds[sendReq.sendid]); + } else { + delayedSend(&_sendIds[sendReq.sendid], sendReq.name); + } } void Interpreter::delayedSend(void* userdata, std::string eventName) { @@ -1553,7 +1619,13 @@ void Interpreter::exitStates(const Arabica::XPath::NodeSet& enabled monIter = _monitors.begin(); while(monIter != _monitors.end()) { - (*monIter)->beforeExitingStates(this, statesToExit); + try { + (*monIter)->beforeExitingStates(this, statesToExit); + } catch (Event e) { + LOG(ERROR) << "Syntax error when calling beforeExitingStates on monitors: " << std::endl << e << std::endl; + } catch (...) { + LOG(ERROR) << "An exception occured when calling beforeExitingStates on monitors"; + } monIter++; } @@ -1610,7 +1682,13 @@ void Interpreter::exitStates(const Arabica::XPath::NodeSet& enabled monIter = _monitors.begin(); while(monIter != _monitors.end()) { - (*monIter)->afterExitingStates(this); + try { + (*monIter)->afterExitingStates(this); + } catch (Event e) { + LOG(ERROR) << "Syntax error when calling afterExitingStates on monitors: " << std::endl << e << std::endl; + } catch (...) { + LOG(ERROR) << "An exception occured when calling afterExitingStates on monitors"; + } monIter++; } @@ -1722,7 +1800,13 @@ void Interpreter::enterStates(const Arabica::XPath::NodeSet& enable monIter = _monitors.begin(); while(monIter != _monitors.end()) { - (*monIter)->beforeEnteringStates(this, statesToEnter); + try { + (*monIter)->beforeEnteringStates(this, statesToEnter); + } catch (Event e) { + LOG(ERROR) << "Syntax error when calling beforeEnteringStates on monitors: " << std::endl << e << std::endl; + } catch (...) { + LOG(ERROR) << "An exception occured when calling beforeEnteringStates on monitors"; + } monIter++; } @@ -1783,7 +1867,13 @@ void Interpreter::enterStates(const Arabica::XPath::NodeSet& enable monIter = _monitors.begin(); while(monIter != _monitors.end()) { - (*monIter)->afterEnteringStates(this); + try { + (*monIter)->afterEnteringStates(this); + } catch (Event e) { + LOG(ERROR) << "Syntax error when calling afterEnteringStates on monitors: " << std::endl << e << std::endl; + } catch (...) { + LOG(ERROR) << "An exception occured when calling afterEnteringStates on monitors"; + } monIter++; } diff --git a/src/uscxml/plugins/datamodel/ecmascript/v8/V8DataModel.cpp b/src/uscxml/plugins/datamodel/ecmascript/v8/V8DataModel.cpp index dfa8585..d791b9e 100644 --- a/src/uscxml/plugins/datamodel/ecmascript/v8/V8DataModel.cpp +++ b/src/uscxml/plugins/datamodel/ecmascript/v8/V8DataModel.cpp @@ -320,6 +320,10 @@ v8::Handle V8DataModel::evalAsValue(const std::string& expr) { exceptionEvent.data.compound["filename"] = Data(filename, Data::VERBATIM); std::string sourceLine(*v8::String::AsciiValue(message->GetSourceLine())); + size_t startpos = sourceLine.find_first_not_of(" \t"); + if(std::string::npos != startpos) // removoe leading white space + sourceLine = sourceLine.substr(startpos); + exceptionEvent.data.compound["sourceline"] = Data(sourceLine, Data::VERBATIM); std::stringstream ssLineNumber; diff --git a/src/uscxml/plugins/element/postpone/PostponeElement.cpp b/src/uscxml/plugins/element/postpone/PostponeElement.cpp index 644cb1d..5dc1c60 100644 --- a/src/uscxml/plugins/element/postpone/PostponeElement.cpp +++ b/src/uscxml/plugins/element/postpone/PostponeElement.cpp @@ -41,11 +41,22 @@ void PostponeElement::enterElement(const Arabica::DOM::Node& node) } // when will we refire the event? - if (!HAS_ATTR(node, "until")) { - LOG(ERROR) << "Postpone element requires until attribute "; + std::string until; + try { + if (HAS_ATTR(node, "untilexpr")) { + until = _interpreter->getDataModel().evalAsString(ATTR(node, "untilexpr")); + } else if (HAS_ATTR(node, "until")) { + until = ATTR(node, "until"); + } + } catch (Event e) { + LOG(ERROR) << "Syntax error in postpone element untilexpr:" << std::endl << e << std::endl; + return; + } + + if (until.length() == 0) { + LOG(ERROR) << "Postpone element requires until or untilexpr attribute "; return; } - std::string until = ATTR(node, "until"); Event currEvent = _interpreter->getCurrentEvent(); Resubmitter::postpone(currEvent, until, _interpreter); @@ -63,13 +74,16 @@ void PostponeElement::Resubmitter::onStableConfiguration(Interpreter* interprete std::list >::iterator eventIter = _postponedEvents.begin(); while(eventIter != _postponedEvents.end()) { try { + LOG(INFO) << "Reevaluating: >> " << eventIter->first << " <<"; if (interpreter->getDataModel().evalAsBool(eventIter->first)) { + LOG(INFO) << " -> is TRUE"; interpreter->receive(eventIter->second, true); _postponedEvents.erase(eventIter); break; } + LOG(INFO) << " -> is FALSE"; } catch (Event e) { - LOG(ERROR) << "Syntax error in until attribute of postpone element:" << std::endl << e << std::endl; + LOG(ERROR) << "Syntax error while evaluating until attribute of postpone element:" << std::endl << e << std::endl; _postponedEvents.erase(eventIter++); continue; } diff --git a/src/uscxml/plugins/element/response/ResponseElement.cpp b/src/uscxml/plugins/element/response/ResponseElement.cpp index 7f1a479..62ebf34 100644 --- a/src/uscxml/plugins/element/response/ResponseElement.cpp +++ b/src/uscxml/plugins/element/response/ResponseElement.cpp @@ -52,28 +52,103 @@ void ResponseElement::enterElement(const Arabica::DOM::Node& node) return; } httpReply.status = strTo(statusStr);; - + // extract the content Arabica::XPath::NodeSet contents = Interpreter::filterChildElements(_interpreter->getXMLPrefixForNS(getNamespace()) + "content", node); if (contents.size() > 0) { - if (HAS_ATTR(contents[0], "expr")) { + if (HAS_ATTR(contents[0], "expr")) { // -- content is evaluated string from datamodel ------ if (_interpreter->getDataModel()) { try { std::string contentValue = _interpreter->getDataModel().evalAsString(ATTR(contents[0], "expr")); httpReply.content = contentValue; } catch (Event e) { LOG(ERROR) << "Syntax error with expr in content child of response element:" << std::endl << e << std::endl; + return; } } else { LOG(ERROR) << "content element has expr attribute but no datamodel is specified."; + return; + } + } else if (HAS_ATTR(contents[0], "file") || HAS_ATTR(contents[0], "fileexpr")) { // -- content is from file ------ + URL file; + if (HAS_ATTR(contents[0], "fileexpr")) { + if (_interpreter->getDataModel()) { + try { + file = "file://" + _interpreter->getDataModel().evalAsString(ATTR(contents[0], "fileexpr")); + } catch (Event e) { + LOG(ERROR) << "Syntax error with fileexpr in content child of response element:" << std::endl << e << std::endl; + return; + } + } + } else { + file = "file://" + ATTR(contents[0], "fileexpr"); } - } else if (contents[0].hasChildNodes()) { + if (file) { + httpReply.content = file.getInContent(); + size_t lastDot; + if ((lastDot = file.path().find_last_of(".")) != std::string::npos) { + std::string extension = file.path().substr(lastDot + 1); + std::string mimeType = HTTPServer::mimeTypeForExtension(extension); + if (mimeType.length() > 0) { + httpReply.headers["Content-Type"] = mimeType; + } + } + } + } else if (contents[0].hasChildNodes()) { // -- content embedded as child nodes ------ httpReply.content = contents[0].getFirstChild().getNodeValue(); } else { LOG(ERROR) << "content element does not specify any content."; + return; } } + // process headers + Arabica::XPath::NodeSet headers = Interpreter::filterChildElements(_interpreter->getXMLPrefixForNS(getNamespace()) + "header", node); + for (int i = 0; i < headers.size(); i++) { + std::string name; + if (HAS_ATTR(headers[i], "name")) { + name = ATTR(headers[i], "name"); + } else if(HAS_ATTR(headers[i], "nameexpr")) { + if (_interpreter->getDataModel()) { + try { + name = _interpreter->getDataModel().evalAsString(ATTR(headers[i], "nameexpr")); + } catch (Event e) { + LOG(ERROR) << "Syntax error with nameexpr in header child of response element:" << std::endl << e << std::endl; + return; + } + } else { + LOG(ERROR) << "header element has nameexpr attribute but no datamodel is specified."; + return; + } + } else { + LOG(ERROR) << "header element has no name or nameexpr attribute."; + return; + } + + std::string value; + if (HAS_ATTR(headers[i], "value")) { + value = ATTR(headers[i], "value"); + } else if(HAS_ATTR(headers[i], "expr")) { + if (_interpreter->getDataModel()) { + try { + value = _interpreter->getDataModel().evalAsString(ATTR(headers[i], "expr")); + } catch (Event e) { + LOG(ERROR) << "Syntax error with expr in header child of response element:" << std::endl << e << std::endl; + return; + } + } else { + LOG(ERROR) << "header element has expr attribute but no datamodel is specified."; + return; + } + } else { + LOG(ERROR) << "header element has no value or expr attribute."; + return; + } + + httpReply.headers[name] = value; + } + + // send the reply HTTPServer::reply(httpReply); servlet->getRequests().erase(requestId); diff --git a/src/uscxml/plugins/invoker/filesystem/dirmon/DirMonInvoker.cpp b/src/uscxml/plugins/invoker/filesystem/dirmon/DirMonInvoker.cpp index e34517d..b7477df 100644 --- a/src/uscxml/plugins/invoker/filesystem/dirmon/DirMonInvoker.cpp +++ b/src/uscxml/plugins/invoker/filesystem/dirmon/DirMonInvoker.cpp @@ -24,7 +24,11 @@ bool connect(pluma::Host& host) { } #endif -DirMonInvoker::DirMonInvoker() : _reportExisting(false), _recurse(false), _thread(NULL) { +DirMonInvoker::DirMonInvoker() : + _reportExisting(true), + _reportHidden(false), + _recurse(false), + _thread(NULL) { } DirMonInvoker::~DirMonInvoker() { @@ -51,13 +55,31 @@ void DirMonInvoker::cancel(const std::string sendId) { } void DirMonInvoker::invoke(const InvokeRequest& req) { - if (req.params.find("dir") != req.params.end() && boost::iequals(req.params.find("reportexisting")->second, "true")) - _reportExisting = true; + if (req.params.find("dir") != req.params.end() && boost::iequals(req.params.find("reportexisting")->second, "false")) + _reportExisting = false; if (req.params.find("recurse") != req.params.end() && boost::iequals(req.params.find("recurse")->second, "true")) _recurse = true; - if (req.params.find("suffix") != req.params.end()) - _suffix = req.params.find("suffix")->second; - + if (req.params.find("reporthidden") != req.params.end() && boost::iequals(req.params.find("reporthidden")->second, "true")) + _reportHidden = true; + + std::string suffixList; + if (req.params.find("suffix") != req.params.end()) { + suffixList = req.params.find("suffix")->second; + } else if (req.params.find("suffixes") != req.params.end()) { + suffixList = req.params.find("suffixes")->second; + } + + if (suffixList.size() > 0) { + // seperate path into components + std::stringstream ss(suffixList); + std::string item; + while(std::getline(ss, item, ' ')) { + if (item.length() == 0) + continue; + _suffixes.insert(item); + } + } + std::multimap::const_iterator dirIter = req.params.find("dir"); while(dirIter != req.params.upper_bound("dir")) { URL url(dirIter->second); @@ -89,14 +111,20 @@ void DirMonInvoker::reportExisting() { } void DirMonInvoker::handleFileAction(FW::WatchID watchid, const FW::String& dir, const FW::String& filename, FW::Action action) { - if (!boost::algorithm::ends_with(filename, _suffix)) - return; - - struct stat fileStat; - if (stat(filename.c_str(), &fileStat) != 0) { - LOG(ERROR) << "Error with stat on directory entry " << filename << ": " << strerror(errno); - return; - } + + if (_suffixes.size() > 0) { + bool validSuffix = false; + std::set::iterator suffixIter = _suffixes.begin(); + while(suffixIter != _suffixes.end()) { + if (boost::algorithm::ends_with(filename, *suffixIter)) { + validSuffix = true; + break; + } + suffixIter++; + } + if (!validSuffix) + return; + } Event event; event.invokeid = _invokeId; @@ -117,39 +145,56 @@ void DirMonInvoker::handleFileAction(FW::WatchID watchid, const FW::String& dir, break; } + // basename is the filename with suffix std::string basename; size_t lastSep; if ((lastSep = filename.find_last_of(PATH_SEPERATOR)) != std::string::npos) { lastSep++; basename = filename.substr(lastSep, filename.length() - lastSep); - } else { - basename = filename; + event.data.compound["file"].compound["name"] = Data(basename, Data::VERBATIM); } - - std::string extension; + + // return if this is a hidden file + if (boost::algorithm::starts_with(basename, ".") && !_reportHidden) + return; + + struct stat fileStat; + if (action != FW::Actions::Delete) { + if (stat(filename.c_str(), &fileStat) != 0) { + LOG(ERROR) << "Error with stat on directory entry " << filename << ": " << strerror(errno); + return; + } else { + event.data.compound["file"].compound["mtime"] = toStr(fileStat.st_mtime); + event.data.compound["file"].compound["ctime"] = toStr(fileStat.st_ctime); + event.data.compound["file"].compound["atime"] = toStr(fileStat.st_atime); + event.data.compound["file"].compound["size"] = toStr(fileStat.st_size); + } + } + + // extension is the suffix and strippedName the basename without the suffix size_t lastDot; if ((lastDot = basename.find_last_of(".")) != std::string::npos) { - lastDot++; - extension = basename.substr(lastDot, basename.length() - lastDot); + std::string extension = basename.substr(lastDot + 1); + event.data.compound["file"].compound["extension"] = Data(extension, Data::VERBATIM); + std::string strippedName = basename.substr(0, lastDot); + event.data.compound["file"].compound["strippedName"] = Data(strippedName, Data::VERBATIM); } - std::string relPath; + // relpath is the path to the file relative to the dir if (boost::algorithm::starts_with(filename, dir)) { - relPath = filename.substr(dir.length()); - } else { - relPath = filename; + std::string relPath = filename.substr(dir.length()); + event.data.compound["file"].compound["relPath"] = Data(relPath, Data::VERBATIM); + + // relDir is the relpath without the basename + if ((lastSep = relPath.find_last_of(PATH_SEPERATOR)) != std::string::npos) { + lastSep++; + std::string relDir = relPath.substr(0, lastSep); + event.data.compound["file"].compound["relDir"] = Data(relDir, Data::VERBATIM); + } } - event.data.compound["file"].compound["name"] = Data(basename, Data::VERBATIM); event.data.compound["file"].compound["path"] = Data(filename, Data::VERBATIM); - event.data.compound["file"].compound["relPath"] = Data(relPath, Data::VERBATIM); event.data.compound["file"].compound["dir"] = Data(dir, Data::VERBATIM); - event.data.compound["file"].compound["extension"] = Data(extension, Data::VERBATIM); - - event.data.compound["file"].compound["mtime"] = toStr(fileStat.st_mtime); - event.data.compound["file"].compound["ctime"] = toStr(fileStat.st_ctime); - event.data.compound["file"].compound["atime"] = toStr(fileStat.st_atime); - event.data.compound["file"].compound["size"] = toStr(fileStat.st_size); returnEvent(event); } diff --git a/src/uscxml/plugins/invoker/filesystem/dirmon/DirMonInvoker.h b/src/uscxml/plugins/invoker/filesystem/dirmon/DirMonInvoker.h index 1437759..04e670d 100644 --- a/src/uscxml/plugins/invoker/filesystem/dirmon/DirMonInvoker.h +++ b/src/uscxml/plugins/invoker/filesystem/dirmon/DirMonInvoker.h @@ -39,8 +39,9 @@ public: protected: bool _reportExisting; + bool _reportHidden; bool _recurse; - std::string _suffix; + std::set _suffixes; bool _isRunning; tthread::thread* _thread; diff --git a/src/uscxml/plugins/invoker/graphics/openscenegraph/OSGConverter.cpp b/src/uscxml/plugins/invoker/graphics/openscenegraph/OSGConverter.cpp index 726cd08..0337770 100644 --- a/src/uscxml/plugins/invoker/graphics/openscenegraph/OSGConverter.cpp +++ b/src/uscxml/plugins/invoker/graphics/openscenegraph/OSGConverter.cpp @@ -3,15 +3,31 @@ #include "uscxml/config.h" #include +#include +#include #include #include #include #include +#include + +#include #ifdef BUILD_AS_PLUGINS #include #endif +#define EVAL_PARAM_EXPR(param, expr, key) \ +if (param.find(key) == param.end() && param.find(expr) != param.end() && _interpreter->getDataModel()) \ + param.insert(std::make_pair(key, _interpreter->getDataModel().evalAsString(param.find(expr)->second))); + +#define CAST_PARAM(param, var, key, type) \ +if (param.find(key) != param.end()) { \ + try { var = boost::lexical_cast(param.find(key)->second); } \ + catch(...) { LOG(ERROR) << "Attribute " key " of sendrequest to osgconverter is of invalid format: " << param.find(key)->second; } \ +} + + namespace uscxml { #ifdef BUILD_AS_PLUGINS @@ -23,6 +39,7 @@ bool connect(pluma::Host& host) { #endif OSGConverter::OSGConverter() : _isRunning(false) { +// osg::setNotifyLevel(osg::DEBUG_FP); } OSGConverter::~OSGConverter() { @@ -94,20 +111,14 @@ void OSGConverter::send(const SendRequest& req) { } } - if (actualReq.params.find("height") == actualReq.params.end()) { - // no explicit height - if (actualReq.params.find("heightexpr") != actualReq.params.end() && _interpreter->getDataModel()) { - actualReq.params.insert(std::make_pair("height", _interpreter->getDataModel().evalAsString(actualReq.params.find("heightexpr")->second))); - } - } - - if (actualReq.params.find("width") == actualReq.params.end()) { - // no explicit width - if (actualReq.params.find("widthexpr") != actualReq.params.end() && _interpreter->getDataModel()) { - actualReq.params.insert(std::make_pair("width", _interpreter->getDataModel().evalAsString(actualReq.params.find("widthexpr")->second))); - } - } + EVAL_PARAM_EXPR(actualReq.params, "heightexpr", "height"); + EVAL_PARAM_EXPR(actualReq.params, "widthexpr", "width"); + EVAL_PARAM_EXPR(actualReq.params, "pitchexpr", "pitch"); + EVAL_PARAM_EXPR(actualReq.params, "rollexpr", "roll"); + EVAL_PARAM_EXPR(actualReq.params, "yawexpr", "yaw"); + EVAL_PARAM_EXPR(actualReq.params, "zoomexpr", "zoom"); +// process(actualReq); _workQueue.push(actualReq); } @@ -139,9 +150,13 @@ void OSGConverter::run(void* instance) { } void OSGConverter::process(const SendRequest& req) { - - int width = (req.params.find("width") != req.params.end() ? strTo(req.params.find("width")->second) : 640); - int height = (req.params.find("height") != req.params.end() ? strTo(req.params.find("height")->second) : 480); + +// std::cout << req; + + int width = 640; + int height = 480; + CAST_PARAM(req.params, width, "width", int); + CAST_PARAM(req.params, height, "height", int); assert(req.params.find("source") != req.params.end()); assert(req.params.find("dest") != req.params.end()); @@ -191,10 +206,10 @@ void OSGConverter::process(const SendRequest& req) { viewer.getCamera()->setClearColor(osg::Vec4f(1.0f,1.0f,1.0f,1.0f)); viewer.getCamera()->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - ((osg::MatrixTransform*)sceneGraph.get())->setMatrix(requestToModelPose(req)); - viewer.getCamera()->setViewMatrix(requestToCamPose(req)); +// viewer.getCamera()->setViewMatrix(requestToCamPose(req)); -// viewer.home(); + viewer.home(); + ((osg::MatrixTransform*)sceneGraph.get())->setMatrix(requestToModelPose(req)); // perform one viewer iteration viewer.realize(); @@ -202,19 +217,32 @@ void OSGConverter::process(const SendRequest& req) { } } -osg::Matrix OSGConverter::requestToModelPose(const SendRequest& req) { - double pitch = (req.params.find("pitch") != req.params.end() ? strTo(req.params.find("pitch")->second) : 0); - double roll = (req.params.find("roll") != req.params.end() ? strTo(req.params.find("roll")->second) : 0); - double yaw = (req.params.find("yaw") != req.params.end() ? strTo(req.params.find("yaw")->second) : 0); - - return eulerToMatrix(pitch, roll, yaw); -// osg::Matrix m; -// m.makeIdentity(); -// return m; +osg::Matrix OSGConverter::requestToModelPose(const SendRequest& req) { + double pitch = 0; + double roll = 0; + double yaw = 0; + double zoom = 1; + CAST_PARAM(req.params, pitch, "pitch", double); + CAST_PARAM(req.params, roll, "roll", double); + CAST_PARAM(req.params, yaw, "yaw", double); + CAST_PARAM(req.params, zoom, "zoom", double); + + osg::Matrix m = osg::Matrix::scale(zoom, zoom, zoom) * eulerToMatrix(pitch, roll, yaw); + +#if 0 + dumpMatrix(m); +#endif + return m; } osg::Matrix OSGConverter::requestToCamPose(const SendRequest& req) { - return eulerToMatrix(0, 0, 0); +// double zoom = 1; +// CAST_PARAM(req.params, zoom, "zoom", double); +// osg::Matrix scale = osg::Matrix::scale(zoom, zoom, zoom); +// return scale; + osg::Matrix identity; + identity.makeIdentity(); + return identity; } osg::ref_ptr OSGConverter::setupGraph(const std::string filename) { @@ -240,7 +268,9 @@ osg::ref_ptr OSGConverter::setupGraph(const std::string filename) { } _models[filename] = std::make_pair(now, model); } + _models[filename].first = now; +#if 1 // remove old models from cache std::map > >::iterator modelIter = _models.begin(); while(modelIter != _models.end()) { @@ -251,12 +281,13 @@ osg::ref_ptr OSGConverter::setupGraph(const std::string filename) { modelIter++; } } - } +#endif + } + osg::ref_ptr root = new osg::MatrixTransform(); osg::ref_ptr model = _models[filename].second; - _models[filename].first = now; // translation matrix to move model into center osg::ref_ptr modelCenter = new osg::MatrixTransform(); @@ -268,7 +299,7 @@ osg::ref_ptr OSGConverter::setupGraph(const std::string filename) { // add to model pose matrix root->addChild(modelCenter); - + return root; } @@ -299,7 +330,7 @@ osg::Matrix OSGConverter::eulerToMatrix(double pitch, double roll, double yaw) { m(0,3) = m(1,3) = m(2,3) = m(3,0) = m(3,1) = m(3,2) = 0; m(3,3) = 1; - + return m; } @@ -331,6 +362,15 @@ void OSGConverter::matrixToEuler(const osg::Matrix& m, double& pitch, double& ro yaw = fmod( angle_z, 2 * M_PI ); } +void OSGConverter::dumpMatrix(const osg::Matrix& m) { + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + std::cout << ", " << m(i, j); + } + std::cout << std::endl; + } +} + void OSGConverter::NameRespectingWriteToFile::operator()(const osg::Image& image, const unsigned int context_id) { osgDB::writeImageFile(image, _filename); } diff --git a/src/uscxml/plugins/invoker/graphics/openscenegraph/OSGConverter.h b/src/uscxml/plugins/invoker/graphics/openscenegraph/OSGConverter.h index e96a4e9..67b0da6 100644 --- a/src/uscxml/plugins/invoker/graphics/openscenegraph/OSGConverter.h +++ b/src/uscxml/plugins/invoker/graphics/openscenegraph/OSGConverter.h @@ -34,6 +34,7 @@ public: osg::Matrix requestToModelPose(const SendRequest& req); osg::Matrix requestToCamPose(const SendRequest& req); + static void dumpMatrix(const osg::Matrix& m); static osg::Matrix eulerToMatrix(double pitch, double roll, double yaw); static void matrixToEuler(const osg::Matrix& m, double& pitch, double& roll, double& yaw); diff --git a/src/uscxml/plugins/invoker/heartbeat/HeartbeatInvoker.cpp b/src/uscxml/plugins/invoker/heartbeat/HeartbeatInvoker.cpp index f89c1b0..16947a7 100644 --- a/src/uscxml/plugins/invoker/heartbeat/HeartbeatInvoker.cpp +++ b/src/uscxml/plugins/invoker/heartbeat/HeartbeatInvoker.cpp @@ -41,6 +41,7 @@ void HeartbeatInvoker::cancel(const std::string sendId) { } void HeartbeatInvoker::invoke(const InvokeRequest& req) { + _invokeId = req.invokeid; _event.invokeid = _invokeId; std::string intervalStr; double interval = 0; diff --git a/src/uscxml/plugins/invoker/http/HTTPServletInvoker.cpp b/src/uscxml/plugins/invoker/http/HTTPServletInvoker.cpp index 753877c..a3556c2 100644 --- a/src/uscxml/plugins/invoker/http/HTTPServletInvoker.cpp +++ b/src/uscxml/plugins/invoker/http/HTTPServletInvoker.cpp @@ -1,6 +1,8 @@ #include "HTTPServletInvoker.h" #include +#include "uscxml/config.h" + #ifdef BUILD_AS_PLUGINS #include #endif diff --git a/src/uscxml/server/HTTPServer.cpp b/src/uscxml/server/HTTPServer.cpp index d8a6474..a7957ea 100644 --- a/src/uscxml/server/HTTPServer.cpp +++ b/src/uscxml/server/HTTPServer.cpp @@ -1,3 +1,5 @@ +#include "uscxml/config.h" + #ifdef _WIN32 #include #include @@ -31,9 +33,9 @@ #endif namespace uscxml { - + HTTPServer::HTTPServer(unsigned short port) { - _port = port; + _port = port; _base = event_base_new(); _http = evhttp_new(_base); _handle = NULL; @@ -51,10 +53,30 @@ HTTPServer::~HTTPServer() { HTTPServer* HTTPServer::_instance = NULL; tthread::recursive_mutex HTTPServer::_instanceMutex; +std::map HTTPServer::mimeTypes; HTTPServer* HTTPServer::getInstance(int port) { tthread::lock_guard lock(_instanceMutex); if (_instance == NULL) { + + // this is but a tiny list, supply a content-type
yourself + mimeTypes["txt"] = "text/plain"; + mimeTypes["c"] = "text/plain"; + mimeTypes["h"] = "text/plain"; + mimeTypes["html"] = "text/html"; + mimeTypes["htm"] = "text/htm"; + mimeTypes["css"] = "text/css"; + mimeTypes["gif"] = "image/gif"; + mimeTypes["jpg"] = "image/jpeg"; + mimeTypes["jpeg"] = "image/jpeg"; + mimeTypes["mpg"] = "video/mpeg"; + mimeTypes["mov"] = "video/quicktime"; + mimeTypes["png"] = "image/png"; + mimeTypes["pdf"] = "application/pdf"; + mimeTypes["ps"] = "application/postscript"; + mimeTypes["tif"] = "image/tiff"; + mimeTypes["tiff"] = "image/tiff"; + #ifndef _WIN32 evthread_use_pthreads(); #else @@ -66,6 +88,12 @@ HTTPServer* HTTPServer::getInstance(int port) { return _instance; } +std::string HTTPServer::mimeTypeForExtension(const std::string& ext) { + if (mimeTypes.find(ext) != mimeTypes.end()) + return mimeTypes[ext]; + return ""; +} + void HTTPServer::httpRecvReqCallback(struct evhttp_request *req, void *callbackData) { // std::cout << (uintptr_t)req << ": Replying" << std::endl; // evhttp_send_reply(req, 200, NULL, NULL); @@ -194,6 +222,10 @@ void HTTPServer::processByMatchingServlet(const Request& request) { void HTTPServer::reply(const Reply& reply) { struct evbuffer *evb = evbuffer_new(); + if (reply.content.size() > 0 && reply.headers.find("Content-Type") == reply.headers.end()) { + LOG(INFO) << "Sending content without Content-Type header"; + } + std::map::const_iterator headerIter = reply.headers.begin(); while(headerIter != reply.headers.end()) { evhttp_add_header(evhttp_request_get_output_headers(reply.curlReq), headerIter->first.c_str(), headerIter->second.c_str()); diff --git a/src/uscxml/server/HTTPServer.h b/src/uscxml/server/HTTPServer.h index 1ec28c7..9bab1a1 100644 --- a/src/uscxml/server/HTTPServer.h +++ b/src/uscxml/server/HTTPServer.h @@ -41,7 +41,7 @@ public: static std::string getBaseURL(); static void reply(const Reply& reply); - + static std::string mimeTypeForExtension(const std::string& ext); static bool registerServlet(const std::string& path, HTTPServlet* servlet); ///< Register a servlet, returns false if path is already taken static void unregisterServlet(HTTPServlet* servlet); @@ -58,7 +58,7 @@ private: static void httpRecvReqCallback(struct evhttp_request *req, void *callbackData); void processByMatchingServlet(const Request& request); - + static std::map mimeTypes; std::map _servlets; typedef std::map::iterator servlet_iter_t; -- cgit v0.12