diff options
author | Stefan Radomski <github@mintwerk.de> | 2017-01-13 16:47:44 (GMT) |
---|---|---|
committer | Stefan Radomski <github@mintwerk.de> | 2017-01-13 16:47:44 (GMT) |
commit | 4f6cbe9e7aec2b4a6c8f286f9097abfb011a6235 (patch) | |
tree | 8c023473bb342780ddf51a893d18369f1319bb5c /src/uscxml/interpreter | |
parent | 0aa0fe08dc308c94379c47d0bf9745e341cb4c81 (diff) | |
download | uscxml-4f6cbe9e7aec2b4a6c8f286f9097abfb011a6235.zip uscxml-4f6cbe9e7aec2b4a6c8f286f9097abfb011a6235.tar.gz uscxml-4f6cbe9e7aec2b4a6c8f286f9097abfb011a6235.tar.bz2 |
First support for serialization and some bug fixes for DOM per data.src
Diffstat (limited to 'src/uscxml/interpreter')
20 files changed, 445 insertions, 84 deletions
diff --git a/src/uscxml/interpreter/BasicContentExecutor.cpp b/src/uscxml/interpreter/BasicContentExecutor.cpp index 60a45e5..bbd6bca 100644 --- a/src/uscxml/interpreter/BasicContentExecutor.cpp +++ b/src/uscxml/interpreter/BasicContentExecutor.cpp @@ -438,9 +438,10 @@ void BasicContentExecutor::invoke(XERCESC_NS::DOMElement* element) { // content std::list<DOMElement*> contents = DOMUtils::filterChildElements(XML_PREFIX(element).str() + "content", element); if (contents.size() > 0) { -#if 1 +#if 0 invokeEvent.data.node = contents.front(); #else + // test530 Data d = elementAsData(contents.front()); if (d.type == Data::INTERPRETED && d.atom.size() > 0) { // immediately evaluate! @@ -558,7 +559,7 @@ void BasicContentExecutor::processParams(std::multimap<std::string, Data>& param } } -Data BasicContentExecutor::elementAsData(XERCESC_NS::DOMElement* element) { +Data BasicContentExecutor::elementAsData(XERCESC_NS::DOMElement* element, bool asExpression) { if (HAS_ATTR(element, "expr")) { // return _callbacks->evalAsData(ATTR(element, "expr")); #if 0 @@ -570,6 +571,8 @@ Data BasicContentExecutor::elementAsData(XERCESC_NS::DOMElement* element) { return Data(ATTR(element, "expr"), Data::INTERPRETED); } #endif + if (asExpression) // test 453 + return Data(ATTR(element, "expr"), Data::INTERPRETED); return _callbacks->evalAsData(ATTR(element, "expr")); } @@ -587,6 +590,7 @@ Data BasicContentExecutor::elementAsData(XERCESC_NS::DOMElement* element) { // make an attempt to parse as XML try { +#if 0 XERCESC_NS::XercesDOMParser parser; parser.setValidationScheme(XERCESC_NS::XercesDOMParser::Val_Never); parser.setDoNamespaces(true); @@ -604,8 +608,46 @@ Data BasicContentExecutor::elementAsData(XERCESC_NS::DOMElement* element) { XERCESC_NS::DOMDocument* doc = parser.adoptDocument(); d.adoptedDoc = std::shared_ptr<XERCESC_NS::DOMDocument>(doc); d.node = doc->getDocumentElement(); - return d; +#else + std::unique_ptr<XERCESC_NS::XercesDOMParser> parser(new XERCESC_NS::XercesDOMParser()); + parser->setValidationScheme(XERCESC_NS::XercesDOMParser::Val_Always); + parser->setDoNamespaces(true); + parser->useScanner(XERCESC_NS::XMLUni::fgWFXMLScanner); + + std::unique_ptr<XERCESC_NS::ErrorHandler> errHandler(new XERCESC_NS::HandlerBase()); + parser->setErrorHandler(errHandler.get()); + + try { + std::string tmp = url; + parser->parse(tmp.c_str()); + + XERCESC_NS::DOMNode* newNode = element->getOwnerDocument()->importNode(parser->getDocument()->getDocumentElement(), true); + + // remove any old child elements + while(element->getFirstElementChild() != NULL) { + element->removeChild(element->getFirstElementChild()); + } + // we need to save the DOM somewhere .. Data::adoptedDoc was not good enough + element->appendChild(newNode); + + Data d; +// d.adoptedDoc = std::shared_ptr<XERCESC_NS::DOMDocument>(parser->adoptDocument()); + d.node = newNode; + return d; + } + + catch (const XERCESC_NS::SAXParseException& toCatch) { + ERROR_PLATFORM_THROW(X(toCatch.getMessage()).str()); + } catch (const XERCESC_NS::RuntimeException& toCatch) { + ERROR_PLATFORM_THROW(X(toCatch.getMessage()).str()); + } catch (const XERCESC_NS::XMLException& toCatch) { + ERROR_PLATFORM_THROW(X(toCatch.getMessage()).str()); + } catch (const XERCESC_NS::DOMException& toCatch) { + ERROR_PLATFORM_THROW(X(toCatch.getMessage()).str()); + } + +#endif } catch (...) { // just ignore and return as an interpreted string below @@ -622,10 +664,9 @@ Data BasicContentExecutor::elementAsData(XERCESC_NS::DOMElement* element) { // local content in document std::list<DOMNode*> elementChildren = DOMUtils::filterChildType(DOMNode::ELEMENT_NODE, element); - if (elementChildren.size() == 1) { - return Data(elementChildren.front()); - } else if (elementChildren.size() > 1) { - return Data(element); + if (elementChildren.size() > 0) { + // always return parent element, even with a single child node + return Data(static_cast<DOMNode*>(element)); } std::list<DOMNode*> textChildren = DOMUtils::filterChildType(DOMNode::TEXT_NODE, element); @@ -634,14 +675,23 @@ Data BasicContentExecutor::elementAsData(XERCESC_NS::DOMElement* element) { for (auto textIter = textChildren.begin(); textIter != textChildren.end(); textIter++) { contentSS << X((*textIter)->getNodeValue()); } -#if 0 + + // test294, test562 + if (LOCALNAME(element) == "content") { + return Data(spaceNormalize(contentSS.str()), Data::VERBATIM); + } + + if (asExpression) // not actually used, but likely expected + return Data(contentSS.str(), Data::INTERPRETED); + + // test153 try { Data d = _callbacks->getAsData(contentSS.str()); if (!d.empty()) return d; } catch(...) {} -#endif - // test294, test562 + + // never actually occurs with the w3c tests return Data(spaceNormalize(contentSS.str()), Data::VERBATIM); } } diff --git a/src/uscxml/interpreter/BasicContentExecutor.h b/src/uscxml/interpreter/BasicContentExecutor.h index db53eb2..032da6f 100644 --- a/src/uscxml/interpreter/BasicContentExecutor.h +++ b/src/uscxml/interpreter/BasicContentExecutor.h @@ -52,7 +52,7 @@ public: virtual void uninvoke(XERCESC_NS::DOMElement* invoke); virtual void raiseDoneEvent(XERCESC_NS::DOMElement* state, XERCESC_NS::DOMElement* doneData); - virtual Data elementAsData(XERCESC_NS::DOMElement* element); + virtual Data elementAsData(XERCESC_NS::DOMElement* element, bool asExpression = false); protected: void processNameLists(std::map<std::string, Data>& nameMap, XERCESC_NS::DOMElement* element); diff --git a/src/uscxml/interpreter/BasicDelayedEventQueue.cpp b/src/uscxml/interpreter/BasicDelayedEventQueue.cpp index 49ed2f9..3c87ac0 100644 --- a/src/uscxml/interpreter/BasicDelayedEventQueue.cpp +++ b/src/uscxml/interpreter/BasicDelayedEventQueue.cpp @@ -170,4 +170,53 @@ void BasicDelayedEventQueue::reset() { _queue.clear(); } +Data BasicDelayedEventQueue::serialize() { + std::lock_guard<std::recursive_mutex> lock(_mutex); + + if (_isStarted) { + _isStarted = false; + event_base_loopbreak(_eventLoop); + } + if (_thread) { + _thread->join(); + delete _thread; + _thread = NULL; + } + + Data serialized; + + for (auto event : _queue) { + struct callbackData cb = _callbackData[event.uuid]; + + struct timeval delay, now; + uint64_t delayMs = 0; + gettimeofday(&now, NULL); + + evutil_timersub(&delay, &cb.due, &now); + if (delay.tv_sec > 0 || (delay.tv_sec == 0 && delay.tv_usec > 0)) { + delayMs = delay.tv_sec * 1000 + delay.tv_usec / (double)1000; + } + + Data delayedEvent; + delayedEvent["event"] = event; + delayedEvent["delay"] = Data(delayMs, Data::INTERPRETED); + + serialized["BasicDelayedEventQueue"].array.push_back(event); + } + + start(); + return serialized; +} + +void BasicDelayedEventQueue::deserialize(const Data& data) { + if (data.hasKey("BasicDelayedEventQueue")) { + std::lock_guard<std::recursive_mutex> lock(_mutex); + for (auto event : data["BasicDelayedEventQueue"].array) { + Event e = Event::fromData(event["event"]); + enqueueDelayed(e, strTo<size_t>(event["delay"]), e.uuid); + } + } + +} + } diff --git a/src/uscxml/interpreter/BasicDelayedEventQueue.h b/src/uscxml/interpreter/BasicDelayedEventQueue.h index df5b13f..dfcd002 100644 --- a/src/uscxml/interpreter/BasicDelayedEventQueue.h +++ b/src/uscxml/interpreter/BasicDelayedEventQueue.h @@ -52,6 +52,9 @@ public: } virtual void reset(); + virtual Data serialize(); + virtual void deserialize(const Data& data); + protected: virtual std::shared_ptr<EventQueueImpl> create() { ErrorEvent e("Cannot create a DelayedEventQueue without callbacks"); diff --git a/src/uscxml/interpreter/BasicEventQueue.cpp b/src/uscxml/interpreter/BasicEventQueue.cpp index 519754e..bb7c78b 100644 --- a/src/uscxml/interpreter/BasicEventQueue.cpp +++ b/src/uscxml/interpreter/BasicEventQueue.cpp @@ -73,6 +73,25 @@ void BasicEventQueue::reset() { _queue.clear(); } +Data BasicEventQueue::serialize() { + std::lock_guard<std::recursive_mutex> lock(_mutex); + Data serialized; + + for (auto event : _queue) { + serialized["BasicEventQueue"].array.push_back(event); + } + return serialized; +} + +void BasicEventQueue::deserialize(const Data& data) { + if (data.hasKey("BasicEventQueue")) { + std::lock_guard<std::recursive_mutex> lock(_mutex); + for (auto event : data["BasicEventQueue"].array) { + _queue.push_back(Event::fromData(event)); + } + } +} + std::shared_ptr<EventQueueImpl> BasicEventQueue::create() { return std::shared_ptr<EventQueueImpl>(new BasicEventQueue()); } diff --git a/src/uscxml/interpreter/BasicEventQueue.h b/src/uscxml/interpreter/BasicEventQueue.h index 927b9c4..db74825 100644 --- a/src/uscxml/interpreter/BasicEventQueue.h +++ b/src/uscxml/interpreter/BasicEventQueue.h @@ -42,6 +42,8 @@ public: virtual Event dequeue(size_t blockMs); virtual void enqueue(const Event& event); virtual void reset(); + virtual Data serialize(); + virtual void deserialize(const Data& data); protected: std::list<Event> _queue; diff --git a/src/uscxml/interpreter/ContentExecutor.cpp b/src/uscxml/interpreter/ContentExecutor.cpp index ca9d877..de142a1 100644 --- a/src/uscxml/interpreter/ContentExecutor.cpp +++ b/src/uscxml/interpreter/ContentExecutor.cpp @@ -35,8 +35,8 @@ void ContentExecutor::uninvoke(XERCESC_NS::DOMElement* invoke) { _impl->uninvoke(invoke); } -Data ContentExecutor::elementAsData(XERCESC_NS::DOMElement* element) { - return _impl->elementAsData(element); +Data ContentExecutor::elementAsData(XERCESC_NS::DOMElement* element, bool asExpression) { + return _impl->elementAsData(element, asExpression); } void ContentExecutor::raiseDoneEvent(XERCESC_NS::DOMElement* state, XERCESC_NS::DOMElement* doneData) { diff --git a/src/uscxml/interpreter/ContentExecutor.h b/src/uscxml/interpreter/ContentExecutor.h index 1559c5c..7a0b125 100644 --- a/src/uscxml/interpreter/ContentExecutor.h +++ b/src/uscxml/interpreter/ContentExecutor.h @@ -46,7 +46,7 @@ public: virtual void process(XERCESC_NS::DOMElement* block, const X& xmlPrefix); virtual void invoke(XERCESC_NS::DOMElement* invoke); virtual void uninvoke(XERCESC_NS::DOMElement* invoke); - virtual Data elementAsData(XERCESC_NS::DOMElement* element); + virtual Data elementAsData(XERCESC_NS::DOMElement* element, bool asExpression = false); virtual void raiseDoneEvent(XERCESC_NS::DOMElement* state, XERCESC_NS::DOMElement* doneData); virtual std::shared_ptr<ContentExecutorImpl> getImpl() const; diff --git a/src/uscxml/interpreter/ContentExecutorImpl.h b/src/uscxml/interpreter/ContentExecutorImpl.h index dfd4c5f..278cbb9 100644 --- a/src/uscxml/interpreter/ContentExecutorImpl.h +++ b/src/uscxml/interpreter/ContentExecutorImpl.h @@ -94,7 +94,7 @@ public: virtual void uninvoke(XERCESC_NS::DOMElement* invoke) = 0; virtual void raiseDoneEvent(XERCESC_NS::DOMElement* state, XERCESC_NS::DOMElement* doneData) = 0; - virtual Data elementAsData(XERCESC_NS::DOMElement* element) = 0; + virtual Data elementAsData(XERCESC_NS::DOMElement* element, bool asExpression = false) = 0; protected: ContentExecutorCallbacks* _callbacks; diff --git a/src/uscxml/interpreter/EventQueue.cpp b/src/uscxml/interpreter/EventQueue.cpp index 9b345d5..fa9c4c1 100644 --- a/src/uscxml/interpreter/EventQueue.cpp +++ b/src/uscxml/interpreter/EventQueue.cpp @@ -42,6 +42,14 @@ void EventQueue::reset() { return _impl->reset(); } +Data EventQueue::serialize() { + return _impl->serialize(); +} + +void EventQueue::deserialize(const Data& data) { + return _impl->deserialize(data); +} + std::shared_ptr<EventQueueImpl> EventQueue::getImplBase() { return _impl; } diff --git a/src/uscxml/interpreter/EventQueue.h b/src/uscxml/interpreter/EventQueue.h index 5ca1f52..76fdaa2 100644 --- a/src/uscxml/interpreter/EventQueue.h +++ b/src/uscxml/interpreter/EventQueue.h @@ -39,6 +39,10 @@ public: virtual Event dequeue(size_t blockMs); virtual void enqueue(const Event& event); virtual void reset(); + + Data serialize(); + void deserialize(const Data& data); + virtual std::shared_ptr<EventQueueImpl> getImplBase(); protected: diff --git a/src/uscxml/interpreter/EventQueueImpl.h b/src/uscxml/interpreter/EventQueueImpl.h index 2a33a75..50ac608 100644 --- a/src/uscxml/interpreter/EventQueueImpl.h +++ b/src/uscxml/interpreter/EventQueueImpl.h @@ -42,6 +42,8 @@ public: virtual Event dequeue(size_t blockMs) = 0; virtual void enqueue(const Event& event) = 0; virtual void reset() = 0; + virtual Data serialize() = 0; + virtual void deserialize(const Data& data) = 0; }; /** @@ -63,6 +65,10 @@ public: virtual void enqueueDelayed(const Event& event, size_t delayMs, const std::string& eventUUID) = 0; virtual void cancelDelayed(const std::string& eventId) = 0; virtual void cancelAllDelayed() = 0; + + virtual Data serialize() = 0; + virtual void deserialize(const Data& data) = 0; + }; } diff --git a/src/uscxml/interpreter/FastMicroStep.cpp b/src/uscxml/interpreter/FastMicroStep.cpp index 149ccb4..bf9749d 100644 --- a/src/uscxml/interpreter/FastMicroStep.cpp +++ b/src/uscxml/interpreter/FastMicroStep.cpp @@ -94,6 +94,45 @@ std::shared_ptr<MicroStepImpl> FastMicroStep::create(MicroStepCallbacks* callbac return std::shared_ptr<MicroStepImpl>(new FastMicroStep(callbacks)); } +void FastMicroStep::deserialize(const Data& encodedState) { + if (!encodedState.hasKey("configuration") || + !encodedState.hasKey("invocations") || + !encodedState.hasKey("histories") || + !encodedState.hasKey("intializedData")) { + ERROR_PLATFORM_THROW("Data does not contain required fields for deserialization "); + } + + _configuration = fromBase64(encodedState["configuration"].atom); + assert(_configuration.size() > 0 && _configuration.num_blocks() > 0); + _invocations = fromBase64(encodedState["invocations"].atom); + _history = fromBase64(encodedState["histories"].atom); + _initializedData = fromBase64(encodedState["intializedData"].atom); + + for (size_t i = 0; i < USCXML_NUMBER_STATES; i++) { + if (BIT_HAS(i, _invocations) && USCXML_GET_STATE(i).invoke.size() > 0) { + for (auto invIter = USCXML_GET_STATE(i).invoke.begin(); invIter != USCXML_GET_STATE(i).invoke.end(); invIter++) { + try { + _callbacks->invoke(*invIter); + } catch (ErrorEvent e) { + LOG(_callbacks->getLogger(), USCXML_WARN) << e; + } catch (...) { + } + } + } + } + + _flags |= USCXML_CTX_INITIALIZED; +} + +Data FastMicroStep::serialize() { + Data encodedState; + encodedState["configuration"] = Data(toBase64(_configuration)); + encodedState["invocations"] = Data(toBase64(_invocations)); + encodedState["histories"] = Data(toBase64(_history)); + encodedState["intializedData"] = Data(toBase64(_initializedData)); + return encodedState; +} + void FastMicroStep::resortStates(DOMElement* element, const X& xmlPrefix) { /** @@ -197,11 +236,11 @@ void FastMicroStep::init(XERCESC_NS::DOMElement* scxml) { _xmlPrefix = std::string(_xmlPrefix) + ":"; } - resortStates(_scxml, _xmlPrefix); + resortStates(_scxml, _xmlPrefix); #ifdef WITH_CACHE_FILES bool withCache = !envVarIsTrue("USCXML_NOCACHE_FILES"); - Data& cache = _callbacks->getCache().compound["FastMicroStep"]; + Data& cache = _callbacks->getCache().compound["FastMicroStep"]; #endif /** -- All things states -- */ @@ -319,10 +358,17 @@ void FastMicroStep::init(XERCESC_NS::DOMElement* scxml) { // establish the states' completion #ifdef WITH_CACHE_FILES if (withCache && cachedState->compound.find("completion") != cachedState->compound.end()) { - _states[i]->completion = fromBase64(cachedState->compound["completion"]); - } else { + boost::dynamic_bitset<BITSET_BLOCKTYPE> completion = fromBase64(cachedState->compound["completion"]); + if (completion.size() != _states.size()) { + LOG(_callbacks->getLogger(), USCXML_WARN) << "State completion has wrong size: Cache corrupted"; + } else { + _states[i]->completion = completion; + goto COMPLETION_STABLISHED; + } + } #endif - std::list<DOMElement*> completion = getCompletion(_states[i]->element); + { + std::list<DOMElement*> completion = getCompletion(_states[i]->element); for (j = 0; j < _states.size(); j++) { if (!completion.empty() && _states[j]->element == completion.front()) { _states[i]->completion[j] = true; @@ -332,12 +378,13 @@ void FastMicroStep::init(XERCESC_NS::DOMElement* scxml) { } } assert(completion.size() == 0); -#ifdef WITH_CACHE_FILES - if (withCache) - cachedState->compound["completion"] = Data(toBase64(_states[i]->completion)); } +#ifdef WITH_CACHE_FILES + if (withCache) + cachedState->compound["completion"] = Data(toBase64(_states[i]->completion)); +COMPLETION_STABLISHED: #endif - // this is set when establishing the completion + // this is set when establishing the completion if (_states[i]->element->getUserData(X("hasHistoryChild")) == _states[i]) { _states[i]->type |= USCXML_STATE_HAS_HISTORY; } @@ -400,16 +447,23 @@ void FastMicroStep::init(XERCESC_NS::DOMElement* scxml) { } } #endif - + // establish the transitions' exit set assert(_transitions[i]->element != NULL); #ifdef WITH_CACHE_FILES if (withCache && cachedTrans->compound.find("exitset") != cachedTrans->compound.end()) { - _transitions[i]->exitSet = fromBase64(cachedTrans->compound["exitset"]); - } else { + boost::dynamic_bitset<BITSET_BLOCKTYPE> exitSet = fromBase64(cachedTrans->compound["exitset"]); + if (exitSet.size() != _states.size()) { + LOG(_callbacks->getLogger(), USCXML_WARN) << "Transition exit set has wrong size: Cache corrupted"; + } else { + _transitions[i]->exitSet = exitSet; + goto EXIT_SET_ESTABLISHED; + } + } #endif - std::list<DOMElement*> exitList = getExitSetCached(_transitions[i]->element, _scxml); + { + std::list<DOMElement*> exitList = getExitSetCached(_transitions[i]->element, _scxml); for (j = 0; j < _states.size(); j++) { if (!exitList.empty() && _states[j]->element == exitList.front()) { @@ -420,54 +474,67 @@ void FastMicroStep::init(XERCESC_NS::DOMElement* scxml) { } } assert(exitList.size() == 0); - -#ifdef WITH_CACHE_FILES - if (withCache) - cachedTrans->compound["exitset"] = Data(toBase64(_transitions[i]->exitSet)); } +#ifdef WITH_CACHE_FILES + if (withCache) + cachedTrans->compound["exitset"] = Data(toBase64(_transitions[i]->exitSet)); +EXIT_SET_ESTABLISHED: #endif // establish the transitions' conflict set #ifdef WITH_CACHE_FILES if (withCache && cachedTrans->compound.find("conflicts") != cachedTrans->compound.end()) { - _transitions[i]->conflicts = fromBase64(cachedTrans->compound["conflicts"]); - } else { -#endif - for (j = i; j < _transitions.size(); j++) { - if (conflictsCached(_transitions[i]->element, _transitions[j]->element, _scxml)) { - _transitions[i]->conflicts[j] = true; - } else { - _transitions[i]->conflicts[j] = false; - } - // std::cout << "."; + boost::dynamic_bitset<BITSET_BLOCKTYPE> conflicts = fromBase64(cachedTrans->compound["conflicts"]); + if (conflicts.size() != _transitions.size()) { + LOG(_callbacks->getLogger(), USCXML_WARN) << "Transition conflicts has wrong size: Cache corrupted"; + } else { + _transitions[i]->conflicts = conflicts; + goto CONFLICTS_ESTABLISHED; } - - // conflicts matrix is symmetric - for (j = 0; j < i; j++) { - _transitions[i]->conflicts[j] = _transitions[j]->conflicts[i]; + } +#endif + for (j = i; j < _transitions.size(); j++) { + if (conflictsCached(_transitions[i]->element, _transitions[j]->element, _scxml)) { + _transitions[i]->conflicts[j] = true; + } else { + _transitions[i]->conflicts[j] = false; } -#ifdef WITH_CACHE_FILES - if (withCache) - cachedTrans->compound["conflicts"] = Data(toBase64(_transitions[i]->conflicts)); + // std::cout << "."; + } + // conflicts matrix is symmetric + for (j = 0; j < i; j++) { + _transitions[i]->conflicts[j] = _transitions[j]->conflicts[i]; } +#ifdef WITH_CACHE_FILES + if (withCache) + cachedTrans->compound["conflicts"] = Data(toBase64(_transitions[i]->conflicts)); +CONFLICTS_ESTABLISHED: #endif // establish the transitions' target set #ifdef WITH_CACHE_FILES if (withCache && cachedTrans->compound.find("target") != cachedTrans->compound.end()) { - _transitions[i]->target = fromBase64(cachedTrans->compound["target"]); - } else { + boost::dynamic_bitset<BITSET_BLOCKTYPE> target = fromBase64(cachedTrans->compound["target"]); + if (target.size() != _states.size()) { + LOG(_callbacks->getLogger(), USCXML_WARN) << "Transition target set has wrong size: Cache corrupted"; + } else { + _transitions[i]->target = target; + goto TARGET_SET_ESTABLISHED; + } + } #endif - std::list<std::string> targets = tokenize(ATTR(_transitions[i]->element, "target")); + { + std::list<std::string> targets = tokenize(ATTR(_transitions[i]->element, "target")); for (auto tIter = targets.begin(); tIter != targets.end(); tIter++) { if (_stateIds.find(*tIter) != _stateIds.end()) { _transitions[i]->target[_stateIds[*tIter]] = true; } } -#ifdef WITH_CACHE_FILES - if (withCache) - cachedTrans->compound["target"] = Data(toBase64(_transitions[i]->target)); } +#ifdef WITH_CACHE_FILES + if (withCache) + cachedTrans->compound["target"] = Data(toBase64(_transitions[i]->target)); +TARGET_SET_ESTABLISHED: #endif // the transition's source State* uscxmlState = (State*)(_transitions[i]->element->getParentNode()->getUserData(X("uscxmlState"))); @@ -656,6 +723,7 @@ InterpreterState FastMicroStep::step(size_t blockMs) { USCXML_MONITOR_CALLBACK(_callbacks->getMonitors(), onStableConfiguration); _microstepConfigurations.clear(); _flags |= USCXML_CTX_STABLE; + return USCXML_MACROSTEPPED; } if ((_event = _callbacks->dequeueExternal(blockMs))) { @@ -726,9 +794,9 @@ SELECT_TRANSITIONS: _flags |= USCXML_CTX_SPONTANEOUS; _flags &= ~USCXML_CTX_TRANSITION_FOUND; } else { - // spontaneuous transitions are exhausted + // spontaneuous transitions are exhausted and we will attempt to dequeue an internal event next round _flags &= ~USCXML_CTX_SPONTANEOUS; - return USCXML_MACROSTEPPED; + return USCXML_MICROSTEPPED; } USCXML_MONITOR_CALLBACK(_callbacks->getMonitors(), beforeMicroStep); diff --git a/src/uscxml/interpreter/FastMicroStep.h b/src/uscxml/interpreter/FastMicroStep.h index db3e724..97b7eeb 100644 --- a/src/uscxml/interpreter/FastMicroStep.h +++ b/src/uscxml/interpreter/FastMicroStep.h @@ -57,6 +57,9 @@ public: virtual std::list<XERCESC_NS::DOMElement*> getConfiguration(); void markAsCancelled(); + virtual void deserialize(const Data& encodedState); + virtual Data serialize(); + protected: class Transition { public: diff --git a/src/uscxml/interpreter/InterpreterImpl.cpp b/src/uscxml/interpreter/InterpreterImpl.cpp index f9da040..60ce29d 100644 --- a/src/uscxml/interpreter/InterpreterImpl.cpp +++ b/src/uscxml/interpreter/InterpreterImpl.cpp @@ -111,7 +111,7 @@ InterpreterImpl::~InterpreterImpl() { // ::xercesc_3_1::XMLPlatformUtils::Terminate(); #ifdef WITH_CACHE_FILES - if (!envVarIsTrue("USCXML_NOCACHE_FILES")) { + if (!envVarIsTrue("USCXML_NOCACHE_FILES") && _document != NULL) { // save our cache std::string sharedTemp = URL::getTempDir(true); std::ofstream dataFS(sharedTemp + PATH_SEPERATOR + md5(_baseURL) + ".uscxml.cache"); @@ -123,12 +123,139 @@ InterpreterImpl::~InterpreterImpl() { #endif } +InterpreterState InterpreterImpl::step(size_t blockMs) { + std::lock_guard<std::recursive_mutex> lock(_serializationMutex); + if (!_isInitialized) { + init(); + _state = USCXML_INITIALIZED; + } else { + _state = _microStepper.step(blockMs); + } + return _state; +} + +void InterpreterImpl::reset() { + if (_microStepper) + _microStepper.reset(); + + _isInitialized = false; + _state = USCXML_INSTANTIATED; + // _dataModel.reset(); + if (_delayQueue) + _delayQueue.reset(); + // _contentExecutor.reset(); +} + void InterpreterImpl::cancel() { + _microStepper.markAsCancelled(); Event unblock; enqueueExternal(unblock); } +void InterpreterImpl::deserialize(const std::string& encodedState) { + + init(); +// std::cout << encodedState << std::endl; + Data state = Data::fromJSON(encodedState); + if (!state.hasKey("microstepper")) { + ERROR_PLATFORM_THROW("No microstepper information in serialized state"); + } + + if (!state.hasKey("url")) { + ERROR_PLATFORM_THROW("No url information in serialized state"); + } + + if (!state.hasKey("md5")) { + ERROR_PLATFORM_THROW("No MD5 hash in serialized state"); + } + + if (state.hasKey("externalQueue")) { + _externalQueue.deserialize(state["externalQueue"]); + } + + if (state.hasKey("delayQueue")) { + _delayQueue.deserialize(state["delayQueue"]); + } + + if (_md5.size() == 0) { + // get md5 of current document + std::stringstream ss; + ss << *_document; + _md5 = md5(ss.str()); + } + + if (state["md5"].atom != _md5) { + ERROR_PLATFORM_THROW("MD5 hash mismatch in serialized state"); + } + + std::list<XERCESC_NS::DOMElement*> datas = DOMUtils::inDocumentOrder({ XML_PREFIX(_scxml).str() + "data" }, _scxml); + for (auto data : datas) { + if (HAS_ATTR(data, "id") && state["datamodel"].hasKey(ATTR(data, "id"))) + _dataModel.init(ATTR(data, "id"), state["datamodel"][ATTR(data, "id")]); + } + + _microStepper.deserialize(state["microstepper"]); + + std::list<XERCESC_NS::DOMElement*> invokes = DOMUtils::inDocumentOrder({ XML_PREFIX(_scxml).str() + "invoke" }, _scxml); + for (auto invokeElem : invokes) { + // BasicContentExecutor sets invokeid userdata upon invocation + char* invokeId = (char*)invokeElem->getUserData(X("invokeid")); + if (invokeId != NULL && _invokers.find(invokeId) != _invokers.end()) { + std::string path = DOMUtils::xPathForNode(invokeElem); + if (state.hasKey("invoker") && state["invoker"].hasKey(path)) { + _invokers[invokeId].deserialize(state["invoker"][path]); + } + } + } + +} + +std::string InterpreterImpl::serialize() { + std::lock_guard<std::recursive_mutex> lock(_serializationMutex); + + Data serialized; + if (_state != USCXML_IDLE && _state != USCXML_MACROSTEPPED && _state != USCXML_FINISHED) { + ERROR_PLATFORM_THROW("Cannot serialize an unstable interpreter"); + } + + if (_md5.size() == 0) { + // get md5 of current document + std::stringstream ss; + ss << *_document; + _md5 = md5(ss.str()); + } + + serialized["md5"] = Data(_md5); + serialized["url"] = Data(std::string(_baseURL)); + serialized["microstepper"] = _microStepper.serialize(); + + // SCXML Rec: "the values of all attributes of type "id" must be unique within the session" + std::list<XERCESC_NS::DOMElement*> datas = DOMUtils::inDocumentOrder({ XML_PREFIX(_scxml).str() + "data" }, _scxml); + for (auto data : datas) { + if (HAS_ATTR(data, "id")) { + serialized["datamodel"][ATTR(data, "id")] = _dataModel.evalAsData(ATTR(data, "id")); + } + } + + // save all invokers' state + std::list<XERCESC_NS::DOMElement*> invokes = DOMUtils::inDocumentOrder({ XML_PREFIX(_scxml).str() + "invoke" }, _scxml); + for (auto invokeElem : invokes) { + // BasicContentExecutor sets invokeid userdata upon invocation + char* invokeId = (char*)invokeElem->getUserData(X("invokeid")); + if (invokeId != NULL && _invokers.find(invokeId) != _invokers.end()) { + std::string path = DOMUtils::xPathForNode(invokeElem); + serialized["invoker"][path] = _invokers[invokeId].serialize(); + } + } + +// serialized["internalQueue"] = _internalQueue.serialize(); + serialized["externalQueue"] = _externalQueue.serialize(); + serialized["delayQueue"] = _delayQueue.serialize(); + + return serialized.asJSON(); +} + void InterpreterImpl::setupDOM() { @@ -209,7 +336,7 @@ void InterpreterImpl::init() { _cache.compound["InterpreterImpl"].compound["md5"] = Data(_md5); } #endif - + // register io processors std::map<std::string, IOProcessorImpl*> ioProcs = _factory->getIOProcessors(); for (auto ioProcIter = ioProcs.begin(); ioProcIter != ioProcs.end(); ioProcIter++) { @@ -231,7 +358,6 @@ void InterpreterImpl::init() { _ioProcs[*nameIter] = _ioProcs[ioProcIter->first]; } } - } if (!_microStepper) { @@ -268,7 +394,13 @@ void InterpreterImpl::initData(XERCESC_NS::DOMElement* root) { } else if (_invokeReq.namelist.find(id) != _invokeReq.namelist.end()) { _dataModel.init(id, _invokeReq.namelist[id]); } else { - _dataModel.init(id, _execContent.elementAsData(root)); + try { + _dataModel.init(id, _execContent.elementAsData(root)); + } catch (ErrorEvent e) { + // test 453 + _dataModel.init(id, _execContent.elementAsData(root, true)); + + } } } catch(ErrorEvent e) { // test 277 diff --git a/src/uscxml/interpreter/InterpreterImpl.h b/src/uscxml/interpreter/InterpreterImpl.h index 0efc70a..2b12624 100644 --- a/src/uscxml/interpreter/InterpreterImpl.h +++ b/src/uscxml/interpreter/InterpreterImpl.h @@ -64,30 +64,14 @@ public: void cloneFrom(InterpreterImpl* other); void cloneFrom(std::shared_ptr<InterpreterImpl> other); - virtual InterpreterState step(size_t blockMs) { - if (!_isInitialized) { - init(); - _state = USCXML_INITIALIZED; - } else { - _state = _microStepper.step(blockMs); - } - return _state; - } - - virtual void reset() {///< Reset state machine - if (_microStepper) - _microStepper.reset(); - - _isInitialized = false; - _state = USCXML_INSTANTIATED; -// _dataModel.reset(); - if (_delayQueue) - _delayQueue.reset(); -// _contentExecutor.reset(); - } + virtual InterpreterState step(size_t blockMs); + virtual void reset();///< Reset state machine virtual void cancel(); ///< Cancel and finalize state machine + virtual void deserialize(const std::string& encodedState); + virtual std::string serialize(); + InterpreterState getState() { return _state; } @@ -240,6 +224,18 @@ public: _delayQueue = al.delayedQueue; } + ActionLanguage getActionLanguage() { + ActionLanguage al; + al.logger = _logger; + al.execContent = _execContent; + al.microStepper = _microStepper; + al.dataModel = _dataModel; + al.internalQueue = _internalQueue; + al.externalQueue = _externalQueue; + al.delayedQueue = _delayQueue; + return al; + } + void setFactory(Factory* factory) { _factory = factory; } @@ -274,6 +270,7 @@ protected: static std::map<std::string, std::weak_ptr<InterpreterImpl> > _instances; static std::recursive_mutex _instanceMutex; std::recursive_mutex _delayMutex; + std::recursive_mutex _serializationMutex; friend class Interpreter; friend class InterpreterIssue; diff --git a/src/uscxml/interpreter/InterpreterMonitor.h b/src/uscxml/interpreter/InterpreterMonitor.h index a3c527d..7207558 100644 --- a/src/uscxml/interpreter/InterpreterMonitor.h +++ b/src/uscxml/interpreter/InterpreterMonitor.h @@ -102,7 +102,7 @@ protected: class USCXML_API StateTransitionMonitor : public uscxml::InterpreterMonitor { public: - StateTransitionMonitor() {} + StateTransitionMonitor(std::string prefix = "") : _logPrefix(prefix) {} virtual ~StateTransitionMonitor() {} virtual void beforeTakingTransition(Interpreter& interpreter, const XERCESC_NS::DOMElement* transition); @@ -115,6 +115,7 @@ public: protected: static std::recursive_mutex _mutex; + std::string _logPrefix; }; } diff --git a/src/uscxml/interpreter/MicroStep.cpp b/src/uscxml/interpreter/MicroStep.cpp index 896c92a..e5de1dc 100644 --- a/src/uscxml/interpreter/MicroStep.cpp +++ b/src/uscxml/interpreter/MicroStep.cpp @@ -44,6 +44,14 @@ void MicroStep::markAsCancelled() { _impl->markAsCancelled(); } +void MicroStep::deserialize(const Data& encodedState) { + _impl->deserialize(encodedState); +} + +Data MicroStep::serialize() { + return _impl->serialize(); +} + std::shared_ptr<MicroStepImpl> MicroStep::getImpl() const { return _impl; } diff --git a/src/uscxml/interpreter/MicroStep.h b/src/uscxml/interpreter/MicroStep.h index 56a7ee8..bda8fd7 100644 --- a/src/uscxml/interpreter/MicroStep.h +++ b/src/uscxml/interpreter/MicroStep.h @@ -26,6 +26,7 @@ #include <string> #include "uscxml/Common.h" +#include "uscxml/messages/Data.h" #include "uscxml/interpreter/InterpreterState.h" @@ -55,6 +56,12 @@ public: virtual void init(XERCESC_NS::DOMElement* scxml); virtual void markAsCancelled(); + /// @copydoc MicroStepImpl::deserialize + virtual void deserialize(const Data& encodedState); + + /// @copydoc MicroStepImpl::serialize + virtual Data serialize(); + std::shared_ptr<MicroStepImpl> getImpl() const; protected: std::shared_ptr<MicroStepImpl> _impl; diff --git a/src/uscxml/interpreter/MicroStepImpl.h b/src/uscxml/interpreter/MicroStepImpl.h index e3f8299..7ff9469 100644 --- a/src/uscxml/interpreter/MicroStepImpl.h +++ b/src/uscxml/interpreter/MicroStepImpl.h @@ -61,8 +61,9 @@ public: virtual Interpreter getInterpreter() = 0; virtual Logger getLogger() = 0; - /** Saved State */ + /** Cache Data */ virtual Data& getCache() = 0; + }; /** @@ -87,6 +88,9 @@ public: virtual void init(XERCESC_NS::DOMElement* scxml) = 0; virtual void markAsCancelled() = 0; + virtual void deserialize(const Data& encodedState) = 0; + virtual Data serialize() = 0; + protected: MicroStepCallbacks* _callbacks; |