From d90b15c96271d2afd45d649e3e22030004359d6e Mon Sep 17 00:00:00 2001 From: Stefan Radomski Date: Tue, 26 Aug 2014 10:54:58 +0200 Subject: API changes with receiveEvent - receiveEvent will deliver to external queue now per default - switched Interpreter implementations to the new algorithm from spec --- CMakeLists.txt | 2 +- apps/uscxml-browser.cpp | 1 + contrib/build-scripts/build-arabica-linux.sh | 3 +- contrib/build-scripts/build-arabica-macosx.sh | 3 +- contrib/build-scripts/build-ffmpeg-linux.sh | 3 +- contrib/build-scripts/build-glog-linux.sh | 3 +- contrib/build-scripts/build-glog-macosx.sh | 3 +- contrib/build-scripts/build-libevent-linux.sh | 3 +- contrib/build-scripts/build-libevent-macosx.sh | 3 +- contrib/build-scripts/build-swi-linux.sh | 3 +- contrib/build-scripts/build-swi-macosx.sh | 3 +- contrib/build-scripts/build-v8-linux.sh | 3 +- contrib/build-scripts/build-v8-mac.sh | 78 -- contrib/build-scripts/build-v8-macosx.sh | 79 ++ contrib/build-scripts/platform-id-linux.sh | 12 + contrib/build-scripts/platform-id-mac.sh | 19 + contrib/ctest/CTestCustom.ctest.in | 31 +- src/uscxml/CMakeLists.txt | 2 +- src/uscxml/Factory.cpp | 10 +- src/uscxml/Interpreter.cpp | 516 +++++++- src/uscxml/Interpreter.h | 23 +- src/uscxml/interpreter/InterpreterDraft6.cpp | 533 +------- src/uscxml/interpreter/InterpreterDraft6.h | 12 +- src/uscxml/interpreter/InterpreterRC.cpp | 579 +++++++++ .../interpreter/InterpreterRC.cpp.deactivated | 1292 -------------------- src/uscxml/interpreter/InterpreterRC.h | 64 + src/uscxml/interpreter/InterpreterRC.h.deactivated | 87 -- src/uscxml/plugins/EventHandler.h | 5 +- .../plugins/invoker/heartbeat/HeartbeatInvoker.cpp | 2 +- src/uscxml/plugins/invoker/xhtml/XHTMLInvoker.cpp | 2 +- .../ioprocessor/basichttp/BasicHTTPIOProcessor.cpp | 2 +- src/uscxml/transform/ChartToFSM.cpp | 2 +- test/src/test-w3c.cpp | 2 + 33 files changed, 1316 insertions(+), 2069 deletions(-) delete mode 100755 contrib/build-scripts/build-v8-mac.sh create mode 100755 contrib/build-scripts/build-v8-macosx.sh create mode 100755 contrib/build-scripts/platform-id-linux.sh create mode 100755 contrib/build-scripts/platform-id-mac.sh create mode 100644 src/uscxml/interpreter/InterpreterRC.cpp delete mode 100644 src/uscxml/interpreter/InterpreterRC.cpp.deactivated create mode 100644 src/uscxml/interpreter/InterpreterRC.h delete mode 100644 src/uscxml/interpreter/InterpreterRC.h.deactivated diff --git a/CMakeLists.txt b/CMakeLists.txt index 619bcc7..73d4a3e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1329,7 +1329,7 @@ if (BUILD_SHARED_LIBS AND BUILD_BINDINGS) endif() message(STATUS "General information:") -message(STATUS " Build type ..................... : ${CMAKE_BUILD_TYPE} for ${USCXML_PLATFORM_ID} (${CMAKE_CXX_COMPILER_ID_LC})") +message(STATUS " Build type ..................... : ${CMAKE_BUILD_TYPE} for ${USCXML_PLATFORM_ID}") if (BUILD_SHARED_LIBS) if (BUILD_AS_PLUGINS) message(STATUS " Building library as ............ : SHARED with plugins") diff --git a/apps/uscxml-browser.cpp b/apps/uscxml-browser.cpp index 0e7b22b..e54735e 100644 --- a/apps/uscxml-browser.cpp +++ b/apps/uscxml-browser.cpp @@ -191,6 +191,7 @@ int main(int argc, char** argv) { } + interpreter.setCmdLineOptions(options.additionalParameters); interpreter.setCmdLineOptions(currOptions->additionalParameters); interpreter.setCapabilities(options.getCapabilities()); diff --git a/contrib/build-scripts/build-arabica-linux.sh b/contrib/build-scripts/build-arabica-linux.sh index be20faf..712aec4 100755 --- a/contrib/build-scripts/build-arabica-linux.sh +++ b/contrib/build-scripts/build-arabica-linux.sh @@ -10,7 +10,8 @@ set -e ME=`basename $0` DIR="$( cd "$( dirname "$0" )" && pwd )" CPUARCH=`uname -m` -DEST_DIR="${DIR}/../prebuilt/linux-${CPUARCH}/gnu" +PLATFORM_ID=`${DIR}/platform-id-linux.sh` +DEST_DIR="${DIR}/../prebuilt/${PLATFORM_ID}" if [ ! -f src/arabica.cpp ]; then echo diff --git a/contrib/build-scripts/build-arabica-macosx.sh b/contrib/build-scripts/build-arabica-macosx.sh index c5a4bb9..c79cc2e 100755 --- a/contrib/build-scripts/build-arabica-macosx.sh +++ b/contrib/build-scripts/build-arabica-macosx.sh @@ -12,7 +12,8 @@ ME=`basename $0` DIR="$( cd "$( dirname "$0" )" && pwd )" MACOSX_VER=`/usr/bin/sw_vers -productVersion` MACOSX_COMP=(`echo $MACOSX_VER | tr '.' ' '`) -DEST_DIR="${DIR}/../prebuilt/darwin-i386/${MACOSX_COMP[0]}.${MACOSX_COMP[1]}/gnu" +PLATFORM_ID=`${DIR}/platform-id-mac.sh` +DEST_DIR="${DIR}/../prebuilt/${PLATFORM_ID}" SYSROOT=`xcrun --show-sdk-path` if [ ! -f src/arabica.cpp ]; then diff --git a/contrib/build-scripts/build-ffmpeg-linux.sh b/contrib/build-scripts/build-ffmpeg-linux.sh index cb4ec92..b10482e 100755 --- a/contrib/build-scripts/build-ffmpeg-linux.sh +++ b/contrib/build-scripts/build-ffmpeg-linux.sh @@ -10,7 +10,8 @@ set -e ME=`basename $0` DIR="$( cd "$( dirname "$0" )" && pwd )" CPUARCH=`uname -m` -DEST_DIR="${DIR}/../prebuilt/linux-${CPUARCH}/gnu" +PLATFORM_ID=`${DIR}/platform-id-linux.sh` +DEST_DIR="${DIR}/../prebuilt/${PLATFORM_ID}" if [ -d /tmp/build-ffmpeg ]; then rm -rf /tmp/build-ffmpeg diff --git a/contrib/build-scripts/build-glog-linux.sh b/contrib/build-scripts/build-glog-linux.sh index a33698e..6fa5d78 100755 --- a/contrib/build-scripts/build-glog-linux.sh +++ b/contrib/build-scripts/build-glog-linux.sh @@ -10,7 +10,8 @@ set -e ME=`basename $0` DIR="$( cd "$( dirname "$0" )" && pwd )" CPUARCH=`uname -m` -DEST_DIR="${DIR}/../prebuilt/linux-${CPUARCH}/gnu" +PLATFORM_ID=`${DIR}/platform-id-linux.sh` +DEST_DIR="${DIR}/../prebuilt/${PLATFORM_ID}" if [ ! -f src/glog/log_severity.h ]; then echo diff --git a/contrib/build-scripts/build-glog-macosx.sh b/contrib/build-scripts/build-glog-macosx.sh index 3363883..a1d6cd4 100755 --- a/contrib/build-scripts/build-glog-macosx.sh +++ b/contrib/build-scripts/build-glog-macosx.sh @@ -11,7 +11,8 @@ ME=`basename $0` DIR="$( cd "$( dirname "$0" )" && pwd )" MACOSX_VER=`/usr/bin/sw_vers -productVersion` MACOSX_COMP=(`echo $MACOSX_VER | tr '.' ' '`) -DEST_DIR="${DIR}/../prebuilt/darwin-i386/${MACOSX_COMP[0]}.${MACOSX_COMP[1]}/gnu" +PLATFORM_ID=`${DIR}/platform-id-mac.sh` +DEST_DIR="${DIR}/../prebuilt/${PLATFORM_ID}" if [ ! -f src/glog/log_severity.h ]; then echo diff --git a/contrib/build-scripts/build-libevent-linux.sh b/contrib/build-scripts/build-libevent-linux.sh index c57ad52..401e16d 100755 --- a/contrib/build-scripts/build-libevent-linux.sh +++ b/contrib/build-scripts/build-libevent-linux.sh @@ -10,7 +10,8 @@ set -e ME=`basename $0` DIR="$( cd "$( dirname "$0" )" && pwd )" CPUARCH=`uname -m` -DEST_DIR="${DIR}/../prebuilt/linux-${CPUARCH}/gnu" +PLATFORM_ID=`${DIR}/platform-id-linux.sh` +DEST_DIR="${DIR}/../prebuilt/${PLATFORM_ID}" if [ ! -f event.c ]; then echo diff --git a/contrib/build-scripts/build-libevent-macosx.sh b/contrib/build-scripts/build-libevent-macosx.sh index 23f288b..5423992 100755 --- a/contrib/build-scripts/build-libevent-macosx.sh +++ b/contrib/build-scripts/build-libevent-macosx.sh @@ -11,7 +11,8 @@ ME=`basename $0` DIR="$( cd "$( dirname "$0" )" && pwd )" MACOSX_VER=`/usr/bin/sw_vers -productVersion` MACOSX_COMP=(`echo $MACOSX_VER | tr '.' ' '`) -DEST_DIR="${DIR}/../prebuilt/darwin-i386/${MACOSX_COMP[0]}.${MACOSX_COMP[1]}/gnu" +PLATFORM_ID=`${DIR}/platform-id-mac.sh` +DEST_DIR="${DIR}/../prebuilt/${PLATFORM_ID}" if [ ! -f event.c ]; then echo diff --git a/contrib/build-scripts/build-swi-linux.sh b/contrib/build-scripts/build-swi-linux.sh index 7b32d4d..239d292 100755 --- a/contrib/build-scripts/build-swi-linux.sh +++ b/contrib/build-scripts/build-swi-linux.sh @@ -11,7 +11,8 @@ ME=`basename $0` DIR="$( cd "$( dirname "$0" )" && pwd )" CPUARCH=`uname -m` #DEST_DIR="${DIR}/../prebuilt/linux-${CPUARCH}/gnu" -DEST_DIR="/home/sradomski/Desktop" +PLATFORM_ID=`${DIR}/platform-id-linux.sh` +DEST_DIR="${DIR}/../prebuilt/${PLATFORM_ID}" VERSION=`cat VERSION` if [ ! -f src/pl-main.c ]; then diff --git a/contrib/build-scripts/build-swi-macosx.sh b/contrib/build-scripts/build-swi-macosx.sh index eff80b5..289327d 100755 --- a/contrib/build-scripts/build-swi-macosx.sh +++ b/contrib/build-scripts/build-swi-macosx.sh @@ -11,7 +11,8 @@ ME=`basename $0` DIR="$( cd "$( dirname "$0" )" && pwd )" MACOSX_VER=`/usr/bin/sw_vers -productVersion` MACOSX_COMP=(`echo $MACOSX_VER | tr '.' ' '`) -DEST_DIR="${DIR}/../prebuilt/darwin-i386/${MACOSX_COMP[0]}.${MACOSX_COMP[1]}/gnu" +PLATFORM_ID=`${DIR}/platform-id-mac.sh` +DEST_DIR="${DIR}/../prebuilt/${PLATFORM_ID}" VERSION=`cat VERSION` if [ ! -f src/pl-main.c ]; then diff --git a/contrib/build-scripts/build-v8-linux.sh b/contrib/build-scripts/build-v8-linux.sh index 5991e1c..256a5c6 100755 --- a/contrib/build-scripts/build-v8-linux.sh +++ b/contrib/build-scripts/build-v8-linux.sh @@ -10,7 +10,8 @@ set -e ME=`basename $0` DIR="$( cd "$( dirname "$0" )" && pwd )" CPUARCH=`uname -m` -DEST_DIR="${DIR}/../prebuilt/linux-${CPUARCH}/gnu" +PLATFORM_ID=`${DIR}/platform-id-linux.sh` +DEST_DIR="${DIR}/../prebuilt/${PLATFORM_ID}" PWD=`pwd` if [ ! -f src/v8.h ]; then diff --git a/contrib/build-scripts/build-v8-mac.sh b/contrib/build-scripts/build-v8-mac.sh deleted file mode 100755 index e979c4d..0000000 --- a/contrib/build-scripts/build-v8-mac.sh +++ /dev/null @@ -1,78 +0,0 @@ -#!/bin/bash - -# -# build v8 for MacOSX -# http://code.google.com/p/v8/wiki/BuildingWithGYP -# - -# exit on error -set -e - -ME=`basename $0` -DIR="$( cd "$( dirname "$0" )" && pwd )" -MACOSX_VER=`/usr/bin/sw_vers -productVersion` -MACOSX_COMP=(`echo $MACOSX_VER | tr '.' ' '`) -DEST_DIR="${DIR}/../prebuilt/darwin-i386/${MACOSX_COMP[0]}.${MACOSX_COMP[1]}/gnu" -PWD=`pwd` - -export MACOSX_DEPLOYMENT_TARGET=10.6 - -if [ ! -f src/v8.h ]; then - echo - echo "Cannot find src/v8.h" - echo "Run script from within v8 directory" - echo - exit -fi - -if [ ! -f ../depot_tools/update_depot_tools ]; then - echo - echo "Cannot find ../depot_tools/update_depot_tools" - echo "Checkout depot_tools as a sibling directory" - echo "svn co http://src.chromium.org/svn/trunk/tools/depot_tools" - echo - exit -fi - -DEPOT_PATH="${PWD}/../depot_tools" -export PATH="${DEPOT_PATH}:${PATH}" - -if [ ${MACOSX_COMP[1]} -lt 9 ]; then - CXXFLAGS="-mmacosx-version-min=10.6 -stdlib=libstdc++" - LDFLAGS="-stdlib=libstdc++" -else - CXXFLAGS="-mmacosx-version-min=10.7 -stdlib=libc++" - LDFLAGS="-stdlib=libc++" -fi - -make dependencies - -make ia32.release -make ia32.debug - -make x64.release -make x64.debug - -cp include/* ${DEST_DIR}/include - -lipo -create \ - ./out/x64.release/libv8_base.x64.a \ - ./out/ia32.release/libv8_base.ia32.a \ - -output ${DEST_DIR}/lib/libv8_base.a - -lipo -create \ - ./out/x64.release/libv8_snapshot.a \ - ./out/ia32.release/libv8_snapshot.a \ - -output ${DEST_DIR}/lib/libv8_snapshot.a - -lipo -create \ - ./out/x64.debug/libv8_base.x64.a \ - ./out/ia32.debug/libv8_base.ia32.a \ - -output ${DEST_DIR}/lib/libv8_base_d.a - -lipo -create \ - ./out/x64.debug/libv8_snapshot.a \ - ./out/ia32.debug/libv8_snapshot.a \ - -output ${DEST_DIR}/lib/libv8_snapshot_d.a - - diff --git a/contrib/build-scripts/build-v8-macosx.sh b/contrib/build-scripts/build-v8-macosx.sh new file mode 100755 index 0000000..9396251 --- /dev/null +++ b/contrib/build-scripts/build-v8-macosx.sh @@ -0,0 +1,79 @@ +#!/bin/bash + +# +# build v8 for MacOSX +# http://code.google.com/p/v8/wiki/BuildingWithGYP +# + +# exit on error +set -e + +ME=`basename $0` +DIR="$( cd "$( dirname "$0" )" && pwd )" +MACOSX_VER=`/usr/bin/sw_vers -productVersion` +MACOSX_COMP=(`echo $MACOSX_VER | tr '.' ' '`) +PLATFORM_ID=`${DIR}/platform-id-mac.sh` +DEST_DIR="${DIR}/../prebuilt/${PLATFORM_ID}" +PWD=`pwd` + +export MACOSX_DEPLOYMENT_TARGET=10.6 + +if [ ! -f src/v8.h ]; then + echo + echo "Cannot find src/v8.h" + echo "Run script from within v8 directory" + echo + exit +fi + +if [ ! -f ../depot_tools/update_depot_tools ]; then + echo + echo "Cannot find ../depot_tools/update_depot_tools" + echo "Checkout depot_tools as a sibling directory" + echo "svn co http://src.chromium.org/svn/trunk/tools/depot_tools" + echo + exit +fi + +DEPOT_PATH="${PWD}/../depot_tools" +export PATH="${DEPOT_PATH}:${PATH}" + +if [ ${MACOSX_COMP[1]} -lt 9 ]; then + CXXFLAGS="-mmacosx-version-min=10.6 -stdlib=libstdc++" + LDFLAGS="-stdlib=libstdc++" +else + CXXFLAGS="-mmacosx-version-min=10.7 -stdlib=libc++" + LDFLAGS="-stdlib=libc++" +fi + +make dependencies + +make ia32.release +make ia32.debug + +make x64.release +make x64.debug + +cp include/* ${DEST_DIR}/include + +lipo -create \ + ./out/x64.release/libv8_base.x64.a \ + ./out/ia32.release/libv8_base.ia32.a \ + -output ${DEST_DIR}/lib/libv8_base.a + +lipo -create \ + ./out/x64.release/libv8_snapshot.a \ + ./out/ia32.release/libv8_snapshot.a \ + -output ${DEST_DIR}/lib/libv8_snapshot.a + +lipo -create \ + ./out/x64.debug/libv8_base.x64.a \ + ./out/ia32.debug/libv8_base.ia32.a \ + -output ${DEST_DIR}/lib/libv8_base_d.a + +lipo -create \ + ./out/x64.debug/libv8_snapshot.a \ + ./out/ia32.debug/libv8_snapshot.a \ + -output ${DEST_DIR}/lib/libv8_snapshot_d.a + + diff --git a/contrib/build-scripts/platform-id-linux.sh b/contrib/build-scripts/platform-id-linux.sh new file mode 100755 index 0000000..9efac91 --- /dev/null +++ b/contrib/build-scripts/platform-id-linux.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +function version { + echo "$@" | awk -F. '{ printf("%03d%03d%03d\n", $1,$2,$3); }'; +} + +CMAKE_INFO=`cmake --system-information 2> /dev/null` + +CMAKE_CXX_COMPILER_ID=`echo "$CMAKE_INFO" |grep 'CMAKE_CXX_COMPILER_ID ' | awk '{print tolower($2)}' |sed 's/\"//g'` +CPU=`uname -m` + +echo "linux-${CPU}-${CMAKE_CXX_COMPILER_ID}" diff --git a/contrib/build-scripts/platform-id-mac.sh b/contrib/build-scripts/platform-id-mac.sh new file mode 100755 index 0000000..b105be3 --- /dev/null +++ b/contrib/build-scripts/platform-id-mac.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +function version { + echo "$@" | awk -F. '{ printf("%03d%03d%03d\n", $1,$2,$3); }'; +} + +CMAKE_INFO=`cmake --system-information 2> /dev/null` + +CURRENT_OSX_VERSION=`echo "$CMAKE_INFO" |grep CURRENT_OSX_VERSION |grep -v _CURRENT_OSX_VERSION | awk '{print $2}' |sed 's/\"//g'` +CMAKE_CXX_COMPILER_ID=`echo "$CMAKE_INFO" |grep 'CMAKE_CXX_COMPILER_ID ' | awk '{print tolower($2)}' |sed 's/\"//g' | sed 's/apple//g'` +CPU=`uname -m` + +CPP_LIB="libc++" +if [ "$(version "10.9.0")" -gt "$(version "$CURRENT_OSX_VERSION")" ]; then + # pre-mavericks + CPP_LIB="libstdc++" +fi + +echo "darwin-${CPU}-${CMAKE_CXX_COMPILER_ID}-${CPP_LIB}" diff --git a/contrib/ctest/CTestCustom.ctest.in b/contrib/ctest/CTestCustom.ctest.in index 4f4ef83..6b5fd3c 100644 --- a/contrib/ctest/CTestCustom.ctest.in +++ b/contrib/ctest/CTestCustom.ctest.in @@ -12,38 +12,9 @@ set(CTEST_CUSTOM_TESTS_IGNORE "ecma/test178.scxml" # Manual - PASSED "ecma/test230.scxml" # Manual - PASSED "ecma/test250.scxml" # Manual - PASSED + "ecma/test301.scxml" # Manual - PASSED "ecma/test307.scxml" # Manual - PASSED "ecma/test415.scxml" # Manual - PASSED - "ecma/test463.scxml" # XPath hardcoded - "ecma/test464.scxml" # XPath hardcoded - "ecma/test465.scxml" # XPath hardcoded - "ecma/test466.scxml" # XPath hardcoded - "ecma/test467.scxml" # XPath hardcoded - "ecma/test468.scxml" # XPath hardcoded - "ecma/test469.scxml" # XPath hardcoded - "ecma/test470.scxml" # XPath hardcoded - "ecma/test473.scxml" # XPath hardcoded - "ecma/test474.scxml" # XPath hardcoded - "ecma/test475.scxml" # XPath hardcoded - "ecma/test476.scxml" # XPath hardcoded - "ecma/test477.scxml" # XPath hardcoded - "ecma/test478.scxml" # XPath hardcoded - "ecma/test479.scxml" # XPath hardcoded - "ecma/test480.scxml" # XPath hardcoded - "ecma/test481.scxml" # XPath hardcoded - "ecma/test482.scxml" # XPath hardcoded - "ecma/test483.scxml" # XPath hardcoded - "ecma/test537.scxml" # XPath hardcoded - "ecma/test539.scxml" # XPath hardcoded - "ecma/test540.scxml" # XPath hardcoded - "ecma/test542.scxml" # XPath hardcoded - "ecma/test543.scxml" # XPath hardcoded - "ecma/test544.scxml" # XPath hardcoded - "ecma/test545.scxml" # XPath hardcoded - "ecma/test546.scxml" # XPath hardcoded - "ecma/test547.scxml" # XPath hardcoded - "ecma/test555.scxml" # XPath hardcoded - "ecma/test568.scxml" # XPath hardcoded "fsm/ecma/test178.scxml" # manual test "fsm/ecma/test224.scxml" # automatically generated id has the form stateid.platformid diff --git a/src/uscxml/CMakeLists.txt b/src/uscxml/CMakeLists.txt index 51ee4ea..979f8fd 100644 --- a/src/uscxml/CMakeLists.txt +++ b/src/uscxml/CMakeLists.txt @@ -85,7 +85,7 @@ list (APPEND USCXML_FILES ${USCXML_PLUGINS}) if (BUILD_AS_PLUGINS) list (APPEND USCXML_INCLUDE_DIRS ${PROJECT_SOURCE_DIR}/plugins) file(GLOB PLUMA - plugins/Pluma/*.cpp + plugins/Pluma/*.cpp plugins/Pluma/*.h plugins/*.cpp plugins/*.h diff --git a/src/uscxml/Factory.cpp b/src/uscxml/Factory.cpp index 7117de2..4013c58 100644 --- a/src/uscxml/Factory.cpp +++ b/src/uscxml/Factory.cpp @@ -730,20 +730,20 @@ void EventHandlerImpl::returnErrorCommunication(const std::string& cause) { returnEvent(exc); } -void EventHandlerImpl::returnEvent(Event& event, bool external) { +void EventHandlerImpl::returnEvent(Event& event, bool internal) { if (event.invokeid.length() == 0) event.invokeid = _invokeId; if (event.eventType == 0) - event.eventType = (external ? Event::EXTERNAL : Event::INTERNAL); + event.eventType = (internal ? Event::INTERNAL : Event::EXTERNAL); if (event.origin.length() == 0) event.origin = "#_" + _invokeId; if (event.origintype.length() == 0) event.origintype = _type; - if (external) { - _interpreter->receive(event); - } else { + if (internal) { _interpreter->receiveInternal(event); + } else { + _interpreter->receive(event); } } diff --git a/src/uscxml/Interpreter.cpp b/src/uscxml/Interpreter.cpp index b9e7145..eface5e 100644 --- a/src/uscxml/Interpreter.cpp +++ b/src/uscxml/Interpreter.cpp @@ -47,14 +47,14 @@ #include #include -#include "uscxml/interpreter/InterpreterDraft6.h" -//#include "uscxml/interpreter/InterpreterRC.h" #include "uscxml/Factory.h" #if 1 -#define INTERPRETER_IMPL InterpreterDraft6 +# define INTERPRETER_IMPL InterpreterDraft6 +# include "uscxml/interpreter/InterpreterDraft6.h" #else -#define INTERPRETER_IMPL InterpreterRC +# define INTERPRETER_IMPL InterpreterRC +# include "uscxml/interpreter/InterpreterRC.h" #endif #define VERBOSE 0 @@ -620,6 +620,498 @@ DONE_THREAD: ((InterpreterImpl*)instance)->_isStarted = false; } +void InterpreterImpl::exitInterpreter() { + NodeSet statesToExit = _configuration; + statesToExit.forward(false); + statesToExit.sort(); + + for (int i = 0; i < statesToExit.size(); i++) { + Arabica::XPath::NodeSet onExitElems = filterChildElements(_nsInfo.xmlNSPrefix + "onexit", statesToExit[i]); + for (int j = 0; j < onExitElems.size(); j++) { + executeContent(Element(onExitElems[j])); + } + Arabica::XPath::NodeSet invokeElems = filterChildElements(_nsInfo.xmlNSPrefix + "invoke", statesToExit[i]); + // TODO: we ought to cancel all remaining invokers just to be sure with the persist extension + for (int j = 0; j < invokeElems.size(); j++) { + cancelInvoke(Element(invokeElems[j])); + } + Element stateElem(statesToExit[i]); + if (isFinal(stateElem) && parentIsScxmlState(stateElem)) { + returnDoneEvent(statesToExit[i]); + } + } + _configuration = NodeSet(); +} + + +InterpreterState InterpreterImpl::interpret() { + InterpreterState state; + while(true) { + state = step(-1); + + switch (state) { + case uscxml::USCXML_FINISHED: + case uscxml::USCXML_DESTROYED: + // return as we finished + return state; + default: + + // process invokers on main thread + if(_thread == NULL) { + runOnMainThread(200); + } + + // process next step + break; + } + } + return state; +} + +// setup / fetch the documents initial transitions +NodeSet InterpreterImpl::getDocumentInitialTransitions() { + NodeSet initialTransitions; + + if (_startConfiguration.size() > 0) { + // we emulate entering a given configuration by creating a pseudo deep history + Element initHistory = _document.createElementNS(_nsInfo.nsURL, "history"); + _nsInfo.setPrefix(initHistory); + + initHistory.setAttribute("id", UUID::getUUID()); + initHistory.setAttribute("type", "deep"); + _scxml.insertBefore(initHistory, _scxml.getFirstChild()); + + std::string histId = ATTR(initHistory, "id"); + NodeSet histStates; + for (std::list::const_iterator stateIter = _startConfiguration.begin(); stateIter != _startConfiguration.end(); stateIter++) { + histStates.push_back(getState(*stateIter)); + } + _historyValue[histId] = histStates; + + Element initialElem = _document.createElementNS(_nsInfo.nsURL, "initial"); + _nsInfo.setPrefix(initialElem); + + initialElem.setAttribute("generated", "true"); + Element transitionElem = _document.createElementNS(_nsInfo.nsURL, "transition"); + _nsInfo.setPrefix(transitionElem); + + transitionElem.setAttribute("target", histId); + initialElem.appendChild(transitionElem); + _scxml.appendChild(initialElem); + initialTransitions.push_back(transitionElem); + + } else { + // try to get initial transition from initial element + initialTransitions = _xpath.evaluate("/" + _nsInfo.xpathPrefix + "initial/" + _nsInfo.xpathPrefix + "transition", _scxml).asNodeSet(); + if (initialTransitions.size() == 0) { + Arabica::XPath::NodeSet initialStates; + // fetch per draft + initialStates = getInitialStates(); + assert(initialStates.size() > 0); + for (int i = 0; i < initialStates.size(); i++) { + Element initialElem = _document.createElementNS(_nsInfo.nsURL, "initial"); + _nsInfo.setPrefix(initialElem); + + initialElem.setAttribute("generated", "true"); + Element transitionElem = _document.createElementNS(_nsInfo.nsURL, "transition"); + _nsInfo.setPrefix(transitionElem); + + transitionElem.setAttribute("target", ATTR_CAST(initialStates[i], "id")); + initialElem.appendChild(transitionElem); + _scxml.appendChild(initialElem); + initialTransitions.push_back(transitionElem); + } + } + } + return initialTransitions; +} + +InterpreterState InterpreterImpl::step(int waitForMS) { + try { + tthread::lock_guard lock(_mutex); + + if (_state == USCXML_FINISHED || _state == USCXML_DESTROYED) { + return _state; + } + + NodeSet enabledTransitions; + + // setup document and interpreter + if (!_isInitialized) { + init(); // will throw + } + + if (_configuration.size() == 0) { + // goto initial configuration + NodeSet initialTransitions = getDocumentInitialTransitions(); + assert(initialTransitions.size() > 0); +#if 1 + std::cout << _name << ": initialTransitions: " << std::endl; + for (int i = 0; i < initialTransitions.size(); i++) { + std::cout << initialTransitions[i] << std::endl; + } + std::cout << std::endl; +#endif + + enterStates(initialTransitions); + setInterpreterState(USCXML_MICROSTEPPED); + } + + assert(isLegalConfiguration(_configuration)); + + // are there spontaneous transitions? + if (!_stable) { + enabledTransitions = selectEventlessTransitions(); + if (!enabledTransitions.empty()) { + // test 403b + enabledTransitions.to_document_order(); + microstep(enabledTransitions); + + setInterpreterState(USCXML_MICROSTEPPED); + return _state; + } + _stable = true; + } + + // test415 + if (_topLevelFinalReached) + goto EXIT_INTERPRETER; + + // process internal event + if (!_internalQueue.empty()) { + _currEvent = _internalQueue.front(); + _internalQueue.pop_front(); + _stable = false; + + USCXML_MONITOR_CALLBACK2(beforeProcessingEvent, _currEvent) + + _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(USCXML_MICROSTEPPED); + return _state; + + } else { + _stable = true; + } + + if (_state != USCXML_MACROSTEPPED && _state != USCXML_IDLE) + USCXML_MONITOR_CALLBACK(onStableConfiguration) + + setInterpreterState(USCXML_MACROSTEPPED); + + if (_topLevelFinalReached) + goto EXIT_INTERPRETER; + + + // when we reach a stable configuration, invoke + for (unsigned int i = 0; i < _statesToInvoke.size(); i++) { + NodeSet invokes = filterChildElements(_nsInfo.xmlNSPrefix + "invoke", _statesToInvoke[i]); + for (unsigned int j = 0; j < invokes.size(); j++) { + Element invokeElem = Element(invokes[j]); + if (!HAS_ATTR(invokeElem, "persist") || !DOMUtils::attributeIsTrue(ATTR(invokeElem, "persist"))) { + invoke(invokeElem); + } + } + } + _statesToInvoke = NodeSet(); + + if (_externalQueue.isEmpty()) { + setInterpreterState(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; + } + + setInterpreterState(USCXML_MACROSTEPPED); + } + + _currEvent = _externalQueue.pop(); + _currEvent.eventType = Event::EXTERNAL; // make sure it is set to external + _stable = false; + + if (_topLevelFinalReached) + goto EXIT_INTERPRETER; + + USCXML_MONITOR_CALLBACK2(beforeProcessingEvent, _currEvent) + + if (iequals(_currEvent.name, "cancel.invoke." + _sessionId)) { + goto EXIT_INTERPRETER; + } + + try { + _dataModel.setEvent(_currEvent); + } catch (Event e) { + LOG(ERROR) << "Syntax error while setting external event:" << std::endl << e << std::endl << _currEvent; + } + + finalizeAndAutoForwardCurrentEvent(); + + // run internal processing until we reach a stable configuration again + enabledTransitions = selectTransitions(_currEvent.name); + if (!enabledTransitions.empty()) { + // test 403b + enabledTransitions.to_document_order(); + microstep(enabledTransitions); + } + + if (_topLevelFinalReached) + goto EXIT_INTERPRETER; + + return _state; + + EXIT_INTERPRETER: + USCXML_MONITOR_CALLBACK(beforeCompletion) + + exitInterpreter(); + if (_sendQueue) { + std::map >::iterator sendIter = _sendIds.begin(); + while(sendIter != _sendIds.end()) { + _sendQueue->cancelEvent(sendIter->first); + sendIter++; + } + } + + USCXML_MONITOR_CALLBACK(afterCompletion) + + // assert(hasLegalConfiguration()); + _mutex.unlock(); + + // remove datamodel + if(!_userSuppliedDataModel) + _dataModel = DataModel(); + + setInterpreterState(USCXML_FINISHED); + return _state; + } catch (boost::bad_weak_ptr e) { + LOG(ERROR) << "Unclean shutdown " << std::endl << std::endl; + setInterpreterState(USCXML_DESTROYED); + return _state; + } + + // set datamodel to null from this thread + if(_dataModel) + _dataModel = DataModel(); + +} + +void InterpreterImpl::microstep(const Arabica::XPath::NodeSet& enabledTransitions) { + + USCXML_MONITOR_CALLBACK(beforeMicroStep) + + exitStates(enabledTransitions); + + for (int i = 0; i < enabledTransitions.size(); i++) { + Element transition(enabledTransitions[i]); + + USCXML_MONITOR_CALLBACK3(beforeTakingTransition, transition, (i + 1 < enabledTransitions.size())) + + executeContent(transition); + + USCXML_MONITOR_CALLBACK3(afterTakingTransition, transition, (i + 1 < enabledTransitions.size())) + } + + enterStates(enabledTransitions); + + USCXML_MONITOR_CALLBACK(afterMicroStep) + +} + +// process transitions until we are in a stable configuration again +void InterpreterImpl::stabilize() { + + NodeSet enabledTransitions; + _stable = false; + + if (_configuration.size() == 0) { + // goto initial configuration + NodeSet initialTransitions = getDocumentInitialTransitions(); + assert(initialTransitions.size() > 0); + enterStates(initialTransitions); + } + + do { // process microsteps for enabled transitions until there are no more left + + enabledTransitions = selectEventlessTransitions(); + + if (enabledTransitions.size() == 0) { + if (_internalQueue.size() == 0) { + _stable = true; + } else { + _currEvent = _internalQueue.front(); + _internalQueue.pop_front(); +#if VERBOSE + std::cout << "Received internal event " << _currEvent.name << std::endl; +#endif + + 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); + } + } while(!_internalQueue.empty() || !_stable); + + USCXML_MONITOR_CALLBACK(onStableConfiguration) + + // when we reach a stable configuration, invoke + for (unsigned int i = 0; i < _statesToInvoke.size(); i++) { + NodeSet invokes = filterChildElements(_nsInfo.xmlNSPrefix + "invoke", _statesToInvoke[i]); + for (unsigned int j = 0; j < invokes.size(); j++) { + Element invokeElem = Element(invokes[j]); + if (!HAS_ATTR(invokeElem, "persist") || !DOMUtils::attributeIsTrue(ATTR(invokeElem, "persist"))) { + invoke(invokeElem); + } + } + } + _statesToInvoke = NodeSet(); +} + +Arabica::XPath::NodeSet InterpreterImpl::selectTransitions(const std::string& event) { + Arabica::XPath::NodeSet enabledTransitions; + + NodeSet states; + for (unsigned int i = 0; i < _configuration.size(); i++) { + if (isAtomic(Element(_configuration[i]))) + states.push_back(_configuration[i]); + } + states.to_document_order(); + + unsigned int index = 0; + while(states.size() > index) { + NodeSet transitions = filterChildElements(_nsInfo.xmlNSPrefix + "transition", states[index]); + for (unsigned int k = 0; k < transitions.size(); k++) { + if (isEnabledTransition(Element(transitions[k]), event)) { + enabledTransitions.push_back(transitions[k]); + goto LOOP; + } + } + { + Node parent = states[index].getParentNode(); + if (parent) { + states.push_back(parent); + } + } + LOOP: + index++; + } + + enabledTransitions = removeConflictingTransitions(enabledTransitions); + return enabledTransitions; +} + + +Arabica::XPath::NodeSet InterpreterImpl::selectEventlessTransitions() { + Arabica::XPath::NodeSet enabledTransitions; + + NodeSet states; + for (unsigned int i = 0; i < _configuration.size(); i++) { + if (isAtomic(Element(_configuration[i]))) + states.push_back(_configuration[i]); + } + states.to_document_order(); + +#if 0 + std::cout << "Atomic States: "; + for (int i = 0; i < atomicStates.size(); i++) { + std::cout << ATTR(atomicStates[i], "id") << ", "; + } + std::cout << std::endl; +#endif + + unsigned int index = 0; + while(states.size() > index) { + bool foundTransition = false; + NodeSet transitions = filterChildElements(_nsInfo.xmlNSPrefix + "transition", states[index]); + for (unsigned int k = 0; k < transitions.size(); k++) { + Element transElem(transitions[k]); + if (!HAS_ATTR(transElem, "event") && hasConditionMatch(transElem)) { + enabledTransitions.push_back(transitions[k]); + foundTransition = true; + goto LOOP; + } + } + if (!foundTransition) { + Node parent = states[index].getParentNode(); + if (parent) { + states.push_back(parent); + } + } + LOOP: + index++; + } + +#if 0 + std::cout << "Enabled eventless transitions: " << std::endl; + for (int i = 0; i < enabledTransitions.size(); i++) { + std::cout << enabledTransitions[i] << std::endl << "----" << std::endl; + } + std::cout << std::endl; +#endif + + enabledTransitions = removeConflictingTransitions(enabledTransitions); + return enabledTransitions; +} + + +bool InterpreterImpl::isEnabledTransition(const Element& transition, const std::string& event) { + std::string eventName; + if (HAS_ATTR(transition, "event")) { + eventName = ATTR(transition, "event"); + } else if(HAS_ATTR(transition, "eventexpr")) { + if (_dataModel) { + eventName = _dataModel.evalAsString(ATTR(transition, "eventexpr")); + } else { + LOG(ERROR) << "Transition has eventexpr attribute with no datamodel defined"; + return false; + } + } else { + return false; + } + + std::list eventNames = tokenizeIdRefs(eventName); + std::list::iterator eventIter = eventNames.begin(); + while(eventIter != eventNames.end()) { + if(nameMatch(*eventIter, event) && hasConditionMatch(transition)) { + return true; + } + eventIter++; + } + return false; +} + + InterpreterState InterpreterImpl::getInterpreterState() { return _state; } @@ -849,8 +1341,6 @@ void InterpreterImpl::init() { _binding = (HAS_ATTR(_scxml, "binding") && iequals(ATTR(_scxml, "binding"), "late") ? LATE : EARLY); - // @TODO: Reread http://www.w3.org/TR/scxml/#DataBinding - if (_binding == EARLY) { // initialize all data elements NodeSet dataElems = _xpath.evaluate("//" + _nsInfo.xpathPrefix + "data", _scxml).asNodeSet(); @@ -2532,18 +3022,6 @@ bool InterpreterImpl::isAtomic(const Arabica::DOM::Element& state) if (iequals("parallel", LOCALNAME(state))) return false; -#if 0 - Arabica::DOM::Node child = state.getFirstChild(); - while(child) { - if (child.getNodeType() == Node_base::ELEMENT_NODE) { - if (isState(Element(child))) - return false; - } - child = child.getNextSibling(); - } - -#else - Arabica::DOM::NodeList childs = state.getChildNodes(); for (unsigned int i = 0; i < childs.getLength(); i++) { if (childs.item(i).getNodeType() != Node_base::ELEMENT_NODE) @@ -2552,8 +3030,6 @@ bool InterpreterImpl::isAtomic(const Arabica::DOM::Element& state) if (isState(Element(childs.item(i)))) return false; } -#endif - return true; } diff --git a/src/uscxml/Interpreter.h b/src/uscxml/Interpreter.h index b2ea027..1f62255 100644 --- a/src/uscxml/Interpreter.h +++ b/src/uscxml/Interpreter.h @@ -232,12 +232,8 @@ public: void copyTo(boost::shared_ptr other); // TODO: We need to move the destructor to the implementations to make these pure virtual - virtual InterpreterState interpret() { - return _state; ///< Start interpreter blockingly - } - virtual InterpreterState step(int waitForMS = 0) { - return _state; - }; ///< Perform a single step + virtual InterpreterState interpret(); + virtual InterpreterState step(int waitForMS = 0); void start(); ///< Start interpretation in a thread void stop(); ///< Stop interpreter thread @@ -416,6 +412,7 @@ public: Arabica::XPath::NodeSet getStates(const std::list& stateIds); Arabica::XPath::NodeSet getAllStates(); + Arabica::XPath::NodeSet getDocumentInitialTransitions(); Arabica::XPath::NodeSet getInitialStates(Arabica::DOM::Element state = Arabica::DOM::Element()); static Arabica::XPath::NodeSet getChildStates(const Arabica::DOM::Node& state); static Arabica::XPath::NodeSet getChildStates(const Arabica::XPath::NodeSet& state); @@ -458,6 +455,13 @@ protected: void initializeData(const Arabica::DOM::Element& data); void finalizeAndAutoForwardCurrentEvent(); + void stabilize(); + void microstep(const Arabica::XPath::NodeSet& enabledTransitions); + void exitInterpreter(); + + virtual Arabica::XPath::NodeSet selectEventlessTransitions(); + virtual Arabica::XPath::NodeSet selectTransitions(const std::string& event); + virtual bool isEnabledTransition(const Arabica::DOM::Element& transition, const std::string& event); void setInterpreterState(InterpreterState newState); @@ -467,6 +471,13 @@ protected: tthread::condition_variable _condVar; tthread::recursive_mutex _pluginMutex; + // to be overwritten by implementations - these ought to be pure, but impl destructor runs first + virtual void enterStates(const Arabica::XPath::NodeSet& enabledTransitions) {} + virtual void exitStates(const Arabica::XPath::NodeSet& enabledTransitions) {} + virtual Arabica::XPath::NodeSet removeConflictingTransitions(const Arabica::XPath::NodeSet& enabledTransitions) { + return enabledTransitions; + } + InterpreterState _state; URL _baseURI; URL _sourceURI; diff --git a/src/uscxml/interpreter/InterpreterDraft6.cpp b/src/uscxml/interpreter/InterpreterDraft6.cpp index aedf516..7dcb768 100644 --- a/src/uscxml/interpreter/InterpreterDraft6.cpp +++ b/src/uscxml/interpreter/InterpreterDraft6.cpp @@ -33,460 +33,8 @@ using namespace Arabica::DOM; // see: http://www.w3.org/TR/scxml/#AlgorithmforSCXMLInterpretation -InterpreterState InterpreterDraft6::interpret() { - InterpreterState state; - while(true) { - state = step(-1); - - switch (state) { - case uscxml::USCXML_FINISHED: - case uscxml::USCXML_DESTROYED: - // return as we finished - return state; - default: - - // process invokers on main thread - if(_thread == NULL) { - runOnMainThread(200); - } - - // process next step - break; - } - } - return state; -} - -// setup / fetch the documents initial transitions -NodeSet InterpreterDraft6::getDocumentInitialTransitions() { - NodeSet initialTransitions; - - if (_startConfiguration.size() > 0) { - // we emulate entering a given configuration by creating a pseudo deep history - Element initHistory = _document.createElementNS(_nsInfo.nsURL, "history"); - _nsInfo.setPrefix(initHistory); - - initHistory.setAttribute("id", UUID::getUUID()); - initHistory.setAttribute("type", "deep"); - _scxml.insertBefore(initHistory, _scxml.getFirstChild()); - - std::string histId = ATTR(initHistory, "id"); - NodeSet histStates; - for (std::list::const_iterator stateIter = _startConfiguration.begin(); stateIter != _startConfiguration.end(); stateIter++) { - histStates.push_back(getState(*stateIter)); - } - _historyValue[histId] = histStates; - - Element initialElem = _document.createElementNS(_nsInfo.nsURL, "initial"); - _nsInfo.setPrefix(initialElem); - - initialElem.setAttribute("generated", "true"); - Element transitionElem = _document.createElementNS(_nsInfo.nsURL, "transition"); - _nsInfo.setPrefix(transitionElem); - - transitionElem.setAttribute("target", histId); - initialElem.appendChild(transitionElem); - _scxml.appendChild(initialElem); - initialTransitions.push_back(transitionElem); - - } else { - // try to get initial transition from initial element - initialTransitions = _xpath.evaluate("/" + _nsInfo.xpathPrefix + "initial/" + _nsInfo.xpathPrefix + "transition", _scxml).asNodeSet(); - if (initialTransitions.size() == 0) { - Arabica::XPath::NodeSet initialStates; - // fetch per draft - initialStates = getInitialStates(); - assert(initialStates.size() > 0); - for (int i = 0; i < initialStates.size(); i++) { - Element initialElem = _document.createElementNS(_nsInfo.nsURL, "initial"); - _nsInfo.setPrefix(initialElem); - - initialElem.setAttribute("generated", "true"); - Element transitionElem = _document.createElementNS(_nsInfo.nsURL, "transition"); - _nsInfo.setPrefix(transitionElem); - - transitionElem.setAttribute("target", ATTR_CAST(initialStates[i], "id")); - initialElem.appendChild(transitionElem); - _scxml.appendChild(initialElem); - initialTransitions.push_back(transitionElem); - } - } - } - return initialTransitions; -} - -InterpreterState InterpreterDraft6::step(int waitForMS = 0) { - try { - tthread::lock_guard lock(_mutex); - - if (_state == USCXML_FINISHED || _state == USCXML_DESTROYED) { - return _state; - } - - NodeSet enabledTransitions; - - // setup document and interpreter - if (!_isInitialized) { - init(); // will throw - } - - if (_configuration.size() == 0) { - // goto initial configuration - NodeSet initialTransitions = getDocumentInitialTransitions(); - assert(initialTransitions.size() > 0); - enterStates(initialTransitions); - setInterpreterState(USCXML_MICROSTEPPED); - } - - assert(isLegalConfiguration(_configuration)); - - // are there spontaneous transitions? - if (!_stable) { - enabledTransitions = selectEventlessTransitions(); - if (!enabledTransitions.empty()) { - // test 403b - enabledTransitions.to_document_order(); - microstep(enabledTransitions); - - setInterpreterState(USCXML_MICROSTEPPED); - return _state; - } - _stable = true; - } - - // test415 - if (_topLevelFinalReached) - goto EXIT_INTERPRETER; - - // process internal event - if (!_internalQueue.empty()) { - _currEvent = _internalQueue.front(); - _internalQueue.pop_front(); - _stable = false; - - USCXML_MONITOR_CALLBACK2(beforeProcessingEvent, _currEvent) - - _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(USCXML_MICROSTEPPED); - return _state; - - } else { - _stable = true; - } - - if (_state != USCXML_MACROSTEPPED && _state != USCXML_IDLE) - USCXML_MONITOR_CALLBACK(onStableConfiguration) - - setInterpreterState(USCXML_MACROSTEPPED); - - if (_topLevelFinalReached) - goto EXIT_INTERPRETER; - - - // when we reach a stable configuration, invoke - for (unsigned int i = 0; i < _statesToInvoke.size(); i++) { - NodeSet invokes = filterChildElements(_nsInfo.xmlNSPrefix + "invoke", _statesToInvoke[i]); - for (unsigned int j = 0; j < invokes.size(); j++) { - Element invokeElem = Element(invokes[j]); - if (!HAS_ATTR(invokeElem, "persist") || !DOMUtils::attributeIsTrue(ATTR(invokeElem, "persist"))) { - invoke(invokeElem); - } - } - } - _statesToInvoke = NodeSet(); - - if (_externalQueue.isEmpty()) { - setInterpreterState(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; - } - - setInterpreterState(USCXML_MACROSTEPPED); - } - - _currEvent = _externalQueue.pop(); - _currEvent.eventType = Event::EXTERNAL; // make sure it is set to external - _stable = false; - - if (_topLevelFinalReached) - goto EXIT_INTERPRETER; - - USCXML_MONITOR_CALLBACK2(beforeProcessingEvent, _currEvent) - - if (iequals(_currEvent.name, "cancel.invoke." + _sessionId)) { - goto EXIT_INTERPRETER; - } - - try { - _dataModel.setEvent(_currEvent); - } catch (Event e) { - LOG(ERROR) << "Syntax error while setting external event:" << std::endl << e << std::endl << _currEvent; - } - - finalizeAndAutoForwardCurrentEvent(); - - // run internal processing until we reach a stable configuration again - enabledTransitions = selectTransitions(_currEvent.name); - if (!enabledTransitions.empty()) { - // test 403b - enabledTransitions.to_document_order(); - microstep(enabledTransitions); - } - - if (_topLevelFinalReached) - goto EXIT_INTERPRETER; - - return _state; - -EXIT_INTERPRETER: - USCXML_MONITOR_CALLBACK(beforeCompletion) - - exitInterpreter(); - if (_sendQueue) { - std::map >::iterator sendIter = _sendIds.begin(); - while(sendIter != _sendIds.end()) { - _sendQueue->cancelEvent(sendIter->first); - sendIter++; - } - } - - USCXML_MONITOR_CALLBACK(afterCompletion) - -// assert(hasLegalConfiguration()); - _mutex.unlock(); - - // remove datamodel - if(!_userSuppliedDataModel) - _dataModel = DataModel(); - - setInterpreterState(USCXML_FINISHED); - return _state; - } catch (boost::bad_weak_ptr e) { - LOG(ERROR) << "Unclean shutdown " << std::endl << std::endl; - setInterpreterState(USCXML_DESTROYED); - return _state; - } - - // set datamodel to null from this thread - if(_dataModel) - _dataModel = DataModel(); - -} - -// process transitions until we are in a stable configuration again -void InterpreterDraft6::stabilize() { - - NodeSet enabledTransitions; - _stable = false; - - if (_configuration.size() == 0) { - // goto initial configuration - NodeSet initialTransitions = getDocumentInitialTransitions(); - assert(initialTransitions.size() > 0); - enterStates(initialTransitions); - } - - do { // process microsteps for enabled transitions until there are no more left - - enabledTransitions = selectEventlessTransitions(); - - if (enabledTransitions.size() == 0) { - if (_internalQueue.size() == 0) { - _stable = true; - } else { - _currEvent = _internalQueue.front(); - _internalQueue.pop_front(); -#if VERBOSE - std::cout << "Received internal event " << _currEvent.name << std::endl; -#endif - - 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); - } - } while(!_internalQueue.empty() || !_stable); - - USCXML_MONITOR_CALLBACK(onStableConfiguration) - - // when we reach a stable configuration, invoke - for (unsigned int i = 0; i < _statesToInvoke.size(); i++) { - NodeSet invokes = filterChildElements(_nsInfo.xmlNSPrefix + "invoke", _statesToInvoke[i]); - for (unsigned int j = 0; j < invokes.size(); j++) { - Element invokeElem = Element(invokes[j]); - if (!HAS_ATTR(invokeElem, "persist") || !DOMUtils::attributeIsTrue(ATTR(invokeElem, "persist"))) { - invoke(invokeElem); - } - } - } - _statesToInvoke = NodeSet(); - -} - -Arabica::XPath::NodeSet InterpreterDraft6::selectTransitions(const std::string& event) { - Arabica::XPath::NodeSet enabledTransitions; - - NodeSet states; - for (unsigned int i = 0; i < _configuration.size(); i++) { - if (isAtomic(Element(_configuration[i]))) - states.push_back(_configuration[i]); - } - states.to_document_order(); - -#if 0 - std::cout << "Atomic states: " << std::endl; - for (int i = 0; i < states.size(); i++) { - std::cout << states[i] << std::endl << "----" << std::endl; - } - std::cout << std::endl; -#endif - - unsigned int index = 0; - while(states.size() > index) { - NodeSet transitions = filterChildElements(_nsInfo.xmlNSPrefix + "transition", states[index]); - for (unsigned int k = 0; k < transitions.size(); k++) { - if (isEnabledTransition(Element(transitions[k]), event)) { - enabledTransitions.push_back(transitions[k]); - goto LOOP; - } - } - { - Node parent = states[index].getParentNode(); - if (parent) { - states.push_back(parent); - } - } -LOOP: - index++; - } - enabledTransitions = filterPreempted(enabledTransitions); - -#if 0 - std::cout << "Enabled transitions: " << std::endl; - for (int i = 0; i < enabledTransitions.size(); i++) { - std::cout << DOMUtils::xPathForNode(enabledTransitions[i]) << std::endl; - } - std::cout << std::endl; -#endif - - return enabledTransitions; -} - -bool InterpreterDraft6::isEnabledTransition(const Element& transition, const std::string& event) { - std::string eventName; - if (HAS_ATTR(transition, "event")) { - eventName = ATTR(transition, "event"); - } else if(HAS_ATTR(transition, "eventexpr")) { - if (_dataModel) { - eventName = _dataModel.evalAsString(ATTR(transition, "eventexpr")); - } else { - LOG(ERROR) << "Transition has eventexpr attribute with no datamodel defined"; - return false; - } - } else { - return false; - } - - std::list eventNames = tokenizeIdRefs(eventName); - std::list::iterator eventIter = eventNames.begin(); - while(eventIter != eventNames.end()) { - if(nameMatch(*eventIter, event) && hasConditionMatch(transition)) { - return true; - } - eventIter++; - } - return false; -} - -Arabica::XPath::NodeSet InterpreterDraft6::selectEventlessTransitions() { - Arabica::XPath::NodeSet enabledTransitions; - - NodeSet states; - for (unsigned int i = 0; i < _configuration.size(); i++) { - if (isAtomic(Element(_configuration[i]))) - states.push_back(_configuration[i]); - } - states.to_document_order(); - -#if 0 - std::cout << "Atomic States: "; - for (int i = 0; i < atomicStates.size(); i++) { - std::cout << ATTR(atomicStates[i], "id") << ", "; - } - std::cout << std::endl; -#endif - - unsigned int index = 0; - while(states.size() > index) { - bool foundTransition = false; - NodeSet transitions = filterChildElements(_nsInfo.xmlNSPrefix + "transition", states[index]); - for (unsigned int k = 0; k < transitions.size(); k++) { - Element transElem(transitions[k]); - if (!HAS_ATTR(transElem, "event") && hasConditionMatch(transElem)) { - enabledTransitions.push_back(transitions[k]); - foundTransition = true; - goto LOOP; - } - } - if (!foundTransition) { - Node parent = states[index].getParentNode(); - if (parent) { - states.push_back(parent); - } - } -LOOP: - index++; - } - -#if 0 - std::cout << "Enabled eventless transitions: " << std::endl; - for (int i = 0; i < enabledTransitions.size(); i++) { - std::cout << enabledTransitions[i] << std::endl << "----" << std::endl; - } - std::cout << std::endl; -#endif - - enabledTransitions = filterPreempted(enabledTransitions); - return enabledTransitions; -} - -Arabica::XPath::NodeSet InterpreterDraft6::filterPreempted(const Arabica::XPath::NodeSet& enabledTransitions) { +Arabica::XPath::NodeSet InterpreterDraft6::removeConflictingTransitions(const Arabica::XPath::NodeSet& enabledTransitions) { Arabica::XPath::NodeSet filteredTransitions; for (unsigned int i = 0; i < enabledTransitions.size(); i++) { Element t(enabledTransitions[i]); @@ -566,7 +114,6 @@ bool InterpreterDraft6::isWithinParallel(const Element& transition) _transWithinParallel[transition] = lcpa; return _transWithinParallel[transition]; - } Node InterpreterDraft6::findLCPA(const Arabica::XPath::NodeSet& states) { @@ -587,64 +134,6 @@ NEXT_ANCESTOR: return ancestor; } -void InterpreterDraft6::microstep(const Arabica::XPath::NodeSet& enabledTransitions) { -#if VERBOSE - std::cout << "Transitions: "; - for (int i = 0; i < enabledTransitions.size(); i++) { - std::cout << ((Element)getSourceState(enabledTransitions[i])).getAttribute("id") << " -> " << std::endl; - NodeSet targetSet = getTargetStates(enabledTransitions[i]); - for (int j = 0; j < targetSet.size(); j++) { - std::cout << " " << ((Element)targetSet[j]).getAttribute("id") << std::endl; - } - } - std::cout << std::endl; -#endif - - USCXML_MONITOR_CALLBACK(beforeMicroStep) - - exitStates(enabledTransitions); - - for (int i = 0; i < enabledTransitions.size(); i++) { - Element transition(enabledTransitions[i]); - - USCXML_MONITOR_CALLBACK3(beforeTakingTransition, transition, (i + 1 < enabledTransitions.size())) - - executeContent(transition); - - USCXML_MONITOR_CALLBACK3(afterTakingTransition, transition, (i + 1 < enabledTransitions.size())) - } - - enterStates(enabledTransitions); - - USCXML_MONITOR_CALLBACK(afterMicroStep) - -} - -void InterpreterDraft6::exitInterpreter() { -#if VERBOSE - std::cout << "Exiting interpreter " << _name << std::endl; -#endif - NodeSet statesToExit = _configuration; - statesToExit.forward(false); - statesToExit.sort(); - - for (int i = 0; i < statesToExit.size(); i++) { - Arabica::XPath::NodeSet onExitElems = filterChildElements(_nsInfo.xmlNSPrefix + "onexit", statesToExit[i]); - for (int j = 0; j < onExitElems.size(); j++) { - executeContent(Element(onExitElems[j])); - } - Arabica::XPath::NodeSet invokeElems = filterChildElements(_nsInfo.xmlNSPrefix + "invoke", statesToExit[i]); - // TODO: we ought to cancel all remaining invokers just to be sure with the persist extension - for (int j = 0; j < invokeElems.size(); j++) { - cancelInvoke(Element(invokeElems[j])); - } - Element stateElem(statesToExit[i]); - if (isFinal(stateElem) && parentIsScxmlState(stateElem)) { - returnDoneEvent(statesToExit[i]); - } - } - _configuration = NodeSet(); -} void InterpreterDraft6::exitStates(const Arabica::XPath::NodeSet& enabledTransitions) { @@ -737,7 +226,7 @@ void InterpreterDraft6::exitStates(const Arabica::XPath::NodeSet& e #if VERBOSE std::cout << _name << ": History node " << ATTR(historyElem, "id") << " contains: "; for (int i = 0; i < historyNodes.size(); i++) { - std::cout << ATTR(historyNodes[i], "id") << ", "; + std::cout << ATTR_CAST(historyNodes[i], "id") << ", "; } std::cout << std::endl; #endif @@ -801,7 +290,7 @@ void InterpreterDraft6::enterStates(const Arabica::XPath::NodeSet& #if VERBOSE std::cout << _name << ": Target States: "; for (int i = 0; i < tStates.size(); i++) { - std::cout << ATTR(tStates[i], "id") << ", "; + std::cout << ATTR_CAST(tStates[i], "id") << ", "; } std::cout << std::endl; #endif @@ -809,7 +298,7 @@ void InterpreterDraft6::enterStates(const Arabica::XPath::NodeSet& Node ancestor; Node source = getSourceState(transition); #if VERBOSE - std::cout << _name << ": Source States: " << ATTR(source, "id") << std::endl; + std::cout << _name << ": Source States: " << ATTR_CAST(source, "id") << std::endl; #endif assert(source); @@ -833,7 +322,7 @@ void InterpreterDraft6::enterStates(const Arabica::XPath::NodeSet& } #if VERBOSE - std::cout << _name << ": Ancestor: " << ATTR(ancestor, "id") << std::endl; + std::cout << _name << ": Ancestor: " << ATTR_CAST(ancestor, "id") << std::endl; #endif for (int j = 0; j < tStates.size(); j++) { @@ -843,7 +332,7 @@ void InterpreterDraft6::enterStates(const Arabica::XPath::NodeSet& #if VERBOSE std::cout << _name << ": States to enter: "; for (int i = 0; i < statesToEnter.size(); i++) { - std::cout << LOCALNAME(statesToEnter[i]) << ":" << ATTR(statesToEnter[i], "id") << ", "; + std::cout << LOCALNAME(statesToEnter[i]) << ":" << ATTR_CAST(statesToEnter[i], "id") << ", "; } std::cout << std::endl; #endif @@ -852,9 +341,9 @@ void InterpreterDraft6::enterStates(const Arabica::XPath::NodeSet& NodeSet ancestors = getProperAncestors(tStates[j], ancestor); #if VERBOSE - std::cout << _name << ": Proper Ancestors of " << ATTR(tStates[j], "id") << " and " << ATTR(ancestor, "id") << ": "; + std::cout << _name << ": Proper Ancestors of " << ATTR_CAST(tStates[j], "id") << " and " << ATTR_CAST(ancestor, "id") << ": "; for (int i = 0; i < ancestors.size(); i++) { - std::cout << ATTR(ancestors[i], "id") << ", "; + std::cout << ATTR_CAST(ancestors[i], "id") << ", "; } std::cout << std::endl; #endif @@ -885,7 +374,7 @@ void InterpreterDraft6::enterStates(const Arabica::XPath::NodeSet& #if VERBOSE std::cout << _name << ": States to enter: "; for (int i = 0; i < statesToEnter.size(); i++) { - std::cout << ATTR(statesToEnter[i], "id") << ", "; + std::cout << ATTR_CAST(statesToEnter[i], "id") << ", "; } std::cout << std::endl; #endif @@ -998,7 +487,7 @@ void InterpreterDraft6::addStatesToEnter(const Element& state, #if VERBOSE std::cout << "History State " << ATTR(state, "id") << ": "; for (int i = 0; i < historyValue.size(); i++) { - std::cout << ATTR(historyValue[i], "id") << ", "; + std::cout << ATTR_CAST(historyValue[i], "id") << ", "; } std::cout << std::endl; #endif @@ -1010,7 +499,7 @@ void InterpreterDraft6::addStatesToEnter(const Element& state, #if VERBOSE std::cout << "Proper Ancestors: "; for (int i = 0; i < ancestors.size(); i++) { - std::cout << ATTR(ancestors[i], "id") << ", "; + std::cout << ATTR_CAST(ancestors[i], "id") << ", "; } std::cout << std::endl; #endif diff --git a/src/uscxml/interpreter/InterpreterDraft6.h b/src/uscxml/interpreter/InterpreterDraft6.h index 3414e5e..6a1275b 100644 --- a/src/uscxml/interpreter/InterpreterDraft6.h +++ b/src/uscxml/interpreter/InterpreterDraft6.h @@ -29,11 +29,7 @@ public: virtual ~InterpreterDraft6() {}; protected: - virtual InterpreterState interpret(); - virtual InterpreterState step(int blocking); - void stabilize(); - void microstep(const Arabica::XPath::NodeSet& enabledTransitions); void enterStates(const Arabica::XPath::NodeSet& enabledTransitions); void addStatesToEnter(const Arabica::DOM::Element& state, Arabica::XPath::NodeSet& statesToEnter, @@ -41,15 +37,9 @@ protected: Arabica::XPath::NodeSet& defaultHistoryContent); void exitStates(const Arabica::XPath::NodeSet& enabledTransitions); - void exitInterpreter(); - Arabica::XPath::NodeSet selectEventlessTransitions(); - Arabica::XPath::NodeSet selectTransitions(const std::string& event); - Arabica::XPath::NodeSet filterPreempted(const Arabica::XPath::NodeSet& enabledTransitions); + Arabica::XPath::NodeSet removeConflictingTransitions(const Arabica::XPath::NodeSet& enabledTransitions); bool isPreemptingTransition(const Arabica::DOM::Element& t1, const Arabica::DOM::Element& t2); - bool isEnabledTransition(const Arabica::DOM::Element& transition, const std::string& event); - - Arabica::XPath::NodeSet getDocumentInitialTransitions(); bool isCrossingBounds(const Arabica::DOM::Element& transition); bool isWithinParallel(const Arabica::DOM::Element& transition); diff --git a/src/uscxml/interpreter/InterpreterRC.cpp b/src/uscxml/interpreter/InterpreterRC.cpp new file mode 100644 index 0000000..6aea740 --- /dev/null +++ b/src/uscxml/interpreter/InterpreterRC.cpp @@ -0,0 +1,579 @@ +/** + * @file + * @author 2012-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 . + * @endcond + */ + +#include "InterpreterRC.h" + +#include "uscxml/Factory.h" +#include "uscxml/concurrency/DelayedEventQueue.h" + +#include +#include "uscxml/UUID.h" +#include "uscxml/DOMUtils.h" + +namespace uscxml { + +using namespace Arabica::XPath; +using namespace Arabica::DOM; + +size_t padding = 0; + +std::string getPadding() { + std::string pad = ""; + for (int i = 0; i < padding; i++) { + pad += " "; + } + return pad; +} + +Arabica::XPath::NodeSet InterpreterRC::removeConflictingTransitions(const Arabica::XPath::NodeSet& enabledTransitions) { + Arabica::XPath::NodeSet filteredTransitions; + + for (unsigned int i = 0; i < enabledTransitions.size(); i++) { + Element t1(enabledTransitions[i]); + bool t1Preempted = false; + Arabica::XPath::NodeSet transitionsToRemove; + + for (unsigned int j = 0; j < filteredTransitions.size(); j++) { + Element t2(enabledTransitions[j]); + if (hasIntersection(computeExitSet(t1), computeExitSet(t2))) { + if (isDescendant(getSourceState(t1), getSourceState(t2))) { + transitionsToRemove.push_back(t2); + } else { + t1Preempted = true; + break; + } + } + } + + if (!t1Preempted) { + // remove transitionsToRemove from filteredTransitions + std::list > tmp; + for (int i = 0; i < filteredTransitions.size(); i++) { + if (!isMember(filteredTransitions[i], transitionsToRemove)) { + tmp.push_back(filteredTransitions[i]); + } + } + filteredTransitions = NodeSet(); + filteredTransitions.insert(filteredTransitions.end(), tmp.begin(), tmp.end()); + + filteredTransitions.push_back(t1); + } + } + return filteredTransitions; +} + +bool InterpreterRC::hasIntersection(const Arabica::XPath::NodeSet& nodeSet1, const Arabica::XPath::NodeSet& nodeSet2) { + for (unsigned int i = 0; i < nodeSet1.size(); i++) { + for (unsigned int j = 0; j < nodeSet2.size(); j++) { + if (nodeSet1[i] == nodeSet2[j]) + return true; + } + } + return false; +} + + +void InterpreterRC::exitStates(const Arabica::XPath::NodeSet& enabledTransitions) { + NodeSet statesToExit = computeExitSet(enabledTransitions); + + // remove statesToExit from _statesToInvoke + std::list > tmp; + for (int i = 0; i < _statesToInvoke.size(); i++) { + if (!isMember(_statesToInvoke[i], statesToExit)) { + tmp.push_back(_statesToInvoke[i]); + } + } + _statesToInvoke = NodeSet(); + _statesToInvoke.insert(_statesToInvoke.end(), tmp.begin(), tmp.end()); + + statesToExit.forward(false); + statesToExit.sort(); + + for (int i = 0; i < statesToExit.size(); i++) { + NodeSet histories = filterChildElements(_nsInfo.xmlNSPrefix + "history", statesToExit[i]); + for (int j = 0; j < histories.size(); j++) { + Element historyElem = (Element)histories[j]; + std::string historyType = (historyElem.hasAttribute("type") ? historyElem.getAttribute("type") : "shallow"); + NodeSet historyNodes; + for (int k = 0; k < _configuration.size(); k++) { + if (iequals(historyType, "deep")) { + if (isAtomic(Element(_configuration[k])) && isDescendant(_configuration[k], statesToExit[i])) + historyNodes.push_back(_configuration[k]); + } else { + if (_configuration[k].getParentNode() == statesToExit[i]) + historyNodes.push_back(_configuration[k]); + } + } + _historyValue[historyElem.getAttribute("id")] = historyNodes; + } + } + + for (int i = 0; i < statesToExit.size(); i++) { + USCXML_MONITOR_CALLBACK3(beforeExitingState, Element(statesToExit[i]), (i + 1 < statesToExit.size())) + + NodeSet onExits = filterChildElements(_nsInfo.xmlNSPrefix + "onExit", statesToExit[i]); + for (int j = 0; j < onExits.size(); j++) { + Element onExitElem = (Element)onExits[j]; + executeContent(onExitElem); + } + + USCXML_MONITOR_CALLBACK3(afterExitingState, Element(statesToExit[i]), (i + 1 < statesToExit.size())) + + NodeSet invokes = filterChildElements(_nsInfo.xmlNSPrefix + "invoke", statesToExit[i]); + for (int j = 0; j < invokes.size(); j++) { + Element invokeElem = (Element)invokes[j]; + cancelInvoke(invokeElem); + } + + // remove statesToExit[i] from _configuration - test409 + tmp.clear(); + for (int j = 0; j < _configuration.size(); j++) { + if (_configuration[j] != statesToExit[i]) { + tmp.push_back(_configuration[j]); + } + } + _configuration = NodeSet(); + _configuration.insert(_configuration.end(), tmp.begin(), tmp.end()); + } +} + + +Arabica::XPath::NodeSet InterpreterRC::computeExitSet(const Arabica::XPath::NodeSet& transitions) { + NodeSet statesToExit; + for (unsigned int i = 0; i < transitions.size(); i++) { + Element t(transitions[i]); + if (isTargetless(t)) + continue; + Arabica::DOM::Node domain = getTransitionDomain(t); + if (!domain) + continue; + for (unsigned int j = 0; j < _configuration.size(); j++) { + const Node& s = _configuration[j]; + if (isDescendant(s, domain)) { + statesToExit.push_back(s); + } + } + } +#if 0 + std::cout << "computeExitSet: "; + for (int i = 0; i < statesToExit.size(); i++) { + std::cout << ATTR(statesToExit[i], "id") << " "; + } + std::cout << std::endl; +#endif + return statesToExit; +} + +Arabica::XPath::NodeSet InterpreterRC::computeExitSet(const Arabica::DOM::Node& transition) { + Arabica::XPath::NodeSet transitions; + transitions.push_back(transition); + return computeExitSet(transitions); +} + + +void InterpreterRC::enterStates(const Arabica::XPath::NodeSet& enabledTransitions) { + NodeSet statesToEnter; + NodeSet statesForDefaultEntry; + // initialize the temporary table for default content in history states + std::map > defaultHistoryContent; + + computeEntrySet(enabledTransitions, statesToEnter, statesForDefaultEntry, defaultHistoryContent); + statesToEnter.to_document_order(); + + for (int i = 0; i < statesToEnter.size(); i++) { + Element s = (Element)statesToEnter[i]; + + USCXML_MONITOR_CALLBACK3(beforeEnteringState, s, i + 1 < statesToEnter.size()) + + _configuration.push_back(s); + _statesToInvoke.push_back(s); + + // if (_binding == LATE && stateElem.getAttribute("isFirstEntry").size() > 0) { + if (_binding == LATE && !isMember(s, _alreadyEntered)) { + NodeSet dataModelElems = filterChildElements(_nsInfo.xmlNSPrefix + "datamodel", s); + if(dataModelElems.size() > 0 && _dataModel) { + Arabica::XPath::NodeSet dataElems = filterChildElements(_nsInfo.xmlNSPrefix + "data", dataModelElems[0]); + for (int j = 0; j < dataElems.size(); j++) { + if (dataElems[j].getNodeType() == Node_base::ELEMENT_NODE) + initializeData(Element(dataElems[j])); + } + } + _alreadyEntered.push_back(s); + // stateElem.setAttribute("isFirstEntry", ""); + } + // execute onentry executable content + NodeSet onEntryElems = filterChildElements(_nsInfo.xmlNSPrefix + "onEntry", s); + executeContent(onEntryElems, false); + + USCXML_MONITOR_CALLBACK3(afterEnteringState, s, i + 1 < statesToEnter.size()) + + if (isMember(s, statesForDefaultEntry)) { + // execute initial transition content for compound states + Arabica::XPath::NodeSet transitions = _xpath.evaluate("" + _nsInfo.xpathPrefix + "initial/" + _nsInfo.xpathPrefix + "transition", s).asNodeSet(); + executeContent(transitions); + } + if (defaultHistoryContent.find(ATTR(s, "id")) != defaultHistoryContent.end()) { + executeContent(Element(defaultHistoryContent[ATTR(s, "id")])); + } + + if (isFinal(s)) { + internalDoneSend(s); + if (parentIsScxmlState(s)) { + _topLevelFinalReached = true; + } else { + Element parent = (Element)s.getParentNode(); + Element grandParent = (Element)parent.getParentNode(); + + internalDoneSend(parent); + + if (isParallel(grandParent)) { + Arabica::XPath::NodeSet childs = getChildStates(grandParent); + bool inFinalState = true; + for (int j = 0; j < childs.size(); j++) { + if (!isInFinalState(Element(childs[j]))) { + inFinalState = false; + break; + } + } + if (inFinalState) { + internalDoneSend(grandParent); + } + } + } + } + } +} + + +void InterpreterRC::computeEntrySet(const Arabica::XPath::NodeSet& transitions, + NodeSet& statesToEnter, + NodeSet& statesForDefaultEntry, + std::map > defaultHistoryContent) { + +#if 1 + for (int i = 0; i < transitions.size(); i++) { + Element t(transitions[i]); + + NodeSet targets = getTargetStates(t); + +#if 1 + std::cout << "computeEntrySet: "; + for (int i = 0; i < targets.size(); i++) { + std::cout << ATTR_CAST(targets[i], "id") << " "; + } + std::cout << std::endl; +#endif + + + for (int j = 0; j < targets.size(); j++) { + Element s = Element(targets[j]); + addDescendantStatesToEnter(s,statesToEnter,statesForDefaultEntry, defaultHistoryContent); + } + + for (int j = 0; j < targets.size(); j++) { + Element s = Element(targets[j]); + addDescendantStatesToEnter(s,statesToEnter,statesForDefaultEntry, defaultHistoryContent); + } + +#if 1 + std::cout << "after addDescendantStatesToEnter: "; + for (int i = 0; i < statesToEnter.size(); i++) { + std::cout << ATTR_CAST(statesToEnter[i], "id") << " "; + } + std::cout << std::endl; +#endif + + Element ancestor = Element(getTransitionDomain(t)); + NodeSet effectiveTargetStates = getEffectiveTargetStates(t); + + for (int j = 0; j < effectiveTargetStates.size(); j++) { + Element s = Element(effectiveTargetStates[j]); + addAncestorStatesToEnter(s, ancestor, statesToEnter, statesForDefaultEntry, defaultHistoryContent); + } + +#if 1 + std::cout << "after addAncestorStatesToEnter: "; + for (int i = 0; i < statesToEnter.size(); i++) { + std::cout << ATTR_CAST(statesToEnter[i], "id") << " "; + } + std::cout << std::endl; +#endif + + } + + + + +#else + for (int i = 0; i < transitions.size(); i++) { + Element t(transitions[i]); + + NodeSet targets = getTargetStates(t); + for (int j = 0; j < targets.size(); j++) { + if (!isMember(targets[j], statesToEnter)) { + std::cout << "adding: " << ATTR_CAST(anc, "id") << std::endl; + statesToEnter.push_back(targets[j]); + } + } + } + +#if 1 + std::cout << "before addDescendantStatesToEnter: "; + for (int i = 0; i < statesToEnter.size(); i++) { + std::cout << ATTR_CAST(statesToEnter[i], "id") << " "; + } + std::cout << std::endl; +#endif + + NodeSet tmp = statesToEnter; + for (int i = 0; i < tmp.size(); i++) { + assert(tmp[i]); + addDescendantStatesToEnter(tmp[i],statesToEnter,statesForDefaultEntry, defaultHistoryContent); + } + +#if 1 + std::cout << "after addDescendantStatesToEnter: "; + for (int i = 0; i < statesToEnter.size(); i++) { + std::cout << ATTR_CAST(statesToEnter[i], "id") << " "; + } + std::cout << std::endl; +#endif + + for (int i = 0; i < transitions.size(); i++) { + Element t = (Element)transitions[i]; + Node ancestor = getTransitionDomain(t); + NodeSet targets = getTargetStates(t); + for (int j = 0; j < targets.size(); j++) { + const Node& s = targets[j]; + addAncestorStatesToEnter(s, ancestor, statesToEnter, statesForDefaultEntry, defaultHistoryContent); + } + } + +#if 1 + std::cout << "after addAncestorStatesToEnter: "; + for (int i = 0; i < statesToEnter.size(); i++) { + std::cout << ATTR_CAST(statesToEnter[i], "id") << " "; + } + std::cout << std::endl; +#endif + +#endif +} + +Arabica::XPath::NodeSet InterpreterRC::getEffectiveTargetStates(const Arabica::DOM::Element& transition) { + NodeSet effectiveTargets; + NodeSet targets = getTargetStates(transition); + + for (int j = 0; j < targets.size(); j++) { + Element s = Element(targets[j]); + if (isHistory(s)) { + if (_historyValue.find(ATTR(s, "id")) != _historyValue.end()) { + targets.push_back(_historyValue["id"]); + } else { + NodeSet histTrans = filterChildElements(_nsInfo.xmlNSPrefix + "transition", s); + // TODO: what if there are many history transitions? + if (histTrans.size() > 0) + targets.push_back(getEffectiveTargetStates(Element(histTrans[0]))); + } + } else { + effectiveTargets.push_back(s); + } + } + + return effectiveTargets; +} + +void InterpreterRC::computeEntrySet(const Arabica::DOM::Node& transition, + NodeSet& statesToEnter, + NodeSet& statesForDefaultEntry, + std::map > defaultHistoryContent) { + Arabica::XPath::NodeSet transitions; + transitions.push_back(transition); + computeEntrySet(transitions, statesToEnter, statesForDefaultEntry, defaultHistoryContent); +} + +void InterpreterRC::addDescendantStatesToEnter(const Arabica::DOM::Element& state, + Arabica::XPath::NodeSet& statesToEnter, + Arabica::XPath::NodeSet& statesForDefaultEntry, + std::map > defaultHistoryContent) { + + std::cout << getPadding() << "addDescendantStatesToEnter: " << ATTR(state, "id") << std::endl; + padding++; + + if (isHistory(state)) { + + std::string stateId = ATTR(state, "id"); + if (_historyValue.find(stateId) != _historyValue.end()) { + const Arabica::XPath::NodeSet& historyValue = _historyValue[stateId]; + for (int i = 0; i < historyValue.size(); i++) { + const Element& s = Element(historyValue[i]); + addDescendantStatesToEnter(s,statesToEnter,statesForDefaultEntry, defaultHistoryContent); + addAncestorStatesToEnter(s, getParentState(s), statesToEnter, statesForDefaultEntry, defaultHistoryContent); + } + + } else { + NodeSet transitions = filterChildElements(_nsInfo.xmlNSPrefix + "transition", state); + if (transitions.size() > 0) { + // TODO: what if there are many history transitions? + defaultHistoryContent[stateId] = transitions[0]; + } + + for (int i = 0; i < transitions.size(); i++) { + NodeSet targets = getTargetStates(Element(transitions[i])); + for (int j = 0; j < targets.size(); j++) { + const Element& s = Element(targets[i]); + addDescendantStatesToEnter(s,statesToEnter,statesForDefaultEntry, defaultHistoryContent); + addAncestorStatesToEnter(s, getParentState(s), statesToEnter, statesForDefaultEntry, defaultHistoryContent); + } + } + } + } else { + + if (!isMember(state, statesToEnter)) { // adding an existing element invalidates old reference + std::cout << getPadding() << "adding: " << ATTR_CAST(state, "id") << std::endl; + statesToEnter.push_back(state); + } + + if (isCompound(state)) { + statesForDefaultEntry.push_back(state); + + NodeSet targets = getInitialStates(Element(state)); + for (int i = 0; i < targets.size(); i++) { + const Element& s = Element(targets[i]); + addDescendantStatesToEnter(s,statesToEnter,statesForDefaultEntry, defaultHistoryContent); + } + + for (int i = 0; i < targets.size(); i++) { + const Element& s = Element(targets[i]); + addAncestorStatesToEnter(s, state, statesToEnter, statesForDefaultEntry, defaultHistoryContent); + } + + } else if(isParallel(state)) { + NodeSet childStates = getChildStates(state); + + for (int i = 0; i < childStates.size(); i++) { + const Element& child = Element(childStates[i]); + + for (int j = 0; j < statesToEnter.size(); j++) { + const Node& s = statesToEnter[j]; + if (isDescendant(s, child)) { + goto BREAK_LOOP; + } + + } + addDescendantStatesToEnter(child,statesToEnter,statesForDefaultEntry, defaultHistoryContent); +BREAK_LOOP: + ; + } + } + } + padding--; +} + +void InterpreterRC::addAncestorStatesToEnter(const Arabica::DOM::Element& state, + const Arabica::DOM::Element& ancestor, + Arabica::XPath::NodeSet& statesToEnter, + Arabica::XPath::NodeSet& statesForDefaultEntry, + std::map > defaultHistoryContent) { + + std::cout << getPadding() << "addAncestorStatesToEnter: " << ATTR(state, "id") << " - " << ATTR(ancestor, "id") << std::endl; + padding++; + + NodeSet ancestors = getProperAncestors(state, ancestor); + for (int i = 0; i < ancestors.size(); i++) { + const Node& anc = ancestors[i]; + std::cout << getPadding() << "adding: " << ATTR_CAST(anc, "id") << std::endl; + statesToEnter.push_back(anc); + if (isParallel(Element(anc))) { + NodeSet childStates = getChildStates(anc); + for (int j = 0; j < childStates.size(); j++) { + const Element& child = Element(childStates[j]); + for (int k = 0; k < statesToEnter.size(); k++) { + const Node& s = statesToEnter[k]; + if (isDescendant(s, child)) { + goto BREAK_LOOP; + } + } + addDescendantStatesToEnter(child,statesToEnter,statesForDefaultEntry, defaultHistoryContent); +BREAK_LOOP: + ; + } + } + } + padding--; +} + + +Arabica::DOM::Node InterpreterRC::getTransitionDomain(const Arabica::DOM::Element& transition) { +#if 1 + + NodeSet tStates = getEffectiveTargetStates(transition); + Node source = getSourceState(transition); + + if (tStates.size() == 0) { + return Arabica::DOM::Node(); // null + } + std::string transitionType = (HAS_ATTR(transition, "type") ? ATTR(transition, "type") : "external"); + + if (iequals(transitionType, "internal") && isCompound(Element(source))) { + for (int i = 0; i < tStates.size(); i++) { + const Node& s = tStates[i]; + if (!isDescendant(s, source)) + goto BREAK_LOOP; + } + return source; + } + +BREAK_LOOP: + Arabica::XPath::NodeSet states; + states.push_back(source); + states.push_back(tStates); + return findLCCA(states); + +#else + NodeSet tStates = getTargetStates(transition); + Node source = getSourceState(transition); + +#if 0 + std::cout << "getTransitionDomain: " << std::endl << transition << std::endl; +#endif + + if (tStates.size() == 0) { + return Arabica::DOM::Node(); // null + } + std::string transitionType = (HAS_ATTR(transition, "type") ? ATTR(transition, "type") : "external"); + + if (iequals(transitionType, "internal") && isCompound(Element(source))) { + for (int i = 0; i < tStates.size(); i++) { + const Node& s = tStates[i]; + if (!isDescendant(s, source)) + goto BREAK_LOOP; + } + return source; + } +BREAK_LOOP: + ; + Arabica::XPath::NodeSet states; + states.push_back(source); + states.push_back(tStates); + return findLCCA(states); +#endif +} + +} \ No newline at end of file diff --git a/src/uscxml/interpreter/InterpreterRC.cpp.deactivated b/src/uscxml/interpreter/InterpreterRC.cpp.deactivated deleted file mode 100644 index 9993227..0000000 --- a/src/uscxml/interpreter/InterpreterRC.cpp.deactivated +++ /dev/null @@ -1,1292 +0,0 @@ -/** - * @file - * @author 2012-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 . - * @endcond - */ - -#include "InterpreterRC.h" - -#include "uscxml/Factory.h" -#include "uscxml/concurrency/DelayedEventQueue.h" - -#include -#include "uscxml/UUID.h" -#include "uscxml/DOMUtils.h" - -namespace uscxml { - -using namespace Arabica::XPath; -using namespace Arabica::DOM; - -/** -procedure interpret(doc): - if not valid(doc): failWithError() - expandScxmlSource(doc) - configuration = new OrderedSet() - statesToInvoke = new OrderedSet() - internalQueue = new Queue() - externalQueue = new BlockingQueue() - historyValue = new HashTable() - datamodel = new Datamodel(doc) - if doc.binding == "early": - initializeDatamodel(datamodel, doc) - running = true - executeGlobalScriptElement(doc) - enterStates([doc.initial.transition]) - mainEventLoop() - */ -InterpreterState InterpreterRC::interpret() { - try { - tthread::lock_guard lock(_mutex); - if (!_isInitialized) - init(); - - // dump(); - - // just make sure we have a session id - assert(_sessionId.length() > 0); - - setupIOProcessors(); - - std::string datamodelName; - if (datamodelName.length() == 0 && HAS_ATTR(_scxml, "datamodel")) - datamodelName = ATTR(_scxml, "datamodel"); - if (datamodelName.length() == 0 && HAS_ATTR(_scxml, "profile")) // SCION SCXML uses profile to specify datamodel - datamodelName = ATTR(_scxml, "profile"); - if(datamodelName.length() > 0) { - _dataModel = _factory->createDataModel(datamodelName, this); - if (!_dataModel) { - Event e; - e.data.compound["cause"] = Data("Cannot instantiate datamodel", Data::VERBATIM); - throw e; - } - } else { - _dataModel = _factory->createDataModel("null", this); - } - if(datamodelName.length() > 0 && !_dataModel) { - LOG(ERROR) << "No datamodel for " << datamodelName << " registered"; - } - - if (_dataModel) { - _dataModel.assign("_x.args", _cmdLineOptions); - } - - _binding = (HAS_ATTR(_scxml, "binding") && iequals(ATTR(_scxml, "binding"), "late") ? LATE : EARLY); - - // @TODO: Reread http://www.w3.org/TR/scxml/#DataBinding - - if (_dataModel && _binding == EARLY) { - // initialize all data elements - NodeSet dataElems = _xpath.evaluate("//" + _nsInfo.xpathPrefix + "data", _scxml).asNodeSet(); - for (unsigned int i = 0; i < dataElems.size(); i++) { - // do not process data elements of nested documents from invokers - if (!getAncestorElement(dataElems[i], _nsInfo.xmlNSPrefix + "invoke")) - if (dataElems[i].getNodeType() == Node_base::ELEMENT_NODE) { - initializeData(Element(dataElems[i])); - } - } - } else if(_dataModel) { - // initialize current data elements - NodeSet topDataElems = filterChildElements(_nsInfo.xmlNSPrefix + "data", filterChildElements(_nsInfo.xmlNSPrefix + "datamodel", _scxml)); - for (unsigned int i = 0; i < topDataElems.size(); i++) { - if (topDataElems[i].getNodeType() == Node_base::ELEMENT_NODE) - initializeData(Element(topDataElems[i])); - } - } - - // executeGlobalScriptElements - NodeSet globalScriptElems = filterChildElements(_nsInfo.xmlNSPrefix + "script", _scxml); - for (unsigned int i = 0; i < globalScriptElems.size(); i++) { - if (_dataModel) { - executeContent(Element(globalScriptElems[i])); - } - } - - NodeSet initialTransitions; - - if (_startConfiguration.size() > 0) { - // we emulate entering a given configuration by creating a pseudo deep history - Element initHistory = _document.createElementNS(_nsInfo.nsURL, "history"); - _nsInfo.setPrefix(initHistory); - - initHistory.setAttribute("id", UUID::getUUID()); - initHistory.setAttribute("type", "deep"); - _scxml.insertBefore(initHistory, _scxml.getFirstChild()); - - std::string histId = ATTR(initHistory, "id"); - NodeSet histStates; - for (std::list::const_iterator stateIter = _startConfiguration.begin(); stateIter != _startConfiguration.end(); stateIter++) { - histStates.push_back(getState(*stateIter)); - } - _historyValue[histId] = histStates; - - Element initialElem = _document.createElementNS(_nsInfo.nsURL, "initial"); - _nsInfo.setPrefix(initialElem); - - initialElem.setAttribute("generated", "true"); - Element transitionElem = _document.createElementNS(_nsInfo.nsURL, "transition"); - _nsInfo.setPrefix(transitionElem); - - transitionElem.setAttribute("target", histId); - initialElem.appendChild(transitionElem); - _scxml.appendChild(initialElem); - initialTransitions.push_back(transitionElem); - - } else { - // try to get initial transition from initial element - initialTransitions = _xpath.evaluate("/" + _nsInfo.xpathPrefix + "initial/" + _nsInfo.xpathPrefix + "transition", _scxml).asNodeSet(); - if (initialTransitions.size() == 0) { - Arabica::XPath::NodeSet initialStates; - // fetch per draft - initialStates = getInitialStates(); - assert(initialStates.size() > 0); - for (int i = 0; i < initialStates.size(); i++) { - Element initialElem = _document.createElementNS(_nsInfo.nsURL, "initial"); - _nsInfo.setPrefix(initialElem); - - initialElem.setAttribute("generated", "true"); - Element transitionElem = _document.createElementNS(_nsInfo.nsURL, "transition"); - _nsInfo.setPrefix(transitionElem); - - transitionElem.setAttribute("target", ATTR_CAST(initialStates[i], "id")); - initialElem.appendChild(transitionElem); - _scxml.appendChild(initialElem); - initialTransitions.push_back(transitionElem); - } - } - } - - assert(initialTransitions.size() > 0); - - enterStates(initialTransitions); - // _mutex.unlock(); - - // assert(hasLegalConfiguration()); - mainEventLoop(); - } catch (boost::bad_weak_ptr e) { - LOG(ERROR) << "Unclean shutdown " << std::endl << std::endl; - } - // set datamodel to null from this thread - if(_dataModel) - _dataModel = DataModel(); - - return _state; -} - -/** -procedure mainEventLoop(): - while running: - enabledTransitions = null - macrostepDone = false - # Here we handle eventless transitions and transitions - # triggered by internal events until macrostep is complete - while running and not macrostepDone: - enabledTransitions = selectEventlessTransitions() - if enabledTransitions.isEmpty(): - if internalQueue.isEmpty(): - macrostepDone = true - else: - internalEvent = internalQueue.dequeue() - datamodel["_event"] = internalEvent - enabledTransitions = selectTransitions(internalEvent) - if not enabledTransitions.isEmpty(): - microstep(enabledTransitions.toList()) - # either we're in a final state, and we break out of the loop - if not running: - break; - # or we've completed a macrostep, so we start a new macrostep by waiting for an external event - # Here we invoke whatever needs to be invoked. The implementation of 'invoke' is platform-specific - for state in statesToInvoke: - for inv in state.invoke: - invoke(inv) - statesToInvoke.clear() - # Invoking may have raised internal error events and we iterate to handle them - if not internalQueue.isEmpty(): - continue - # A blocking wait for an external event. Alternatively, if we have been invoked - # our parent session also might cancel us. The mechanism for this is platform specific, - # but here we assume it's a special event we receive - externalEvent = externalQueue.dequeue() - if isCancelEvent(externalEvent): - running = false - continue - datamodel["_event"] = externalEvent - for state in configuration: - for inv in state.invoke: - if inv.invokeid == externalEvent.invokeid: - applyFinalize(inv, externalEvent) - if inv.autoforward: - send(inv.id, externalEvent) - enabledTransitions = selectTransitions(externalEvent) - if not enabledTransitions.isEmpty(): - microstep(enabledTransitions.toList()) - # End of outer while running loop. If we get here, we have reached a top-level final state or have been cancelled - exitInterpreter() - */ -void InterpreterRC::mainEventLoop() { - - while(_running) { - NodeSet enabledTransitions; - _stable = false; - - // Here we handle eventless transitions and transitions - // triggered by internal events until machine is stable - while(_running && !_stable) { - - enabledTransitions = selectEventlessTransitions(); - if (enabledTransitions.size() == 0) { - if (_internalQueue.size() == 0) { - _stable = true; - } else { - _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); - } - } - - if (!_running) - goto EXIT_INTERPRETER; - - for (unsigned int i = 0; i < _statesToInvoke.size(); i++) { - NodeSet invokes = filterChildElements(_nsInfo.xmlNSPrefix + "invoke", _statesToInvoke[i]); - for (unsigned int j = 0; j < invokes.size(); j++) { - invoke(invokes[j]); - } - } - _statesToInvoke = NodeSet(); - - if (!_internalQueue.empty()) - continue; - - // assume that we have a legal configuration as soon as the internal queue is empty - if (!hasLegalConfiguration()) { - std::cout << "Illegal configuration!" << std::endl; - for (unsigned int j = 0; j < _configuration.size(); j++) { - std::cout << ATTR_CAST(_configuration[j], "id") << " "; - } - std::cout << std::endl; - } - assert(hasLegalConfiguration()); - - // if (!_sendQueue || _sendQueue->isEmpty()) { - - USCXML_MONITOR_CALLBACK(onStableConfiguration) - - // } - - _mutex.unlock(); - // whenever we have a stable configuration, run the mainThread hooks with 200fps - while(_externalQueue.isEmpty() && _thread == NULL) { - runOnMainThread(200); - } - _mutex.lock(); - - // A blocking wait for an external event. Alternatively, if we have been invoked - // our parent session also might cancel us. The mechanism for this is platform specific, - // but here we assume it's a special event we receive - - while(_externalQueue.isEmpty()) { - _condVar.wait(_mutex); - } - _currEvent = _externalQueue.pop(); -#if 0 - std::cout << "Received externalEvent event " << _currEvent.name << std::endl; -#endif - _currEvent.eventType = Event::EXTERNAL; // make sure it is set to external - if (!_running) - goto EXIT_INTERPRETER; - - USCXML_MONITOR_CALLBACK2(beforeProcessingEvent, _currEvent) - - if (_dataModel && iequals(_currEvent.name, "cancel.invoke." + _sessionId)) - break; - - if (_dataModel) { - try { - _dataModel.setEvent(_currEvent); - } catch (Event e) { - LOG(ERROR) << "Syntax error while setting external event:" << std::endl << e << std::endl << _currEvent; - } - } - for (unsigned int i = 0; i < _configuration.size(); i++) { - NodeSet invokes = filterChildElements(_nsInfo.xmlNSPrefix + "invoke", _configuration[i]); - for (unsigned int j = 0; j < invokes.size(); j++) { - Element invokeElem = (Element)invokes[j]; - std::string invokeId; - if (HAS_ATTR(invokeElem, "id")) { - invokeId = ATTR(invokeElem, "id"); - } else { - if (HAS_ATTR(invokeElem, "idlocation") && _dataModel) { - try { - invokeId = _dataModel.evalAsString(ATTR(invokeElem, "idlocation")); - } catch(Event e) { - LOG(ERROR) << "Syntax error while assigning idlocation from invoke:" << std::endl << e << std::endl; - } - } - } - std::string autoForward = invokeElem.getAttribute("autoforward"); - if (iequals(invokeId, _currEvent.invokeid)) { - - Arabica::XPath::NodeSet finalizes = filterChildElements(_nsInfo.xmlNSPrefix + "finalize", invokeElem); - for (int k = 0; k < finalizes.size(); k++) { - Element finalizeElem = Element(finalizes[k]); - executeContent(finalizeElem); - } - - } - if (iequals(autoForward, "true")) { - 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")) - _invokers[invokeId].send(_currEvent); - } catch(...) { - LOG(ERROR) << "Exception caught while sending event to invoker " << invokeId; - } - } - } - } - enabledTransitions = selectTransitions(_currEvent.name); - if (!enabledTransitions.empty()) { - // test 403b - enabledTransitions.to_document_order(); - microstep(enabledTransitions); - } - } - -EXIT_INTERPRETER: - USCXML_MONITOR_CALLBACK(beforeCompletion) - - exitInterpreter(); - if (_sendQueue) { - std::map >::iterator sendIter = _sendIds.begin(); - while(sendIter != _sendIds.end()) { - _sendQueue->cancelEvent(sendIter->first); - sendIter++; - } - } - - USCXML_MONITOR_CALLBACK(afterCompletion) - -} - -/** -procedure exitInterpreter(): - statesToExit = configuration.toList().sort(exitOrder) - for s in statesToExit: - for content in s.onexit: - executeContent(content) - for inv in s.invoke: - cancelInvoke(inv) - configuration.delete(s) - if isFinalState(s) and isScxmlState(s.parent): - returnDoneEvent(s.donedata) - */ -void InterpreterRC::exitInterpreter() { - NodeSet statesToExit = _configuration; - statesToExit.forward(false); - statesToExit.sort(); - - for (int i = 0; i < statesToExit.size(); i++) { - Arabica::XPath::NodeSet onExitElems = filterChildElements(_nsInfo.xmlNSPrefix + "onexit", statesToExit[i]); - for (int j = 0; j < onExitElems.size(); j++) { - executeContent(Element(onExitElems[j])); - } - Arabica::XPath::NodeSet invokeElems = filterChildElements(_nsInfo.xmlNSPrefix + "invoke", statesToExit[i]); - for (int j = 0; j < invokeElems.size(); j++) { - cancelInvoke(invokeElems[j]); - } - Element stateElem(statesToExit[i]); - if (isFinal(stateElem) && parentIsScxmlState(stateElem)) { - returnDoneEvent(stateElem); - } - } - _configuration = NodeSet(); -} - -/** -function selectEventlessTransitions(): - enabledTransitions = new OrderedSet() - atomicStates = configuration.toList().filter(isAtomicState).sort(documentOrder) - for state in atomicStates: - loop: for s in [state].append(getProperAncestors(state, null)): - for t in s.transition: - if not t.event and conditionMatch(t): - enabledTransitions.add(t) - break loop - enabledTransitions = removeConflictingTransitions(enabledTransitions) - return enabledTransitions - */ -Arabica::XPath::NodeSet InterpreterRC::selectEventlessTransitions() { - Arabica::XPath::NodeSet enabledTransitions; - - NodeSet atomicStates; - for (unsigned int i = 0; i < _configuration.size(); i++) { - if (isAtomic(Element(_configuration[i]))) - atomicStates.push_back(_configuration[i]); - } - atomicStates.to_document_order(); - - for (unsigned int i = 0; i < atomicStates.size(); i++) { - const Node& state = atomicStates[i]; - NodeSet withAncestors; - withAncestors.push_back(state); - withAncestors.push_back(getProperAncestors(state, Node())); - for (unsigned int j = 0; j < withAncestors.size(); j++) { - const Node& ancestor = withAncestors[i]; - NodeSet transitions = filterChildElements(_nsInfo.xmlNSPrefix + "transition", ancestor); - for (unsigned int k = 0; k < transitions.size(); k++) { - Element transElem(transitions[k]); - if (!HAS_ATTR(transElem, "event") && hasConditionMatch(transElem)) { - enabledTransitions.push_back(transitions[k]); - goto BREAK_LOOP; - } - } - } -BREAK_LOOP: - ; - } - - enabledTransitions = removeConflictingTransitions(enabledTransitions); - return enabledTransitions; -} - -/** -function selectTransitions(event): - enabledTransitions = new OrderedSet() - atomicStates = configuration.toList().filter(isAtomicState).sort(documentOrder) - for state in atomicStates: - loop: for s in [state].append(getProperAncestors(state, null)): - for t in s.transition: - if t.event and nameMatch(t.event, event.name) and conditionMatch(t): - enabledTransitions.add(t) - break loop - enabledTransitions = removeConflictingTransitions(enabledTransitions) - return enabledTransitions - */ -Arabica::XPath::NodeSet InterpreterRC::selectTransitions(const std::string& event) { - Arabica::XPath::NodeSet enabledTransitions; - - NodeSet atomicStates; - for (unsigned int i = 0; i < _configuration.size(); i++) { - if (isAtomic(Element(_configuration[i]))) - atomicStates.push_back(_configuration[i]); - } - atomicStates.to_document_order(); - -#if 0 - std::cout << "selectTransitions for " << event << "========" << std::endl; -#endif - for (unsigned int i = 0; i < atomicStates.size(); i++) { - const Node& state = atomicStates[i]; -#if 0 - std::cout << " == from " << ATTR(state, "id") << std::endl; -#endif - - NodeSet withAncestors; - withAncestors.push_back(state); - withAncestors.push_back(getProperAncestors(state, Node())); - for (unsigned int j = 0; j < withAncestors.size(); j++) { - const Node& ancestor = withAncestors[j]; - NodeSet transitions = filterChildElements(_nsInfo.xmlNSPrefix + "transition", ancestor); - for (unsigned int k = 0; k < transitions.size(); k++) { - if (isEnabledTransition(Element(transitions[k]), event)) { - enabledTransitions.push_back(transitions[k]); - goto BREAK_LOOP; - } - } - } -BREAK_LOOP: - ; - } - - enabledTransitions = removeConflictingTransitions(enabledTransitions); - -#if 0 - std::cout << "enabledTransitions ========" << std::endl; - for (unsigned int k = 0; k < enabledTransitions.size(); k++) { - std::cout << enabledTransitions[k]; - } - std::cout << std::endl; - std::cout << "======== enabledTransitions" << std::endl; -#endif - return enabledTransitions; -} - -bool InterpreterRC::isEnabledTransition(const Element& transition, const std::string& event) { - std::string eventName; - if (HAS_ATTR(transition, "event")) { - eventName = ATTR(transition, "event"); - } else if(HAS_ATTR(transition, "eventexpr")) { - if (_dataModel) { - eventName = _dataModel.evalAsString(ATTR(transition, "eventexpr")); - } else { - LOG(ERROR) << "Transition has eventexpr attribute with no datamodel defined"; - return false; - } - } else { - return false; - } - - std::list eventNames = tokenizeIdRefs(eventName); - std::list::iterator eventIter = eventNames.begin(); - while(eventIter != eventNames.end()) { - if(nameMatch(*eventIter, event) && hasConditionMatch(transition)) { - return true; - } - eventIter++; - } - return false; -} - - -/** -function removeConflictingTransitions(enabledTransitions): - filteredTransitions = new OrderedSet() - // toList sorts the transitions in the order of the states that selected them - for t1 in enabledTransitions.toList(): - t1Preempted = false; - transitionsToRemove = new OrderedSet() - for t2 in filteredTransitions.toList(): - if computeExitSet([t1]).hasIntersection(computeExitSet([t2])): - if isDescendant(t1.source, t2.source): - transitionsToRemove.add(t2) - else: - t1Preempted = true - break - if not t1Preempted: - for t3 in transitionsToRemove.toList(): - filteredTransitions.delete(t3) - filteredTransitions.add(t1) - - return filteredTransitions - */ -Arabica::XPath::NodeSet InterpreterRC::removeConflictingTransitions(const Arabica::XPath::NodeSet& enabledTransitions) { - Arabica::XPath::NodeSet filteredTransitions; - - for (unsigned int i = 0; i < enabledTransitions.size(); i++) { - Element t1(enabledTransitions[i]); - bool t1Preempted = false; - Arabica::XPath::NodeSet transitionsToRemove; - - for (unsigned int j = 0; j < filteredTransitions.size(); j++) { - Element t2(enabledTransitions[j]); - if (hasIntersection(computeExitSet(t1), computeExitSet(t2))) { - if (isDescendant(getSourceState(t1), getSourceState(t2))) { - transitionsToRemove.push_back(t2); - } else { - t1Preempted = true; - break; - } - } - } - - if (!t1Preempted) { - // remove transitionsToRemove from filteredTransitions - std::list > tmp; - for (int i = 0; i < filteredTransitions.size(); i++) { - if (!isMember(filteredTransitions[i], transitionsToRemove)) { - tmp.push_back(filteredTransitions[i]); - } - } - filteredTransitions = NodeSet(); - filteredTransitions.insert(_statesToInvoke.end(), tmp.begin(), tmp.end()); - - filteredTransitions.push_back(t1); - } - } - return filteredTransitions; -} - -bool InterpreterRC::hasIntersection(const Arabica::XPath::NodeSet& nodeSet1, const Arabica::XPath::NodeSet& nodeSet2) { - for (unsigned int i = 0; i < nodeSet1.size(); i++) { - for (unsigned int j = 0; j < nodeSet2.size(); j++) { - if (nodeSet1[i] == nodeSet2[j]) - return true; - } - } - return false; -} - -/** -procedure microstep(enabledTransitions): - exitStates(enabledTransitions) - executeTransitionContent(enabledTransitions) - enterStates(enabledTransitions) - */ -void InterpreterRC::microstep(const Arabica::XPath::NodeSet& enabledTransitions) { - - USCXML_MONITOR_CALLBACK(beforeMicroStep) - - exitStates(enabledTransitions); - - for (int i = 0; i < enabledTransitions.size(); i++) { - Element transition(enabledTransitions[i]); - - USCXML_MONITOR_CALLBACK3(beforeTakingTransition, transition, (i + 1 < enabledTransitions.size())) - - executeContent(transition); - - USCXML_MONITOR_CALLBACK3(afterTakingTransition, transition, (i + 1 < enabledTransitions.size())) - } - - enterStates(enabledTransitions); - - USCXML_MONITOR_CALLBACK(afterMicroStep) -} - - -/** -procedure exitStates(enabledTransitions): - statesToExit = computeExitSet(enabledTransitions) - for s in statesToExit: - statesToInvoke.delete(s) - statesToExit = statesToExit.toList().sort(exitOrder) - for s in statesToExit: - for h in s.history: - if h.type == "deep": - f = lambda s0: isAtomicState(s0) and isDescendant(s0,s) - else: - f = lambda s0: s0.parent == s - historyValue[h.id] = configuration.toList().filter(f) - for s in statesToExit: - for content in s.onexit: - executeContent(content) - for inv in s.invoke: - cancelInvoke(inv) - configuration.delete(s) - */ -void InterpreterRC::exitStates(const Arabica::XPath::NodeSet& enabledTransitions) { - NodeSet statesToExit = computeExitSet(enabledTransitions); - - // remove statesToExit from _statesToInvoke - std::list > tmp; - for (int i = 0; i < _statesToInvoke.size(); i++) { - if (!isMember(_statesToInvoke[i], statesToExit)) { - tmp.push_back(_statesToInvoke[i]); - } - } - _statesToInvoke = NodeSet(); - _statesToInvoke.insert(_statesToInvoke.end(), tmp.begin(), tmp.end()); - - statesToExit.forward(false); - statesToExit.sort(); - - for (int i = 0; i < statesToExit.size(); i++) { - NodeSet histories = filterChildElements(_nsInfo.xmlNSPrefix + "history", statesToExit[i]); - for (int j = 0; j < histories.size(); j++) { - Element historyElem = (Element)histories[j]; - std::string historyType = (historyElem.hasAttribute("type") ? historyElem.getAttribute("type") : "shallow"); - NodeSet historyNodes; - for (int k = 0; k < _configuration.size(); k++) { - if (iequals(historyType, "deep")) { - if (isAtomic(Element(_configuration[k])) && isDescendant(_configuration[k], statesToExit[i])) - historyNodes.push_back(_configuration[k]); - } else { - if (_configuration[k].getParentNode() == statesToExit[i]) - historyNodes.push_back(_configuration[k]); - } - } - _historyValue[historyElem.getAttribute("id")] = historyNodes; - } - } - - for (int i = 0; i < statesToExit.size(); i++) { - USCXML_MONITOR_CALLBACK3(beforeExitingState, Element(statesToExit[i]), (i + 1 < statesToExit.size())) - - NodeSet onExits = filterChildElements(_nsInfo.xmlNSPrefix + "onExit", statesToExit[i]); - for (int j = 0; j < onExits.size(); j++) { - Element onExitElem = (Element)onExits[j]; - executeContent(onExitElem); - } - - USCXML_MONITOR_CALLBACK3(afterExitingState, Element(statesToExit[i]), (i + 1 < statesToExit.size())) - - NodeSet invokes = filterChildElements(_nsInfo.xmlNSPrefix + "invoke", statesToExit[i]); - for (int j = 0; j < invokes.size(); j++) { - Element invokeElem = (Element)invokes[j]; - cancelInvoke(invokeElem); - } - - // remove statesToExit[i] from _configuration - test409 - tmp.clear(); - for (int j = 0; j < _configuration.size(); j++) { - if (_configuration[j] != statesToExit[i]) { - tmp.push_back(_configuration[j]); - } - } - _configuration = NodeSet(); - _configuration.insert(_configuration.end(), tmp.begin(), tmp.end()); - } -} - - -/** -function computeExitSet(transitions) - statesToExit = new OrderedSet - for t in transitions: - if(getTargetSet(t.target)): - domain = getTransitionDomain(t) - for s in configuration: - if isDescendant(s,domain): - statesToExit.add(s) - return statesToExit - */ -Arabica::XPath::NodeSet InterpreterRC::computeExitSet(const Arabica::XPath::NodeSet& transitions) { - NodeSet statesToExit; - for (unsigned int i = 0; i < transitions.size(); i++) { - Element t(transitions[i]); - if (isTargetless(t)) - continue; - Arabica::DOM::Node domain = getTransitionDomain(t); - if (!domain) - continue; - for (unsigned int j = 0; j < _configuration.size(); j++) { - const Node& s = _configuration[j]; - if (isDescendant(s, domain)) { - statesToExit.push_back(s); - } - } - } -#if 0 - std::cout << "computeExitSet: "; - for (int i = 0; i < statesToExit.size(); i++) { - std::cout << ATTR(statesToExit[i], "id") << " "; - } - std::cout << std::endl; -#endif - return statesToExit; -} - -Arabica::XPath::NodeSet InterpreterRC::computeExitSet(const Arabica::DOM::Node& transition) { - Arabica::XPath::NodeSet transitions; - transitions.push_back(transition); - return computeExitSet(transitions); -} - - -/** -procedure enterStates(enabledTransitions): - statesToEnter = new OrderedSet() - statesForDefaultEntry = new OrderedSet() - computeEntrySet(enabledTransitions, statesToEnter, statesForDefaultEntry) - for s in statesToEnter.toList().sort(entryOrder): - configuration.add(s) - statesToInvoke.add(s) - if binding == "late" and s.isFirstEntry: - initializeDataModel(datamodel.s,doc.s) - s.isFirstEntry = false - for content in s.onentry: - executeContent(content) - if statesForDefaultEntry.isMember(s): - executeContent(s.initial.transition) - if isFinalState(s): - if isSCXMLElement(s.parent): - running = false - else: - parent = s.parent - grandparent = parent.parent - internalQueue.enqueue(new Event("done.state." + parent.id, s.donedata)) - if isParallelState(grandparent): - if getChildStates(grandparent).every(isInFinalState): - internalQueue.enqueue(new Event("done.state." + grandparent.id)) - */ -void InterpreterRC::enterStates(const Arabica::XPath::NodeSet& enabledTransitions) { - NodeSet statesToEnter; - NodeSet statesForDefaultEntry; - // initialize the temporary table for default content in history states - std::map > defaultHistoryContent; - - computeEntrySet(enabledTransitions, statesToEnter, statesForDefaultEntry, defaultHistoryContent); - statesToEnter.to_document_order(); - - for (int i = 0; i < statesToEnter.size(); i++) { - Element s = (Element)statesToEnter[i]; - - USCXML_MONITOR_CALLBACK3(beforeEnteringState, s, i + 1 < statesToEnter.size()) - - _configuration.push_back(s); - _statesToInvoke.push_back(s); - - // if (_binding == LATE && stateElem.getAttribute("isFirstEntry").size() > 0) { - if (_binding == LATE && !isMember(s, _alreadyEntered)) { - NodeSet dataModelElems = filterChildElements(_nsInfo.xmlNSPrefix + "datamodel", s); - if(dataModelElems.size() > 0 && _dataModel) { - Arabica::XPath::NodeSet dataElems = filterChildElements(_nsInfo.xmlNSPrefix + "data", dataModelElems[0]); - for (int j = 0; j < dataElems.size(); j++) { - if (dataElems[j].getNodeType() == Node_base::ELEMENT_NODE) - initializeData(Element(dataElems[j])); - } - } - _alreadyEntered.push_back(s); - // stateElem.setAttribute("isFirstEntry", ""); - } - // execute onentry executable content - NodeSet onEntryElems = filterChildElements(_nsInfo.xmlNSPrefix + "onEntry", s); - executeContent(onEntryElems, false); - - USCXML_MONITOR_CALLBACK3(afterEnteringState, s, i + 1 < statesToEnter.size()) - - if (isMember(s, statesForDefaultEntry)) { - // execute initial transition content for compound states - Arabica::XPath::NodeSet transitions = _xpath.evaluate("" + _nsInfo.xpathPrefix + "initial/" + _nsInfo.xpathPrefix + "transition", s).asNodeSet(); - executeContent(transitions); - } - if (defaultHistoryContent.find(ATTR(s, "id")) != defaultHistoryContent.end()) { - executeContent(Element(defaultHistoryContent[ATTR(s, "id")])); - } - - /** - if isFinalState(s): - if isSCXMLElement(s.parent): - running = false - else: - parent = s.parent - grandparent = parent.parent - internalQueue.enqueue(new Event("done.state." + parent.id, s.donedata)) - if isParallelState(grandparent): - if getChildStates(grandparent).every(isInFinalState): - internalQueue.enqueue(new Event("done.state." + grandparent.id)) - */ - //std::cout << _name << ": " << s << std::endl; - - if (isFinal(s)) { - internalDoneSend(s); - if (parentIsScxmlState(s)) { - _running = false; - _topLevelFinalReached = true; - } else { - Element parent = (Element)s.getParentNode(); - Element grandParent = (Element)parent.getParentNode(); - - internalDoneSend(parent); - - if (isParallel(grandParent)) { - Arabica::XPath::NodeSet childs = getChildStates(grandParent); - bool inFinalState = true; - for (int j = 0; j < childs.size(); j++) { - if (!isInFinalState(childs[j])) { - inFinalState = false; - break; - } - } - if (inFinalState) { - internalDoneSend(grandParent); - } - } - } - } - } -} - - -/** -procedure computeEntrySet(transitions, statesToEnter, statesForDefaultEntry) - for t in transitions: - statesToEnter.union(getTargetStates(t.target)) - for s in statesToEnter: - addDescendantStatesToEnter(s,statesToEnter,statesForDefaultEntry) - for t in transitions: - ancestor = getTransitionDomain(t) - for s in getTargetStates(t.target)): - addAncestorStatesToEnter(s, ancestor, statesToEnter, statesForDefaultEntry) - */ -void InterpreterRC::computeEntrySet(const Arabica::XPath::NodeSet& transitions, - NodeSet& statesToEnter, - NodeSet& statesForDefaultEntry, - std::map > defaultHistoryContent) { - for (int i = 0; i < transitions.size(); i++) { - Element t(transitions[i]); - - NodeSet targets = getTargetStates(t); - for (int j = 0; j < targets.size(); j++) { - if (!isMember(targets[j], statesToEnter)) { - statesToEnter.push_back(targets[j]); - } - } - } - -#if 0 - std::cout << "before addDescendantStatesToEnter: "; - for (int i = 0; i < statesToEnter.size(); i++) { - std::cout << ATTR(statesToEnter[i], "id") << " "; - } - std::cout << std::endl; -#endif - - NodeSet tmp = statesToEnter; - for (int i = 0; i < tmp.size(); i++) { - assert(tmp[i]); - addDescendantStatesToEnter(tmp[i],statesToEnter,statesForDefaultEntry, defaultHistoryContent); - } - -#if 0 - std::cout << "after addDescendantStatesToEnter: "; - for (int i = 0; i < statesToEnter.size(); i++) { - std::cout << ATTR(statesToEnter[i], "id") << " "; - } - std::cout << std::endl; -#endif - - for (int i = 0; i < transitions.size(); i++) { - Element t = (Element)transitions[i]; - Node ancestor = getTransitionDomain(t); - NodeSet targets = getTargetStates(t); - for (int j = 0; j < targets.size(); j++) { - const Node& s = targets[j]; - addAncestorStatesToEnter(s, ancestor, statesToEnter, statesForDefaultEntry, defaultHistoryContent); - } - } - -#if 0 - std::cout << "after addAncestorStatesToEnter: "; - for (int i = 0; i < statesToEnter.size(); i++) { - std::cout << ATTR(statesToEnter[i], "id") << " "; - } - std::cout << std::endl; -#endif -} - -void InterpreterRC::computeEntrySet(const Arabica::DOM::Node& transition, - NodeSet& statesToEnter, - NodeSet& statesForDefaultEntry, - std::map > defaultHistoryContent) { - Arabica::XPath::NodeSet transitions; - transitions.push_back(transition); - computeEntrySet(transitions, statesToEnter, statesForDefaultEntry, defaultHistoryContent); -} - - -/** -procedure addDescendantStatesToEnter(state,statesToEnter,statesForDefaultEntry): - if isHistoryState(state): - if historyValue[state.id]: - for s in historyValue[state.id]: - addDescendantStatesToEnter(s,statesToEnter,statesForDefaultEntry) - addAncestorStatesToEnter(s, state.parent, statesToEnter, statesForDefaultEntry) - else: - for t in state.transition: - for s in getTargetStates(t.target): - addDescendantStatesToEnter(s,statesToEnter,statesForDefaultEntry) - addAncestorStatesToEnter(s, state.parent, statesToEnter, statesForDefaultEntry) - else: - statesToEnter.add(state) - if isCompoundState(state): - statesForDefaultEntry.add(state) - for s in getTargetStates(state.initial): - addDescendantStatesToEnter(s,statesToEnter,statesForDefaultEntry) - addAncestorStatesToEnter(s, state, statesToEnter, statesForDefaultEntry) - else: - if isParallelState(state): - for child in getChildStates(state): - if not statesToEnter.some(lambda s: isDescendant(s,child)): - addDescendantStatesToEnter(child,statesToEnter,statesForDefaultEntry) - */ -void InterpreterRC::addDescendantStatesToEnter(const Arabica::DOM::Node& state, - Arabica::XPath::NodeSet& statesToEnter, - Arabica::XPath::NodeSet& statesForDefaultEntry, - std::map > defaultHistoryContent) { - if (isHistory(Element(state))) { - std::string stateId = ATTR(state, "id"); - if (_historyValue.find(stateId) != _historyValue.end()) { - const Arabica::XPath::NodeSet& historyValue = _historyValue[stateId]; - for (int i = 0; i < historyValue.size(); i++) { - const Node& s = historyValue[i]; - addDescendantStatesToEnter(s,statesToEnter,statesForDefaultEntry, defaultHistoryContent); - addAncestorStatesToEnter(s, getParentState(s), statesToEnter, statesForDefaultEntry, defaultHistoryContent); - } - } else { - NodeSet transitions = filterChildElements(_nsInfo.xmlNSPrefix + "transition", state); - if (transitions.size() > 0) { - defaultHistoryContent[ATTR(state, "id")] = transitions[0]; - } - if (transitions.size() > 1) { - LOG(ERROR) << "Only one transition allowed in history"; - } - for (int i = 0; i < transitions.size(); i++) { - NodeSet targets = getTargetStates(Element(transitions[i])); - for (int j = 0; j < targets.size(); j++) { - const Node& s = targets[i]; - addDescendantStatesToEnter(s,statesToEnter,statesForDefaultEntry, defaultHistoryContent); - addAncestorStatesToEnter(s, getParentState(s), statesToEnter, statesForDefaultEntry, defaultHistoryContent); - } - } - } - } else { - if (!isMember(state, statesToEnter)) // adding an existing element invalidates old reference - statesToEnter.push_back(state); - - if (isCompound(Element(state))) { - statesForDefaultEntry.push_back(state); - NodeSet targets = getInitialStates(Element(state)); - for (int i = 0; i < targets.size(); i++) { - const Node& s = targets[i]; - addDescendantStatesToEnter(s,statesToEnter,statesForDefaultEntry, defaultHistoryContent); - addAncestorStatesToEnter(s, getParentState(s), statesToEnter, statesForDefaultEntry, defaultHistoryContent); - } - } else if(isParallel(Element(state))) { - // if state is a parallel state, recursively call addStatesToEnter on any of its child - // states that don't already have a descendant on statesToEnter. - NodeSet childStates = getChildStates(state); - for (int i = 0; i < childStates.size(); i++) { - const Node& child = childStates[i]; - for (int j = 0; j < statesToEnter.size(); j++) { - const Node& s = statesToEnter[j]; - if (isDescendant(s, child)) { - goto BREAK_LOOP; - } - } - addDescendantStatesToEnter(child,statesToEnter,statesForDefaultEntry, defaultHistoryContent); -BREAK_LOOP: - ; - } - } - } -} - - -/** -procedure addAncestorStatesToEnter(state, ancestor, statesToEnter, statesForDefaultEntry) - for anc in getProperAncestors(state,ancestor): - statesToEnter.add(anc) - if isParallelState(anc): - for child in getChildStates(anc): - if not statesToEnter.some(lambda s: isDescendant(s,child)): - addDescendantStatesToEnter(child,statesToEnter,statesForDefaultEntry) - */ -void InterpreterRC::addAncestorStatesToEnter(const Arabica::DOM::Node& state, - const Arabica::DOM::Node& ancestor, - Arabica::XPath::NodeSet& statesToEnter, - Arabica::XPath::NodeSet& statesForDefaultEntry, - std::map > defaultHistoryContent) { - NodeSet ancestors = getProperAncestors(state,ancestor); - for (int i = 0; i < ancestors.size(); i++) { - const Node& anc = ancestors[i]; - statesToEnter.push_back(anc); - if (isParallel(Element(anc))) { - NodeSet childStates = getChildStates(anc); - for (int j = 0; j < childStates.size(); j++) { - const Node& child = childStates[j]; - for (int k = 0; k < statesToEnter.size(); k++) { - const Node& s = statesToEnter[k]; - if (isDescendant(s, child)) { - goto BREAK_LOOP; - } - } - addDescendantStatesToEnter(child,statesToEnter,statesForDefaultEntry, defaultHistoryContent); -BREAK_LOOP: - ; - } - } - } -} - -/** -function isInFinalState(s): - if isCompoundState(s): - return getChildStates(s).some(lambda s: isFinalState(s) and configuration.isMember(s)) - elif isParallelState(s): - return getChildStates(s).every(isInFinalState) - else: - return false -*/ -bool InterpreterRC::isInFinalState(const Arabica::DOM::Node& state) { - if (isCompound(Element(state))) { - Arabica::XPath::NodeSet childs = getChildStates(state); - for (int i = 0; i < childs.size(); i++) { - if (isFinal(Element(childs[i])) && isMember(childs[i], _configuration)) - return true; - } - } else if (isParallel(Element(state))) { - Arabica::XPath::NodeSet childs = getChildStates(state); - for (int i = 0; i < childs.size(); i++) { - if (!isInFinalState(childs[i])) - return false; - } - return true; - } - return false; -} - - -/** -function getTransitionDomain(t) - tstates = getTargetStates(t.target) - if not tstates - return t.source - elif t.type == "internal" and isCompoundState(t.source) and tstates.every(lambda s: isDescendant(s,t.source)): - return t.source - else: - return findLCCA([t.source].append(tstates)) - */ -Arabica::DOM::Node InterpreterRC::getTransitionDomain(const Arabica::DOM::Element& transition) { - NodeSet tStates = getTargetStates(transition); - Node source = getSourceState(transition); - -#if 0 - std::cout << "getTransitionDomain: " << std::endl << transition << std::endl; -#endif - - if (tStates.size() == 0) { - return Arabica::DOM::Node(); // null - } - std::string transitionType = (HAS_ATTR(transition, "type") ? ATTR(transition, "type") : "external"); - - if (iequals(transitionType, "internal") && isCompound(Element(source))) { - for (int i = 0; i < tStates.size(); i++) { - const Node& s = tStates[i]; - if (!isDescendant(s, source)) - goto BREAK_LOOP; - } - return source; - } -BREAK_LOOP: - ; - Arabica::XPath::NodeSet states; - states.push_back(source); - states.push_back(tStates); - return findLCCA(states); -} - - -/** -function findLCCA(stateList): - for anc in getProperAncestors(stateList.head(),null).filter(isCompoundStateOrScxmlElement): - if stateList.tail().every(lambda s: isDescendant(s,anc)): - return anc - */ -Arabica::DOM::Node InterpreterRC::findLCCA(const Arabica::XPath::NodeSet& states) { - Arabica::XPath::NodeSet ancestors = getProperAncestors(states[0], Arabica::DOM::Node()); - // ancestors.push_back(states[0]); // state[0] may already be the ancestor - bug in W3C spec? - Arabica::DOM::Node ancestor; - for (int i = 0; i < ancestors.size(); i++) { - if (!isCompound(Element(ancestors[i]))) - continue; - for (int j = 0; j < states.size(); j++) { - if (!isDescendant(states[j], ancestors[i]) && (states[j] != ancestors[i])) - goto NEXT_ANCESTOR; - } - ancestor = ancestors[i]; - break; -NEXT_ANCESTOR: - ; - } - - // take uppermost root as ancestor - if (!ancestor) - ancestor = _scxml; - assert(ancestor); - return ancestor; -} - -/** - If state2 is null, returns the set of all ancestors of state1 in ancestry order - (state1's parent followed by the parent's parent, etc. up to an including the - element). If state2 is non-null, returns inancestry order the set of all - ancestors of state1, up to but not including state2. (A "proper ancestor" of a - state is its parent, or the parent's parent, or the parent's parent's parent, - etc.)) If state2 is state1's parent, or equal to state1, or a descendant of - state1, this returns the empty set. -*/ -Arabica::XPath::NodeSet InterpreterRC::getProperAncestors(const Arabica::DOM::Node& state1, const Arabica::DOM::Node& state2) { - NodeSet ancestors; - - if (!state1 || !isState(Element(state1))) - return ancestors; - - if (!state2) { - /** - If state2 is null, returns the set of all ancestors of state1 in ancestry - order (state1's parent followed by the parent's parent, etc. up to an - including the element). - */ - Arabica::DOM::Node parent = state1.getParentNode(); - while(parent && isState(Element(parent))) { - ancestors.push_back(parent); - parent = parent.getParentNode(); - } - return ancestors; - } - - /** - If state2 is state1's parent, or equal to state1, or a descendant of - state1, this returns the empty set - */ - if (state1.getParentNode() == state2 || state1 == state2 || isDescendant(state2, state1)) { - return ancestors; - } - - /** - If state2 is non-null, returns in ancestry order the set of all ancestors - of state1, up to but not including state2. - */ - Arabica::DOM::Node parent = state1.getParentNode(); - while(parent && isState(Element(parent)) && parent != state2) { - ancestors.push_back(parent); - parent = parent.getParentNode(); - } - return ancestors; -} - -NodeSet InterpreterRC::getTargetStates(const Arabica::DOM::Element& transition) { - NodeSet targetStates; - - std::string targetId = ((Arabica::DOM::Element)transition).getAttribute("target"); - std::list targetIds = InterpreterImpl::tokenizeIdRefs(ATTR(transition, "target")); - if (targetIds.size() > 0) { - for (std::list::const_iterator targetIter = targetIds.begin(); targetIter != targetIds.end(); targetIter++) { - Arabica::DOM::Node state = getState(*targetIter); - if (state) { - assert(HAS_ATTR(state, "id")); - targetStates.push_back(state); - } - } - } else { - targetStates.push_back(getSourceState(transition)); // TODO: is this till correct? - } - return targetStates; -} - - -#if 0 -/** -Returns 'true' if state1 is a descendant of state2 (a child, or a child of a child, or a child of a child of a child, etc.) Otherwise returns 'false'. -*/ -bool InterpreterRC::isDescendant(const Arabica::DOM::Node& state1, const Arabica::DOM::Node& state2) { - return false; -} - -/** -Returns a list containing all , , and children of state1. -*/ -Arabica::XPath::NodeSet InterpreterRC::getChildStates(const Arabica::DOM::Node& state) { - return Arabica::XPath::NodeSet(); -} -#endif - - -} \ No newline at end of file diff --git a/src/uscxml/interpreter/InterpreterRC.h b/src/uscxml/interpreter/InterpreterRC.h new file mode 100644 index 0000000..3dae973 --- /dev/null +++ b/src/uscxml/interpreter/InterpreterRC.h @@ -0,0 +1,64 @@ +/** + * @file + * @author 2012-2013 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 . + * @endcond + */ + +#ifndef INTERPRETERRC_H_WLJEI019 +#define INTERPRETERRC_H_WLJEI019 + +#include "uscxml/Interpreter.h" + +namespace uscxml { + +class InterpreterRC : public InterpreterImpl { + void enterStates(const Arabica::XPath::NodeSet& enabledTransitions); + void exitStates(const Arabica::XPath::NodeSet& enabledTransitions); + Arabica::XPath::NodeSet removeConflictingTransitions(const Arabica::XPath::NodeSet& enabledTransitions); + + bool hasIntersection(const Arabica::XPath::NodeSet& nodeSet1, const Arabica::XPath::NodeSet& nodeSet2); + + Arabica::XPath::NodeSet computeExitSet(const Arabica::XPath::NodeSet& transitions); + Arabica::XPath::NodeSet computeExitSet(const Arabica::DOM::Node& transition); + + void computeEntrySet(const Arabica::XPath::NodeSet& transitions, + Arabica::XPath::NodeSet& statesToEnter, + Arabica::XPath::NodeSet& statesForDefaultEntry, + std::map > defaultHistoryContent); + void computeEntrySet(const Arabica::DOM::Node& transition, + Arabica::XPath::NodeSet& statesToEnter, + Arabica::XPath::NodeSet& statesForDefaultEntry, + std::map > defaultHistoryContent); + + Arabica::DOM::Node getTransitionDomain(const Arabica::DOM::Element& transition); + Arabica::XPath::NodeSet getEffectiveTargetStates(const Arabica::DOM::Element& transition); + + void addDescendantStatesToEnter(const Arabica::DOM::Element& state, + Arabica::XPath::NodeSet& statesToEnter, + Arabica::XPath::NodeSet& statesForDefaultEntry, + std::map > defaultHistoryContent); + + void addAncestorStatesToEnter(const Arabica::DOM::Element& state, + const Arabica::DOM::Element& ancestor, + Arabica::XPath::NodeSet& statesToEnter, + Arabica::XPath::NodeSet& statesForDefaultEntry, + std::map > defaultHistoryContent); + +}; + +} + +#endif /* end of include guard: INTERPRETERRC_H_WLJEI019 */ diff --git a/src/uscxml/interpreter/InterpreterRC.h.deactivated b/src/uscxml/interpreter/InterpreterRC.h.deactivated deleted file mode 100644 index 9afc2e6..0000000 --- a/src/uscxml/interpreter/InterpreterRC.h.deactivated +++ /dev/null @@ -1,87 +0,0 @@ -/** - * @file - * @author 2012-2013 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 . - * @endcond - */ - -#ifndef INTERPRETERRC_H_WLJEI019 -#define INTERPRETERRC_H_WLJEI019 - -#include "uscxml/Interpreter.h" - -namespace uscxml { - -class InterpreterRC : public InterpreterImpl { - InterpreterState interpret(); - void mainEventLoop(); - void exitInterpreter(); - - void microstep(const Arabica::XPath::NodeSet& enabledTransitions); - - Arabica::XPath::NodeSet selectEventlessTransitions(); - Arabica::XPath::NodeSet selectTransitions(const std::string& event); - bool isEnabledTransition(const Arabica::DOM::Element& transition, const std::string& event); - bool hasIntersection(const Arabica::XPath::NodeSet& nodeSet1, const Arabica::XPath::NodeSet& nodeSet2); - - void enterStates(const Arabica::XPath::NodeSet& enabledTransitions); - void exitStates(const Arabica::XPath::NodeSet& enabledTransitions); - - Arabica::XPath::NodeSet computeExitSet(const Arabica::XPath::NodeSet& transitions); - Arabica::XPath::NodeSet computeExitSet(const Arabica::DOM::Node& transition); - - void computeEntrySet(const Arabica::XPath::NodeSet& transitions, - Arabica::XPath::NodeSet& statesToEnter, - Arabica::XPath::NodeSet& statesForDefaultEntry, - std::map > defaultHistoryContent); - void computeEntrySet(const Arabica::DOM::Node& transition, - Arabica::XPath::NodeSet& statesToEnter, - Arabica::XPath::NodeSet& statesForDefaultEntry, - std::map > defaultHistoryContent); - - Arabica::XPath::NodeSet removeConflictingTransitions(const Arabica::XPath::NodeSet& enabledTransitions); - Arabica::DOM::Node getTransitionDomain(const Arabica::DOM::Element& transition); - - void addDescendantStatesToEnter(const Arabica::DOM::Node& state, - Arabica::XPath::NodeSet& statesToEnter, - Arabica::XPath::NodeSet& statesForDefaultEntry, - std::map > defaultHistoryContent); - - void addAncestorStatesToEnter(const Arabica::DOM::Node& state, - const Arabica::DOM::Node& ancestor, - Arabica::XPath::NodeSet& statesToEnter, - Arabica::XPath::NodeSet& statesForDefaultEntry, - std::map > defaultHistoryContent); - - bool isInFinalState(const Arabica::DOM::Node& state); - Arabica::DOM::Node findLCCA(const Arabica::XPath::NodeSet& states); - - Arabica::XPath::NodeSet getProperAncestors(const Arabica::DOM::Node& s1, - const Arabica::DOM::Node& s2); - - Arabica::XPath::NodeSet getTargetStates(const Arabica::DOM::Element& transition); - -#if 0 - bool isDescendant(const Arabica::DOM::Node& state1, const Arabica::DOM::Node& state2); - Arabica::XPath::NodeSet getChildStates(const Arabica::DOM::Node& state); -#endif - - bool _running; - -}; - -} - -#endif /* end of include guard: INTERPRETERRC_H_WLJEI019 */ diff --git a/src/uscxml/plugins/EventHandler.h b/src/uscxml/plugins/EventHandler.h index 2b41ae5..8ac31b1 100644 --- a/src/uscxml/plugins/EventHandler.h +++ b/src/uscxml/plugins/EventHandler.h @@ -65,16 +65,17 @@ public: virtual void send(const SendRequest& req) = 0; virtual void runOnMainThread() {}; - void returnEvent(Event& event, bool external = false); void returnErrorExecution(const std::string&); void returnErrorCommunication(const std::string&); + void returnEvent(Event& event, bool internal = false); protected: InterpreterImpl* _interpreter; Arabica::DOM::Element _element; std::string _invokeId; std::string _type; - + + }; class USCXML_API EventHandler { diff --git a/src/uscxml/plugins/invoker/heartbeat/HeartbeatInvoker.cpp b/src/uscxml/plugins/invoker/heartbeat/HeartbeatInvoker.cpp index cc06b52..f8b4904 100644 --- a/src/uscxml/plugins/invoker/heartbeat/HeartbeatInvoker.cpp +++ b/src/uscxml/plugins/invoker/heartbeat/HeartbeatInvoker.cpp @@ -98,7 +98,7 @@ void HeartbeatInvoker::invoke(const InvokeRequest& req) { void HeartbeatInvoker::dispatch(void* instance, std::string name) { HeartbeatInvoker* invoker = (HeartbeatInvoker*)instance; - invoker->returnEvent(invoker->_event, true); + invoker->returnEvent(invoker->_event); } HeartbeatDispatcher* HeartbeatDispatcher::_instance = NULL; diff --git a/src/uscxml/plugins/invoker/xhtml/XHTMLInvoker.cpp b/src/uscxml/plugins/invoker/xhtml/XHTMLInvoker.cpp index 861e922..e2b32df 100644 --- a/src/uscxml/plugins/invoker/xhtml/XHTMLInvoker.cpp +++ b/src/uscxml/plugins/invoker/xhtml/XHTMLInvoker.cpp @@ -101,7 +101,7 @@ bool XHTMLInvoker::httpRecvRequest(const HTTPServer::Request& req) { HTTPServer::Reply reply(req); HTTPServer::reply(reply); - returnEvent(ev, true); + returnEvent(ev); return true; } } diff --git a/src/uscxml/plugins/ioprocessor/basichttp/BasicHTTPIOProcessor.cpp b/src/uscxml/plugins/ioprocessor/basichttp/BasicHTTPIOProcessor.cpp index 845e142..cf7c8e7 100644 --- a/src/uscxml/plugins/ioprocessor/basichttp/BasicHTTPIOProcessor.cpp +++ b/src/uscxml/plugins/ioprocessor/basichttp/BasicHTTPIOProcessor.cpp @@ -157,7 +157,7 @@ bool BasicHTTPIOProcessor::httpRecvRequest(const HTTPServer::Request& req) { if (reqEvent.name.length() == 0) reqEvent.name = "http." + req.data.compound.at("type").atom; - returnEvent(reqEvent, true); + returnEvent(reqEvent); evhttp_send_reply(req.evhttpReq, 200, "OK", NULL); return true; } diff --git a/src/uscxml/transform/ChartToFSM.cpp b/src/uscxml/transform/ChartToFSM.cpp index c31853c..1971ae2 100644 --- a/src/uscxml/transform/ChartToFSM.cpp +++ b/src/uscxml/transform/ChartToFSM.cpp @@ -580,7 +580,7 @@ void FlatteningInterpreter::explode() { continue; // reduce to conflict-free subset - transitions = filterPreempted(transitions); + transitions = removeConflictingTransitions(transitions); // algorithm can never reduce to empty set assert(transitions.size() > 0); diff --git a/test/src/test-w3c.cpp b/test/src/test-w3c.cpp index 31ab9b9..0ff67df 100644 --- a/test/src/test-w3c.cpp +++ b/test/src/test-w3c.cpp @@ -150,11 +150,13 @@ class W3CStatusMonitor : public uscxml::InterpreterMonitor { if (boost::starts_with(ATTR_CAST(config[0], "id"), "active:{pass")) { std::cout << "TEST SUCCEEDED" << std::endl; retCode = EXIT_SUCCESS; + return; } } else { if (boost::iequals(ATTR_CAST(config[0], "id"), "pass")) { std::cout << "TEST SUCCEEDED" << std::endl; retCode = EXIT_SUCCESS; + return; } } } -- cgit v0.12