diff options
Diffstat (limited to 'src/uscxml')
25 files changed, 944 insertions, 721 deletions
diff --git a/src/uscxml/Factory.cpp b/src/uscxml/Factory.cpp index c63fc0d..5b7426f 100644 --- a/src/uscxml/Factory.cpp +++ b/src/uscxml/Factory.cpp @@ -24,6 +24,8 @@ #include "uscxml/Interpreter.h" #include <glog/logging.h> +#include "uscxml/plugins/datamodel/null/NULLDataModel.h" + // see http://nadeausoftware.com/articles/2012/01/c_c_tip_how_use_compiler_predefined_macros_detect_operating_system #ifdef BUILD_AS_PLUGINS @@ -100,7 +102,6 @@ # include "uscxml/plugins/datamodel/prolog/swi/SWIDataModel.h" # endif -#include "uscxml/plugins/datamodel/null/NULLDataModel.h" #include "uscxml/plugins/datamodel/xpath/XPathDataModel.h" #include "uscxml/plugins/datamodel/promela/PromelaDataModel.h" @@ -142,8 +143,6 @@ std::string Factory::getDefaultPluginPath() { } void Factory::registerPlugins() { -#ifdef BUILD_AS_PLUGINS - // these are part of core { InterpreterHTTPServlet* ioProcessor = new InterpreterHTTPServlet(); registerIOProcessor(ioProcessor); @@ -152,6 +151,13 @@ void Factory::registerPlugins() { InterpreterWebSocketServlet* ioProcessor = new InterpreterWebSocketServlet(); registerIOProcessor(ioProcessor); } + { + NULLDataModel* dataModel = new NULLDataModel(); + registerDataModel(dataModel); + } + +#ifdef BUILD_AS_PLUGINS + // these are part of core if (_pluginPath.length() == 0) { // try to read USCXML_PLUGIN_PATH environment variable @@ -291,14 +297,12 @@ void Factory::registerPlugins() { } #endif -#if 1 #if (defined BUILD_DM_PROMELA) { PromelaDataModel* dataModel = new PromelaDataModel(); registerDataModel(dataModel); } #endif -#endif #ifdef BUILD_DM_XPATH { @@ -323,10 +327,6 @@ void Factory::registerPlugins() { #endif // these are always available - { - NULLDataModel* dataModel = new NULLDataModel(); - registerDataModel(dataModel); - } #if 1 { XHTMLInvoker* invoker = new XHTMLInvoker(); @@ -690,7 +690,7 @@ void EventHandlerImpl::returnEvent(Event& event) { void DataModelImpl::throwErrorExecution(const std::string& cause) { uscxml::Event exc; - exc.data.compound["exception"] = uscxml::Data(cause, uscxml::Data::VERBATIM); + exc.data.compound["cause"] = uscxml::Data(cause, uscxml::Data::VERBATIM); exc.name = "error.execution"; exc.eventType = uscxml::Event::PLATFORM; throw exc; @@ -698,7 +698,7 @@ void DataModelImpl::throwErrorExecution(const std::string& cause) { void DataModelImpl::throwErrorPlatform(const std::string& cause) { uscxml::Event exc; - exc.data.compound["exception"] = uscxml::Data(cause, uscxml::Data::VERBATIM); + exc.data.compound["cause"] = uscxml::Data(cause, uscxml::Data::VERBATIM); exc.name = "error.platform"; exc.eventType = uscxml::Event::PLATFORM; throw exc; diff --git a/src/uscxml/Interpreter.cpp b/src/uscxml/Interpreter.cpp index 3d3c181..f3d30de 100644 --- a/src/uscxml/Interpreter.cpp +++ b/src/uscxml/Interpreter.cpp @@ -50,6 +50,47 @@ #define VERBOSE 0 +/// valid interpreter state transitions +#define VALID_FROM_INSTANTIATED(newState) ( \ + newState == InterpreterState::USCXML_FAULTED || \ + newState == InterpreterState::USCXML_MICROSTEPPED || \ + newState == InterpreterState::USCXML_DESTROYED\ +) + +#define VALID_FROM_FAULTED(newState) ( \ + newState == InterpreterState::USCXML_DESTROYED\ +) + +#define VALID_FROM_INITIALIZED(newState) ( \ + newState == InterpreterState::USCXML_MICROSTEPPED || \ + newState == InterpreterState::USCXML_FINISHED \ +) + +#define VALID_FROM_MICROSTEPPED(newState) ( \ + newState == InterpreterState::USCXML_DESTROYED || \ + newState == InterpreterState::USCXML_MACROSTEPPED || \ + newState == InterpreterState::USCXML_MICROSTEPPED || \ + newState == InterpreterState::USCXML_FINISHED \ +) + +#define VALID_FROM_MACROSTEPPED(newState) ( \ + newState == InterpreterState::USCXML_DESTROYED || \ + newState == InterpreterState::USCXML_MICROSTEPPED || \ + newState == InterpreterState::USCXML_IDLE || \ + newState == InterpreterState::USCXML_FINISHED \ +) + +#define VALID_FROM_IDLE(newState) ( \ + newState == InterpreterState::USCXML_DESTROYED || \ + newState == InterpreterState::USCXML_MICROSTEPPED \ +) + +#define VALID_FROM_FINISHED(newState) ( \ + newState == InterpreterState::USCXML_DESTROYED || \ + newState == InterpreterState::USCXML_INSTANTIATED \ +) + + /// macro to catch exceptions in executeContent #define CATCH_AND_DISTRIBUTE(msg) \ catch (Event e) {\ @@ -169,7 +210,7 @@ InterpreterOptions InterpreterOptions::fromCmdLine(int argc, char** argv) { } switch(option) { - // cases without short option + // cases without short option case 0: { if (boost::equals(longOptions[optionInd].name, "disable-http")) { currOptions->withHTTP = false; @@ -269,7 +310,7 @@ void NameSpaceInfo::init(const std::map<std::string, std::string>& namespaceInfo nsIter++; } } - + std::map<std::string, boost::weak_ptr<InterpreterImpl> > Interpreter::_instances; tthread::recursive_mutex Interpreter::_instanceMutex; @@ -288,15 +329,16 @@ std::map<std::string, boost::weak_ptr<InterpreterImpl> > Interpreter::getInstanc InterpreterImpl::InterpreterImpl() { + _state.state = InterpreterState::USCXML_INSTANTIATED; + _state.thread = 0; _lastRunOnMainThread = 0; _thread = NULL; _sendQueue = NULL; _parentQueue = NULL; - _running = false; - _destroyed = false; - _done = true; + _topLevelFinalReached = false; _stable = false; _isInitialized = false; + _domIsSetup = false; _httpServlet = NULL; _factory = NULL; _sessionId = UUID::getUUID(); @@ -317,7 +359,7 @@ Interpreter Interpreter::fromDOM(const Arabica::DOM::Document<std::string>& dom, interpreterImpl->setNameSpaceInfo(nameSpaceInfo); interpreterImpl->_document = dom; - interpreterImpl->init(); +// interpreterImpl->init(); _instances[interpreterImpl->getSessionId()] = interpreterImpl; return interpreter; } @@ -389,20 +431,17 @@ Interpreter Interpreter::fromInputSource(Arabica::SAX::InputSource<std::string>& } boost::shared_ptr<INTERPRETER_IMPL> interpreterImpl = boost::shared_ptr<INTERPRETER_IMPL>(new INTERPRETER_IMPL); - Interpreter interpreter; + Interpreter interpreter(interpreterImpl); + _instances[interpreterImpl->getSessionId()] = interpreterImpl; + NameSpacingParser parser; if (parser.parse(source) && parser.getDocument() && parser.getDocument().hasChildNodes()) { interpreterImpl->setNameSpaceInfo(parser.nameSpace); interpreterImpl->_document = parser.getDocument(); -// interpreterImpl->init(); - interpreter = Interpreter(interpreterImpl); - _instances[interpreterImpl->getSessionId()] = interpreterImpl; } else { - if (parser.errorsReported()) { - LOG(ERROR) << parser.errors(); - } +// assert(parser.errorsReported()); + interpreterImpl->setInterpreterState(InterpreterState::USCXML_FAULTED, parser.errors()); } - // interpreter->init(); return interpreter; } @@ -459,20 +498,15 @@ void InterpreterImpl::copyTo(boost::shared_ptr<InterpreterImpl> other) { copyTo(other.get()); } - void InterpreterImpl::setName(const std::string& name) { - if (!_running) { - _name = name; - } else { - LOG(ERROR) << "Cannot change name of running interpreter"; - } + _name = name; } InterpreterImpl::~InterpreterImpl() { { // make sure we are done with setting up with early abort tthread::lock_guard<tthread::recursive_mutex> lock(_mutex); - _running = false; + stop(); // unset started bit } // std::cout << "stopped " << this << std::endl; // tthread::lock_guard<tthread::recursive_mutex> lock(_mutex); @@ -488,27 +522,53 @@ InterpreterImpl::~InterpreterImpl() { delete(_thread); } else { // this can happen with a shared_from_this at an interpretermonitor - _destroyed = true; + setInterpreterState(InterpreterState::USCXML_DESTROYED); } } + join(); if (_sendQueue) delete _sendQueue; } void InterpreterImpl::start() { - _done = false; + _state.thread |= InterpreterState::USCXML_THREAD_STARTED; _thread = new tthread::thread(InterpreterImpl::run, this); } void InterpreterImpl::stop() { - _running = false; + _state.thread &= ~InterpreterState::USCXML_THREAD_STARTED; } +void InterpreterImpl::join() { + stop(); + if (_thread != NULL) _thread->join(); +}; + +bool InterpreterImpl::isRunning() { + // return _running || !_topLevelFinalReached; + return _state.thread & InterpreterState::USCXML_THREAD_RUNNING; +} void InterpreterImpl::run(void* instance) { + InterpreterImpl* interpreter = ((InterpreterImpl*)instance); + interpreter->_state.thread |= InterpreterState::USCXML_THREAD_RUNNING; + try { - ((InterpreterImpl*)instance)->interpret(); + InterpreterState state; + while(interpreter->_state.thread & InterpreterState::USCXML_THREAD_STARTED) { + state = interpreter->step(-1); + + switch (state & InterpreterState::USCXML_INTERPRETER_MASK) { + case uscxml::InterpreterState::USCXML_FAULTED: + case uscxml::InterpreterState::USCXML_FINISHED: + case uscxml::InterpreterState::USCXML_DESTROYED: + // return as we finished + goto DONE_THREAD; + default: + break; + } + } } catch (Event e) { LOG(ERROR) << e; } catch(boost::bad_lexical_cast e) { @@ -516,12 +576,73 @@ void InterpreterImpl::run(void* instance) { } catch (...) { LOG(ERROR) << "InterpreterImpl::run catched unknown exception"; } - ((InterpreterImpl*)instance)->_done = true; - ((InterpreterImpl*)instance)->_running = false; +DONE_THREAD: + ((InterpreterImpl*)instance)->_state.thread &= ~InterpreterState::USCXML_THREAD_RUNNING; + ((InterpreterImpl*)instance)->_state.thread &= ~InterpreterState::USCXML_THREAD_STARTED; +} + +InterpreterState InterpreterImpl::getInterpreterState() { + return _state; +} + +void InterpreterImpl::setInterpreterState(InterpreterState::State newState) { + setInterpreterState(newState, Event()); } +void InterpreterImpl::setInterpreterState(InterpreterState::State newState, const std::string& error) { + Event e; + e.name = "error.platform"; + e.data.compound["cause"] = Data(error, Data::VERBATIM); + setInterpreterState(newState, e); +} + +void InterpreterImpl::setInterpreterState(InterpreterState::State newState, const Event& error) { + switch (_state) { + case InterpreterState::USCXML_INSTANTIATED: + if (VALID_FROM_INSTANTIATED(newState)) + break; + assert(false); + break; + case InterpreterState::USCXML_FAULTED: + if (VALID_FROM_FAULTED(newState)) + break; + assert(false); + break; + case InterpreterState::USCXML_MICROSTEPPED: + if (VALID_FROM_MICROSTEPPED(newState)) + break; + assert(false); + break; + case InterpreterState::USCXML_MACROSTEPPED: + if (VALID_FROM_MACROSTEPPED(newState)) + break; + assert(false); + break; + case InterpreterState::USCXML_IDLE: + if (VALID_FROM_IDLE(newState)) + break; + assert(false); + break; + case InterpreterState::USCXML_FINISHED: + if (VALID_FROM_FINISHED(newState)) + break; + assert(false); + break; + case InterpreterState::USCXML_DESTROYED: + assert(false); + break; + + default: + break; + } + + _state.state = newState; + _state.msg = error; +} + bool InterpreterImpl::runOnMainThread(int fps, bool blocking) { - if (_done) + tthread::lock_guard<tthread::recursive_mutex> lock(_mutex); + if (_state == InterpreterState::USCXML_FINISHED || _state == InterpreterState::USCXML_FAULTED || _state == InterpreterState::USCXML_DESTROYED) return false; if (fps > 0) { @@ -553,13 +674,31 @@ bool InterpreterImpl::runOnMainThread(int fps, bool blocking) { return (_thread != NULL); } -void InterpreterImpl::init() { - if (!_document) { - LOG(ERROR) << "Interpreter has no DOM at all!" << std::endl; - _done = true; +void InterpreterImpl::reset() { + tthread::lock_guard<tthread::recursive_mutex> lock(_mutex); + + _externalQueue.clear(); + _internalQueue.clear(); + _historyValue.clear(); + + _alreadyEntered = NodeSet<std::string>(); + _configuration = NodeSet<std::string>(); + _topLevelFinalReached = false; + _isInitialized = false; + + setInterpreterState(InterpreterState::USCXML_INSTANTIATED); +} + +void InterpreterImpl::setupAndNormalizeDOM() { + if (_domIsSetup) return; - } + if (!_document) { + Event error("error.platform"); + error.data.compound["cause"] = Data("Interpreter has no DOM", Data::VERBATIM); + throw error; + } + // find scxml element NodeList<std::string> scxmls; if (_nsInfo.nsURL.size() == 0) { @@ -567,54 +706,79 @@ void InterpreterImpl::init() { } else { scxmls = _document.getElementsByTagNameNS(_nsInfo.nsURL, "scxml"); } - - if (scxmls.getLength() > 0) { - _scxml = (Arabica::DOM::Element<std::string>)scxmls.item(0); - // setup xpath and check that it works - if (_nsInfo.getNSContext() != NULL) - _xpath.setNamespaceContext(*_nsInfo.getNSContext()); + if (scxmls.getLength() == 0) { + Event error("error.platform"); + error.data.compound["cause"] = Data("Cannot find SCXML element in DOM", Data::VERBATIM); + throw error; + } - if (_name.length() == 0) - _name = (HAS_ATTR(_scxml, "name") ? ATTR(_scxml, "name") : UUID::getUUID()); + _scxml = (Arabica::DOM::Element<std::string>)scxmls.item(0); - // normalize document - normalize(_scxml); + if (_nsInfo.getNSContext() != NULL) + _xpath.setNamespaceContext(*_nsInfo.getNSContext()); - // setup event queue for delayed send - _sendQueue = new DelayedEventQueue(); - _sendQueue->start(); - - // register for dom events to manage cached states - Arabica::DOM::Events::EventTarget<std::string> eventTarget(_scxml); - eventTarget.addEventListener("DOMNodeInserted", _domEventListener, true); - eventTarget.addEventListener("DOMNodeRemoved", _domEventListener, true); - eventTarget.addEventListener("DOMSubtreeModified", _domEventListener, true); + // normalize document + // TODO: Resolve XML includes + + // make sure every state has an id + Arabica::XPath::NodeSet<std::string> states; + states.push_back(_xpath.evaluate("//" + _nsInfo.xpathPrefix + "state", _scxml).asNodeSet()); + states.push_back(_xpath.evaluate("//" + _nsInfo.xpathPrefix + "final", _scxml).asNodeSet()); + states.push_back(_xpath.evaluate("//" + _nsInfo.xpathPrefix + "history", _scxml).asNodeSet()); + for (int i = 0; i < states.size(); i++) { + Arabica::DOM::Element<std::string> stateElem = Arabica::DOM::Element<std::string>(states[i]); + if (!stateElem.hasAttribute("id")) { + stateElem.setAttribute("id", UUID::getUUID()); + } + } + + // make sure every invoke has an idlocation or id + Arabica::XPath::NodeSet<std::string> invokes = _xpath.evaluate("//" + _nsInfo.xpathPrefix + "invoke", _scxml).asNodeSet(); + for (int i = 0; i < invokes.size(); i++) { + Arabica::DOM::Element<std::string> invokeElem = Arabica::DOM::Element<std::string>(invokes[i]); + if (!invokeElem.hasAttribute("id") && !invokeElem.hasAttribute("idlocation")) { + invokeElem.setAttribute("id", UUID::getUUID()); + } + } + + // add an id to the scxml element + if (!_scxml.hasAttribute("id")) { + _scxml.setAttribute("id", UUID::getUUID()); + } - if (_factory == NULL) - _factory = Factory::getInstance(); + // register for dom events to manage cached states + Arabica::DOM::Events::EventTarget<std::string> eventTarget(_scxml); + eventTarget.addEventListener("DOMNodeInserted", _domEventListener, true); + eventTarget.addEventListener("DOMNodeRemoved", _domEventListener, true); + eventTarget.addEventListener("DOMSubtreeModified", _domEventListener, true); - } else { - LOG(ERROR) << "Cannot find SCXML element" << std::endl; - _done = true; - return; - } +} - if (_sessionId.length() == 0) - _sessionId = UUID::getUUID(); +void InterpreterImpl::init() { + // make sure we have a factory if none was set before + if (_factory == NULL) + _factory = Factory::getInstance(); + // setup and normalize DOM + setupAndNormalizeDOM(); + // get our name or generate as UUID + if (_name.length() == 0) + _name = (HAS_ATTR(_scxml, "name") ? ATTR(_scxml, "name") : UUID::getUUID()); + + // setup event queue for delayed send + if (!_sendQueue) { + _sendQueue = new DelayedEventQueue(); + _sendQueue->start(); + } + + // start io processoes setupIOProcessors(); - std::string datamodelName; + // instantiate datamodel if (HAS_ATTR(_scxml, "datamodel")) { - datamodelName = ATTR(_scxml, "datamodel"); - } else if (HAS_ATTR(_scxml, "profile")) {// SCION SCXML uses profile to specify datamodel - datamodelName = ATTR(_scxml, "profile"); - } - - if(datamodelName.length() > 0) { - _dataModel = _factory->createDataModel(datamodelName, this); + _dataModel = _factory->createDataModel(ATTR(_scxml, "datamodel"), this); if (!_dataModel) { Event e; e.data.compound["cause"] = Data("Cannot instantiate datamodel", Data::VERBATIM); @@ -623,10 +787,10 @@ void InterpreterImpl::init() { } else { _dataModel = _factory->createDataModel("null", this); } - + _dataModel.assign("_x.args", _cmdLineOptions); - _running = true; +// _running = true; #if VERBOSE std::cout << "running " << this << std::endl; #endif @@ -706,35 +870,6 @@ void InterpreterImpl::initializeData(const Element<std::string>& data) { } } -void InterpreterImpl::normalize(Arabica::DOM::Element<std::string>& scxml) { - // TODO: Resolve XML includes - - // make sure every state has an id - Arabica::XPath::NodeSet<std::string> states; - states.push_back(_xpath.evaluate("//" + _nsInfo.xpathPrefix + "state", _scxml).asNodeSet()); - states.push_back(_xpath.evaluate("//" + _nsInfo.xpathPrefix + "final", _scxml).asNodeSet()); - states.push_back(_xpath.evaluate("//" + _nsInfo.xpathPrefix + "history", _scxml).asNodeSet()); - for (int i = 0; i < states.size(); i++) { - Arabica::DOM::Element<std::string> stateElem = Arabica::DOM::Element<std::string>(states[i]); - if (!stateElem.hasAttribute("id")) { - stateElem.setAttribute("id", UUID::getUUID()); - } - } - - // make sure every invoke has an idlocation or id - Arabica::XPath::NodeSet<std::string> invokes = _xpath.evaluate("//" + _nsInfo.xpathPrefix + "invoke", _scxml).asNodeSet(); - for (int i = 0; i < invokes.size(); i++) { - Arabica::DOM::Element<std::string> invokeElem = Arabica::DOM::Element<std::string>(invokes[i]); - if (!invokeElem.hasAttribute("id") && !invokeElem.hasAttribute("idlocation")) { - invokeElem.setAttribute("id", UUID::getUUID()); - } - } - - if (!scxml.hasAttribute("id")) { - scxml.setAttribute("id", UUID::getUUID()); - } -} - void InterpreterImpl::receiveInternal(const Event& event) { #if VERBOSE std::cout << _name << " receiveInternal: " << event.name << std::endl; @@ -1249,24 +1384,12 @@ void InterpreterImpl::invoke(const Arabica::DOM::Node<std::string>& element) { _invokers[invokeReq.invokeid] = invoker; try { - // --- MONITOR: beforeInvoking ------------------------------ - for(monIter_t monIter = _monitors.begin(); monIter != _monitors.end(); monIter++) { - try { - (*monIter)->beforeInvoking(shared_from_this(), Element<std::string>(element), invokeReq.invokeid); - } - USCXML_MONITOR_CATCH_BLOCK(beforeInvoking) - } + USCXML_MONITOR_CALLBACK3(beforeInvoking, Arabica::DOM::Element<std::string>(element), invokeReq.invokeid); invoker.invoke(invokeReq); LOG(INFO) << "Added invoker " << invokeReq.type << " at " << invokeReq.invokeid; - // --- MONITOR: afterInvoking ------------------------------ - for(monIter_t monIter = _monitors.begin(); monIter != _monitors.end(); monIter++) { - try { - (*monIter)->afterInvoking(shared_from_this(), Element<std::string>(element), invokeReq.invokeid); - } - USCXML_MONITOR_CATCH_BLOCK(afterInvoking) - } + USCXML_MONITOR_CALLBACK3(afterInvoking, Arabica::DOM::Element<std::string>(element), invokeReq.invokeid) // this is out of draft but so useful to know when an invoker started // Event invSuccess; @@ -1316,23 +1439,11 @@ void InterpreterImpl::cancelInvoke(const Arabica::DOM::Node<std::string>& elemen } } - // --- MONITOR: beforeUninvoking ------------------------------ - for(monIter_t monIter = _monitors.begin(); monIter != _monitors.end(); monIter++) { - try { - (*monIter)->beforeUninvoking(shared_from_this(), Element<std::string>(element), invokeId); - } - USCXML_MONITOR_CATCH_BLOCK(beforeUninvoking) - } + USCXML_MONITOR_CALLBACK3(beforeUninvoking, Element<std::string>(element), invokeId) _invokers.erase(invokeId); - // --- MONITOR: afterUninvoking ------------------------------ - for(monIter_t monIter = _monitors.begin(); monIter != _monitors.end(); monIter++) { - try { - (*monIter)->afterUninvoking(shared_from_this(), Element<std::string>(element), invokeId); - } - USCXML_MONITOR_CATCH_BLOCK(beforeUninvoking) - } + USCXML_MONITOR_CALLBACK3(beforeUninvoking, Element<std::string>(element), invokeId) } else { LOG(ERROR) << "Cannot cancel invoke for id " << invokeId << ": no such invokation"; @@ -1388,10 +1499,6 @@ bool InterpreterImpl::nameMatch(const std::string& transitionEvent, const std::s bool InterpreterImpl::hasConditionMatch(const Arabica::DOM::Node<std::string>& conditional) { if (HAS_ATTR(conditional, "cond") && ATTR(conditional, "cond").length() > 0) { - if (!_dataModel) { - LOG(ERROR) << "Cannot check a condition without a datamodel"; - return false; - } try { return _dataModel.evalAsBool(ATTR_NODE(conditional, "cond"), ATTR(conditional, "cond")); } catch (Event e) { @@ -1445,13 +1552,7 @@ void InterpreterImpl::executeContent(const Arabica::DOM::Node<std::string>& cont return; } - // --- MONITOR: beforeExecutingContent ------------------------------ - for(monIter_t monIter = _monitors.begin(); monIter != _monitors.end(); monIter++) { - try { - (*monIter)->beforeExecutingContent(shared_from_this(), Element<std::string>(content)); - } - USCXML_MONITOR_CATCH_BLOCK(beforeExecutingContent) - } + USCXML_MONITOR_CALLBACK2(beforeExecutingContent, Element<std::string>(content)) if (false) { } else if (iequals(TAGNAME(content), _nsInfo.xmlNSPrefix + "raise")) { @@ -1712,14 +1813,32 @@ void InterpreterImpl::executeContent(const Arabica::DOM::Node<std::string>& cont execContent.exitElement(content); } - // --- MONITOR: afterExecutingContent ------------------------------ - for(monIter_t monIter = _monitors.begin(); monIter != _monitors.end(); monIter++) { - try { - (*monIter)->afterExecutingContent(shared_from_this(), Element<std::string>(content)); + USCXML_MONITOR_CALLBACK2(afterExecutingContent, Element<std::string>(content)) + +} + +void InterpreterImpl::finalizeAndAutoForwardCurrentEvent() { + for (std::map<std::string, Invoker>::iterator invokeIter = _invokers.begin(); + invokeIter != _invokers.end(); + invokeIter++) { + if (iequals(invokeIter->first, _currEvent.invokeid)) { + Arabica::XPath::NodeSet<std::string> finalizes = filterChildElements(_nsInfo.xmlNSPrefix + "finalize", invokeIter->second.getElement()); + for (int k = 0; k < finalizes.size(); k++) { + Element<std::string> finalizeElem = Element<std::string>(finalizes[k]); + executeContent(finalizeElem); + } + } + if (HAS_ATTR(invokeIter->second.getElement(), "autoforward") && DOMUtils::attributeIsTrue(ATTR(invokeIter->second.getElement(), "autoforward"))) { + try { + // do not autoforward to invokers that send to #_parent from the SCXML IO Processor! + // Yes do so, see test229! + // if (!boost::equals(_currEvent.getOriginType(), "http://www.w3.org/TR/scxml/#SCXMLEventProcessor")) + invokeIter->second.send(_currEvent); + } catch(...) { + LOG(ERROR) << "Exception caught while sending event to invoker " << invokeIter->first; + } } - USCXML_MONITOR_CATCH_BLOCK(afterExecutingContent) } - } void InterpreterImpl::returnDoneEvent(const Arabica::DOM::Node<std::string>& state) { @@ -2497,10 +2616,38 @@ void InterpreterImpl::DOMEventListener::handleEvent(Arabica::DOM::Events::Event< } } -void InterpreterImpl::dump() { - if (!_document) - return; - std::cout << _document; +std::ostream& operator<< (std::ostream& os, const InterpreterState& interpreterState) { + os << "[" << InterpreterState::stateToString(interpreterState.state) << "]:" << std::endl; + os << interpreterState.msg; + return os; +} + +std::string InterpreterState::stateToString(int32_t state) { + std::stringstream ss; + + switch(state & USCXML_INTERPRETER_MASK) { + case USCXML_INSTANTIATED: ss << "INSTANTIATED"; break; + case USCXML_FAULTED: ss << "FAULTED"; break; + case USCXML_MICROSTEPPED: ss << "MICROSTEPPED"; break; + case USCXML_MACROSTEPPED: ss << "MACROSTEPPED"; break; + case USCXML_IDLE: ss << "IDLE"; break; + case USCXML_FINISHED: ss << "FINISHED"; break; + case USCXML_DESTROYED: ss << "DESTROYED"; break; + default: ss << "INVALID"; break; + } + + if (state & USCXML_THREAD_STARTED) { + ss << ", " << "STARTED"; + } else { + ss << ", " << "STOPPED"; + } + if (state & USCXML_THREAD_RUNNING) { + ss << ", " << "RUNNING"; + } else { + ss << ", " << "JOINED"; + } + + return ss.str(); } diff --git a/src/uscxml/Interpreter.h b/src/uscxml/Interpreter.h index 3a02cb7..81ccdb9 100644 --- a/src/uscxml/Interpreter.h +++ b/src/uscxml/Interpreter.h @@ -48,16 +48,41 @@ #include "uscxml/server/InterpreterServlet.h" -#define USCXML_MONITOR_CATCH_BLOCK(callback)\ -catch (Event e) {\ - LOG(ERROR) << "Syntax error when calling " #callback " on monitors: " << std::endl << e << std::endl;\ -} catch (boost::bad_weak_ptr e) {\ - LOG(ERROR) << "Unclean shutdown " << std::endl << std::endl;\ -} catch (...) {\ - LOG(ERROR) << "An exception occured when calling " #callback " on monitors";\ -}\ -if (_destroyed) {\ - throw boost::bad_weak_ptr();\ +#define USCXML_MONITOR_CATCH(callback) \ +catch (Event e) { \ + LOG(ERROR) << "Syntax error when calling " #callback " on monitors: " << std::endl << e << std::endl; \ +} catch (boost::bad_weak_ptr e) { \ + LOG(ERROR) << "Unclean shutdown " << std::endl << std::endl; \ +} catch (...) { \ + LOG(ERROR) << "An exception occured when calling " #callback " on monitors"; \ +} \ +if (_state == InterpreterState::USCXML_DESTROYED) { \ + throw boost::bad_weak_ptr(); \ +} \ + + +#define USCXML_MONITOR_CALLBACK(callback)\ +for(monIter_t monIter = _monitors.begin(); monIter != _monitors.end(); monIter++) { \ + try { \ + (*monIter)->callback(shared_from_this()); \ + } \ + USCXML_MONITOR_CATCH(callback) \ +} + +#define USCXML_MONITOR_CALLBACK2(callback, arg1)\ +for(monIter_t monIter = _monitors.begin(); monIter != _monitors.end(); monIter++) { \ + try { \ + (*monIter)->callback(shared_from_this(), arg1); \ + } \ + USCXML_MONITOR_CATCH(callback) \ +} + +#define USCXML_MONITOR_CALLBACK3(callback, arg1, arg2)\ +for(monIter_t monIter = _monitors.begin(); monIter != _monitors.end(); monIter++) { \ + try { \ + (*monIter)->callback(shared_from_this(), arg1, arg2); \ + } \ + USCXML_MONITOR_CATCH(callback) \ } namespace uscxml { @@ -172,6 +197,12 @@ public: attribute.setPrefix(nsToPrefix[nsURL]); } + std::string getXMLPrefixForNS(const std::string& ns) const { + if (nsToPrefix.find(ns) != nsToPrefix.end() && nsToPrefix.at(ns).size()) + return nsToPrefix.at(ns) + ":"; + return ""; + } + const Arabica::XPath::StandardNamespaceContext<std::string>* getNSContext() { return nsContext; } @@ -188,16 +219,56 @@ private: void init(const std::map<std::string, std::string>& nsInfo); }; -// values larger than 0 indicate that you ought to step again -enum InterpreterState { - FINISHED = 0, // machine reached a final configuration - INIT_FAILED = -1, // could not initialize interpreter - INTERRUPTED = -2, // machine was interrupted - NOTHING_TODO = 4, // when non-blocking returns nothing to do - PROCESSED = 8, // an event was processed - INITIALIZED = 16 // initial stable configuration was assumed -}; +struct USCXML_API InterpreterState { + // see: http://stackoverflow.com/questions/18591924/how-to-use-bitmask + enum State { + USCXML_DESTROYED = 0x0001, // + USCXML_INSTANTIATED = 0x0002, // nothing really, just instantiated + USCXML_MICROSTEPPED = 0x0004, // + USCXML_MACROSTEPPED = 0x0008, // + USCXML_IDLE = 0x0010, // + USCXML_FAULTED = 0x0020, // something went very wrong + USCXML_FINISHED = 0x0040, // machine reached a final configuration + }; + + enum ThreadState { + USCXML_THREAD_RUNNING = 0x0100, // + USCXML_THREAD_STARTED = 0x0200, // + }; + + enum BitMask { + USCXML_THREAD_MASK = 0xff00, + USCXML_INTERPRETER_MASK = 0x00ff, + }; + + bool operator==(const InterpreterState& other) const { + return state == other.state && msg == other.msg; + } + bool operator!=(const InterpreterState& other) const { + return !(*this == other); + } + + operator int() { + return (int)(state | thread); + } + + Event getMessage() { + return msg; + } + + static std::string stateToString(int32_t state); + + friend USCXML_API std::ostream& operator<< (std::ostream& os, const InterpreterState& interpreterState); + friend USCXML_API class InterpreterImpl; +protected: + int32_t thread; + State state; + Event msg; +}; + +USCXML_API std::ostream& operator<< (std::ostream& os, const InterpreterState& interpreterState); + class USCXML_API InterpreterImpl : public boost::enable_shared_from_this<InterpreterImpl> { public: @@ -213,20 +284,18 @@ public: void copyTo(InterpreterImpl* other); void copyTo(boost::shared_ptr<InterpreterImpl> other); - void start(); - void stop(); - static void run(void*); - void join() { - if (_thread != NULL) _thread->join(); - }; - bool isRunning() { - return _running || !_done; - } + // TODO: Look into that pure virtual issue and make these abstract! + virtual InterpreterState interpret() { return _state; } ///< Start interpreter blockingly + virtual InterpreterState step(int waitForMS = 0) { return _state; }; ///< Perform a single step - /// This one ought to be pure, but SWIG will generate gibberish if it is - virtual void interpret() = 0; - virtual InterpreterState step(bool blocking = false) = 0; + void start(); ///< Start interpretation in a thread + void stop(); ///< Stop interpreter thread + void reset(); ///< Reset state machine + void join(); + bool isRunning(); + InterpreterState getInterpreterState(); + void addMonitor(InterpreterMonitor* monitor) { _monitors.insert(monitor); } @@ -261,9 +330,11 @@ public: DataModel getDataModel() { return _dataModel; } + void setParentQueue(uscxml::concurrency::BlockingQueue<SendRequest>* parentQueue) { _parentQueue = parentQueue; } + void setFactory(Factory* factory) { _factory = factory; } @@ -275,12 +346,6 @@ public: return _xpath.evaluate(xpathExpr, _scxml).asNodeSet(); } - std::string getXMLPrefixForNS(const std::string& ns) const { - if (_nsInfo.nsToPrefix.find(ns) != _nsInfo.nsToPrefix.end() && _nsInfo.nsToPrefix.at(ns).size()) - return _nsInfo.nsToPrefix.at(ns) + ":"; - return ""; - } - void setNameSpaceInfo(const NameSpaceInfo& nsInfo) { _nsInfo = nsInfo; _xpath.setNamespaceContext(*_nsInfo.getNSContext()); @@ -314,17 +379,13 @@ public: return basicConfig; } - void setConfiguration(const std::vector<std::string>& states) { + void setInitalConfiguration(const std::vector<std::string>& states) { _userDefinedStartConfiguration = states; } void setInvokeRequest(const InvokeRequest& req) { _invokeReq = req; } - Arabica::DOM::Node<std::string> getState(const std::string& stateId); - Arabica::XPath::NodeSet<std::string> getStates(const std::list<std::string>& stateIds); - Arabica::XPath::NodeSet<std::string> getAllStates(); - virtual Arabica::DOM::Document<std::string> getDocument() const { return _document; } @@ -357,7 +418,6 @@ public: static bool isMember(const Arabica::DOM::Node<std::string>& node, const Arabica::XPath::NodeSet<std::string>& set); - void dump(); bool hasLegalConfiguration(); bool isLegalConfiguration(const Arabica::XPath::NodeSet<std::string>&); bool isLegalConfiguration(const std::vector<std::string>&); @@ -372,13 +432,13 @@ public: static bool isParallel(const Arabica::DOM::Node<std::string>& state); static bool isCompound(const Arabica::DOM::Node<std::string>& state); static bool isDescendant(const Arabica::DOM::Node<std::string>& s1, const Arabica::DOM::Node<std::string>& s2); - - static std::list<std::string> tokenizeIdRefs(const std::string& idRefs); - static std::string spaceNormalize(const std::string& text); - static bool nameMatch(const std::string& transitionEvent, const std::string& event); - bool isInEmbeddedDocument(const Arabica::DOM::Node<std::string>& node); bool isInitial(const Arabica::DOM::Node<std::string>& state); + + Arabica::DOM::Node<std::string> getState(const std::string& stateId); + Arabica::XPath::NodeSet<std::string> getStates(const std::list<std::string>& stateIds); + Arabica::XPath::NodeSet<std::string> getAllStates(); + Arabica::XPath::NodeSet<std::string> getInitialStates(Arabica::DOM::Node<std::string> state = Arabica::DOM::Node<std::string>()); static Arabica::XPath::NodeSet<std::string> getChildStates(const Arabica::DOM::Node<std::string>& state); static Arabica::XPath::NodeSet<std::string> getChildStates(const Arabica::XPath::NodeSet<std::string>& state); @@ -392,11 +452,17 @@ public: static Arabica::XPath::NodeSet<std::string> filterChildElements(const std::string& tagName, const Arabica::XPath::NodeSet<std::string>& nodeSet, bool recurse = false); static Arabica::XPath::NodeSet<std::string> filterChildType(const Arabica::DOM::Node_base::Type type, const Arabica::DOM::Node<std::string>& node, bool recurse = false); static Arabica::XPath::NodeSet<std::string> filterChildType(const Arabica::DOM::Node_base::Type type, const Arabica::XPath::NodeSet<std::string>& nodeSet, bool recurse = false); + + static std::list<std::string> tokenizeIdRefs(const std::string& idRefs); + static std::string spaceNormalize(const std::string& text); + static bool nameMatch(const std::string& transitionEvent, const std::string& event); Arabica::DOM::Node<std::string> findLCCA(const Arabica::XPath::NodeSet<std::string>& states); virtual Arabica::XPath::NodeSet<std::string> getProperAncestors(const Arabica::DOM::Node<std::string>& s1, const Arabica::DOM::Node<std::string>& s2); protected: + static void run(void*); // static method for thread to run + class DOMEventListener : public Arabica::DOM::Events::EventListener<std::string> { public: void handleEvent(Arabica::DOM::Events::Event<std::string>& event); @@ -405,17 +471,23 @@ protected: InterpreterImpl(); void init(); + void setupAndNormalizeDOM(); + virtual void setupIOProcessors(); - void normalize(Arabica::DOM::Element<std::string>& scxml); void initializeData(const Arabica::DOM::Element<std::string>& data); - virtual void setupIOProcessors(); + void finalizeAndAutoForwardCurrentEvent(); + void setInterpreterState(InterpreterState::State newState, const std::string& error); + void setInterpreterState(InterpreterState::State newState, const Event& error); + void setInterpreterState(InterpreterState::State newState); + bool _stable; tthread::thread* _thread; tthread::recursive_mutex _mutex; tthread::condition_variable _condVar; tthread::recursive_mutex _pluginMutex; + InterpreterState _state; URL _baseURI; URL _sourceURI; Arabica::DOM::Document<std::string> _document; @@ -423,10 +495,10 @@ protected: Arabica::XPath::XPath<std::string> _xpath; NameSpaceInfo _nsInfo; - bool _running; - bool _done; - bool _destroyed; // see comment in destructor + bool _topLevelFinalReached; bool _isInitialized; + bool _domIsSetup; + InterpreterImpl::Binding _binding; Arabica::XPath::NodeSet<std::string> _configuration; Arabica::XPath::NodeSet<std::string> _alreadyEntered; @@ -450,6 +522,13 @@ protected: InterpreterWebSocketServlet* _wsServlet; std::set<InterpreterMonitor*> _monitors; + long _lastRunOnMainThread; + std::string _name; + std::string _sessionId; + unsigned int _capabilities; + + Data _cmdLineOptions; + virtual void executeContent(const Arabica::DOM::Node<std::string>& content, bool rethrow = false); virtual void executeContent(const Arabica::DOM::NodeList<std::string>& content, bool rethrow = false); virtual void executeContent(const Arabica::XPath::NodeSet<std::string>& content, bool rethrow = false); @@ -475,13 +554,6 @@ protected: bool isInFinalState(const Arabica::DOM::Node<std::string>& state); bool parentIsScxmlState(const Arabica::DOM::Node<std::string>& state); - long _lastRunOnMainThread; - std::string _name; - std::string _sessionId; - unsigned int _capabilities; - - Data _cmdLineOptions; - IOProcessor getIOProcessor(const std::string& type); std::map<std::string, IOProcessor> _ioProcessors; @@ -489,7 +561,7 @@ protected: std::map<std::string, Invoker> _invokers; std::map<Arabica::DOM::Node<std::string>, ExecutableContent> _executableContent; - /// TODO: We need to remember to adapt them when the DOM is operated upon + /// TODO: We need to adapt them when the DOM is operated upon std::map<std::string, Arabica::DOM::Node<std::string> > _cachedStates; std::map<std::string, URL> _cachedURLs; @@ -504,7 +576,6 @@ public: const NameSpaceInfo& nameSpaceInfo); static Interpreter fromXML(const std::string& xml); static Interpreter fromURI(const std::string& uri); - static Interpreter fromInputSource(Arabica::SAX::InputSource<std::string>& source); static Interpreter fromClone(const Interpreter& other); Interpreter() : _impl() {} // the empty, invalid interpreter @@ -513,7 +584,9 @@ public: virtual ~Interpreter() {}; operator bool() const { - return _impl; + return (_impl && + _impl->_state != InterpreterState::USCXML_FAULTED && + _impl->_state != InterpreterState::USCXML_DESTROYED); } bool operator< (const Interpreter& other) const { return _impl < other._impl; @@ -529,15 +602,19 @@ public: return *this; } + void reset() { + return _impl->reset(); + } + void start() { return _impl->start(); } void stop() { return _impl->stop(); } - void join() { - return _impl->join(); - }; +// void join() { +// return _impl->join(); +// }; bool isRunning() { return _impl->isRunning(); } @@ -546,10 +623,20 @@ public: _impl->interpret(); }; - InterpreterState step(bool blocking = false) { - return _impl->step(blocking); + InterpreterState step(int waitForMS = 0) { + return _impl->step(waitForMS); + }; + + InterpreterState step(bool blocking) { + if (blocking) + return _impl->step(-1); + return _impl->step(0); }; + InterpreterState getState() { + return _impl->getInterpreterState(); + } + void addMonitor(InterpreterMonitor* monitor) { return _impl->addMonitor(monitor); } @@ -568,10 +655,6 @@ public: return _impl->getBaseURI(); } - std::string getXMLPrefixForNS(const std::string& ns) const { - return _impl->getXMLPrefixForNS(ns); - } - void setNameSpaceInfo(const NameSpaceInfo& nsInfo) { _impl->setNameSpaceInfo(nsInfo); } @@ -629,22 +712,19 @@ public: return _impl->getBasicConfiguration(); } - void setConfiguration(const std::vector<std::string>& states) { - return _impl->setConfiguration(states); - } - void setInvokeRequest(const InvokeRequest& req) { - return _impl->setInvokeRequest(req); + void setInitalConfiguration(const std::vector<std::string>& states) { + return _impl->setInitalConfiguration(states); } - Arabica::DOM::Node<std::string> getState(const std::string& stateId) { - return _impl->getState(stateId); - } - Arabica::XPath::NodeSet<std::string> getStates(const std::list<std::string>& stateIds) { - return _impl->getStates(stateIds); - } - Arabica::XPath::NodeSet<std::string> getAllStates() { - return _impl->getAllStates(); - } +// Arabica::DOM::Node<std::string> getState(const std::string& stateId) { +// return _impl->getState(stateId); +// } +// Arabica::XPath::NodeSet<std::string> getStates(const std::list<std::string>& stateIds) { +// return _impl->getStates(stateIds); +// } +// Arabica::XPath::NodeSet<std::string> getAllStates() { +// return _impl->getAllStates(); +// } Arabica::DOM::Document<std::string> getDocument() const { return _impl->getDocument(); @@ -680,13 +760,6 @@ public: return _impl->runOnMainThread(fps, blocking); } - static bool isMember(const Arabica::DOM::Node<std::string>& node, const Arabica::XPath::NodeSet<std::string>& set) { - return InterpreterImpl::isMember(node, set); - } - - void dump() { - return _impl->dump(); - } bool hasLegalConfiguration() { return _impl->hasLegalConfiguration(); } @@ -699,98 +772,6 @@ public: return _impl->isLegalConfiguration(config); } -#if 0 - static bool isState(const Arabica::DOM::Node<std::string>& state) { - return InterpreterImpl::isState(state); - } - static bool isPseudoState(const Arabica::DOM::Node<std::string>& state) { - return InterpreterImpl::isPseudoState(state); - } - static bool isTransitionTarget(const Arabica::DOM::Node<std::string>& elem) { - return InterpreterImpl::isTransitionTarget(elem); - } - static bool isTargetless(const Arabica::DOM::Node<std::string>& transition) { - return InterpreterImpl::isTargetless(transition); - } - static bool isAtomic(const Arabica::DOM::Node<std::string>& state) { - return InterpreterImpl::isAtomic(state); - } - static bool isFinal(const Arabica::DOM::Node<std::string>& state) { - return InterpreterImpl::isFinal(state); - } - static bool isHistory(const Arabica::DOM::Node<std::string>& state) { - return InterpreterImpl::isHistory(state); - } - static bool isParallel(const Arabica::DOM::Node<std::string>& state) { - return InterpreterImpl::isParallel(state); - } - static bool isCompound(const Arabica::DOM::Node<std::string>& state) { - return InterpreterImpl::isCompound(state); - } - static bool isDescendant(const Arabica::DOM::Node<std::string>& s1, const Arabica::DOM::Node<std::string>& s2) { - return InterpreterImpl::isDescendant(s1, s2); - } - - static std::list<std::string> tokenizeIdRefs(const std::string& idRefs) { - return InterpreterImpl::tokenizeIdRefs(idRefs); - } - static bool nameMatch(const std::string& transitionEvent, const std::string& event) { - return InterpreterImpl::nameMatch(transitionEvent, event); - } - - static std::string spaceNormalize(const std::string& text) { - return InterpreterImpl::spaceNormalize(text); - } - - bool isInitial(const Arabica::DOM::Node<std::string>& state) { - return _impl->isInitial(state); - } - Arabica::XPath::NodeSet<std::string> getInitialStates(Arabica::DOM::Node<std::string> state = Arabica::DOM::Node<std::string>()) { - return _impl->getInitialStates(state); - } - static Arabica::XPath::NodeSet<std::string> getChildStates(const Arabica::DOM::Node<std::string>& state) { - return InterpreterImpl::getChildStates(state); - } - static Arabica::XPath::NodeSet<std::string> getChildStates(const Arabica::XPath::NodeSet<std::string>& state) { - return InterpreterImpl::getChildStates(state); - } - static Arabica::DOM::Node<std::string> getParentState(const Arabica::DOM::Node<std::string>& element) { - return InterpreterImpl::getParentState(element); - } - static Arabica::DOM::Node<std::string> getAncestorElement(const Arabica::DOM::Node<std::string>& node, const std::string tagName) { - return InterpreterImpl::getAncestorElement(node, tagName); - } - Arabica::XPath::NodeSet<std::string> getTargetStates(const Arabica::DOM::Node<std::string>& transition) { - return _impl->getTargetStates(transition); - } - Arabica::XPath::NodeSet<std::string> getTargetStates(const Arabica::XPath::NodeSet<std::string>& transitions) { - return _impl->getTargetStates(transitions); - } - Arabica::DOM::Node<std::string> getSourceState(const Arabica::DOM::Node<std::string>& transition) { - return _impl->getSourceState(transition); - } - - static Arabica::XPath::NodeSet<std::string> filterChildElements(const std::string& tagname, const Arabica::DOM::Node<std::string>& node, bool recurse = false) { - return InterpreterImpl::filterChildElements(tagname, node, recurse); - } - static Arabica::XPath::NodeSet<std::string> filterChildElements(const std::string& tagName, const Arabica::XPath::NodeSet<std::string>& nodeSet, bool recurse = false) { - return InterpreterImpl::filterChildElements(tagName, nodeSet, recurse); - } - static Arabica::XPath::NodeSet<std::string> filterChildType(const Arabica::DOM::Node_base::Type type, const Arabica::DOM::Node<std::string>& node, bool recurse = false) { - return InterpreterImpl::filterChildType(type, node, recurse); - } - static Arabica::XPath::NodeSet<std::string> filterChildType(const Arabica::DOM::Node_base::Type type, const Arabica::XPath::NodeSet<std::string>& nodeSet, bool recurse = false) { - return InterpreterImpl::filterChildType(type, nodeSet, recurse); - } - - Arabica::DOM::Node<std::string> findLCCA(const Arabica::XPath::NodeSet<std::string>& states) { - return _impl->findLCCA(states); - } - Arabica::XPath::NodeSet<std::string> getProperAncestors(const Arabica::DOM::Node<std::string>& s1, const Arabica::DOM::Node<std::string>& s2) { - return _impl->getProperAncestors(s1, s2); - } -#endif - boost::shared_ptr<InterpreterImpl> getImpl() const { return _impl; } @@ -798,6 +779,13 @@ public: static std::map<std::string, boost::weak_ptr<InterpreterImpl> > getInstances(); protected: + + void setInvokeRequest(const InvokeRequest& req) { + return _impl->setInvokeRequest(req); + } + + static Interpreter fromInputSource(Arabica::SAX::InputSource<std::string>& source); + boost::shared_ptr<InterpreterImpl> _impl; static std::map<std::string, boost::weak_ptr<InterpreterImpl> > _instances; static tthread::recursive_mutex _instanceMutex; diff --git a/src/uscxml/Message.cpp b/src/uscxml/Message.cpp index 0a0eed0..1ac3573 100644 --- a/src/uscxml/Message.cpp +++ b/src/uscxml/Message.cpp @@ -673,6 +673,29 @@ std::ostream& operator<< (std::ostream& os, const Event& event) { os << indent << " origin: " << event.origin << std::endl; if (event.origintype.size() > 0) os << indent << " origintype: " << event.origintype << std::endl; + if (event.params.size() > 0) { + std::multimap<std::string, Data>::const_iterator paramIter = event.params.begin(); + os << indent << " params:" << std::endl; + _dataIndentation++; + while(paramIter != event.params.end()) { + os << indent << " " << paramIter->first << ": "; + os << indent << paramIter->second << std::endl; + paramIter++; + } + _dataIndentation--; + } + if (event.namelist.size() > 0) { + std::map<std::string, Data>::const_iterator namelistIter = event.namelist.begin(); + os << indent << " namelist:" << std::endl; + _dataIndentation++; + while(namelistIter != event.namelist.end()) { + os << indent << " " << namelistIter->first << ": "; + os << indent << namelistIter->second << std::endl; + namelistIter++; + } + _dataIndentation--; + + } _dataIndentation++; os << indent << " data: " << event.data << std::endl; _dataIndentation--; diff --git a/src/uscxml/Message.h b/src/uscxml/Message.h index 27b2ac5..44d9207 100644 --- a/src/uscxml/Message.h +++ b/src/uscxml/Message.h @@ -279,6 +279,16 @@ public: return this < &other; } + bool operator==(const Event& other) const { + return (this->name == other.name && + this->sendid == other.sendid && + this->invokeid == other.invokeid && + this->data == other.data); + } + bool operator!=(const Event& other) const { + return !(*this == other); + } + std::string getName() { return name; } diff --git a/src/uscxml/concurrency/tinythread.h b/src/uscxml/concurrency/tinythread.h index 0490e4d..a47b46a 100644 --- a/src/uscxml/concurrency/tinythread.h +++ b/src/uscxml/concurrency/tinythread.h @@ -428,6 +428,31 @@ public: #endif } + template <class _mutexT> + inline void wait_for(_mutexT &aMutex, unsigned int ms) { +#if defined(_TTHREAD_WIN32_) + // Increment number of waiters + EnterCriticalSection(&mWaitersCountLock); + ++ mWaitersCount; + LeaveCriticalSection(&mWaitersCountLock); + + // Release the mutex while waiting for the condition (will decrease + // the number of waiters when done)... + aMutex.unlock(); + _wait(ms); + aMutex.lock(); +#else + struct timeval tv; + gettimeofday(&tv, NULL); + + struct timespec ts; + ts.tv_sec = tv.tv_sec + (ms / 1000); + ts.tv_nsec = (tv.tv_usec * 1000); // convert tv microseconds to nanoseconds + ts.tv_nsec += (ms % 1000) * 1000000; // add millisecond part of wait time + pthread_cond_timedwait(&mHandle, &aMutex.mHandle, &ts); +#endif + } + /// Notify one thread that is waiting for the condition. /// If at least one thread is blocked waiting for this condition variable, /// one will be woken up. diff --git a/src/uscxml/debug/Breakpoint.cpp b/src/uscxml/debug/Breakpoint.cpp index 54f5d75..e66e0fb 100644 --- a/src/uscxml/debug/Breakpoint.cpp +++ b/src/uscxml/debug/Breakpoint.cpp @@ -238,7 +238,7 @@ bool Breakpoint::matches(Interpreter interpreter, const Breakpoint& other) const } catch (...) { return false; } - return Interpreter::isMember(other.element, nodes); + return InterpreterImpl::isMember(other.element, nodes); } if(transSourceId.length() > 0 && transSourceId != other.transSourceId) { diff --git a/src/uscxml/debug/SCXMLDotWriter.cpp b/src/uscxml/debug/SCXMLDotWriter.cpp index 93f09ab..2bccb14 100644 --- a/src/uscxml/debug/SCXMLDotWriter.cpp +++ b/src/uscxml/debug/SCXMLDotWriter.cpp @@ -130,13 +130,13 @@ void SCXMLDotWriter::writeStateElement(std::ostream& os, const Arabica::DOM::Ele os << "shape=doublecircle,"; // is the current state in the basic configuration? - if (_interpreter.isMember(elem, _interpreter.getBasicConfiguration())) + if (InterpreterImpl::isMember(elem, _interpreter.getBasicConfiguration())) os << "color=red, penwidth=3,"; // is the current state a target state? #if 0 for (int i = 0; i < _transitions.size(); i++) { - if (_interpreter.isMember(elem, _interpreter.getTargetStates(_transitions[i]))) { + if (InterpreterImpl::isMember(elem, _interpreter.getTargetStates(_transitions[i]))) { os << "color=red, penwidth=3,"; break; } diff --git a/src/uscxml/interpreter/InterpreterDraft6.cpp b/src/uscxml/interpreter/InterpreterDraft6.cpp index 12d6123..5e64e66 100644 --- a/src/uscxml/interpreter/InterpreterDraft6.cpp +++ b/src/uscxml/interpreter/InterpreterDraft6.cpp @@ -32,22 +32,18 @@ using namespace Arabica::DOM; // see: http://www.w3.org/TR/scxml/#AlgorithmforSCXMLInterpretation -void InterpreterDraft6::interpret() { +InterpreterState InterpreterDraft6::interpret() { InterpreterState state; while(true) { - state = step(true); + state = step(-1); - switch (state) { - case uscxml::INIT_FAILED: - case uscxml::FINISHED: - case uscxml::INTERRUPTED: + switch (state & InterpreterState::USCXML_INTERPRETER_MASK) { + case uscxml::InterpreterState::USCXML_FAULTED: + case uscxml::InterpreterState::USCXML_FINISHED: + case uscxml::InterpreterState::USCXML_DESTROYED: // return as we finished - return; - case uscxml::NOTHING_TODO: - // die as this can never happen with a blocking call - assert(false); - case uscxml::INITIALIZED: - case uscxml::PROCESSED: + return state; + default: // process invokers on main thread if(_thread == NULL) { @@ -58,6 +54,7 @@ void InterpreterDraft6::interpret() { break; } } + return state; } // setup / fetch the documents initial transitions @@ -117,74 +114,130 @@ NodeSet<std::string> InterpreterDraft6::getDocumentInitialTransitions() { } return initialTransitions; } - -// a macrostep -InterpreterState InterpreterDraft6::step(bool blocking) { + +InterpreterState InterpreterDraft6::step(int waitForMS = 0) { try { tthread::lock_guard<tthread::recursive_mutex> lock(_mutex); - monIter_t monIter; + if (_state & InterpreterState::USCXML_FINISHED || + _state & InterpreterState::USCXML_FAULTED || + _state & InterpreterState::USCXML_DESTROYED) { + return _state; + } + NodeSet<std::string> enabledTransitions; // setup document and interpreter - if (!_isInitialized) - init(); - - // if we failed return false - if (!_isInitialized) - return INIT_FAILED; - - // run initial transitions - if (!_stable) { - stabilize(); - // we might only need a single step - if (!_running) - goto EXIT_INTERPRETER; - return INITIALIZED; + if (!_isInitialized) { + init(); // will throw } - if (!_running) - return FINISHED; + if (_configuration.size() == 0) { + // goto initial configuration + NodeSet<std::string> initialTransitions = getDocumentInitialTransitions(); + assert(initialTransitions.size() > 0); + enterStates(initialTransitions); + } + + _stable = false; - // read an external event and react - if (blocking) { + // are there spontaneous transitions? + enabledTransitions = selectEventlessTransitions(); + if (!enabledTransitions.empty()) { + // test 403b + enabledTransitions.to_document_order(); + microstep(enabledTransitions); - // wait until an event becomes available - while(_externalQueue.isEmpty()) { - _condVar.wait(_mutex); - } - } else { - // return immediately if external queue is empty - if (_externalQueue.isEmpty()) - return NOTHING_TODO; + setInterpreterState(InterpreterState::USCXML_MICROSTEPPED); + return _state; } - _currEvent = _externalQueue.pop(); - #if VERBOSE - std::cout << "Received externalEvent event " << _currEvent.name << std::endl; - if (_running && _currEvent.name == "unblock.and.die") { - std::cout << "Still running " << this << std::endl; + // test415 + if (_topLevelFinalReached) + goto EXIT_INTERPRETER; + + // process internal event + if (!_internalQueue.empty()) { + _currEvent = _internalQueue.front(); + _internalQueue.pop_front(); + + USCXML_MONITOR_CALLBACK2(beforeProcessingEvent, _currEvent) + + if (_dataModel) + _dataModel.setEvent(_currEvent); + enabledTransitions = selectTransitions(_currEvent.name); + + if (!enabledTransitions.empty()) { + // test 403b + enabledTransitions.to_document_order(); + microstep(enabledTransitions); + } + + // test 319 - even if we do not enable transitions, consider it a microstep + setInterpreterState(InterpreterState::USCXML_MICROSTEPPED); + return _state; + } else { - std::cout << "Aborting " << this << std::endl; + _stable = true; } - #endif - _currEvent.eventType = Event::EXTERNAL; // make sure it is set to external + // even if we did nothing - count as microstep + setInterpreterState(InterpreterState::USCXML_MICROSTEPPED); - // when we were blocking on destructor invocation - if (!_running) { + if (_topLevelFinalReached) goto EXIT_INTERPRETER; - return INTERRUPTED; + + setInterpreterState(InterpreterState::USCXML_MACROSTEPPED); + USCXML_MONITOR_CALLBACK(onStableConfiguration) + + // when we reach a stable configuration, invoke + for (unsigned int i = 0; i < _statesToInvoke.size(); i++) { + NodeSet<std::string> invokes = filterChildElements(_nsInfo.xmlNSPrefix + "invoke", _statesToInvoke[i]); + for (unsigned int j = 0; j < invokes.size(); j++) { + if (!HAS_ATTR(invokes[j], "persist") || !DOMUtils::attributeIsTrue(ATTR(invokes[j], "persist"))) { + invoke(invokes[j]); + } + } } - // --- MONITOR: beforeProcessingEvent ------------------------------ - for(monIter_t monIter = _monitors.begin(); monIter != _monitors.end(); monIter++) { - try { - (*monIter)->beforeProcessingEvent(shared_from_this(), _currEvent); + _statesToInvoke = NodeSet<std::string>(); + + if (_externalQueue.isEmpty()) { + setInterpreterState(InterpreterState::USCXML_IDLE); + + if (waitForMS < 0) { + // wait blockingly for an event forever + while(_externalQueue.isEmpty()) { + _condVar.wait(_mutex); + } + } + + if (waitForMS > 0) { + // wait given number of milliseconds max + uint64_t now = tthread::chrono::system_clock::now(); + uint64_t then = now + waitForMS; + while(_externalQueue.isEmpty() && now < then) { + _condVar.wait_for(_mutex, then - now); + now = tthread::chrono::system_clock::now(); + } + } + + if (_externalQueue.isEmpty()) { + return _state; } - USCXML_MONITOR_CATCH_BLOCK(beforeProcessingEvent) + + setInterpreterState(InterpreterState::USCXML_MACROSTEPPED); } - if (iequals(_currEvent.name, "cancel.invoke." + _sessionId)) - return INTERRUPTED; + _currEvent = _externalQueue.pop(); + _currEvent.eventType = Event::EXTERNAL; // make sure it is set to external + + if (_topLevelFinalReached) + goto EXIT_INTERPRETER; + + USCXML_MONITOR_CALLBACK2(beforeProcessingEvent, _currEvent) + + if (iequals(_currEvent.name, "cancel.invoke." + _sessionId)) { + goto EXIT_INTERPRETER; + } try { _dataModel.setEvent(_currEvent); @@ -192,28 +245,7 @@ InterpreterState InterpreterDraft6::step(bool blocking) { LOG(ERROR) << "Syntax error while setting external event:" << std::endl << e << std::endl << _currEvent; } - for (std::map<std::string, Invoker>::iterator invokeIter = _invokers.begin(); - invokeIter != _invokers.end(); - invokeIter++) { - if (iequals(invokeIter->first, _currEvent.invokeid)) { - Arabica::XPath::NodeSet<std::string> finalizes = filterChildElements(_nsInfo.xmlNSPrefix + "finalize", invokeIter->second.getElement()); - for (int k = 0; k < finalizes.size(); k++) { - Element<std::string> finalizeElem = Element<std::string>(finalizes[k]); - executeContent(finalizeElem); - } - } - if (HAS_ATTR(invokeIter->second.getElement(), "autoforward") && DOMUtils::attributeIsTrue(ATTR(invokeIter->second.getElement(), "autoforward"))) { - try { - // do not autoforward to invokers that send to #_parent from the SCXML IO Processor! - // Yes do so, see test229! - // if (!boost::equals(_currEvent.getOriginType(), "http://www.w3.org/TR/scxml/#SCXMLEventProcessor")) - invokeIter->second.send(_currEvent); - } catch(...) { - LOG(ERROR) << "Exception caught while sending event to invoker " << invokeIter->first; - } - } - } - + finalizeAndAutoForwardCurrentEvent(); // run internal processing until we reach a stable configuration again enabledTransitions = selectTransitions(_currEvent.name); @@ -223,50 +255,42 @@ InterpreterState InterpreterDraft6::step(bool blocking) { microstep(enabledTransitions); } - stabilize(); - return PROCESSED; + if (_topLevelFinalReached) + goto EXIT_INTERPRETER; + return _state; + EXIT_INTERPRETER: - if (!_running) { - // --- MONITOR: beforeCompletion ------------------------------ - for(monIter_t monIter = _monitors.begin(); monIter != _monitors.end(); monIter++) { - try { - (*monIter)->beforeCompletion(shared_from_this()); - } - USCXML_MONITOR_CATCH_BLOCK(beforeCompletion) - } - - exitInterpreter(); - if (_sendQueue) { - std::map<std::string, std::pair<InterpreterImpl*, SendRequest> >::iterator sendIter = _sendIds.begin(); - while(sendIter != _sendIds.end()) { - _sendQueue->cancelEvent(sendIter->first); - sendIter++; - } - } - - // --- MONITOR: afterCompletion ------------------------------ - for(monIter_t monIter = _monitors.begin(); monIter != _monitors.end(); monIter++) { - try { - (*monIter)->afterCompletion(shared_from_this()); - } - USCXML_MONITOR_CATCH_BLOCK(afterCompletion) + USCXML_MONITOR_CALLBACK(beforeCompletion) + + exitInterpreter(); + if (_sendQueue) { + std::map<std::string, std::pair<InterpreterImpl*, SendRequest> >::iterator sendIter = _sendIds.begin(); + while(sendIter != _sendIds.end()) { + _sendQueue->cancelEvent(sendIter->first); + sendIter++; } - return FINISHED; } - assert(hasLegalConfiguration()); + USCXML_MONITOR_CALLBACK(afterCompletion) + +// assert(hasLegalConfiguration()); _mutex.unlock(); // remove datamodel if(_dataModel) _dataModel = DataModel(); - return PROCESSED; + setInterpreterState(InterpreterState::USCXML_FINISHED); + return _state; + } catch (Event e) { + setInterpreterState(InterpreterState::USCXML_FAULTED, e); + return _state; } catch (boost::bad_weak_ptr e) { LOG(ERROR) << "Unclean shutdown " << std::endl << std::endl; - return INTERRUPTED; + setInterpreterState(InterpreterState::USCXML_DESTROYED); + return _state; } // set datamodel to null from this thread @@ -278,7 +302,6 @@ InterpreterState InterpreterDraft6::step(bool blocking) { // process transitions until we are in a stable configuration again void InterpreterDraft6::stabilize() { - monIter_t monIter; NodeSet<std::string> enabledTransitions; _stable = false; @@ -303,13 +326,7 @@ void InterpreterDraft6::stabilize() { std::cout << "Received internal event " << _currEvent.name << std::endl; #endif - // --- MONITOR: beforeProcessingEvent ------------------------------ - for(monIter_t monIter = _monitors.begin(); monIter != _monitors.end(); monIter++) { - try { - (*monIter)->beforeProcessingEvent(shared_from_this(), _currEvent); - } - USCXML_MONITOR_CATCH_BLOCK(beforeProcessingEvent) - } + USCXML_MONITOR_CALLBACK2(beforeProcessingEvent, _currEvent) if (_dataModel) _dataModel.setEvent(_currEvent); @@ -323,16 +340,8 @@ void InterpreterDraft6::stabilize() { microstep(enabledTransitions); } } while(!_internalQueue.empty() || !_stable); - - monIter = _monitors.begin(); - - // --- MONITOR: onStableConfiguration ------------------------------ - for(monIter_t monIter = _monitors.begin(); monIter != _monitors.end(); monIter++) { - try { - (*monIter)->onStableConfiguration(shared_from_this()); - } - USCXML_MONITOR_CATCH_BLOCK(onStableConfiguration) - } + + USCXML_MONITOR_CALLBACK(onStableConfiguration) // when we reach a stable configuration, invoke for (unsigned int i = 0; i < _statesToInvoke.size(); i++) { @@ -349,7 +358,6 @@ void InterpreterDraft6::stabilize() { #if 0 void InterpreterDraft6::mainEventLoop() { - monIter_t monIter; while(_running) { NodeSet<std::string> enabledTransitions; @@ -377,13 +385,7 @@ void InterpreterDraft6::mainEventLoop() { std::cout << "Received internal event " << _currEvent.name << std::endl; #endif - // --- MONITOR: beforeProcessingEvent ------------------------------ - for(monIter_t monIter = _monitors.begin(); monIter != _monitors.end(); monIter++) { - try { - (*monIter)->beforeProcessingEvent(shared_from_this(), _currEvent); - } - USCXML_MONITOR_CATCH_BLOCK(beforeProcessingEvent) - } + USCXML_MONITOR_CALLBACK(beforeProcessingEvent) if (_dataModel) _dataModel.setEvent(_currEvent); @@ -413,15 +415,7 @@ void InterpreterDraft6::mainEventLoop() { // assume that we have a legal configuration as soon as the internal queue is empty assert(hasLegalConfiguration()); - monIter = _monitors.begin(); - - // --- MONITOR: onStableConfiguration ------------------------------ - for(monIter_t monIter = _monitors.begin(); monIter != _monitors.end(); monIter++) { - try { - (*monIter)->onStableConfiguration(shared_from_this()); - } - USCXML_MONITOR_CATCH_BLOCK(onStableConfiguration) - } + USCXML_MONITOR_CALLBACK(onStableConfiguration) _mutex.unlock(); @@ -447,13 +441,7 @@ void InterpreterDraft6::mainEventLoop() { if (!_running) goto EXIT_INTERPRETER; - // --- MONITOR: beforeProcessingEvent ------------------------------ - for(monIter_t monIter = _monitors.begin(); monIter != _monitors.end(); monIter++) { - try { - (*monIter)->beforeProcessingEvent(shared_from_this(), _currEvent); - } - USCXML_MONITOR_CATCH_BLOCK(beforeProcessingEvent) - } + USCXML_MONITOR_CALLBACK(beforeProcessingEvent) if (_dataModel && iequals(_currEvent.name, "cancel.invoke." + _sessionId)) break; @@ -495,13 +483,7 @@ void InterpreterDraft6::mainEventLoop() { } EXIT_INTERPRETER: - // --- MONITOR: beforeCompletion ------------------------------ - for(monIter_t monIter = _monitors.begin(); monIter != _monitors.end(); monIter++) { - try { - (*monIter)->beforeCompletion(shared_from_this()); - } - USCXML_MONITOR_CATCH_BLOCK(beforeCompletion) - } + USCXML_MONITOR_CALLBACK(beforeCompletion) exitInterpreter(); if (_sendQueue) { @@ -512,13 +494,7 @@ EXIT_INTERPRETER: } } - // --- MONITOR: afterCompletion ------------------------------ - for(monIter_t monIter = _monitors.begin(); monIter != _monitors.end(); monIter++) { - try { - (*monIter)->afterCompletion(shared_from_this()); - } - USCXML_MONITOR_CATCH_BLOCK(afterCompletion) - } + USCXML_MONITOR_CALLBACK(afterCompletion) } #endif @@ -760,48 +736,23 @@ void InterpreterDraft6::microstep(const Arabica::XPath::NodeSet<std::string>& en std::cout << std::endl; #endif - // --- MONITOR: beforeMicroStep ------------------------------ - for(monIter_t monIter = _monitors.begin(); monIter != _monitors.end(); monIter++) { - try { - (*monIter)->beforeMicroStep(shared_from_this()); - } - USCXML_MONITOR_CATCH_BLOCK(beforeMicroStep) - } + USCXML_MONITOR_CALLBACK(beforeMicroStep) exitStates(enabledTransitions); - monIter_t monIter; for (int i = 0; i < enabledTransitions.size(); i++) { Element<std::string> transition(enabledTransitions[i]); - // --- MONITOR: beforeTakingTransitions ------------------------------ - for(monIter_t monIter = _monitors.begin(); monIter != _monitors.end(); monIter++) { - try { - (*monIter)->beforeTakingTransition(shared_from_this(), transition, (i + 1 < enabledTransitions.size())); - } - USCXML_MONITOR_CATCH_BLOCK(beforeTakingTransitions) - } + USCXML_MONITOR_CALLBACK3(beforeTakingTransition, transition, (i + 1 < enabledTransitions.size())) executeContent(transition); - // --- MONITOR: afterTakingTransitions ------------------------------ - for(monIter_t monIter = _monitors.begin(); monIter != _monitors.end(); monIter++) { - try { - (*monIter)->afterTakingTransition(shared_from_this(), transition, (i + 1 < enabledTransitions.size())); - } - USCXML_MONITOR_CATCH_BLOCK(afterTakingTransitions) - } + USCXML_MONITOR_CALLBACK3(afterTakingTransition, transition, (i + 1 < enabledTransitions.size())) } enterStates(enabledTransitions); - // --- MONITOR: afterMicroStep ------------------------------ - for(monIter_t monIter = _monitors.begin(); monIter != _monitors.end(); monIter++) { - try { - (*monIter)->afterMicroStep(shared_from_this()); - } - USCXML_MONITOR_CATCH_BLOCK(afterMicroStep) - } + USCXML_MONITOR_CALLBACK(afterMicroStep) } @@ -833,7 +784,6 @@ void InterpreterDraft6::exitInterpreter() { void InterpreterDraft6::exitStates(const Arabica::XPath::NodeSet<std::string>& enabledTransitions) { NodeSet<std::string> statesToExit; - monIter_t monIter; #if VERBOSE std::cout << _name << ": Enabled exit transitions: " << std::endl; @@ -864,7 +814,7 @@ void InterpreterDraft6::exitStates(const Arabica::XPath::NodeSet<std::string>& e NodeSet<std::string> tmpStates; tmpStates.push_back(source); tmpStates.insert(tmpStates.end(), tStates.begin(), tStates.end()); -#if VERBOSE +#if 1 std::cout << _name << ": tmpStates: "; for (int i = 0; i < tmpStates.size(); i++) { std::cout << ATTR(tmpStates[i], "id") << ", "; @@ -873,7 +823,7 @@ void InterpreterDraft6::exitStates(const Arabica::XPath::NodeSet<std::string>& e #endif ancestor = findLCCA(tmpStates); } -#if VERBOSE +#if 1 std::cout << _name << ": Ancestor: " << ATTR(ancestor, "id") << std::endl;; #endif for (int j = 0; j < _configuration.size(); j++) { @@ -895,7 +845,7 @@ void InterpreterDraft6::exitStates(const Arabica::XPath::NodeSet<std::string>& e statesToExit.forward(false); statesToExit.sort(); -#if VERBOSE +#if 0 std::cout << _name << ": States to exit: "; for (int i = 0; i < statesToExit.size(); i++) { std::cout << LOCALNAME(statesToExit[i]) << ":" << ATTR(statesToExit[i], "id") << ", "; @@ -931,13 +881,7 @@ void InterpreterDraft6::exitStates(const Arabica::XPath::NodeSet<std::string>& e } for (int i = 0; i < statesToExit.size(); i++) { - // --- MONITOR: beforeExitingState ------------------------------ - for(monIter_t monIter = _monitors.begin(); monIter != _monitors.end(); monIter++) { - try { - (*monIter)->beforeExitingState(shared_from_this(), Element<std::string>(statesToExit[i]), (i + 1 < statesToExit.size())); - } - USCXML_MONITOR_CATCH_BLOCK(beforeExitingState) - } + USCXML_MONITOR_CALLBACK3(beforeExitingState, Element<std::string>(statesToExit[i]), (i + 1 < statesToExit.size())) NodeSet<std::string> onExits = filterChildElements(_nsInfo.xmlNSPrefix + "onExit", statesToExit[i]); for (int j = 0; j < onExits.size(); j++) { @@ -945,13 +889,7 @@ void InterpreterDraft6::exitStates(const Arabica::XPath::NodeSet<std::string>& e executeContent(onExitElem); } - // --- MONITOR: afterExitingState ------------------------------ - for(monIter_t monIter = _monitors.begin(); monIter != _monitors.end(); monIter++) { - try { - (*monIter)->afterExitingState(shared_from_this(), Element<std::string>(statesToExit[i]), (i + 1 < statesToExit.size())); - } - USCXML_MONITOR_CATCH_BLOCK(afterExitingState) - } + USCXML_MONITOR_CALLBACK3(afterExitingState, Element<std::string>(statesToExit[i]), (i + 1 < statesToExit.size())) NodeSet<std::string> invokes = filterChildElements(_nsInfo.xmlNSPrefix + "invoke", statesToExit[i]); for (int j = 0; j < invokes.size(); j++) { @@ -978,7 +916,8 @@ void InterpreterDraft6::exitStates(const Arabica::XPath::NodeSet<std::string>& e void InterpreterDraft6::enterStates(const Arabica::XPath::NodeSet<std::string>& enabledTransitions) { NodeSet<std::string> statesToEnter; NodeSet<std::string> statesForDefaultEntry; - monIter_t monIter; + // initialize the temporary table for default content in history states + NodeSet<std::string> defaultHistoryContent; #if VERBOSE std::cout << _name << ": Enabled enter transitions: " << std::endl; @@ -1033,7 +972,7 @@ void InterpreterDraft6::enterStates(const Arabica::XPath::NodeSet<std::string>& #endif for (int j = 0; j < tStates.size(); j++) { - addStatesToEnter(tStates[j], statesToEnter, statesForDefaultEntry); + addStatesToEnter(tStates[j], statesToEnter, statesForDefaultEntry, defaultHistoryContent); } #if VERBOSE @@ -1068,7 +1007,7 @@ void InterpreterDraft6::enterStates(const Arabica::XPath::NodeSet<std::string>& } } if (!someIsDescendant) { - addStatesToEnter(childs[l], statesToEnter, statesForDefaultEntry); + addStatesToEnter(childs[l], statesToEnter, statesForDefaultEntry, defaultHistoryContent); } } } @@ -1099,13 +1038,7 @@ void InterpreterDraft6::enterStates(const Arabica::XPath::NodeSet<std::string>& } } - // --- MONITOR: beforeEnteringState ------------------------------ - for(monIter_t monIter = _monitors.begin(); monIter != _monitors.end(); monIter++) { - try { - (*monIter)->beforeEnteringState(shared_from_this(), stateElem, (i + 1 < statesToEnter.size())); - } - USCXML_MONITOR_CATCH_BLOCK(beforeEnteringState) - } + USCXML_MONITOR_CALLBACK3(beforeEnteringState, stateElem, (i + 1 < statesToEnter.size())) // extension for flattened SCXML documents, we will need an explicit uninvoke element NodeSet<std::string> uninvokes = filterChildElements(_nsInfo.xmlNSPrefix + "uninvoke", statesToEnter[i]); @@ -1134,13 +1067,7 @@ void InterpreterDraft6::enterStates(const Arabica::XPath::NodeSet<std::string>& NodeSet<std::string> onEntryElems = filterChildElements(_nsInfo.xmlNSPrefix + "onEntry", stateElem); executeContent(onEntryElems, false); - // --- MONITOR: afterEnteringState ------------------------------ - for(monIter_t monIter = _monitors.begin(); monIter != _monitors.end(); monIter++) { - try { - (*monIter)->afterEnteringState(shared_from_this(), stateElem, (i + 1 < statesToEnter.size())); - } - USCXML_MONITOR_CATCH_BLOCK(afterEnteringState) - } + USCXML_MONITOR_CALLBACK3(afterEnteringState, stateElem, (i + 1 < statesToEnter.size())) if (isMember(stateElem, statesForDefaultEntry)) { // execute initial transition content for compound states @@ -1150,6 +1077,16 @@ void InterpreterDraft6::enterStates(const Arabica::XPath::NodeSet<std::string>& } } +#if 0 + // not working yet + if (isMember(stateElem, defaultHistoryContent)) { + // execute history transition + Arabica::XPath::NodeSet<std::string> transitions = _xpath.evaluate("" + _nsInfo.xpathPrefix + "history/" + _nsInfo.xpathPrefix + "transition", stateElem).asNodeSet(); + for (int j = 0; j < transitions.size(); j++) { + executeContent(transitions[j]); + } + } +#endif if (isFinal(stateElem)) { internalDoneSend(stateElem); Element<std::string> parent = (Element<std::string>)stateElem.getParentNode(); @@ -1174,15 +1111,15 @@ void InterpreterDraft6::enterStates(const Arabica::XPath::NodeSet<std::string>& for (int i = 0; i < _configuration.size(); i++) { Element<std::string> stateElem = (Element<std::string>)_configuration[i]; if (isFinal(stateElem) && parentIsScxmlState(stateElem)) { - _running = false; - _done = true; + _topLevelFinalReached = true; } } } void InterpreterDraft6::addStatesToEnter(const Node<std::string>& state, - Arabica::XPath::NodeSet<std::string>& statesToEnter, - Arabica::XPath::NodeSet<std::string>& statesForDefaultEntry) { + Arabica::XPath::NodeSet<std::string>& statesToEnter, + Arabica::XPath::NodeSet<std::string>& statesForDefaultEntry, + Arabica::XPath::NodeSet<std::string>& defaultHistoryContent) { std::string stateId = ((Element<std::string>)state).getAttribute("id"); #if VERBOSE @@ -1201,7 +1138,7 @@ void InterpreterDraft6::addStatesToEnter(const Node<std::string>& state, #endif for (int i = 0; i < historyValue.size(); i++) { - addStatesToEnter(historyValue[i], statesToEnter, statesForDefaultEntry); + addStatesToEnter(historyValue[i], statesToEnter, statesForDefaultEntry, defaultHistoryContent); NodeSet<std::string> ancestors = getProperAncestors(historyValue[i], state); #if VERBOSE @@ -1217,11 +1154,12 @@ void InterpreterDraft6::addStatesToEnter(const Node<std::string>& state, } } } else { + defaultHistoryContent.push_back(getParentState(state)); NodeSet<std::string> transitions = filterChildElements(_nsInfo.xmlNSPrefix + "transition", state); for (int i = 0; i < transitions.size(); i++) { NodeSet<std::string> targets = getTargetStates(transitions[i]); for (int j = 0; j < targets.size(); j++) { - addStatesToEnter(targets[j], statesToEnter, statesForDefaultEntry); + addStatesToEnter(targets[j], statesToEnter, statesForDefaultEntry, defaultHistoryContent); // Modifications from chris nuernberger NodeSet<std::string> ancestors = getProperAncestors(targets[j], state); @@ -1238,7 +1176,7 @@ void InterpreterDraft6::addStatesToEnter(const Node<std::string>& state, NodeSet<std::string> tStates = getInitialStates(state); for (int i = 0; i < tStates.size(); i++) { - addStatesToEnter(tStates[i], statesToEnter, statesForDefaultEntry); + addStatesToEnter(tStates[i], statesToEnter, statesForDefaultEntry, defaultHistoryContent); } // addStatesToEnter(getInitialState(state), statesToEnter, statesForDefaultEntry); @@ -1247,7 +1185,7 @@ void InterpreterDraft6::addStatesToEnter(const Node<std::string>& state, } else if(isParallel(state)) { NodeSet<std::string> childStates = getChildStates(state); for (int i = 0; i < childStates.size(); i++) { - addStatesToEnter(childStates[i], statesToEnter, statesForDefaultEntry); + addStatesToEnter(childStates[i], statesToEnter, statesForDefaultEntry, defaultHistoryContent); } } } diff --git a/src/uscxml/interpreter/InterpreterDraft6.h b/src/uscxml/interpreter/InterpreterDraft6.h index 2ab588e..25b096d 100644 --- a/src/uscxml/interpreter/InterpreterDraft6.h +++ b/src/uscxml/interpreter/InterpreterDraft6.h @@ -26,15 +26,16 @@ namespace uscxml { class USCXML_API InterpreterDraft6 : public InterpreterImpl { protected: - void interpret(); - InterpreterState step(bool blocking); + InterpreterState interpret(); + InterpreterState step(int blocking); void stabilize(); void microstep(const Arabica::XPath::NodeSet<std::string>& enabledTransitions); void enterStates(const Arabica::XPath::NodeSet<std::string>& enabledTransitions); void addStatesToEnter(const Arabica::DOM::Node<std::string>& state, Arabica::XPath::NodeSet<std::string>& statesToEnter, - Arabica::XPath::NodeSet<std::string>& statesForDefaultEntry); + Arabica::XPath::NodeSet<std::string>& statesForDefaultEntry, + Arabica::XPath::NodeSet<std::string>& defaultHistoryContent); void exitStates(const Arabica::XPath::NodeSet<std::string>& enabledTransitions); void exitInterpreter(); diff --git a/src/uscxml/interpreter/InterpreterRC.cpp b/src/uscxml/interpreter/InterpreterRC.cpp index 10c41d0..d651389 100644 --- a/src/uscxml/interpreter/InterpreterRC.cpp +++ b/src/uscxml/interpreter/InterpreterRC.cpp @@ -45,15 +45,12 @@ procedure interpret(doc): enterStates([doc.initial.transition]) mainEventLoop() */ -void InterpreterRC::interpret() { +InterpreterState InterpreterRC::interpret() { try { tthread::lock_guard<tthread::recursive_mutex> lock(_mutex); if (!_isInitialized) init(); - if (!_scxml) { - return; - } // dump(); // just make sure we have a session id @@ -84,7 +81,6 @@ void InterpreterRC::interpret() { _dataModel.assign("_x.args", _cmdLineOptions); } - _running = true; _binding = (HAS_ATTR(_scxml, "binding") && iequals(ATTR(_scxml, "binding"), "late") ? LATE : EARLY); // @TODO: Reread http://www.w3.org/TR/scxml/#DataBinding @@ -184,6 +180,7 @@ void InterpreterRC::interpret() { if(_dataModel) _dataModel = DataModel(); + return _state; } /** @@ -237,7 +234,6 @@ procedure mainEventLoop(): exitInterpreter() */ void InterpreterRC::mainEventLoop() { - monIter_t monIter; while(_running) { NodeSet<std::string> enabledTransitions; @@ -255,13 +251,7 @@ void InterpreterRC::mainEventLoop() { _currEvent = _internalQueue.front(); _internalQueue.pop_front(); - // --- MONITOR: beforeProcessingEvent ------------------------------ - for(monIter_t monIter = _monitors.begin(); monIter != _monitors.end(); monIter++) { - try { - (*monIter)->beforeProcessingEvent(shared_from_this(), _currEvent); - } - USCXML_MONITOR_CATCH_BLOCK(beforeProcessingEvent) - } + USCXML_MONITOR_CALLBACK2(beforeProcessingEvent, _currEvent) if (_dataModel) _dataModel.setEvent(_currEvent); @@ -299,16 +289,9 @@ void InterpreterRC::mainEventLoop() { } assert(hasLegalConfiguration()); - monIter = _monitors.begin(); // if (!_sendQueue || _sendQueue->isEmpty()) { - // --- MONITOR: onStableConfiguration ------------------------------ - for(monIter_t monIter = _monitors.begin(); monIter != _monitors.end(); monIter++) { - try { - (*monIter)->onStableConfiguration(shared_from_this()); - } - USCXML_MONITOR_CATCH_BLOCK(onStableConfiguration) - } + USCXML_MONITOR_CALLBACK(onStableConfiguration) // } @@ -334,13 +317,7 @@ void InterpreterRC::mainEventLoop() { if (!_running) goto EXIT_INTERPRETER; - // --- MONITOR: beforeProcessingEvent ------------------------------ - for(monIter_t monIter = _monitors.begin(); monIter != _monitors.end(); monIter++) { - try { - (*monIter)->beforeProcessingEvent(shared_from_this(), _currEvent); - } - USCXML_MONITOR_CATCH_BLOCK(beforeProcessingEvent) - } + USCXML_MONITOR_CALLBACK2(beforeProcessingEvent, _currEvent) if (_dataModel && iequals(_currEvent.name, "cancel.invoke." + _sessionId)) break; @@ -399,13 +376,7 @@ void InterpreterRC::mainEventLoop() { } EXIT_INTERPRETER: - // --- MONITOR: beforeCompletion ------------------------------ - for(monIter_t monIter = _monitors.begin(); monIter != _monitors.end(); monIter++) { - try { - (*monIter)->beforeCompletion(shared_from_this()); - } - USCXML_MONITOR_CATCH_BLOCK(beforeCompletion) - } + USCXML_MONITOR_CALLBACK(beforeCompletion) exitInterpreter(); if (_sendQueue) { @@ -416,13 +387,7 @@ EXIT_INTERPRETER: } } - // --- MONITOR: afterCompletion ------------------------------ - for(monIter_t monIter = _monitors.begin(); monIter != _monitors.end(); monIter++) { - try { - (*monIter)->afterCompletion(shared_from_this()); - } - USCXML_MONITOR_CATCH_BLOCK(afterCompletion) - } + USCXML_MONITOR_CALLBACK(afterCompletion) } @@ -670,48 +635,23 @@ procedure microstep(enabledTransitions): */ void InterpreterRC::microstep(const Arabica::XPath::NodeSet<std::string>& enabledTransitions) { - // --- MONITOR: beforeMicroStep ------------------------------ - for(monIter_t monIter = _monitors.begin(); monIter != _monitors.end(); monIter++) { - try { - (*monIter)->beforeMicroStep(shared_from_this()); - } - USCXML_MONITOR_CATCH_BLOCK(beforeMicroStep) - } + USCXML_MONITOR_CALLBACK(beforeMicroStep) exitStates(enabledTransitions); - monIter_t monIter; for (int i = 0; i < enabledTransitions.size(); i++) { Element<std::string> transition(enabledTransitions[i]); - // --- MONITOR: beforeTakingTransitions ------------------------------ - for(monIter_t monIter = _monitors.begin(); monIter != _monitors.end(); monIter++) { - try { - (*monIter)->beforeTakingTransition(shared_from_this(), transition, (i + 1 < enabledTransitions.size())); - } - USCXML_MONITOR_CATCH_BLOCK(beforeTakingTransitions) - } + USCXML_MONITOR_CALLBACK3(beforeTakingTransition, transition, (i + 1 < enabledTransitions.size())) executeContent(transition); - // --- MONITOR: afterTakingTransitions ------------------------------ - for(monIter_t monIter = _monitors.begin(); monIter != _monitors.end(); monIter++) { - try { - (*monIter)->afterTakingTransition(shared_from_this(), transition, (i + 1 < enabledTransitions.size())); - } - USCXML_MONITOR_CATCH_BLOCK(afterTakingTransitions) - } + USCXML_MONITOR_CALLBACK3(afterTakingTransition, transition, (i + 1 < enabledTransitions.size())) } enterStates(enabledTransitions); - // --- MONITOR: afterMicroStep ------------------------------ - for(monIter_t monIter = _monitors.begin(); monIter != _monitors.end(); monIter++) { - try { - (*monIter)->afterMicroStep(shared_from_this()); - } - USCXML_MONITOR_CATCH_BLOCK(afterMicroStep) - } + USCXML_MONITOR_CALLBACK(afterMicroStep) } @@ -736,7 +676,6 @@ procedure exitStates(enabledTransitions): configuration.delete(s) */ void InterpreterRC::exitStates(const Arabica::XPath::NodeSet<std::string>& enabledTransitions) { - monIter_t monIter; NodeSet<std::string> statesToExit = computeExitSet(enabledTransitions); // remove statesToExit from _statesToInvoke @@ -772,13 +711,7 @@ void InterpreterRC::exitStates(const Arabica::XPath::NodeSet<std::string>& enabl } for (int i = 0; i < statesToExit.size(); i++) { - // --- MONITOR: beforeExitingState ------------------------------ - for(monIter_t monIter = _monitors.begin(); monIter != _monitors.end(); monIter++) { - try { - (*monIter)->beforeExitingState(shared_from_this(), Element<std::string>(statesToExit[i]), (i + 1 < statesToExit.size())); - } - USCXML_MONITOR_CATCH_BLOCK(beforeExitingState) - } + USCXML_MONITOR_CALLBACK3(beforeExitingState, Element<std::string>(statesToExit[i]), (i + 1 < statesToExit.size())) NodeSet<std::string> onExits = filterChildElements(_nsInfo.xmlNSPrefix + "onExit", statesToExit[i]); for (int j = 0; j < onExits.size(); j++) { @@ -786,13 +719,7 @@ void InterpreterRC::exitStates(const Arabica::XPath::NodeSet<std::string>& enabl executeContent(onExitElem); } - // --- MONITOR: afterExitingState ------------------------------ - for(monIter_t monIter = _monitors.begin(); monIter != _monitors.end(); monIter++) { - try { - (*monIter)->afterExitingState(shared_from_this(), Element<std::string>(statesToExit[i]), (i + 1 < statesToExit.size())); - } - USCXML_MONITOR_CATCH_BLOCK(afterExitingState) - } + USCXML_MONITOR_CALLBACK3(afterExitingState, Element<std::string>(statesToExit[i]), (i + 1 < statesToExit.size())) NodeSet<std::string> invokes = filterChildElements(_nsInfo.xmlNSPrefix + "invoke", statesToExit[i]); for (int j = 0; j < invokes.size(); j++) { @@ -888,7 +815,6 @@ void InterpreterRC::enterStates(const Arabica::XPath::NodeSet<std::string>& enab NodeSet<std::string> statesForDefaultEntry; // initialize the temporary table for default content in history states std::map<std::string, Arabica::DOM::Node<std::string> > defaultHistoryContent; - monIter_t monIter; computeEntrySet(enabledTransitions, statesToEnter, statesForDefaultEntry, defaultHistoryContent); statesToEnter.to_document_order(); @@ -896,13 +822,7 @@ void InterpreterRC::enterStates(const Arabica::XPath::NodeSet<std::string>& enab for (int i = 0; i < statesToEnter.size(); i++) { Element<std::string> s = (Element<std::string>)statesToEnter[i]; - // --- MONITOR: beforeEnteringState ------------------------------ - for(monIter_t monIter = _monitors.begin(); monIter != _monitors.end(); monIter++) { - try { - (*monIter)->beforeEnteringState(shared_from_this(), s, (i + 1 < statesToEnter.size())); - } - USCXML_MONITOR_CATCH_BLOCK(beforeEnteringState) - } + USCXML_MONITOR_CALLBACK3(beforeEnteringState, s, i + 1 < statesToEnter.size()) _configuration.push_back(s); _statesToInvoke.push_back(s); @@ -924,13 +844,7 @@ void InterpreterRC::enterStates(const Arabica::XPath::NodeSet<std::string>& enab NodeSet<std::string> onEntryElems = filterChildElements(_nsInfo.xmlNSPrefix + "onEntry", s); executeContent(onEntryElems, false); - // --- MONITOR: afterEnteringState ------------------------------ - for(monIter_t monIter = _monitors.begin(); monIter != _monitors.end(); monIter++) { - try { - (*monIter)->afterEnteringState(shared_from_this(), s, (i + 1 < statesToEnter.size())); - } - USCXML_MONITOR_CATCH_BLOCK(afterEnteringState) - } + USCXML_MONITOR_CALLBACK3(afterEnteringState, s, i + 1 < statesToEnter.size()) if (isMember(s, statesForDefaultEntry)) { // execute initial transition content for compound states @@ -961,7 +875,7 @@ void InterpreterRC::enterStates(const Arabica::XPath::NodeSet<std::string>& enab internalDoneSend(s); if (parentIsScxmlState(s)) { _running = false; - _done = true; + _topLevelFinalReached = true; } else { Element<std::string> parent = (Element<std::string>)s.getParentNode(); Element<std::string> grandParent = (Element<std::string>)parent.getParentNode(); diff --git a/src/uscxml/interpreter/InterpreterRC.h b/src/uscxml/interpreter/InterpreterRC.h index 0a09c36..2cd2662 100644 --- a/src/uscxml/interpreter/InterpreterRC.h +++ b/src/uscxml/interpreter/InterpreterRC.h @@ -25,7 +25,7 @@ namespace uscxml { class InterpreterRC : public InterpreterImpl { - void interpret(); + InterpreterState interpret(); void mainEventLoop(); void exitInterpreter(); @@ -78,6 +78,8 @@ class InterpreterRC : public InterpreterImpl { Arabica::XPath::NodeSet<std::string> getChildStates(const Arabica::DOM::Node<std::string>& state); #endif + bool _running; + }; } diff --git a/src/uscxml/plugins/datamodel/CMakeLists.txt b/src/uscxml/plugins/datamodel/CMakeLists.txt index f04cb78..1b2a047 100644 --- a/src/uscxml/plugins/datamodel/CMakeLists.txt +++ b/src/uscxml/plugins/datamodel/CMakeLists.txt @@ -69,25 +69,14 @@ if (BUILD_DM_ECMA) endif() -# NULL datamodel +# NULL datamodel (not useful as plugin) set(USCXML_DATAMODELS "null ${USCXML_DATAMODELS}") file(GLOB NULL_DATAMODEL null/*.cpp null/*.h ) -if (BUILD_AS_PLUGINS) - source_group("" FILES ${NULL_DATAMODEL}) - add_library(datamodel_null SHARED ${NULL_DATAMODEL} "../Plugins.cpp") - target_link_libraries(datamodel_null uscxml) - set_target_properties(datamodel_null PROPERTIES FOLDER "Plugin DataModel") - set_target_properties(datamodel_null PROPERTIES COMPILE_FLAGS "-DPLUMA_EXPORTS") - set_target_properties(datamodel_null PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${OUTPUT_DIR}/lib") - - -else() - list (APPEND USCXML_FILES ${NULL_DATAMODEL}) -endif() +list (APPEND USCXML_FILES ${NULL_DATAMODEL}) # XPath datamodel diff --git a/src/uscxml/plugins/datamodel/ecmascript/JavaScriptCore/JSCDataModel.cpp b/src/uscxml/plugins/datamodel/ecmascript/JavaScriptCore/JSCDataModel.cpp index a6909b5..72252e4 100644 --- a/src/uscxml/plugins/datamodel/ecmascript/JavaScriptCore/JSCDataModel.cpp +++ b/src/uscxml/plugins/datamodel/ecmascript/JavaScriptCore/JSCDataModel.cpp @@ -212,6 +212,12 @@ void JSCDataModel::setEvent(const Event& event) { JSValueRef exception = NULL; + if (event.raw.size() == 0) { + std::stringstream ssRaw; + ssRaw << event; + privData->nativeObj->raw = ssRaw.str(); + } + if (event.dom) { JSStringRef propName = JSStringCreateWithUTF8CString("data"); JSObjectSetProperty(_ctx, eventObj, propName, getNodeAsValue(event.dom), 0, &exception); @@ -555,6 +561,8 @@ void JSCDataModel::assign(const Element<std::string>& assignElem, throw Event("error.execution", Event::PLATFORM); if (key.compare("_invokers") == 0) throw Event("error.execution", Event::PLATFORM); + if (key.compare("_event") == 0) + throw Event("error.execution", Event::PLATFORM); if (HAS_ATTR(assignElem, "expr")) { evalAsValue(key + " = " + ATTR(assignElem, "expr")); diff --git a/src/uscxml/plugins/datamodel/ecmascript/v8/V8DataModel.cpp b/src/uscxml/plugins/datamodel/ecmascript/v8/V8DataModel.cpp index 2bdd796..8b222f7 100644 --- a/src/uscxml/plugins/datamodel/ecmascript/v8/V8DataModel.cpp +++ b/src/uscxml/plugins/datamodel/ecmascript/v8/V8DataModel.cpp @@ -228,6 +228,12 @@ void V8DataModel::setEvent(const Event& event) { eventObj->SetInternalField(0, V8DOM::toExternal(privData)); eventObj.MakeWeak(0, V8SCXMLEvent::jsDestructor); + if (event.raw.size() == 0) { + std::stringstream ssRaw; + ssRaw << event; + privData->nativeObj->raw = ssRaw.str(); + } + if (event.dom) { eventObj->Set(v8::String::New("data"), getNodeAsValue(event.dom)); } else if (event.content.length() > 0) { @@ -595,6 +601,17 @@ void V8DataModel::assign(const Element<std::string>& assignElem, if (key.length() == 0) throw Event("error.execution", Event::PLATFORM); + if (key.compare("_sessionid") == 0) // test 322 + throw Event("error.execution", Event::PLATFORM); + if (key.compare("_name") == 0) + throw Event("error.execution", Event::PLATFORM); + if (key.compare("_ioprocessors") == 0) // test 326 + throw Event("error.execution", Event::PLATFORM); + if (key.compare("_invokers") == 0) + throw Event("error.execution", Event::PLATFORM); + if (key.compare("_event") == 0) + throw Event("error.execution", Event::PLATFORM); + if (HAS_ATTR(assignElem, "expr")) { evalAsValue(key + " = " + ATTR(assignElem, "expr")); } else if (node) { diff --git a/src/uscxml/plugins/element/respond/RespondElement.cpp b/src/uscxml/plugins/element/respond/RespondElement.cpp index 4fe0d2e..3eb55ed 100644 --- a/src/uscxml/plugins/element/respond/RespondElement.cpp +++ b/src/uscxml/plugins/element/respond/RespondElement.cpp @@ -78,7 +78,7 @@ void RespondElement::enterElement(const Arabica::DOM::Node<std::string>& node) { httpReply.status = strTo<int>(statusStr);; // extract the content - Arabica::XPath::NodeSet<std::string> contents = InterpreterImpl::filterChildElements(_interpreter->getXMLPrefixForNS(getNamespace()) + "content", node); + Arabica::XPath::NodeSet<std::string> contents = InterpreterImpl::filterChildElements(_interpreter->getNameSpaceInfo().getXMLPrefixForNS(getNamespace()) + "content", node); if (contents.size() > 0) { if (HAS_ATTR(contents[0], "expr")) { // -- content is evaluated string from datamodel ------ if (_interpreter->getDataModel()) { @@ -141,7 +141,7 @@ void RespondElement::enterElement(const Arabica::DOM::Node<std::string>& node) { } // process headers - Arabica::XPath::NodeSet<std::string> headers = InterpreterImpl::filterChildElements(_interpreter->getXMLPrefixForNS(getNamespace()) + "header", node); + Arabica::XPath::NodeSet<std::string> headers = InterpreterImpl::filterChildElements(_interpreter->getNameSpaceInfo().getXMLPrefixForNS(getNamespace()) + "header", node); for (int i = 0; i < headers.size(); i++) { std::string name; if (HAS_ATTR(headers[i], "name")) { diff --git a/src/uscxml/plugins/invoker/CMakeLists.txt b/src/uscxml/plugins/invoker/CMakeLists.txt index f12a7a6..3f63ea3 100644 --- a/src/uscxml/plugins/invoker/CMakeLists.txt +++ b/src/uscxml/plugins/invoker/CMakeLists.txt @@ -271,6 +271,30 @@ if (LIBICAL_FOUND) endif() +# webrtc invoker + +if (LIBJINGLE_FOUND) + set(USCXML_INVOKERS "webrtc ${USCXML_INVOKERS}") + file(GLOB_RECURSE WEBRTC_INVOKER + webrtc/*.cpp + webrtc/*.h + ) + if (BUILD_AS_PLUGINS) + source_group("" FILES ${WEBRTC_INVOKER}) + add_library( + invoker_webrtc SHARED + ${WEBRTC_INVOKER} + "../Plugins.cpp") + target_link_libraries(invoker_webrtc uscxml ${LIBJINGLE_LIBRARIES}) + set_target_properties(invoker_webrtc PROPERTIES FOLDER "Plugin Invoker") + set_target_properties(invoker_webrtc PROPERTIES COMPILE_FLAGS "-DPLUMA_EXPORTS") + set_target_properties(invoker_webrtc PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${OUTPUT_DIR}/lib") + else() + list (APPEND USCXML_FILES ${WEBRTC_INVOKER}) + endif() +endif() + + # location invoker if (CORELOCATION_LIBRARY AND OFF) diff --git a/src/uscxml/plugins/invoker/sample/SampleInvoker.cpp b/src/uscxml/plugins/invoker/sample/SampleInvoker.cpp index 0777c62..d91b14c 100644 --- a/src/uscxml/plugins/invoker/sample/SampleInvoker.cpp +++ b/src/uscxml/plugins/invoker/sample/SampleInvoker.cpp @@ -1,6 +1,6 @@ /** * @file - * @author 2012-2013 Stefan Radomski (stefan.radomski@cs.tu-darmstadt.de) + * @author 2012-2014 Stefan Radomski (stefan.radomski@cs.tu-darmstadt.de) * @copyright Simplified BSD * * @cond diff --git a/src/uscxml/plugins/invoker/sample/SampleInvoker.h b/src/uscxml/plugins/invoker/sample/SampleInvoker.h index 562ebd6..f7bcb24 100644 --- a/src/uscxml/plugins/invoker/sample/SampleInvoker.h +++ b/src/uscxml/plugins/invoker/sample/SampleInvoker.h @@ -1,6 +1,6 @@ /** * @file - * @author 2012-2013 Stefan Radomski (stefan.radomski@cs.tu-darmstadt.de) + * @author 2012-2014 Stefan Radomski (stefan.radomski@cs.tu-darmstadt.de) * @copyright Simplified BSD * * @cond diff --git a/src/uscxml/plugins/invoker/webrtc/WebRTCInvoker.cpp b/src/uscxml/plugins/invoker/webrtc/WebRTCInvoker.cpp new file mode 100644 index 0000000..2d871c8 --- /dev/null +++ b/src/uscxml/plugins/invoker/webrtc/WebRTCInvoker.cpp @@ -0,0 +1,64 @@ +/** + * @file + * @author 2014 Stefan Radomski (stefan.radomski@cs.tu-darmstadt.de) + * @copyright Simplified BSD + * + * @cond + * This program is free software: you can redistribute it and/or modify + * it under the terms of the FreeBSD license as published by the FreeBSD + * project. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the FreeBSD license along with this + * program. If not, see <http://www.opensource.org/licenses/bsd-license>. + * @endcond + */ + +#include "WebRTCInvoker.h" +#include <glog/logging.h> + +#ifdef BUILD_AS_PLUGINS +#include <Pluma/Connector.hpp> +#endif + +//#include "talk/app/webrtc/peerconnection.h" + +namespace uscxml { + +#ifdef BUILD_AS_PLUGINS +PLUMA_CONNECTOR +bool pluginConnect(pluma::Host& host) { + host.add( new WebRTCInvokerProvider() ); + return true; +} +#endif + +WebRTCInvoker::WebRTCInvoker() { +} + +WebRTCInvoker::~WebRTCInvoker() { +}; + +boost::shared_ptr<InvokerImpl> WebRTCInvoker::create(InterpreterImpl* interpreter) { + boost::shared_ptr<WebRTCInvoker> invoker = boost::shared_ptr<WebRTCInvoker>(new WebRTCInvoker()); + return invoker; +} + +Data WebRTCInvoker::getDataModelVariables() { + Data data; + return data; +} + +void WebRTCInvoker::send(const SendRequest& req) { +} + +void WebRTCInvoker::cancel(const std::string sendId) { +} + +void WebRTCInvoker::invoke(const InvokeRequest& req) { +} + +}
\ No newline at end of file diff --git a/src/uscxml/plugins/invoker/webrtc/WebRTCInvoker.h b/src/uscxml/plugins/invoker/webrtc/WebRTCInvoker.h new file mode 100644 index 0000000..e4d7775 --- /dev/null +++ b/src/uscxml/plugins/invoker/webrtc/WebRTCInvoker.h @@ -0,0 +1,59 @@ +/** + * @file + * @author 2014 Stefan Radomski (stefan.radomski@cs.tu-darmstadt.de) + * @copyright Simplified BSD + * + * @cond + * This program is free software: you can redistribute it and/or modify + * it under the terms of the FreeBSD license as published by the FreeBSD + * project. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the FreeBSD license along with this + * program. If not, see <http://www.opensource.org/licenses/bsd-license>. + * @endcond + */ + +#ifndef WEBRTCINVOKER_H_1E704623 +#define WEBRTCINVOKER_H_1E704623 + +#include <uscxml/Interpreter.h> + +#ifdef BUILD_AS_PLUGINS +#include "uscxml/plugins/Plugins.h" +#endif + +namespace uscxml { + +class WebRTCInvoker : public InvokerImpl { +public: + WebRTCInvoker(); + virtual ~WebRTCInvoker(); + virtual boost::shared_ptr<InvokerImpl> create(InterpreterImpl* interpreter); + + virtual std::list<std::string> getNames() { + std::list<std::string> names; + names.push_back("webrtc"); + names.push_back("http://uscxml.tk.informatik.tu-darmstadt.de/#webrtc"); + return names; + } + + virtual Data getDataModelVariables(); + virtual void send(const SendRequest& req); + virtual void cancel(const std::string sendId); + virtual void invoke(const InvokeRequest& req); + +protected: +}; + +#ifdef BUILD_AS_PLUGINS +PLUMA_INHERIT_PROVIDER(WebRTCInvoker, InvokerImpl); +#endif + +} + + +#endif /* end of include guard: WEBRTCINVOKER_H_1E704623 */ diff --git a/src/uscxml/plugins/ioprocessor/basichttp/BasicHTTPIOProcessor.cpp b/src/uscxml/plugins/ioprocessor/basichttp/BasicHTTPIOProcessor.cpp index 7d9fcb8..b6dadc9 100644 --- a/src/uscxml/plugins/ioprocessor/basichttp/BasicHTTPIOProcessor.cpp +++ b/src/uscxml/plugins/ioprocessor/basichttp/BasicHTTPIOProcessor.cpp @@ -124,13 +124,27 @@ bool BasicHTTPIOProcessor::httpRecvRequest(const HTTPServer::Request& req) { */ // this will call the const subscript operator - if (req.data.at("content").hasKey("_scxmleventname")) { - reqEvent.name = req.data.at("content").at("_scxmleventname").atom; - } - if (req.data.at("content").hasKey("content")) { - reqEvent.content = req.data.at("content").at("content").atom; +// if (req.data.at("content").hasKey("_scxmleventname")) { +// reqEvent.name = req.data.at("content").at("_scxmleventname").atom; +// } +// if (req.data.at("content").hasKey("content")) { +// reqEvent.content = req.data.at("content").at("content").atom; +// } + + if (req.data.hasKey("content")) { + const Data& data = req.data["content"]; + for(std::map<std::string, Data>::const_iterator compIter = data.compound.begin(); + compIter!= data.compound.end(); compIter++) { + if (compIter->first == "_scxmleventname") { + reqEvent.name = compIter->second.atom; + } else if (compIter->first == "content") { + reqEvent.content = compIter->second.atom; + } else { + reqEvent.data[compIter->first] = compIter->second; + } + } } - + // check whether we can parse it as XML if (reqEvent.content.length() > 0) { NameSpacingParser parser = NameSpacingParser::fromXML(reqEvent.content); diff --git a/src/uscxml/server/HTTPServer.cpp b/src/uscxml/server/HTTPServer.cpp index 3e7920c..e5c83d7 100644 --- a/src/uscxml/server/HTTPServer.cpp +++ b/src/uscxml/server/HTTPServer.cpp @@ -179,7 +179,6 @@ tthread::recursive_mutex HTTPServer::_instanceMutex; HTTPServer* HTTPServer::getInstance(unsigned short port, unsigned short wsPort, SSLConfig* sslConf) { // tthread::lock_guard<tthread::recursive_mutex> lock(_instanceMutex); if (_instance == NULL) { - std::cout << "Instantiating new HTTPServer" << std::endl; #ifdef _WIN32 WSADATA wsaData; WSAStartup(MAKEWORD(2, 2), &wsaData); diff --git a/src/uscxml/transform/ChartToFSM.cpp b/src/uscxml/transform/ChartToFSM.cpp index 49519dc..581ba85 100644 --- a/src/uscxml/transform/ChartToFSM.cpp +++ b/src/uscxml/transform/ChartToFSM.cpp @@ -26,7 +26,7 @@ #include <math.h> #include <string.h> #include <algorithm> -#undef max
+#undef max #include <limits> namespace uscxml { @@ -101,7 +101,7 @@ Document<std::string> FlatteningInterpreter::getDocument() const { return _flatDoc; } -void FlatteningInterpreter::interpret() { +InterpreterState FlatteningInterpreter::interpret() { init(); setupIOProcessors(); @@ -177,6 +177,7 @@ void FlatteningInterpreter::interpret() { #endif createDocument(); + return _state; } void FlatteningInterpreter::executeContent(const Arabica::DOM::Node<std::string>& content, bool rethrow) { diff --git a/src/uscxml/transform/ChartToFSM.h b/src/uscxml/transform/ChartToFSM.h index ef80a6a..07531e2 100644 --- a/src/uscxml/transform/ChartToFSM.h +++ b/src/uscxml/transform/ChartToFSM.h @@ -105,7 +105,7 @@ public: virtual ~FlatteningInterpreter(); Arabica::DOM::Document<std::string> getDocument() const; // overwrite to return flat FSM - void interpret(); + InterpreterState interpret(); protected: // gather executable content per microstep |