summaryrefslogtreecommitdiffstats
path: root/src/uscxml/interpreter
diff options
context:
space:
mode:
authorStefan Radomski <github@mintwerk.de>2017-01-13 16:47:44 (GMT)
committerStefan Radomski <github@mintwerk.de>2017-01-13 16:47:44 (GMT)
commit4f6cbe9e7aec2b4a6c8f286f9097abfb011a6235 (patch)
tree8c023473bb342780ddf51a893d18369f1319bb5c /src/uscxml/interpreter
parent0aa0fe08dc308c94379c47d0bf9745e341cb4c81 (diff)
downloaduscxml-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')
-rw-r--r--src/uscxml/interpreter/BasicContentExecutor.cpp70
-rw-r--r--src/uscxml/interpreter/BasicContentExecutor.h2
-rw-r--r--src/uscxml/interpreter/BasicDelayedEventQueue.cpp49
-rw-r--r--src/uscxml/interpreter/BasicDelayedEventQueue.h3
-rw-r--r--src/uscxml/interpreter/BasicEventQueue.cpp19
-rw-r--r--src/uscxml/interpreter/BasicEventQueue.h2
-rw-r--r--src/uscxml/interpreter/ContentExecutor.cpp4
-rw-r--r--src/uscxml/interpreter/ContentExecutor.h2
-rw-r--r--src/uscxml/interpreter/ContentExecutorImpl.h2
-rw-r--r--src/uscxml/interpreter/EventQueue.cpp8
-rw-r--r--src/uscxml/interpreter/EventQueue.h4
-rw-r--r--src/uscxml/interpreter/EventQueueImpl.h6
-rw-r--r--src/uscxml/interpreter/FastMicroStep.cpp152
-rw-r--r--src/uscxml/interpreter/FastMicroStep.h3
-rw-r--r--src/uscxml/interpreter/InterpreterImpl.cpp140
-rw-r--r--src/uscxml/interpreter/InterpreterImpl.h39
-rw-r--r--src/uscxml/interpreter/InterpreterMonitor.h3
-rw-r--r--src/uscxml/interpreter/MicroStep.cpp8
-rw-r--r--src/uscxml/interpreter/MicroStep.h7
-rw-r--r--src/uscxml/interpreter/MicroStepImpl.h6
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;