From e1a31a44c946d58a1b4654e5daa2d10d9c6f881d Mon Sep 17 00:00:00 2001 From: Stefan Radomski Date: Wed, 6 Mar 2013 19:23:17 +0100 Subject: Changed directory monitor to polling behaviour :( --- apps/mmi-browser.cpp | 30 +- apps/samples/vrml-server.scxml | 396 ++++++++++++------- src/uscxml/Interpreter.cpp | 194 +++++----- src/uscxml/URL.cpp | 4 +- .../datamodel/ecmascript/v8/V8DataModel.cpp | 6 +- .../plugins/element/postpone/PostponeElement.cpp | 69 +++- .../plugins/element/postpone/PostponeElement.h | 12 +- .../plugins/element/response/ResponseElement.cpp | 133 ++++--- .../invoker/filesystem/dirmon/DirMonInvoker.cpp | 423 +++++++++++++-------- .../invoker/filesystem/dirmon/DirMonInvoker.h | 59 ++- .../dirmon/FileWatcher/FileWatcherOSX.cpp | 62 ++- .../filesystem/dirmon/FileWatcher/FileWatcherOSX.h | 3 +- .../graphics/openscenegraph/OSGConverter.cpp | 316 ++++++++++----- .../invoker/graphics/openscenegraph/OSGConverter.h | 15 +- .../plugins/invoker/heartbeat/HeartbeatInvoker.cpp | 2 +- src/uscxml/server/HTTPServer.cpp | 62 +-- src/uscxml/server/HTTPServer.h | 4 +- test/CMakeLists.txt | 5 + test/src/test-dirmon.cpp | 26 ++ 19 files changed, 1160 insertions(+), 661 deletions(-) create mode 100644 test/src/test-dirmon.cpp diff --git a/apps/mmi-browser.cpp b/apps/mmi-browser.cpp index 1cb82e3..8d3a12f 100644 --- a/apps/mmi-browser.cpp +++ b/apps/mmi-browser.cpp @@ -12,7 +12,7 @@ #ifdef HAS_SIGNAL_H void handler(int s) { - printf("Caught SIGPIPE ############\n"); + printf("Caught SIGPIPE ############\n"); } #endif @@ -34,24 +34,24 @@ int main(int argc, char** argv) { using namespace uscxml; #ifdef HAS_SIGNAL_H - // disable SIGPIPE + // 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); - + + // 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) { @@ -87,7 +87,7 @@ int main(int argc, char** argv) { interpreter->setCmdLineOptions(argc, argv); interpreter->start(); while(interpreter->runOnMainThread(25)); - // interpreter->interpret(); + // interpreter->interpret(); delete interpreter; } diff --git a/apps/samples/vrml-server.scxml b/apps/samples/vrml-server.scxml index 1d356be..bf0e4f6 100644 --- a/apps/samples/vrml-server.scxml +++ b/apps/samples/vrml-server.scxml @@ -1,23 +1,139 @@ @@ -35,28 +151,7 @@ - - - - + @@ -65,174 +160,189 @@ + + + + + + + + + + + + + + + + + - + - + - + - + - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + - - - - + + - +
+ - + - - - - -
- - - - -
- - - - - - - + _event.data.pathComponent.length == 2 && + _event.data.pathComponent[1] === 'processed'"> + + +
+ + + + + + + +
+ + - +
- + diff --git a/src/uscxml/Interpreter.cpp b/src/uscxml/Interpreter.cpp index eeeff0c..5324b9e 100644 --- a/src/uscxml/Interpreter.cpp +++ b/src/uscxml/Interpreter.cpp @@ -275,8 +275,10 @@ void Interpreter::interpret() { LOG(ERROR) << "No datamodel for " << datamodelName << " registered"; } - if (_dataModel) + if (_dataModel) { _dataModel.assign("_x.args", _cmdLineOptions); + _dataModel.assign("_ioprocessors['http']", _httpServlet->getDataModelVariables()); + } setupIOProcessors(); @@ -456,13 +458,13 @@ void Interpreter::mainEventLoop() { #endif monIter = _monitors.begin(); while(monIter != _monitors.end()) { - 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"; - } + 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++; } @@ -484,13 +486,13 @@ void Interpreter::mainEventLoop() { if (!enabledTransitions.empty()) { monIter = _monitors.begin(); while(monIter != _monitors.end()) { - 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"; - } + 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); @@ -514,13 +516,13 @@ void Interpreter::mainEventLoop() { monIter = _monitors.begin(); // if (!_sendQueue || _sendQueue->isEmpty()) { while(monIter != _monitors.end()) { - 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"; - } + 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++; } // } @@ -582,13 +584,13 @@ void Interpreter::mainEventLoop() { } monIter = _monitors.begin(); while(monIter != _monitors.end()) { - 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"; - } + 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++; } @@ -596,13 +598,13 @@ void Interpreter::mainEventLoop() { monIter = _monitors.begin(); while(monIter != _monitors.end()) { - 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"; - } + 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++; } @@ -674,9 +676,9 @@ void Interpreter::send(const Arabica::DOM::Node& element) { } } catch (Event e) { LOG(ERROR) << "Syntax error in send element eventexpr:" << std::endl << e << std::endl; - return; + return; } - try { + try { // target if (HAS_ATTR(element, "targetexpr") && _dataModel) { sendReq.target = _dataModel.evalAsString(ATTR(element, "targetexpr")); @@ -685,9 +687,9 @@ void Interpreter::send(const Arabica::DOM::Node& element) { } } catch (Event e) { LOG(ERROR) << "Syntax error in send element targetexpr:" << std::endl << e << std::endl; - return; + return; } - try { + try { // type if (HAS_ATTR(element, "typeexpr") && _dataModel) { sendReq.type = _dataModel.evalAsString(ATTR(element, "typeexpr")); @@ -698,9 +700,9 @@ void Interpreter::send(const Arabica::DOM::Node& element) { } } catch (Event e) { LOG(ERROR) << "Syntax error in send element typeexpr:" << std::endl << e << std::endl; - return; + return; } - try { + try { // id if (HAS_ATTR(element, "idlocation") && _dataModel) { sendReq.sendid = _dataModel.evalAsString(ATTR(element, "idlocation")); @@ -729,10 +731,10 @@ void Interpreter::send(const Arabica::DOM::Node& element) { */ } catch (Event e) { LOG(ERROR) << "Syntax error in send element idlocation:" << std::endl << e << std::endl; - return; + return; } - try { + try { // delay std::string delay; sendReq.delayMs = 0; @@ -757,10 +759,10 @@ void Interpreter::send(const Arabica::DOM::Node& element) { } } catch (Event e) { LOG(ERROR) << "Syntax error in send element delayexpr:" << std::endl << e << std::endl; - return; + return; } - - try { + + try { // namelist if (HAS_ATTR(element, "namelist")) { if (_dataModel) { @@ -776,10 +778,10 @@ void Interpreter::send(const Arabica::DOM::Node& element) { } } catch (Event e) { LOG(ERROR) << "Syntax error in send element namelist:" << std::endl << e << std::endl; - return; + return; } - try { + try { // params NodeSet params = filterChildElements(_xmlNSPrefix + "param", element); for (int i = 0; i < params.size(); i++) { @@ -803,9 +805,9 @@ void Interpreter::send(const Arabica::DOM::Node& element) { } } catch (Event e) { LOG(ERROR) << "Syntax error in send element param expr:" << std::endl << e << std::endl; - return; + return; } - try { + try { // content NodeSet contents = filterChildElements(_xmlNSPrefix + "content", element); @@ -831,16 +833,16 @@ void Interpreter::send(const Arabica::DOM::Node& element) { } } catch (Event e) { LOG(ERROR) << "Syntax error in send element content:" << std::endl << e << std::endl; - return; + 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); - } + 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) { @@ -1368,20 +1370,20 @@ void Interpreter::executeContent(const Arabica::DOM::Node& content) } else { // condition does not match - do we have an elsif? if (ifElem.hasChildNodes()) { - NodeList elseifElem = ifElem.getElementsByTagNameNS(_nsURL, "elseif"); - for (unsigned int i = 0; i < elseifElem.getLength(); i++) { + NodeSet elseifElem = filterChildElements(_xmlNSPrefix + "elseif", ifElem); + for (unsigned int i = 0; i < elseifElem.size(); i++) { #if 0 - if (HAS_ATTR(elseifElem.item(i), "cond")) - std::cout << ATTR(elseifElem.item(i), "cond") << std::endl; + if (HAS_ATTR(elseifElem[i], "cond")) + std::cout << ATTR(elseifElem[i], "cond") << std::endl; #endif - if (hasConditionMatch(elseifElem.item(i))) { - executeContent(elseifElem.item(i).getChildNodes()); + if (hasConditionMatch(elseifElem[i])) { + executeContent(elseifElem[i].getChildNodes()); goto ELSIF_ELEM_MATCH; } } - NodeList elseElem = ifElem.getElementsByTagNameNS(_nsURL, "else"); - if (elseElem.getLength() > 0) - executeContent(elseElem.item(0).getChildNodes()); + NodeSet elseElem = filterChildElements(_xmlNSPrefix + "else", ifElem); + if (elseElem.size() > 0) + executeContent(elseElem[0].getChildNodes()); } } ELSIF_ELEM_MATCH: @@ -1619,13 +1621,13 @@ void Interpreter::exitStates(const Arabica::XPath::NodeSet& enabled monIter = _monitors.begin(); while(monIter != _monitors.end()) { - 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"; - } + 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++; } @@ -1682,13 +1684,13 @@ void Interpreter::exitStates(const Arabica::XPath::NodeSet& enabled monIter = _monitors.begin(); while(monIter != _monitors.end()) { - 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"; - } + 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++; } @@ -1800,13 +1802,13 @@ void Interpreter::enterStates(const Arabica::XPath::NodeSet& enable monIter = _monitors.begin(); while(monIter != _monitors.end()) { - 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"; - } + 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++; } @@ -1867,13 +1869,13 @@ void Interpreter::enterStates(const Arabica::XPath::NodeSet& enable monIter = _monitors.begin(); while(monIter != _monitors.end()) { - 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"; - } + 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/URL.cpp b/src/uscxml/URL.cpp index d410bcf..1f180e4 100644 --- a/src/uscxml/URL.cpp +++ b/src/uscxml/URL.cpp @@ -64,7 +64,7 @@ size_t URLImpl::headerHandler(void *ptr, size_t size, size_t nmemb, void *userda } void URLImpl::downloadStarted() { - LOG(INFO) << "Starting download of " << asString() << std::endl; +// LOG(INFO) << "Starting download of " << asString() << std::endl; _inContent.str(""); _inContent.clear(); _inHeader.str(""); @@ -80,7 +80,7 @@ void URLImpl::downloadStarted() { void URLImpl::downloadCompleted() { tthread::lock_guard lock(_mutex); - LOG(INFO) << "Finished downloading " << asString() << " with " << _inContent.str().size() << " bytes"; +// LOG(INFO) << "Finished downloading " << asString() << " with " << _inContent.str().size() << " bytes"; _hasFailed = false; _isDownloaded = true; diff --git a/src/uscxml/plugins/datamodel/ecmascript/v8/V8DataModel.cpp b/src/uscxml/plugins/datamodel/ecmascript/v8/V8DataModel.cpp index d791b9e..d00ad43 100644 --- a/src/uscxml/plugins/datamodel/ecmascript/v8/V8DataModel.cpp +++ b/src/uscxml/plugins/datamodel/ecmascript/v8/V8DataModel.cpp @@ -320,9 +320,9 @@ 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); + 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); diff --git a/src/uscxml/plugins/element/postpone/PostponeElement.cpp b/src/uscxml/plugins/element/postpone/PostponeElement.cpp index 5dc1c60..b50b5c2 100644 --- a/src/uscxml/plugins/element/postpone/PostponeElement.cpp +++ b/src/uscxml/plugins/element/postpone/PostponeElement.cpp @@ -41,16 +41,16 @@ void PostponeElement::enterElement(const Arabica::DOM::Node& node) } // when will we refire the event? - 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"); - } + 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; + return; } if (until.length() == 0) { @@ -58,30 +58,65 @@ void PostponeElement::enterElement(const Arabica::DOM::Node& node) return; } + LOG(INFO) << until; + +#if 0 + std::string timeoutStr = "0s"; + try { + if (HAS_ATTR(node, "timeoutexpr")) { + timeoutStr = _interpreter->getDataModel().evalAsString(ATTR(node, "timeoutexpr")); + } else if (HAS_ATTR(node, "timeout")) { + timeoutStr = ATTR(node, "timeout"); + } + } catch (Event e) { + LOG(ERROR) << "Syntax error in postpone element timeoutexpr:" << std::endl << e << std::endl; + return; + } + + uint64_t timeout = 0; + NumAttr timeoutAttr(timeoutStr); + if (boost::iequals(timeoutAttr.unit, "s")) { + timeout = strTo(timeoutAttr.value) * 1000; + } else if (boost::iequals(timeoutAttr.unit, "ms")) { + timeout = strTo(timeoutAttr.value); + } + if (timeout > 0) { + timeout += tthread::chrono::system_clock::now(); + } +#endif Event currEvent = _interpreter->getCurrentEvent(); - Resubmitter::postpone(currEvent, until, _interpreter); + Resubmitter::postpone(currEvent, until, 0, _interpreter); } void PostponeElement::exitElement(const Arabica::DOM::Node& node) { } -void PostponeElement::Resubmitter::postpone(const Event& event, std::string until, Interpreter* interpreter) { +void PostponeElement::Resubmitter::postpone(const Event& event, std::string until, uint64_t timeout, Interpreter* interpreter) { Resubmitter* resubmitter = getInstance(interpreter); - resubmitter->_postponedEvents.push_back(std::make_pair(until, event)); + resubmitter->_postponedEvents.push_back(Postponed(event, until, timeout)); } void PostponeElement::Resubmitter::onStableConfiguration(Interpreter* interpreter) { - std::list >::iterator eventIter = _postponedEvents.begin(); + 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); +// LOG(INFO) << "Reevaluating: >> " << eventIter->first << " <<"; + if (eventIter->timeout > 0 && tthread::chrono::system_clock::now() < eventIter->timeout) { + // TODO: We should use an event queue +// LOG(INFO) << " -> Timeout"; + eventIter->event.name += ".timeout"; + interpreter->receive(eventIter->event, true); + _postponedEvents.erase(eventIter); + break; + } + if (interpreter->getDataModel().evalAsBool(eventIter->until)) { +// LOG(INFO) << " -> is TRUE"; + eventIter->event.name += ".postponed"; + interpreter->receive(eventIter->event, true); _postponedEvents.erase(eventIter); break; } - LOG(INFO) << " -> is FALSE"; +// LOG(INFO) << " -> is FALSE"; } catch (Event e) { LOG(ERROR) << "Syntax error while evaluating until attribute of postpone element:" << std::endl << e << std::endl; _postponedEvents.erase(eventIter++); diff --git a/src/uscxml/plugins/element/postpone/PostponeElement.h b/src/uscxml/plugins/element/postpone/PostponeElement.h index 03aafde..eb7a738 100644 --- a/src/uscxml/plugins/element/postpone/PostponeElement.h +++ b/src/uscxml/plugins/element/postpone/PostponeElement.h @@ -12,6 +12,14 @@ namespace uscxml { class PostponeElement : public ExecutableContentImpl { public: + struct Postponed { + Postponed(const Event& event, const std::string& until, long timeout) : + event(event), until(until), timeout(timeout) {} + Event event; + std::string until; + uint64_t timeout; + }; + PostponeElement() {} virtual ~PostponeElement() {} boost::shared_ptr create(Interpreter* interpreter); @@ -40,13 +48,13 @@ protected: } static Resubmitter* getInstance(Interpreter* interpreter); - static void postpone(const Event& event, std::string until, Interpreter* interpreter); + static void postpone(const Event& event, std::string until, uint64_t timeout, Interpreter* interpreter); // InterpreterMonitor void onStableConfiguration(Interpreter* interpreter); void afterCompletion(Interpreter* interpreter); - std::list > _postponedEvents; + std::list _postponedEvents; static std::map _instances; static tthread::recursive_mutex _accessLock; diff --git a/src/uscxml/plugins/element/response/ResponseElement.cpp b/src/uscxml/plugins/element/response/ResponseElement.cpp index 62ebf34..2e25b27 100644 --- a/src/uscxml/plugins/element/response/ResponseElement.cpp +++ b/src/uscxml/plugins/element/response/ResponseElement.cpp @@ -52,7 +52,7 @@ 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) { @@ -63,91 +63,90 @@ void ResponseElement::enterElement(const Arabica::DOM::Node& node) httpReply.content = contentValue; } catch (Event e) { LOG(ERROR) << "Syntax error with expr in content child of response element:" << std::endl << e << std::endl; - return; + return; } } else { LOG(ERROR) << "content element has expr attribute but no datamodel is specified."; - return; + return; } } else if (HAS_ATTR(contents[0], "file") || HAS_ATTR(contents[0], "fileexpr")) { // -- content is from file ------ - URL 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; - } + 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"); + file = "file://" + ATTR(contents[0], "fileexpr"); + } + 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; + } + } } - 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; + return; } } - // process headers + // 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 { + 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 { + 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; - } - + return; + } + } else { + LOG(ERROR) << "header element has no value or expr attribute."; + return; + } + + httpReply.headers[name] = value; + } // send the reply HTTPServer::reply(httpReply); diff --git a/src/uscxml/plugins/invoker/filesystem/dirmon/DirMonInvoker.cpp b/src/uscxml/plugins/invoker/filesystem/dirmon/DirMonInvoker.cpp index 37be3e0..675135a 100644 --- a/src/uscxml/plugins/invoker/filesystem/dirmon/DirMonInvoker.cpp +++ b/src/uscxml/plugins/invoker/filesystem/dirmon/DirMonInvoker.cpp @@ -25,10 +25,10 @@ bool connect(pluma::Host& host) { #endif DirMonInvoker::DirMonInvoker() : - _reportExisting(true), - _reportHidden(false), - _recurse(false), - _thread(NULL) { + _reportExisting(true), + _reportHidden(false), + _recurse(false), + _thread(NULL) { } DirMonInvoker::~DirMonInvoker() { @@ -55,222 +55,329 @@ 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, "false")) + if (req.params.find("dir") == req.params.end()) { + LOG(ERROR) << "No dir param given"; + return; + } + + if (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")) + if (req.params.find("recurse") != req.params.end() && + boost::iequals(req.params.find("recurse")->second, "true")) _recurse = true; - if (req.params.find("reporthidden") != req.params.end() && boost::iequals(req.params.find("reporthidden")->second, "true")) + 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::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); if (!_interpreter->toAbsoluteURI(url) || !boost::iequals(url.scheme(), "file")) { LOG(ERROR) << "Given directory '" << dirIter->second << "' cannot be transformed to absolute path"; } else { - _watchIds.insert(std::make_pair(url.path(), _fileWatcher.addWatch(url.path(), this, _recurse))); + _dir = url.path(); } - dirIter++; + break; } + + _watcher = new DirectoryWatch(_dir, _recurse); + _watcher->addMonitor(this); + _watcher->updateEntries(true); + _isRunning = true; _thread = new tthread::thread(DirMonInvoker::run, this); } void DirMonInvoker::run(void* instance) { - if (((DirMonInvoker*)instance)->_reportExisting) - ((DirMonInvoker*)instance)->reportExisting(); - - while(((DirMonInvoker*)instance)->_isRunning) - ((DirMonInvoker*)instance)->_fileWatcher.update(); + while(((DirMonInvoker*)instance)->_isRunning) { + ((DirMonInvoker*)instance)->_watcher->updateEntries(); + tthread::this_thread::sleep_for(tthread::chrono::milliseconds(20)); + } } -void DirMonInvoker::reportExisting() { - std::multimap::iterator watchIter = _watchIds.begin(); - while(watchIter != _watchIds.end()) { - reportExistingIn(watchIter->first, watchIter->second); - watchIter++; +void DirMonInvoker::handleChanges(DirectoryWatch::Action action, const std::string reportedDir, const std::string reportedFilename, struct stat fileStat) { + +// std::cout << action << " on " << reportedFilename << std::endl; + + std::string path; // complete path to the file including filename + std::string relPath; // path relative to monitored directory including filename + std::string dir; // the name of the directory we monitor + std::string relDir; // the directory from dir to the actual directory where we found a file + std::string basename; // filename including suffix + std::string strippedName; // filename without the suffix + std::string extension; // the extension + + dir = reportedDir; + + path = dir + reportedFilename; + boost::algorithm::replace_all(path, "\\", "/"); + boost::algorithm::replace_all(path, "//", "/"); + + assert(boost::algorithm::starts_with(path, dir)); + relPath = path.substr(dir.length()); + assert(boost::equal(path, dir + relPath)); + + size_t lastSep; + if ((lastSep = path.find_last_of(PATH_SEPERATOR)) != std::string::npos) { + lastSep++; + basename = path.substr(lastSep, path.length() - lastSep); + } else { + assert(false); } -} + assert(boost::algorithm::ends_with(relPath, basename)); + + // extension is the suffix and strippedName the basename without the suffix + size_t lastDot; + if ((lastDot = basename.find_last_of(".")) != std::string::npos) { + if (lastDot == 0) { + // hidden file + strippedName = basename; + } else { + extension = basename.substr(lastDot + 1); + strippedName = basename.substr(0, lastDot); + } + } else { + strippedName = basename; + } + + relDir = relPath.substr(0, relPath.length() - basename.length()); + assert(boost::equal(path, dir + relDir + basename)); -void DirMonInvoker::handleFileAction(FW::WatchID watchid, const FW::String& dir, const FW::String& filename, FW::Action action) { - - std::string path; - if (!boost::algorithm::starts_with(filename, dir)) { - path = dir + filename; - } else { - path = filename; - } - - if (_suffixes.size() > 0) { - bool validSuffix = false; - std::set::iterator suffixIter = _suffixes.begin(); - while(suffixIter != _suffixes.end()) { - if (boost::algorithm::ends_with(path, *suffixIter)) { - validSuffix = true; - break; - } - suffixIter++; - } - if (!validSuffix) - return; - } + // return if this is a hidden file + if (boost::algorithm::starts_with(basename, ".") && !_reportHidden) + return; + + // ilter suffixes + if (_suffixes.size() > 0) { + bool validSuffix = false; + std::set::iterator suffixIter = _suffixes.begin(); + while(suffixIter != _suffixes.end()) { + if (boost::algorithm::ends_with(path, *suffixIter)) { + validSuffix = true; + break; + } + suffixIter++; + } + if (!validSuffix) + return; + } Event event; event.invokeid = _invokeId; + switch (action) { - case FW::Actions::Existing: + case DirectoryWatch::EXISTING: event.name = "file.existing"; break; - case FW::Actions::Add: + case DirectoryWatch::ADDED: event.name = "file.added"; break; - case FW::Actions::Delete: + case DirectoryWatch::DELETED: event.name = "file.deleted"; break; - case FW::Actions::Modified: + case DirectoryWatch::MODIFIED: event.name = "file.modified"; break; default: break; } - // basename is the filename with suffix - std::string basename; - size_t lastSep; - if ((lastSep = path.find_last_of(PATH_SEPERATOR)) != std::string::npos) { - lastSep++; - basename = path.substr(lastSep, path.length() - lastSep); - event.data.compound["file"].compound["name"] = Data(basename, Data::VERBATIM); - } - - // 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(path.c_str(), &fileStat) != 0) { - LOG(ERROR) << "Error with stat on directory entry " << path << ": " << 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) { - 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); - } - - // relpath is the path to the file relative to the dir - if (boost::algorithm::starts_with(filename, dir)) { - 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); - } + if (action != DirectoryWatch::DELETED) { + 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); } + event.data.compound["file"].compound["name"] = Data(basename, Data::VERBATIM); + event.data.compound["file"].compound["extension"] = Data(extension, Data::VERBATIM); + event.data.compound["file"].compound["strippedName"] = Data(strippedName, Data::VERBATIM); + event.data.compound["file"].compound["relPath"] = Data(relPath, Data::VERBATIM); + event.data.compound["file"].compound["relDir"] = Data(relDir, Data::VERBATIM); event.data.compound["file"].compound["path"] = Data(path, Data::VERBATIM); event.data.compound["file"].compound["dir"] = Data(dir, Data::VERBATIM); returnEvent(event); } -bool DirMonInvoker::filter(const std::string filename) { - return true; +void DirectoryWatch::reportAsDeleted() { + std::map::iterator fileIter = _knownEntries.begin(); + while(fileIter != _knownEntries.end()) { + if (fileIter->second.st_mode & S_IFDIR) { + _knownDirs[fileIter->first]->reportAsDeleted(); + delete _knownDirs[fileIter->first]; + _knownDirs.erase(fileIter->first); + } else { + _monitors_t::iterator monIter = _monitors.begin(); + while(monIter != _monitors.end()) { + (*monIter)->handleChanges(DELETED, _dir, _relDir + PATH_SEPERATOR + fileIter->first, fileIter->second); + monIter++; + } + } + _knownEntries.erase(fileIter->first); + fileIter++; + } + assert(_knownDirs.size() == 0); + assert(_knownEntries.size() == 0); } -void DirMonInvoker::reportExistingIn(const std::string dir, FW::WatchID watchid) { -#ifndef WIN32 - DIR *dp; - dp = opendir(dir.c_str()); - if (dp == NULL) { - LOG(ERROR) << "Error opening directory " << dir << ": " << strerror(errno); +void DirectoryWatch::updateEntries(bool reportAsExisting) { + _monitors_t::iterator monIter; + if (_dir[_dir.length() - 1] == PATH_SEPERATOR) + _dir = _dir.substr(0, _dir.length() - 1); + + // stat directory for modification date + struct stat dirStat; + if (stat((_dir + _relDir).c_str(), &dirStat) != 0) { + LOG(ERROR) << "Error with stat on directory " << _dir << ": " << strerror(errno); return; } - // iterate all entries and see what changed - struct dirent* entry; - while((entry = readdir(dp))) { - std::string dname = entry->d_name; + + if ((unsigned)dirStat.st_mtime >= (unsigned)_lastChecked) { + // there are changes in the directory + std::set currEntries; + +#ifndef WIN32 + DIR *dp; + dp = opendir((_dir + _relDir).c_str()); + if (dp == NULL) { + LOG(ERROR) << "Error opening directory " << _dir + _relDir << ": " << strerror(errno); + return; + } + // iterate all entries and see what changed + struct dirent* entry; + while((entry = readdir(dp))) { + std::string dname = entry->d_name; #else - WIN32_FIND_DATA ffd; - HANDLE hFind = INVALID_HANDLE_VALUE; - TCHAR szDir[MAX_PATH]; - StringCchCopy(szDir, MAX_PATH, dir.c_str()); - StringCchCat(szDir, MAX_PATH, TEXT("\\*")); - - hFind = FindFirstFile(szDir, &ffd); - do { - std::string dname = ffd.cFileName; + WIN32_FIND_DATA ffd; + HANDLE hFind = INVALID_HANDLE_VALUE; + TCHAR szDir[MAX_PATH]; + StringCchCopy(szDir, MAX_PATH, _dir.c_str()); + StringCchCat(szDir, MAX_PATH, TEXT("\\*")); + + hFind = FindFirstFile(szDir, &ffd); + do { + string dname = ffd.cFileName; #endif - if (boost::iequals(dname, ".") || boost::iequals(dname, "..")) - continue; + // see if the file was changed + char* filename; + asprintf(&filename, "%s/%s", (_dir + _relDir).c_str(), dname.c_str()); - char* filename = (char*)malloc(dir.size() + dname.size() + 2); - sprintf(filename, "%s/%s", dir.c_str(), dname.c_str()); + struct stat fileStat; + if (stat(filename, &fileStat) != 0) { + LOG(ERROR) << "Error with stat on directory entry: " << filename << ": " << strerror(errno); + free(filename); + continue; + } - struct stat fileStat; - if (stat(filename, &fileStat) != 0) { - LOG(ERROR) << "Error with stat on directory entry " << filename << ": " << strerror(errno); - free(filename); - continue; - } + if (fileStat.st_mode & S_IFDIR) { + if (boost::equals(dname, ".") || boost::equals(dname, "..")) { + free(filename); + continue; // do not report . or .. + } + } - if (fileStat.st_mode & S_IFDIR) { - if (_recurse) { - reportExistingIn(filename, watchid); + currEntries.insert(dname); + + if (_knownEntries.find(dname) != _knownEntries.end()) { + // we have seen this entry before + struct stat oldStat = _knownEntries[dname]; + if (oldStat.st_mtime < fileStat.st_mtime) { + monIter = _monitors.begin(); + while(monIter != _monitors.end()) { + (*monIter)->handleChanges(MODIFIED, _dir, _relDir + PATH_SEPERATOR + dname, fileStat); + monIter++; + } + } } else { - free(filename); - continue; + // we have not yet seen this entry + if (fileStat.st_mode & S_IFDIR) { + _knownDirs[dname] = new DirectoryWatch(_dir, _relDir + PATH_SEPERATOR + dname); + monIter = _monitors.begin(); + while(monIter != _monitors.end()) { + _knownDirs[dname]->addMonitor(*monIter); + monIter++; + } + } else { + monIter = _monitors.begin(); + while(monIter != _monitors.end()) { + if (reportAsExisting) { + (*monIter)->handleChanges(EXISTING, _dir, _relDir + PATH_SEPERATOR + dname, fileStat); + } else { + (*monIter)->handleChanges(ADDED, _dir, _relDir + PATH_SEPERATOR + dname, fileStat); + } + monIter++; + } + } } - } - if (!filter(dname)) { free(filename); - continue; + _knownEntries[dname] = fileStat; // gets copied on insertion +#ifndef WIN32 } - - handleFileAction(watchid, dir, filename, FW::Actions::Existing); + closedir(dp); +#else + } + while (FindNextFile(hFind, &ffd) != 0); + FindClose(hFind); +#endif + // are there any known entries we have not seen this time around? + std::map::iterator fileIter = _knownEntries.begin(); + while(fileIter != _knownEntries.end()) { + if (currEntries.find(fileIter->first) == currEntries.end()) { + // we used to know this file + if (fileIter->second.st_mode & S_IFDIR) { + if (_recurse) { + _knownDirs[fileIter->first]->reportAsDeleted(); + delete _knownDirs[fileIter->first]; + _knownDirs.erase(fileIter->first); + } + } else { + monIter = _monitors.begin(); + while(monIter != _monitors.end()) { + (*monIter)->handleChanges(DELETED, _dir, _relDir + PATH_SEPERATOR + fileIter->first, fileIter->second); + monIter++; + } + } + _knownEntries.erase(fileIter->first); + } + fileIter++; + } + // remember when we last checked the directory for modifications #ifndef WIN32 - } - closedir(dp); + time(&_lastChecked); #else - } - while (FindNextFile(hFind, &ffd) != 0); - FindClose(hFind); + // TODO: this will fail with sub-millisecond updates to the directory + _lastChecked = dirStat.st_mtime + 1; #endif - + // update all directories + } + if (_recurse) { + std::map::iterator dirIter = _knownDirs.begin(); + while(dirIter != _knownDirs.end()) { + dirIter->second->updateEntries(); + dirIter++; + } + } } } \ No newline at end of file diff --git a/src/uscxml/plugins/invoker/filesystem/dirmon/DirMonInvoker.h b/src/uscxml/plugins/invoker/filesystem/dirmon/DirMonInvoker.h index 04e670d..3fd9258 100644 --- a/src/uscxml/plugins/invoker/filesystem/dirmon/DirMonInvoker.h +++ b/src/uscxml/plugins/invoker/filesystem/dirmon/DirMonInvoker.h @@ -2,8 +2,8 @@ #define DIRMONINVOKER_H_W09J90F0 #include -#include "FileWatcher/FileWatcher.h" #include +#include #ifdef BUILD_AS_PLUGINS #include "uscxml/plugins/Plugins.h" @@ -11,7 +11,48 @@ namespace uscxml { -class DirMonInvoker : public InvokerImpl, public FW::FileWatchListener { +class DirectoryWatchMonitor; + +class DirectoryWatch { +public: + enum Action { + ADDED = 1, + MODIFIED = 2, + DELETED = 4, + EXISTING = 8 + }; + + DirectoryWatch(const std::string& dir, bool recurse = false) : _dir(dir), _recurse(recurse), _lastChecked(0) {} + + void addMonitor(DirectoryWatchMonitor* monitor) { + _monitors.insert(monitor); + } + void removeMonitor(DirectoryWatchMonitor* monitor) { + _monitors.erase(monitor); + } + void updateEntries(bool reportAsExisting = false); + void reportAsDeleted(); + +protected: + DirectoryWatch(const std::string& dir, const std::string& relDir) : _dir(dir), _relDir(relDir), _recurse(true), _lastChecked(0) {} + + std::string _dir; + std::string _relDir; + + bool _recurse; + std::map _knownEntries; + std::map _knownDirs; + std::set _monitors; + typedef std::set _monitors_t; + time_t _lastChecked; +}; + +class DirectoryWatchMonitor { +public: + virtual void handleChanges(DirectoryWatch::Action action, const std::string dir, const std::string file, struct stat fileStat) = 0; +}; + +class DirMonInvoker : public InvokerImpl, public DirectoryWatchMonitor { public: DirMonInvoker(); virtual ~DirMonInvoker(); @@ -30,10 +71,7 @@ public: virtual void cancel(const std::string sendId); virtual void invoke(const InvokeRequest& req); - void handleFileAction(FW::WatchID watchid, const FW::String& dir, const FW::String& filename, FW::Action action); - void reportExisting(); - void reportExistingIn(const std::string dir, FW::WatchID watchid); - virtual bool filter(const std::string filename); + virtual void handleChanges(DirectoryWatch::Action action, const std::string dir, const std::string file, struct stat fileStat); static void run(void* instance); @@ -41,12 +79,15 @@ protected: bool _reportExisting; bool _reportHidden; bool _recurse; - std::set _suffixes; + + std::string _dir; + std::set _suffixes; bool _isRunning; tthread::thread* _thread; - FW::FileWatcher _fileWatcher; - std::multimap _watchIds; + tthread::recursive_mutex _mutex; + + DirectoryWatch* _watcher; }; #ifdef BUILD_AS_PLUGINS diff --git a/src/uscxml/plugins/invoker/filesystem/dirmon/FileWatcher/FileWatcherOSX.cpp b/src/uscxml/plugins/invoker/filesystem/dirmon/FileWatcher/FileWatcherOSX.cpp index e1634b0..a47d635 100644 --- a/src/uscxml/plugins/invoker/filesystem/dirmon/FileWatcher/FileWatcherOSX.cpp +++ b/src/uscxml/plugins/invoker/filesystem/dirmon/FileWatcher/FileWatcherOSX.cpp @@ -35,6 +35,7 @@ #include #include #include +#include // this is more suited: // https://developer.apple.com/library/mac/#documentation/Darwin/Conceptual/FSEvents_ProgGuide/UsingtheFSEventsFramework/UsingtheFSEventsFramework.html @@ -71,18 +72,20 @@ struct WatchStruct { WatchID mWatchID; String mDirName; FileWatchListener* mListener; + FileWatcherOSX* mWatcher; + bool mRecursive; // index 0 is always the directory KEvent mChangeList[MAX_CHANGE_EVENT_SIZE]; size_t mChangeListCount; - WatchStruct(WatchID watchid, const String& dirname, FileWatchListener* listener) - : mWatchID(watchid), mDirName(dirname), mListener(listener) { + WatchStruct(WatchID watchid, const String& dirname, FileWatchListener* listener, FileWatcherOSX* watcher, bool recursive = false) + : mWatchID(watchid), mDirName(dirname), mListener(listener), mWatcher(watcher), mRecursive(recursive) { mChangeListCount = 0; addAll(); } - void addFile(const String& name, bool imitEvents = true) { + void addFile(const String& name, bool emitEvents = true) { //fprintf(stderr, "ADDED: %s\n", name.c_str()); // create entry @@ -104,18 +107,18 @@ struct WatchStruct { // set the event data at the end of the list EV_SET(&mChangeList[mChangeListCount], fd, EVFILT_VNODE, EV_ADD | EV_ENABLE | EV_ONESHOT, - NOTE_DELETE | NOTE_EXTEND | NOTE_WRITE | NOTE_ATTRIB, + NOTE_DELETE | NOTE_EXTEND | NOTE_WRITE | NOTE_ATTRIB | NOTE_RENAME, 0, (void*)entry); // qsort qsort(mChangeList + 1, mChangeListCount, sizeof(KEvent), comparator); // handle action - if(imitEvents) + if(emitEvents) handleAction(name, Actions::Add); } - void removeFile(const String& name, bool imitEvents = true) { + void removeFile(const String& name, bool emitEvents = true) { // bsearch KEvent target; EntryStruct tempEntry(name.c_str(), 0); @@ -140,7 +143,7 @@ struct WatchStruct { qsort(mChangeList + 1, mChangeListCount, sizeof(KEvent), comparator); // handle action - if(imitEvents) + if(emitEvents) handleAction(name, Actions::Delete); } @@ -207,7 +210,7 @@ struct WatchStruct { int fd = open(mDirName.c_str(), O_RDONLY); EV_SET(&mChangeList[0], fd, EVFILT_VNODE, EV_ADD | EV_ENABLE | EV_ONESHOT, - NOTE_DELETE | NOTE_EXTEND | NOTE_WRITE | NOTE_ATTRIB, + NOTE_DELETE | NOTE_EXTEND | NOTE_WRITE | NOTE_ATTRIB | NOTE_RENAME, 0, 0); //fprintf(stderr, "ADDED: %s\n", mDirName.c_str()); @@ -222,11 +225,13 @@ struct WatchStruct { while((entry = readdir(dir)) != NULL) { String fname = (mDirName + "/" + String(entry->d_name)); stat(fname.c_str(), &attrib); - if(S_ISREG(attrib.st_mode)) + if(S_ISREG(attrib.st_mode)) { addFile(fname, false); - //else - // fprintf(stderr, "NOT ADDED: %s (%d)\n", fname.c_str(), attrib.st_mode); - + } else if(S_IFDIR && mRecursive && entry->d_name[0] != '.') { + mWatcher->addWatch(fname, mListener, mRecursive); + } else { + fprintf(stderr, "NOT ADDED: %s (%d)\n", fname.c_str(), attrib.st_mode); + } }//end while closedir(dir); @@ -263,19 +268,33 @@ void FileWatcherOSX::update() { if(nev == -1) perror("kevent"); else { + if (event.fflags & NOTE_DELETE) { + fprintf(stderr, "NOTE_DELETE "); + } + if (event.fflags & NOTE_EXTEND) { + fprintf(stderr, "NOTE_EXTEND "); + } + if (event.fflags & NOTE_WRITE) { + fprintf(stderr, "NOTE_WRITE "); + } + if (event.fflags & NOTE_ATTRIB) { + fprintf(stderr, "NOTE_ATTRIB "); + } + if (event.fflags & NOTE_RENAME) { + fprintf(stderr, "NOTE_RENAME "); + } + EntryStruct* entry = 0; if((entry = (EntryStruct*)event.udata) != 0) { - //fprintf(stderr, "File: %s -- ", (char*)entry->mFilename); + fprintf(stderr, " to %s -- \n", (char*)entry->mFilename); if(event.fflags & NOTE_DELETE) { - //fprintf(stderr, "File deleted\n"); //watch->handleAction(entry->mFilename, Action::Delete); watch->removeFile(entry->mFilename); } if(event.fflags & NOTE_EXTEND || event.fflags & NOTE_WRITE || event.fflags & NOTE_ATTRIB) { - //fprintf(stderr, "modified\n"); //watch->rescan(); struct stat attrib; stat(entry->mFilename, &attrib); @@ -283,7 +302,7 @@ void FileWatcherOSX::update() { watch->handleAction(entry->mFilename, FW::Actions::Modified); } } else { - //fprintf(stderr, "Dir: %s -- rescanning\n", watch->mDirName.c_str()); + fprintf(stderr, " in %s -- rescanning\n", watch->mDirName.c_str()); watch->rescan(); } } @@ -295,7 +314,7 @@ void FileWatcherOSX::update() { FileWatcherOSX::FileWatcherOSX() { mDescriptor = kqueue(); mTimeOut.tv_sec = 0; - mTimeOut.tv_nsec = 2000000; + mTimeOut.tv_nsec = 20000000; } //-------- @@ -322,9 +341,12 @@ WatchID FileWatcherOSX::addWatch(const String& directory, FileWatchListener* wat 0, (void*)"testing"); */ - WatchStruct* watch = new WatchStruct(++mLastWatchID, directory, watcher); - mWatches.insert(std::make_pair(mLastWatchID, watch)); - return mLastWatchID; + std::cout << "Adding watch for " << directory << std::endl; + + WatchID currWatch = ++mLastWatchID; + WatchStruct* watch = new WatchStruct(currWatch, directory, watcher, this, recursive); + mWatches.insert(std::make_pair(currWatch, watch)); + return currWatch; } //-------- diff --git a/src/uscxml/plugins/invoker/filesystem/dirmon/FileWatcher/FileWatcherOSX.h b/src/uscxml/plugins/invoker/filesystem/dirmon/FileWatcher/FileWatcherOSX.h index 39f411c..92e3957 100644 --- a/src/uscxml/plugins/invoker/filesystem/dirmon/FileWatcher/FileWatcherOSX.h +++ b/src/uscxml/plugins/invoker/filesystem/dirmon/FileWatcher/FileWatcherOSX.h @@ -54,7 +54,7 @@ public: /// Add a directory watch /// @exception FileNotFoundException Thrown when the requested directory does not exist - WatchID addWatch(const String& directory, FileWatchListener* watcher, bool recursive); + WatchID addWatch(const String& directory, FileWatchListener* watcher, bool recursive = false); /// Remove a directory watch. This is a brute force lazy search O(nlogn). void removeWatch(const String& directory); @@ -78,6 +78,7 @@ private: /// WatchID allocator int mLastWatchID; + friend class WatchStruct; };//end FileWatcherOSX };//namespace FW diff --git a/src/uscxml/plugins/invoker/graphics/openscenegraph/OSGConverter.cpp b/src/uscxml/plugins/invoker/graphics/openscenegraph/OSGConverter.cpp index ea249e4..2fa2877 100644 --- a/src/uscxml/plugins/invoker/graphics/openscenegraph/OSGConverter.cpp +++ b/src/uscxml/plugins/invoker/graphics/openscenegraph/OSGConverter.cpp @@ -5,10 +5,12 @@ #include #include #include +#include #include #include #include #include +#include #include #include @@ -91,6 +93,20 @@ void OSGConverter::send(const SendRequest& req) { } } + boost::algorithm::replace_all(actualReq.params.find("dest")->second, "//", "/"); + boost::algorithm::replace_all(actualReq.params.find("dest")->second, "\\\\", "\\"); + + if (actualReq.params.find("autorotate") == actualReq.params.end()) { + if (actualReq.params.find("autorotateexpr") != actualReq.params.end()) { + if (_interpreter->getDataModel()) { + actualReq.params.insert(std::make_pair("autorotate", _interpreter->getDataModel().evalAsString(actualReq.params.find("autorotateexpr")->second))); + } else { + LOG(ERROR) << "SendRequests for osginvoker ncludes autorotateexpr but no datamodel is specified"; + return; + } + } + } + if (actualReq.params.find("format") == actualReq.params.end()) { // no explicit format if (actualReq.params.find("formatexpr") != actualReq.params.end() && _interpreter->getDataModel()) { @@ -111,12 +127,26 @@ void OSGConverter::send(const SendRequest& req) { } } - 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"); +// assert(osgDB::Registry::instance()->getReaderWriterForExtension("png")); +// osgDB::Registry::ReaderWriterList formatList = osgDB::Registry::instance()->getReaderWriterList(); +// for (int i = 0; i < formatList.size(); i++) { +// std::map funcDesc = formatList[i]->supportedProtocols(); +// std::map::iterator funcDescIter = funcDesc.begin(); +// while(funcDescIter != funcDesc.end()) { +// std::cout << funcDescIter->first << ": " << funcDescIter->second << std::endl; +// funcDescIter++; +// } +// } + + 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"); + EVAL_PARAM_EXPR(actualReq.params, "xexpr", "x"); + EVAL_PARAM_EXPR(actualReq.params, "yexpr", "y"); + EVAL_PARAM_EXPR(actualReq.params, "zexpr", "z"); // process(actualReq); _workQueue.push(actualReq); @@ -150,13 +180,13 @@ void OSGConverter::run(void* instance) { } void OSGConverter::process(const SendRequest& req) { - + // std::cout << req; - - int width = 640; + + int width = 640; int height = 480; - CAST_PARAM(req.params, width, "width", int); - CAST_PARAM(req.params, height, "height", int); + 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()); @@ -166,80 +196,131 @@ void OSGConverter::process(const SendRequest& req) { std::string dest = req.params.find("dest")->second; std::string format = req.params.find("format")->second; - osg::ref_ptr sceneGraph = setupGraph(source); + bool autoRotate = true; + if (req.params.find("autorotate") != req.params.end()) { + if (boost::iequals(req.params.find("autorotate")->second, "off") || + boost::iequals(req.params.find("autorotate")->second, "0") || + boost::iequals(req.params.find("autorotate")->second, "false")) { + autoRotate = false; + } + } + + osg::ref_ptr model = setupGraph(source, autoRotate); + if (model->asGroup()->getNumChildren() == 0) { + reportFailure(req); + return; + } + + osg::ref_ptr sceneGraph = new osg::Group(); + sceneGraph->addChild(model); osgDB::ReaderWriter::WriteResult result; if (osgDB::Registry::instance()->getReaderWriterForExtension(format) != NULL) { // write as another 3D file result = osgDB::Registry::instance()->writeNode(*sceneGraph, dest, osgDB::Registry::instance()->getOptions()); + if (result.success()) { + // we can know about success right here + reportSuccess(req); + return; + } } - if (result.error()) { - // make a screenshot - osgViewer::ScreenCaptureHandler::CaptureOperation* cOp = new NameRespectingWriteToFile(dest, - format, - osgViewer::ScreenCaptureHandler::WriteToFile::OVERWRITE - ); - - osgViewer::ScreenCaptureHandler* captureHandler = new osgViewer::ScreenCaptureHandler(cOp, -1); - - osgViewer::Viewer viewer; - viewer.setSceneData(sceneGraph); - viewer.setCameraManipulator(new osgGA::TrackballManipulator()); - viewer.addEventHandler(captureHandler); - captureHandler->startCapture(); - - osg::DisplaySettings* ds = osg::DisplaySettings::instance().get(); - osg::ref_ptr traits = new osg::GraphicsContext::Traits(ds); - traits->width = width; - traits->height = height; - traits->pbuffer = true; - osg::ref_ptr gc = osg::GraphicsContext::createGraphicsContext(traits.get()); - - if (!gc.valid()) { - LOG(ERROR) << "Cannot create GraphicsContext!"; - return; - } - - - GLenum pbuffer = gc->getTraits()->doubleBuffer ? GL_BACK : GL_FRONT; - - viewer.getCamera()->setGraphicsContext(gc.get()); - viewer.getCamera()->setViewport(new osg::Viewport(0,0,traits->width,traits->height)); - viewer.getCamera()->setDrawBuffer(pbuffer); - viewer.getCamera()->setReadBuffer(pbuffer); - - // set background color - viewer.getCamera()->setClearColor(osg::Vec4f(1.0f,1.0f,1.0f,1.0f)); - viewer.getCamera()->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - -// viewer.getCamera()->setViewMatrix(requestToCamPose(req)); - - viewer.home(); - ((osg::MatrixTransform*)sceneGraph.get())->setMatrix(requestToModelPose(req)); - - // perform one viewer iteration - viewer.realize(); - viewer.frame(); + /** + * If we failed to interpret the extension as another 3D file, try to make a screenshot. + */ + + ((osg::MatrixTransform*)model.get())->setMatrix(requestToModelPose(req)); + osg::BoundingSphere bs = model->getBound(); + +// osg::ref_ptr scale = new osg::MatrixTransform(); +// scale->setMatrix(osg::Matrix::scale(bs.radius() / 5, bs.radius() / 5, bs.radius() / 5)); +// scale->addChild(getOrigin()); +// sceneGraph->addChild(scale); + + osgViewer::ScreenCaptureHandler::CaptureOperation* cOp = new NameRespectingWriteToFile( + dest, + format, + osgViewer::ScreenCaptureHandler::WriteToFile::OVERWRITE, + req, this); + + osgViewer::ScreenCaptureHandler* captureHandler = new osgViewer::ScreenCaptureHandler(cOp, -1); + + osgViewer::Viewer viewer; + viewer.setSceneData(sceneGraph); + viewer.setCameraManipulator(new osgGA::TrackballManipulator()); + viewer.addEventHandler(captureHandler); + captureHandler->startCapture(); + + osg::DisplaySettings* ds = osg::DisplaySettings::instance().get(); + osg::ref_ptr traits = new osg::GraphicsContext::Traits(ds); + traits->width = width; + traits->height = height; + traits->pbuffer = true; + osg::ref_ptr gc = osg::GraphicsContext::createGraphicsContext(traits.get()); + + if (!gc.valid()) { + LOG(ERROR) << "Cannot create GraphicsContext!"; + return; } + + GLenum pbuffer = gc->getTraits()->doubleBuffer ? GL_BACK : GL_FRONT; + + viewer.getCamera()->setGraphicsContext(gc.get()); + viewer.getCamera()->setViewport(new osg::Viewport(0,0,traits->width,traits->height)); + viewer.getCamera()->setDrawBuffer(pbuffer); + viewer.getCamera()->setReadBuffer(pbuffer); + + // set background color + viewer.getCamera()->setClearColor(osg::Vec4f(1.0f,1.0f,1.0f,1.0f)); + viewer.getCamera()->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + viewer.getCameraManipulator()->setByMatrix(osg::Matrix::lookAt(osg::Vec3d(0,0,bs.radius() * -4), // eye + (osg::Vec3d)bs.center(), // center + osg::Vec3d(0,1,0))); // up + +// viewer.home(); + + // perform one viewer iteration + viewer.realize(); + viewer.frame(); +} + +void OSGConverter::reportSuccess(const SendRequest& req) { + Event event(req); + if (event.name.length() == 0) + event.name = "convert"; + event.name += ".success"; + returnEvent(event); } -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); - +void OSGConverter::reportFailure(const SendRequest& req) { + Event event(req); + if (event.name.length() == 0) + event.name = "convert"; + event.name += ".failure"; + returnEvent(event); +} + +osg::Matrix OSGConverter::requestToModelPose(const SendRequest& req) { + double pitch = 0; + double roll = 0; + double yaw = 0; + double zoom = 1; + double x = 0; + double y = 0; + double z = 0; + 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); + CAST_PARAM(req.params, x, "x", double); + CAST_PARAM(req.params, y, "y", double); + CAST_PARAM(req.params, z, "z", double); + + osg::Matrix m = osg::Matrix::scale(zoom, zoom, zoom) * eulerToMatrix(pitch, roll, yaw) * osg::Matrix::translate(-1 * x, -1 * y, -1 * z); #if 0 - dumpMatrix(m); + dumpMatrix(m); #endif - return m; + return m; } osg::Matrix OSGConverter::requestToCamPose(const SendRequest& req) { @@ -247,17 +328,18 @@ osg::Matrix OSGConverter::requestToCamPose(const SendRequest& req) { // 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::Matrix identity; + identity.makeIdentity(); + return identity; } -osg::ref_ptr OSGConverter::setupGraph(const std::string filename) { +osg::ref_ptr OSGConverter::setupGraph(const std::string filename, bool autoRotate) { /** * root (model pose) - * - modelCenter (center model) - * - model (actual model) + * - rotate (autoRotate to face largest side) + * - modelCenter (center model) + * - model (actual model) */ long now = tthread::chrono::system_clock::now(); @@ -275,7 +357,7 @@ osg::ref_ptr OSGConverter::setupGraph(const std::string filename) { } _models[filename] = std::make_pair(now, model); } - _models[filename].first = now; + _models[filename].first = now; #if 1 // remove old models from cache @@ -291,25 +373,66 @@ osg::ref_ptr OSGConverter::setupGraph(const std::string filename) { #endif } - - osg::ref_ptr root = new osg::MatrixTransform(); + osg::ref_ptr root = new osg::MatrixTransform(); + osg::ref_ptr rotate = new osg::MatrixTransform(); osg::ref_ptr model = _models[filename].second; // translation matrix to move model into center osg::ref_ptr modelCenter = new osg::MatrixTransform(); modelCenter->addChild(model); + rotate->addChild(modelCenter); // move bounding sphere center into origin osg::BoundingSphere bs = model->getBound(); modelCenter->setMatrix(osg::Matrix::translate(bs.center() *= -1)); - // add to model pose matrix - root->addChild(modelCenter); - + // get bounding box + osg::ComputeBoundsVisitor cbv; + osg::BoundingBox& bb(cbv.getBoundingBox()); + modelCenter->accept(cbv); + + if (autoRotate) { + double depth = bb.zMax() - bb.zMin(); + double width = bb.xMax() - bb.xMin(); + double height = bb.yMax() - bb.yMin(); + + double frontArea = width * height; + double sideArea = depth * height; + double topArea = depth * width; + + // rotate by multiples of 90deg to face largest area + if (frontArea < sideArea || frontArea < topArea) { + if (sideArea < topArea) { + // top needs to come to front -> rotate on x + rotate->setMatrix(osg::Matrix::rotate(M_PI_2, osg::Vec3f(1.0,0,0))); + } else { + // side needs to come to front + rotate->setMatrix(osg::Matrix::rotate(M_PI_2, osg::Vec3f(0,1.0,0))); + } + } + } + + // add rotation to root + root->addChild(rotate); return root; } +osg::ref_ptr OSGConverter::getOrigin() { + osg::Geode* geode = new osg::Geode(); +// osg::StateSet* stateset = new osg::StateSet(); +// stateset->setMode(GL_LIGHTING, osg::StateAttribute::ON); +// geode->setStateSet(stateset); + + geode->addDrawable(new osg::ShapeDrawable(new osg::Sphere(osg::Vec3(0.0f,0.0f,0.0f),1))); + geode->addDrawable(new osg::ShapeDrawable(new osg::Box(osg::Vec3(10.0f,0.0f,0.0f),0.5))); + geode->addDrawable(new osg::ShapeDrawable(new osg::Box(osg::Vec3(0.0f,10.0f,0.0f),2))); + geode->addDrawable(new osg::ShapeDrawable(new osg::Box(osg::Vec3(0.0f,0.0f,10.0f),4))); + // geode->addDrawable(new osg::ShapeDrawable(new osg::Cone(osg::Vec3(4.0f,0.0f,0.0f),radius,height),hints)); + + return geode; +} + osg::Matrix OSGConverter::eulerToMatrix(double pitch, double roll, double yaw) { // see http://www.flipcode.com/documents/matrfaq.html#Q36 osg::Matrix m; @@ -337,7 +460,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; } @@ -370,16 +493,21 @@ void OSGConverter::matrixToEuler(const osg::Matrix& m, double& pitch, double& ro } 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; - } + 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); + bool success = osgDB::writeImageFile(image, _filename); + if (success) { + _converter->reportSuccess(_req); + } else { + _converter->reportFailure(_req); + } } } \ No newline at end of file diff --git a/src/uscxml/plugins/invoker/graphics/openscenegraph/OSGConverter.h b/src/uscxml/plugins/invoker/graphics/openscenegraph/OSGConverter.h index 67b0da6..f493e73 100644 --- a/src/uscxml/plugins/invoker/graphics/openscenegraph/OSGConverter.h +++ b/src/uscxml/plugins/invoker/graphics/openscenegraph/OSGConverter.h @@ -31,10 +31,13 @@ public: virtual void cancel(const std::string sendId); virtual void invoke(const InvokeRequest& req); + void reportSuccess(const SendRequest& req); + void reportFailure(const SendRequest& req); + osg::Matrix requestToModelPose(const SendRequest& req); osg::Matrix requestToCamPose(const SendRequest& req); - static void dumpMatrix(const osg::Matrix& m); + 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); @@ -43,14 +46,20 @@ protected: public: NameRespectingWriteToFile(const std::string& filename, const std::string& extension, - SavePolicy savePolicy) : osgViewer::ScreenCaptureHandler::WriteToFile(filename, extension, savePolicy) { + SavePolicy savePolicy, + const SendRequest& req, + OSGConverter* converter) : osgViewer::ScreenCaptureHandler::WriteToFile(filename, extension, savePolicy), + _req(req), _converter(converter) { } virtual void operator()(const osg::Image& image, const unsigned int context_id); + SendRequest _req; + OSGConverter* _converter; }; uscxml::concurrency::BlockingQueue _workQueue; - osg::ref_ptr setupGraph(const std::string filename); + osg::ref_ptr setupGraph(const std::string filename, bool autoRotate = false); + osg::ref_ptr getOrigin(); std::map > > _models; tthread::recursive_mutex _cacheMutex; diff --git a/src/uscxml/plugins/invoker/heartbeat/HeartbeatInvoker.cpp b/src/uscxml/plugins/invoker/heartbeat/HeartbeatInvoker.cpp index 16947a7..6371a43 100644 --- a/src/uscxml/plugins/invoker/heartbeat/HeartbeatInvoker.cpp +++ b/src/uscxml/plugins/invoker/heartbeat/HeartbeatInvoker.cpp @@ -41,7 +41,7 @@ void HeartbeatInvoker::cancel(const std::string sendId) { } void HeartbeatInvoker::invoke(const InvokeRequest& req) { - _invokeId = req.invokeid; + _invokeId = req.invokeid; _event.invokeid = _invokeId; std::string intervalStr; double interval = 0; diff --git a/src/uscxml/server/HTTPServer.cpp b/src/uscxml/server/HTTPServer.cpp index a7957ea..ebc5b91 100644 --- a/src/uscxml/server/HTTPServer.cpp +++ b/src/uscxml/server/HTTPServer.cpp @@ -33,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; @@ -58,24 +58,25 @@ 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"; + + // 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["bmp"] = "image/bmp"; + 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(); @@ -89,9 +90,9 @@ HTTPServer* HTTPServer::getInstance(int port) { } std::string HTTPServer::mimeTypeForExtension(const std::string& ext) { - if (mimeTypes.find(ext) != mimeTypes.end()) - return mimeTypes[ext]; - return ""; + if (mimeTypes.find(ext) != mimeTypes.end()) + return mimeTypes[ext]; + return ""; } void HTTPServer::httpRecvReqCallback(struct evhttp_request *req, void *callbackData) { @@ -220,18 +221,23 @@ 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"; + } - 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()); headerIter++; } + if (reply.status >= 400) { + evhttp_send_error(reply.curlReq, reply.status, NULL); + return; + } + + struct evbuffer *evb = evbuffer_new(); + if (!boost::iequals(reply.type, "HEAD")) evbuffer_add(evb, reply.content.data(), reply.content.size()); diff --git a/src/uscxml/server/HTTPServer.h b/src/uscxml/server/HTTPServer.h index 9bab1a1..c573422 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 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; + static std::map mimeTypes; std::map _servlets; typedef std::map::iterator servlet_iter_t; diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index b6cbdc0..d7f30ed 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -33,6 +33,11 @@ target_link_libraries(test-eventdelay uscxml) add_test(test-eventdelay ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test-eventdelay) set_target_properties(test-eventdelay PROPERTIES FOLDER "Tests") +add_executable(test-dirmon src/test-dirmon.cpp) +target_link_libraries(test-dirmon uscxml) +add_test(test-dirmon ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test-dirmon) +set_target_properties(test-dirmon PROPERTIES FOLDER "Tests") + if (NOT WIN32) add_executable(test-arabica-events src/test-arabica-events.cpp) target_link_libraries(test-arabica-events uscxml) diff --git a/test/src/test-dirmon.cpp b/test/src/test-dirmon.cpp new file mode 100644 index 0000000..f3a83b3 --- /dev/null +++ b/test/src/test-dirmon.cpp @@ -0,0 +1,26 @@ +#include "uscxml/config.h" +#include "uscxml/Message.h" +#include "uscxml/concurrency/tinythread.h" +#include +#include +#include +#include "uscxml/plugins/invoker/filesystem/dirmon/DirMonInvoker.h" + +using namespace uscxml; +using namespace boost; + +class Watcher : public DirectoryWatchMonitor { + void handleChanges(DirectoryWatch::Action action, const std::string dir, const std::string file, struct stat fileStat) { + std::cout << "Monitor on " << dir << ": " << action << " for " << file << std::endl; + } +}; + +int main(int argc, char** argv) { + + Watcher watcher; + DirectoryWatch* dw = new DirectoryWatch("/Users/sradomski/Desktop/tmp", true); + dw->addMonitor(&watcher); + while(true) { + dw->updateEntries(); + } +} \ No newline at end of file -- cgit v0.12