diff options
author | Stefan Radomski <radomski@tk.informatik.tu-darmstadt.de> | 2014-07-21 11:34:44 (GMT) |
---|---|---|
committer | Stefan Radomski <radomski@tk.informatik.tu-darmstadt.de> | 2014-07-21 11:34:44 (GMT) |
commit | dc5f5ddfa10bf91524e6f7555c263eaea069dcb0 (patch) | |
tree | 576b15e7fe75476e8ece954b52aa88871941beb5 | |
parent | 641117e7400e9e5ef0fa451f732eb9009f0914cb (diff) | |
download | uscxml-dc5f5ddfa10bf91524e6f7555c263eaea069dcb0.zip uscxml-dc5f5ddfa10bf91524e6f7555c263eaea069dcb0.tar.gz uscxml-dc5f5ddfa10bf91524e6f7555c263eaea069dcb0.tar.bz2 |
try / catch blocks for applications and work on dot output
-rw-r--r-- | apps/uscxml-browser.cpp | 80 | ||||
-rw-r--r-- | apps/uscxml-dot.cpp | 92 | ||||
-rw-r--r-- | apps/uscxml-transform.cpp | 63 | ||||
-rw-r--r-- | embedding/java/src/org/uscxml/tests/monitor/TestCustomMonitor.java | 51 | ||||
-rw-r--r-- | src/uscxml/Interpreter.cpp | 3 | ||||
-rw-r--r-- | src/uscxml/UUID.cpp | 36 | ||||
-rw-r--r-- | src/uscxml/UUID.h | 1 | ||||
-rw-r--r-- | src/uscxml/debug/SCXMLDotWriter.cpp | 531 | ||||
-rw-r--r-- | src/uscxml/debug/SCXMLDotWriter.h | 67 | ||||
-rw-r--r-- | src/uscxml/plugins/ioprocessor/modality/MMIMessages.cpp | 9 | ||||
-rw-r--r-- | test/CMakeLists.txt | 5 | ||||
-rw-r--r-- | test/src/test-vxml-mmi-socket.cpp | 72 |
12 files changed, 671 insertions, 339 deletions
diff --git a/apps/uscxml-browser.cpp b/apps/uscxml-browser.cpp index e431fdf..f9a06bb 100644 --- a/apps/uscxml-browser.cpp +++ b/apps/uscxml-browser.cpp @@ -173,52 +173,60 @@ int main(int argc, char** argv) { std::string documentURL = options.interpreters[0].first; LOG(INFO) << "Processing " << documentURL; - Interpreter interpreter = Interpreter::fromURI(documentURL); - if (interpreter) { - interpreter.setCmdLineOptions(currOptions->additionalParameters); - interpreter.setCapabilities(options.getCapabilities()); - - if (options.verbose) { - VerboseMonitor* vm = new VerboseMonitor(); - interpreter.addMonitor(vm); - } - if (options.withDebugger) { - interpreter.addMonitor(debugger); - } - interpreters.push_back(interpreter); + try { + Interpreter interpreter = Interpreter::fromURI(documentURL); + if (interpreter) { + interpreter.setCmdLineOptions(currOptions->additionalParameters); + interpreter.setCapabilities(options.getCapabilities()); - } else { - LOG(ERROR) << "Cannot create interpreter from " << documentURL; + if (options.verbose) { + VerboseMonitor* vm = new VerboseMonitor(); + interpreter.addMonitor(vm); + } + if (options.withDebugger) { + interpreter.addMonitor(debugger); + } + + interpreters.push_back(interpreter); + + } else { + LOG(ERROR) << "Cannot create interpreter from " << documentURL; + } + } catch (Event e) { + std::cout << e << std::endl; } } // start interpreters - std::list<Interpreter>::iterator interpreterIter = interpreters.begin(); - while(interpreterIter != interpreters.end()) { - interpreterIter->start(); - interpreterIter++; - } - - bool stillRunning = true; - // call from main thread for UI events - while(interpreters.size() > 0) { - interpreterIter = interpreters.begin(); + try { + std::list<Interpreter>::iterator interpreterIter = interpreters.begin(); while(interpreterIter != interpreters.end()) { - stillRunning = interpreterIter->runOnMainThread(25); - if (!stillRunning) { - interpreters.erase(interpreterIter++); - } else { - interpreterIter++; + interpreterIter->start(); + interpreterIter++; + } + + bool stillRunning = true; + // call from main thread for UI events + while(interpreters.size() > 0) { + interpreterIter = interpreters.begin(); + while(interpreterIter != interpreters.end()) { + stillRunning = interpreterIter->runOnMainThread(25); + if (!stillRunning) { + interpreters.erase(interpreterIter++); + } else { + interpreterIter++; + } } } - } - if (options.withDebugger) { - // idle and wait for CTRL+C or debugging events - while(true) - tthread::this_thread::sleep_for(tthread::chrono::seconds(1)); + if (options.withDebugger) { + // idle and wait for CTRL+C or debugging events + while(true) + tthread::this_thread::sleep_for(tthread::chrono::seconds(1)); + } + } catch (Event e) { + std::cout << e << std::endl; } - return EXIT_SUCCESS; }
\ No newline at end of file diff --git a/apps/uscxml-dot.cpp b/apps/uscxml-dot.cpp index 3324e54..56f5c57 100644 --- a/apps/uscxml-dot.cpp +++ b/apps/uscxml-dot.cpp @@ -7,6 +7,7 @@ #include "uscxml/Factory.h" #include <boost/algorithm/string.hpp> + using namespace uscxml; void printUsageAndExit(const char* progName) { @@ -19,12 +20,17 @@ void printUsageAndExit(const char* progName) { printf("%s version " USCXML_VERSION " (" CMAKE_BUILD_TYPE " build - " CMAKE_COMPILER_STRING ")\n", progStr.c_str()); printf("Usage\n"); printf("\t%s", progStr.c_str()); - printf(" [-dN_0] URL"); - printf(" [[-dN_1] state_id1] .. [[-dN_M] state_idM]"); + printf(" [-eTYPE] [-dN] [-tN] URL"); + printf(" [[-dN] [-tN] [-eTYPE] state_id1] .. [[-dN] [-tN] [-eTYPE] state_idM]"); printf("\n"); printf("Options\n"); printf("\tURL : URL of SCXML document\n"); + printf("\t-e TYPE : type of edges to use:\n"); + printf("\t 'target' - aggregate per target node (default)\n"); + printf("\t 'event' - aggregate per event name\n"); + printf("\t 'transition' no aggregation, display each transition\n"); printf("\t-d : depth below anchor node (INF per default)\n"); + printf("\t-t : transition depth below anchor (INF per default)\n"); printf("\tstate_id : anchor node state id (topmost scxml element per default)\n"); printf("\n"); exit(1); @@ -32,9 +38,9 @@ void printUsageAndExit(const char* progName) { int currOpt = 1; -int consumeDepthOption(int argc, char** argv) { +int32_t consumeNumericOption(int argc, char** argv, const std::string& name, int32_t defaultVal) { std::string test = argv[currOpt]; - if (boost::starts_with(test, "-")) { + if (boost::starts_with(test, std::string("-") + name)) { int value = 0; if (test.size() > 2) { // no space before value @@ -52,7 +58,7 @@ int consumeDepthOption(int argc, char** argv) { return value; } - return -1; + return defaultVal; } int main(int argc, char** argv) { @@ -61,43 +67,69 @@ int main(int argc, char** argv) { google::LogToStderr(); google::InitGoogleLogging(argv[0]); - std::list<SCXMLDotWriter::StateAnchor> stateAnchors; - + if (argc < 2) printUsageAndExit(argv[0]); - try { - // see if there is an initial depth given for root - int depth = consumeDepthOption(argc, argv); - if (depth >= 0) { - SCXMLDotWriter::StateAnchor anchor; - anchor.depth = depth; - stateAnchors.push_back(anchor); + std::list<SCXMLDotWriter::StateAnchor> stateAnchors; + SCXMLDotWriter::StateAnchor currAnchor; + + int option; + while ((option = getopt(argc, argv, "d:t:")) != -1) { + switch(option) { + case 'd': currAnchor.childDepth = strTo<int32_t>(optarg); break; + case 't': currAnchor.transDepth = strTo<int32_t>(optarg); break; + case 'e': { + std::string edgeType(optarg); + if (edgeType == "target") { + currAnchor.type = SCXMLDotWriter::PORT_TARGET; + } else if (edgeType == "event") { + currAnchor.type = SCXMLDotWriter::PORT_EVENT; + } else if (edgeType == "transition") { + currAnchor.type = SCXMLDotWriter::PORT_TRANSITION; + } else { + printUsageAndExit(argv[0]); + } + break; + } + default: break; } + } + + if (currAnchor) + stateAnchors.push_back(currAnchor); + try { // current option has to be the interpreter's name - URL inputFile(argv[currOpt++]); + URL inputFile(argv[optind]); Interpreter interpreter = Interpreter::fromURI(inputFile); - - for (; currOpt < argc; currOpt++) { - SCXMLDotWriter::StateAnchor anchor; - depth = consumeDepthOption(argc, argv); - - if (depth >= 0) { - anchor.depth = depth; + optind++; + + while(optind < argc) { + // are + while ((option = getopt(argc, argv, "d:t:")) != -1) { + switch(option) { + case 'd': currAnchor.childDepth = strTo<int32_t>(optarg); break; + case 't': currAnchor.transDepth = strTo<int32_t>(optarg); break; + default: break; + } } - - if (argc > currOpt) { - std::string expr(argv[currOpt++]); - anchor.element = interpreter.getImpl()->getState(expr); + if (argc > optind) { + std::string expr(argv[optind++]); + currAnchor.element = interpreter.getImpl()->getState(expr); } else { printUsageAndExit(argv[0]); } - - stateAnchors.push_back(anchor); + + if (currAnchor) { + stateAnchors.push_back(currAnchor); + } + + currAnchor = SCXMLDotWriter::StateAnchor(); } - - SCXMLDotWriter::toDot("machine.dot", interpreter, stateAnchors); + + std::string outName = inputFile.file() + ".dot"; + SCXMLDotWriter::toDot(outName, interpreter, stateAnchors); } catch(Event e) { std::cerr << e << std::cout; diff --git a/apps/uscxml-transform.cpp b/apps/uscxml-transform.cpp index d0c3524..245a89c 100644 --- a/apps/uscxml-transform.cpp +++ b/apps/uscxml-transform.cpp @@ -161,41 +161,44 @@ int main(int argc, char** argv) { HTTPServer::getInstance(30444, 30445, NULL); Interpreter interpreter; - if (inputFile.size() == 0 || inputFile == "-") { - LOG(INFO) << "Reading SCXML from STDIN"; - std::stringstream ss; - std::string line; - while (std::getline(std::cin, line)) { - ss << line; + try { + if (inputFile.size() == 0 || inputFile == "-") { + LOG(INFO) << "Reading SCXML from STDIN"; + std::stringstream ss; + std::string line; + while (std::getline(std::cin, line)) { + ss << line; + } + interpreter = Interpreter::fromXML(ss.str()); + } else { + interpreter = Interpreter::fromURI(inputFile); + } + if (!interpreter) { + LOG(ERROR) << "Cannot create interpreter from " << inputFile; + exit(EXIT_FAILURE); } - interpreter = Interpreter::fromXML(ss.str()); - } else { - interpreter = Interpreter::fromURI(inputFile); - } - if (!interpreter) { - LOG(ERROR) << "Cannot create interpreter from " << inputFile; - exit(EXIT_FAILURE); - } - - if (toPromela) { - Interpreter flatInterpreter = ChartToFSM::flatten(interpreter); - if (outputFile.size() == 0 || outputFile == "-") { - FSMToPromela::writeProgram(std::cout, flatInterpreter); - } else { - std::ofstream outStream; - outStream.open(outputFile.c_str()); - FSMToPromela::writeProgram(outStream, flatInterpreter); - outStream.close(); + if (toPromela) { + Interpreter flatInterpreter = ChartToFSM::flatten(interpreter); + + if (outputFile.size() == 0 || outputFile == "-") { + FSMToPromela::writeProgram(std::cout, flatInterpreter); + } else { + std::ofstream outStream; + outStream.open(outputFile.c_str()); + FSMToPromela::writeProgram(outStream, flatInterpreter); + outStream.close(); + } + exit(EXIT_SUCCESS); } - exit(EXIT_SUCCESS); - } - if (toFlat) { - std::cout << ChartToFSM::flatten(interpreter).getDocument(); - exit(EXIT_SUCCESS); + if (toFlat) { + std::cout << ChartToFSM::flatten(interpreter).getDocument(); + exit(EXIT_SUCCESS); + } + } catch (Event e) { + std::cout << e << std::endl; } - return EXIT_SUCCESS; }
\ No newline at end of file diff --git a/embedding/java/src/org/uscxml/tests/monitor/TestCustomMonitor.java b/embedding/java/src/org/uscxml/tests/monitor/TestCustomMonitor.java new file mode 100644 index 0000000..c748f50 --- /dev/null +++ b/embedding/java/src/org/uscxml/tests/monitor/TestCustomMonitor.java @@ -0,0 +1,51 @@ +package org.uscxml.tests.monitor; + +import org.uscxml.Data; +import org.uscxml.Interpreter; +import org.uscxml.InterpreterException; +import org.uscxml.InterpreterMonitor; + +public class TestCustomMonitor extends InterpreterMonitor { + + @Override + public void afterEnteringState(Interpreter interpreter, String stateId, + String xpath, String state, boolean moreComing) { + System.out.println("Entered state " + stateId); + if ("s2".equals(stateId)) { + Data data = interpreter.getDataModel().getStringAsData("foo"); + System.out.println(data); + } + } + + public static void main(String[] args) throws InterpreterException { + System.load("/Users/sradomski/Documents/TK/Code/uscxml/build/cli/lib/libuscxmlNativeJava64.jnilib"); + + String xml = + "<scxml datamodel=\"ecmascript\">" + + " <datamodel>" + + " <data id=\"foo\">" + + " { foo: 'bar', baz: 'foo' }" + + " </data>" + + " </datamodel>" + + " <state id=\"s1\">" + + " <transition target=\"s2\" />" + + " </state>" + + " <state id=\"s2\">" + + " <transition target=\"s3\" />" + + " </state>" + + " <state id=\"s3\">" + + " <transition target=\"done\" />" + + " </state>" + + " <final id=\"done\" />" + + "</scxml>"; + + // parse and interpret + Interpreter interpreter = Interpreter.fromXML(xml); + + TestCustomMonitor monitor = new TestCustomMonitor(); + interpreter.addMonitor(monitor); + + interpreter.interpret(); + } + +} diff --git a/src/uscxml/Interpreter.cpp b/src/uscxml/Interpreter.cpp index 90f00c4..5a1df44 100644 --- a/src/uscxml/Interpreter.cpp +++ b/src/uscxml/Interpreter.cpp @@ -2345,6 +2345,9 @@ bool InterpreterImpl::isInitial(const Arabica::DOM::Element<std::string>& state) if (isMember(state, getInitialStates(parent))) return true; // every nested node + if (isParallel(parent)) + return true; + return false; } diff --git a/src/uscxml/UUID.cpp b/src/uscxml/UUID.cpp index 8647739..b56b727 100644 --- a/src/uscxml/UUID.cpp +++ b/src/uscxml/UUID.cpp @@ -31,4 +31,40 @@ std::string UUID::getUUID() { return os.str(); } +bool UUID::isUUID(const std::string& uuid) { + if (uuid.size() != 36) + return false; + + if (uuid[8] != '-' || uuid[13] != '-' || uuid[18] != '-' || uuid[23] != '-') + return false; + + for (int i = 0; i < 36; i++) { + if (i == 8 || i == 13 || i == 18 || i ==23) + continue; + + char c = uuid[i]; + if (c == 'a' || + c == 'b' || + c == 'c' || + c == 'd' || + c == 'e' || + c == 'f' || + c == '0' || + c == '1' || + c == '2' || + c == '3' || + c == '4' || + c == '5' || + c == '6' || + c == '7' || + c == '8' || + c == '9') { + continue; + } else { + return false; + } + } + return true; +} + }
\ No newline at end of file diff --git a/src/uscxml/UUID.h b/src/uscxml/UUID.h index af129e7..873d963 100644 --- a/src/uscxml/UUID.h +++ b/src/uscxml/UUID.h @@ -29,6 +29,7 @@ namespace uscxml { class USCXML_API UUID { public: static std::string getUUID(); + static bool isUUID(const std::string& uuid); static boost::uuids::random_generator uuidGen; }; diff --git a/src/uscxml/debug/SCXMLDotWriter.cpp b/src/uscxml/debug/SCXMLDotWriter.cpp index 4061f41..88db575 100644 --- a/src/uscxml/debug/SCXMLDotWriter.cpp +++ b/src/uscxml/debug/SCXMLDotWriter.cpp @@ -18,6 +18,7 @@ */ #include "uscxml/Common.h" +#include "uscxml/UUID.h" #include "SCXMLDotWriter.h" #include "uscxml/DOMUtils.h" #include <boost/algorithm/string.hpp> // replace_all @@ -26,7 +27,8 @@ namespace uscxml { using namespace Arabica::DOM; - +using namespace Arabica::XPath; + SCXMLDotWriter::SCXMLDotWriter() { _iteration = 0; _indentation = 0; @@ -34,26 +36,29 @@ SCXMLDotWriter::SCXMLDotWriter() { SCXMLDotWriter::SCXMLDotWriter(Interpreter interpreter, const std::list<SCXMLDotWriter::StateAnchor>& stateAnchors, - const Arabica::DOM::Element<std::string>& transition) { + const Element<std::string>& transition) { _interpreter = interpreter; _xmlNSPrefix = _interpreter.getNameSpaceInfo().xmlNSPrefix; _transition = transition; _anchors = stateAnchors; - if (_anchors.size() == 0) { - NodeList<std::string > scxmlElems = interpreter.getDocument().getElementsByTagName("scxml"); + NodeList<std::string > scxmlElems = interpreter.getDocument().getElementsByTagName("scxml"); + _scxml = (Element<std::string>)scxmlElems.item(0); + if (_anchors.size() == 0) { StateAnchor anchor; - anchor.element = (Arabica::DOM::Element<std::string>)scxmlElems.item(0); + anchor.element = _scxml; _anchors.push_back(anchor); } for (std::list<StateAnchor>::iterator anchIter = _anchors.begin(); anchIter != _anchors.end(); anchIter++) { - if (!anchIter->element) { - NodeList<std::string > scxmlElems = interpreter.getDocument().getElementsByTagName("scxml"); - anchIter->element = (Arabica::DOM::Element<std::string>)scxmlElems.item(0); - } - assembleGraph(anchIter->element, anchIter->depth); + if (!anchIter->element) + anchIter->element = _scxml; + if (anchIter->childDepth >= 0 && anchIter->transDepth == -1) + anchIter->transDepth = anchIter->childDepth; + + _portType = anchIter->type; + assembleGraph(anchIter->element, anchIter->childDepth, anchIter->transDepth + 1); } _iteration = 0; @@ -83,7 +88,7 @@ void SCXMLDotWriter::beforeMicroStep(Interpreter interpreter) { } void SCXMLDotWriter::beforeTakingTransition(Interpreter interpreter, - const Arabica::DOM::Element<std::string>& transition, + const Element<std::string>& transition, bool moreComing) { std::ostringstream fileSS; fileSS << interpreter.getName() << "." << std::setw(6) << std::setfill('0') << _iteration++ << ".dot"; @@ -99,20 +104,60 @@ std::string SCXMLDotWriter::getPrefix() { void SCXMLDotWriter::writeTo(std::ostream& os) { os << "digraph {" << std::endl; - os << " rankdir=TB;" << std::endl; + os << " rankdir=LR;" << std::endl; os << " fontsize=10;" << std::endl; // outfile << " splines=ortho;" << std::endl; // outfile << " splines=false;" << std::endl; // outfile << " nodesep=1.0;" << std::endl; _indentation++; - for (std::list<SCXMLDotWriter::StateAnchor>::iterator anchIter = _anchors.begin(); - anchIter != _anchors.end(); anchIter++) { - writeStateElement(os, _graph[idForNode(anchIter->element)]); - } - _indentation--; + writeStateElement(os, _scxml); + + // write edges at end of file + for(std::set<DotEdge>::iterator edgeIter = _edges.begin(); edgeIter != _edges.end(); edgeIter++) { + if (edgeIter->from == edgeIter->to) + continue; + if (_histories.find(edgeIter->to) != _histories.end()) { + if (_histories.find(edgeIter->to)->second.from == _histories.find(edgeIter->to)->second.to) + continue; + } + + os << getPrefix() << "\"" << edgeIter->from << "\""; + if (edgeIter->fromPort.size() > 0) { + os << std::string(":\"") + edgeIter->fromPort + "\":e"; + } else { + os << ":__name"; + } + os << " -> "; + + if (_histories.find(edgeIter->to) != _histories.end()) { + os << getPrefix() << "\"" << _histories.find(edgeIter->to)->second.to << "\""; + if (_histories.find(edgeIter->to)->second.toPort.size() > 0) { + os << std::string(":\"") + _histories.find(edgeIter->to)->second.toPort + "\""; + } else { + os << ":__name"; + } + } else { + os << getPrefix() << "\"" << edgeIter->to << "\""; + if (edgeIter->toPort.size() > 0) { + os << std::string(":\"") + edgeIter->toPort + "\""; + } else { + os << ":__name"; + } + } + if (edgeIter->type == EDGE_INITAL) { + os << ":nw [style=\"dashed\", color=\"black\"]"; + } else { + os << " [color=\"black\"]"; + } + os << std::endl; + + } + + _indentation--; + os << "}" << std::endl; } @@ -120,7 +165,7 @@ void SCXMLDotWriter::writeTo(std::ostream& os) { void SCXMLDotWriter::toDot(const std::string& filename, Interpreter interpreter, const std::list<SCXMLDotWriter::StateAnchor>& stateAnchors, - const Arabica::DOM::Element<std::string>& transition) { + const Element<std::string>& transition) { std::ofstream outfile(filename.c_str()); SCXMLDotWriter writer(interpreter, stateAnchors, transition); @@ -128,42 +173,50 @@ void SCXMLDotWriter::toDot(const std::string& filename, } -void SCXMLDotWriter::assembleGraph(const Arabica::DOM::Element<std::string>& state, uint32_t depth) { +/** + * Walk the subset of the graph that is reachable and remember the nodes + */ +void SCXMLDotWriter::assembleGraph(const Element<std::string>& state, int32_t childDepth, int32_t transDepth) { std::string nodeId = idForNode(state); + // this node is neither included per child, nor per transition + if (childDepth <= 0 && transDepth <= 0) { + return; + } + // been here if (_graph.find(nodeId) != _graph.end()) return; - if (depth == 0) { - _graph[nodeId].isBorder = true; - } + _graph[nodeId].node = state; + _graph[nodeId].portType = _portType; - if (ATTR(state, "id") == "WiFiOff") { - assert(true); + + if (childDepth == 0 && transDepth == 0) { + _graph[nodeId].isBorder = true; } - _graph[nodeId].node = state; - - if (depth == 0) - return; - Arabica::XPath::NodeSet<std::string> childElems = InterpreterImpl::filterChildType(Arabica::DOM::Node_base::ELEMENT_NODE, state); + NodeSet<std::string> childElems = InterpreterImpl::filterChildType(Node_base::ELEMENT_NODE, state); for (int i = 0; i < childElems.size(); i++) { - Arabica::DOM::Element<std::string> childElem(childElems[i]); + Element<std::string> childElem(childElems[i]); + // remember histories we passed if (iequals(TAGNAME(childElem), "history")) { - _histories[ATTR(childElem, "id")] = ATTR(state, "id") + ":" + ATTR(childElem, "id"); + _histories[ATTR(childElem, "id")].to = ATTR(state, "id"); + _histories[ATTR(childElem, "id")].toPort = ATTR(childElem, "id"); } + // follow transitions if (iequals(TAGNAME(childElem), "transition")) { - Arabica::XPath::NodeSet<std::string> targetStates = _interpreter.getImpl()->getTargetStates(childElem); + _graph[nodeId].transitions.push_back(childElem); + NodeSet<std::string> targetStates = _interpreter.getImpl()->getTargetStates(childElem); for (int j = 0; j < targetStates.size(); j++) { std::string remoteNodeId = idForNode(targetStates[j]); _graph[nodeId].targets.insert(std::make_pair(remoteNodeId, childElem)); - // recurse along the transition targets - assembleGraph((Arabica::DOM::Element<std::string>)targetStates[j], depth - 1); + // recurse along the transition targets, no weight from child depth + assembleGraph((Element<std::string>)targetStates[j], 0, transDepth - 1); } if (targetStates.size() == 0) _graph[nodeId].targets.insert(std::make_pair(nodeId, childElem)); @@ -178,147 +231,247 @@ void SCXMLDotWriter::assembleGraph(const Arabica::DOM::Element<std::string>& sta } } + // follow childs if (InterpreterImpl::isState(Element<std::string>(childElem))) { - // add to initial states if it is initial if (_interpreter.getImpl()->isInitial(Element<std::string>(childElem))) { + // add to initial states if it is initial _graph[nodeId].initialchilds.insert(idForNode(childElem)); } else if (_interpreter.getImpl()->isParallel(Element<std::string>(state))) { + // add to initial states if we are parallel _graph[nodeId].initialchilds.insert(idForNode(childElem)); } // in any case, it is a child state _graph[nodeId].childs.insert(idForNode(childElem)); - // recurse - assembleGraph(childElem, depth - 1); + // recurse (do we really need to?) + assembleGraph(childElem, childDepth - 1, transDepth); } - } } -void SCXMLDotWriter::writeStateElement(std::ostream& os, const DotState& state) { - const Arabica::DOM::Element<std::string>& stateElem = state.node; + +/** + * Walk the complete graph and draw reachable subset + */ +void SCXMLDotWriter::writeStateElement(std::ostream& os, const Element<std::string>& stateElem) { std::string stateId = idForNode(stateElem); if (_knownIds.find(stateId) != _knownIds.end()) return; _knownIds.insert(stateId); + std::list<Node<std::string> > invisParents; + bool isVisible = (_graph.find(stateId) != _graph.end()); + bool subgraph = InterpreterImpl::isCompound(stateElem) || InterpreterImpl::isParallel(stateElem); + bool fatherIsParallel = (stateElem.getParentNode() && + stateElem.getParentNode().getNodeType() == Node_base::ELEMENT_NODE && + InterpreterImpl::isParallel(Element<std::string>(stateElem.getParentNode()))); + if (subgraph) { _indentation++; os << std::endl; os << getPrefix() << "subgraph \"cluster_" << stateId << "\" {" << std::endl; _indentation++; os << getPrefix() << "fontsize=14" << std::endl; - os << getPrefix() << "label=<<b>"; - if (InterpreterImpl::isCompound(stateElem)) { - os << "Compound: "; - } else { - os << "Parallel: "; - } - os << nameForNode(stateElem) << "</b>>" << std::endl; -// os << getPrefix() << "rank=\"same\"" << std::endl; + os << getPrefix() << "label=<<b>" << nameForNode(stateElem) << "</b>>" << std::endl; + // os << getPrefix() << "rank=\"same\"" << std::endl; os << getPrefix() << "labeljust=l" << std::endl; -// os << getPrefix() << "ranksep=\"equally\"" << std::endl; + os << getPrefix() << (fatherIsParallel ? "style=dashed" : "style=solid") << std::endl; + + // os << getPrefix() << "ranksep=\"equally\"" << std::endl; + } - os << std::endl; - os << getPrefix() << "\"" << stateId << "\" [" << std::endl; - _indentation++; + if (isVisible) { + // this state is visible! + const DotState& dotState = _graph.find(stateId)->second; + + // is this a subgraph? + + os << std::endl; + os << getPrefix() << "\"" << stateId << "\" [" << std::endl; + _indentation++; + + os << getPrefix() << "fontsize=10," << std::endl; + os << getPrefix() << "shape=plaintext," << std::endl; + os << getPrefix() << (fatherIsParallel ? "style=dashed," : "style=solid,") << std::endl; + + // is the current state in the basic configuration? + if (InterpreterImpl::isMember(stateElem, _interpreter.getBasicConfiguration())) + os << getPrefix() << "color=red, penwidth=3," << std::endl; + + // is the current state in the basic configuration? + if (dotState.isBorder) + os << getPrefix() << "color=blue," << std::endl; + + // is this state final? + if (_interpreter.getImpl()->isFinal(stateElem)) { + os << getPrefix() << "shape=doublecircle," << std::endl; + os << getPrefix() << "color=black," << std::endl; + os << getPrefix() << "penwidth=2," << std::endl; + os << getPrefix() << "label=<" << nameForNode(stateElem) << ">" << std::endl; + _indentation--; + os << getPrefix() << "];" << std::endl; + + return; + } - os << getPrefix() << "fontsize=10," << std::endl; - os << getPrefix() << "shape=plaintext," << std::endl; + // is the state initial? + bool isInitial = _interpreter.getImpl()->isInitial(stateElem); + // if (isInitial) + // os << getPrefix() << "style=filled, fillcolor=lightgrey, " << std::endl; - // is the current state in the basic configuration? - if (InterpreterImpl::isMember(stateElem, _interpreter.getBasicConfiguration())) - os << getPrefix() << "color=red, penwidth=3," << std::endl; + DotState::mmap_s_e_t::const_iterator destIterF, destIterB; + std::list<std::string> outPorts; // count unique keys - // is the current state in the basic configuration? - if (state.isBorder) - os << getPrefix() << "color=blue," << std::endl; + switch (dotState.portType) { + case PORT_TARGET: // outports are per target + for(DotState::mmap_s_e_t::const_iterator it = dotState.targets.begin(), end = dotState.targets.end(); + it != end; + it = dotState.targets.upper_bound(it->first)) { + outPorts.push_back(it->first); + } + break; + case PORT_EVENT: // outports are per event + for(DotState::mmap_s_e_t::const_iterator it = dotState.events.begin(), end = dotState.events.end(); + it != end; + it = dotState.events.upper_bound(it->first)) { + outPorts.push_back(it->first); + } + break; + case PORT_TRANSITION: + for (int i = 0; i < dotState.transitions.size(); i++) { + outPorts.push_back(idForNode(dotState.transitions[i])); + } + break; + } + + os << getPrefix() << "label = < " << std::endl; + + /* + <table cellborder="1" border="0" cellspacing="0" cellpadding="2" style="rounded"> + <tr><td port="name" rowspan="4"><b>step</b></td></tr> + <tr><td port="foo.error.port" align="right">foo.error.port</td></tr> + <tr><td port="bar" align="right">bar</td></tr> + <tr><td port="baz" align="right">baz</td></tr> + </table> + */ + + std::string details = getDetailedLabel(stateElem); + + os << "<table " << (isInitial ? "bgcolor=\"orange\" " : "") << "cellborder=\"1\" border=\"0\" cellspacing=\"0\" cellpadding=\"2\" >" << std::endl; + os << " <tr><td port=\"__name\" rowspan=\"" << outPorts.size() + 1 << "\"><b>" << nameForNode(stateElem) << "</b></td></tr>" << std::endl; + + switch (dotState.portType) { + case PORT_TARGET: // outports are per target + writePerTargetPorts(os, outPorts, dotState); + break; + case PORT_EVENT: // outports are per event + writePerEventPorts(os, outPorts, dotState); + break; + case PORT_TRANSITION: + writePerTransitionPorts(os, outPorts, dotState); + break; + } + + + // write details of the state + if (details.size() > 0) { + os << " <tr><td colspan=\"" << (outPorts.size() == 0 ? 1 : 2) << "\">" << std::endl; + os << details << std::endl; + os << " </td></tr>" << std::endl; + } + + // write history states + NodeSet<std::string> histories = InterpreterImpl::filterChildElements(_xmlNSPrefix + "history", stateElem); + for (int i = 0; i < histories.size(); i++) { + os << " <tr><td port=\"" << ATTR(histories[i], "id") << "\" colspan=\"" << (outPorts.size() == 0 ? 1 : 2) << "\"><b>history: </b>" << ATTR(histories[i], "id") << "</td></tr>" << std::endl; + + } + + os << "</table>" << std::endl << getPrefix() << ">" << std::endl; - // is this state final? - if (_interpreter.getImpl()->isFinal(stateElem)) { - os << getPrefix() << "shape=doublecircle," << std::endl; - os << getPrefix() << "color=black," << std::endl; - os << getPrefix() << "penwidth=2," << std::endl; - os << getPrefix() << "label=<" << nameForNode(stateElem) << ">" << std::endl; _indentation--; os << getPrefix() << "];" << std::endl; - return; - } - // is the state initial? - bool isInitial = _interpreter.getImpl()->isInitial(stateElem); -// if (isInitial) -// os << getPrefix() << "style=filled, fillcolor=lightgrey, " << std::endl; + for (std::set<std::string>::iterator initIter = dotState.initialchilds.begin(); initIter != dotState.initialchilds.end(); initIter++) { + std::string destId = *initIter; + DotEdge edge(stateId, destId); + edge.type = EDGE_INITAL; + if (_graph.find(destId) != _graph.end()) + _edges.insert(edge); + } - DotState::mmap_s_e_t::const_iterator destIterF, destIterB; - std::list<std::string> outPorts; // count unique keys -#if PER_EVENT_TRANS - // count unique event names - for(DotState::mmap_s_e_t::const_iterator it = state.events.begin(), end = state.events.end(); - it != end; - it = state.events.upper_bound(it->first)) { - outPorts.push_back(it->first); } -#else - // count unique adjecent nodes - for(DotState::mmap_s_e_t::const_iterator it = state.targets.begin(), end = state.targets.end(); - it != end; - it = state.targets.upper_bound(it->first)) { - outPorts.push_back(it->first); + + // recurse into children and search others to draw + NodeSet<std::string> childElems = InterpreterImpl::filterChildType(Node_base::ELEMENT_NODE, stateElem); + for (int i = 0; i < childElems.size(); i++) { + Element<std::string> childElem(childElems[i]); + if (InterpreterImpl::isState(Element<std::string>(childElem))) { + writeStateElement(os, childElem); + } } -#endif - os << getPrefix() << "label = < " << std::endl; + if (subgraph) { + _indentation--; + os << getPrefix() << "} #" << stateId << std::endl; + _indentation--; + } +} - /* - <table cellborder="1" border="0" cellspacing="0" cellpadding="2" style="rounded"> - <tr><td port="name" rowspan="4"><b>step</b></td></tr> - <tr><td port="foo.error.port" align="right">foo.error.port</td></tr> - <tr><td port="bar" align="right">bar</td></tr> - <tr><td port="baz" align="right">baz</td></tr> - </table> - */ - - std::string details = getDetailedLabel(stateElem); - - os << "<table " << (isInitial ? "bgcolor=\"orange\" " : "") << "cellborder=\"1\" border=\"0\" cellspacing=\"0\" cellpadding=\"2\" >" << std::endl; - os << " <tr><td port=\"__name\" rowspan=\"" << outPorts.size() + 1 << "\"><b>" << nameForNode(stateElem) << "</b></td></tr>" << std::endl; - for(std::list<std::string>::iterator nameIter = outPorts.begin(); nameIter != outPorts.end(); nameIter++) { -#ifdef PER_EVENT_TRANS - os << " <tr><td port=\"" << portEscape(*nameIter) << "\" align=\"right\">" << *nameIter << "</td></tr>" << std::endl; -#else - // gather all events that activate the transition - std::string portName = *nameIter; +void SCXMLDotWriter::writePerTransitionPorts(std::ostream& os, const std::list<std::string>& outPorts, const DotState& dotState) { + // TODO: Not implemented +} -// std::cout << ATTR(stateElem, "id") << std::endl; +void SCXMLDotWriter::writePerEventPorts(std::ostream& os, const std::list<std::string>& outPorts, const DotState& dotState) { + // TODO: Not implemented - if (ATTR(stateElem, "id") == "ConfirmQuit") { - assert(true); - } + // outports contain event names + std::string stateId = idForNode(dotState.node); + DotState::mmap_s_e_t::const_iterator destIterF, destIterB; + + for(std::list<std::string>::const_iterator nameIter = outPorts.begin(); nameIter != outPorts.end(); nameIter++) { + os << " <tr><td port=\"" << portEscape(*nameIter) << "\" align=\"right\">" << *nameIter << "</td></tr>" << std::endl; + } +} +void SCXMLDotWriter::writePerTargetPorts(std::ostream& os, const std::list<std::string>& outPorts, const DotState& dotState) { + // outports contain remote node ids + std::string stateId = idForNode(dotState.node); + DotState::mmap_s_e_t::const_iterator destIterF, destIterB; + + for(std::list<std::string>::const_iterator nameIter = outPorts.begin(); nameIter != outPorts.end(); nameIter++) { + + // gather all events that activate the transition + std::string portName = *nameIter; + DotEdge edge(stateId, portName); + edge.fromPort = portName; + std::multimap<std::string, std::string> eventConds; // event to condition - std::pair <DotState::mmap_s_e_t::const_iterator, DotState::mmap_s_e_t::const_iterator> targetKeyRange = state.targets.equal_range(portName); + std::pair <DotState::mmap_s_e_t::const_iterator, DotState::mmap_s_e_t::const_iterator> targetKeyRange = dotState.targets.equal_range(portName); for (destIterB = targetKeyRange.first; destIterB != targetKeyRange.second; ++destIterB) { - const Arabica::DOM::Element<std::string>& transElem = destIterB->second; + const Element<std::string>& transElem = destIterB->second; std::list<std::string> eventNames = InterpreterImpl::tokenizeIdRefs(ATTR(transElem, "event")); for (std::list<std::string>::iterator eventIter = eventNames.begin(); eventIter != eventNames.end(); eventIter++) { eventConds.insert(std::make_pair(*eventIter, ATTR(transElem, "cond"))); } if (eventNames.size() == 0) { // spontaneous transition - eventConds.insert(std::make_pair("∅", ATTR(transElem, "cond"))); + eventConds.insert(std::make_pair("#", ATTR(transElem, "cond"))); + edge.type = EDGE_SPONTANEOUS; } } - + if (_graph.find(portName) != _graph.end()) + _edges.insert(edge); + typedef std::multimap<std::string, std::string>::iterator condIter_t; std::stringstream outPortSS; outPortSS << "<b>" << portName << "</b><br align=\"right\" />"; - + std::string opener = "{"; std::string closer; std::string seperator; @@ -326,125 +479,25 @@ void SCXMLDotWriter::writeStateElement(std::ostream& os, const DotState& state) for(iterA = eventConds.begin(); iterA != eventConds.end(); iterA = iterB) { std::string eventName = iterA->first; bool hasCondition = false; - + std::pair <condIter_t, condIter_t> condRange = eventConds.equal_range(eventName); for (iterB = condRange.first; iterB != condRange.second; ++iterB) { hasCondition = true; } - + outPortSS << opener << seperator << eventName << (hasCondition ? "" : ""); seperator = ", "; opener = ""; closer = "}"; } outPortSS << closer; - + os << " <tr><td port=\"" << portEscape(portName) << "\" align=\"right\">" << outPortSS.str() << "</td></tr>" << std::endl; -#endif - } - - if (details.size() > 0) { - os << " <tr><td colspan=\"" << (outPorts.size() == 0 ? 1 : 2) << "\">" << std::endl; - os << details << std::endl; - os << " </td></tr>" << std::endl; - } - - Arabica::XPath::NodeSet<std::string> histories = InterpreterImpl::filterChildElements(_xmlNSPrefix + "history", stateElem); - for (int i = 0; i < histories.size(); i++) { - os << " <tr><td port=\"" << ATTR(histories[i], "id") << "\" colspan=\"" << (outPorts.size() == 0 ? 1 : 2) << "\"><b>history: </b>" << ATTR(histories[i], "id") << "</td></tr>" << std::endl; - - } - - os << "</table>" << std::endl << getPrefix() << ">" << std::endl; - - _indentation--; - os << getPrefix() << "];" << std::endl; - - // always display childs up to the desired depth - for (std::set<std::string>::iterator childIter = state.childs.begin(); childIter != state.childs.end(); childIter++) { - if (_graph.find(*childIter) != _graph.end()) - writeStateElement(os, _graph[*childIter]); - } - - if (subgraph) { - _indentation--; - os << getPrefix() << "} " << std::endl; - _indentation--; - } - -#if 1 - std::string initialEdgeStyle = "style=\"dashed\", color=\"black\""; - std::string transitionEdgeStyle = "color=black"; - - for (std::set<std::string>::iterator initIter = state.initialchilds.begin(); initIter != state.initialchilds.end(); initIter++) { - std::string destId = *initIter; - if (_histories.find(destId) != _histories.end()) { - os << getPrefix() << stateId << ":__name -> " << _histories[destId] << " [" << initialEdgeStyle << "]" << std::endl; - } else if(InterpreterImpl::isFinal(_graph[destId].node)) { - os << getPrefix() << stateId << ":__name -> " << destId << ":__name:nw [" << initialEdgeStyle << "]" << std::endl; - } else { - os << getPrefix() << stateId << ":__name -> " << destId << ":__name:nw [" << initialEdgeStyle << "]" << std::endl; - } - } - -#if PER_EVENT_TRANS - // iterate all events and make connections - DotState::mmap_s_e_t::const_iterator destIterF, destIterB; - for(destIterF = state.events.begin(); destIterF != state.events.end(); destIterF = destIterB) { - std::string eventName = destIterF->first; - - // all these react to the same event - std::pair <DotState::mmap_s_e_t::const_iterator,DotState::mmap_s_e_t::const_iterator> keyRange = state.events.equal_range(eventName); - for (destIterB = keyRange.first; destIterB != keyRange.second; ++destIterB) { - const Arabica::DOM::Element<std::string>& transElem = destIterB->second; - Arabica::XPath::NodeSet<std::string> targetStates = _interpreter.getImpl()->getTargetStates(transElem); - for (int i = 0; i < targetStates.size(); i++) { - std::string destId = idForNode(targetStates[i]); - if (_histories.find(destId) != _histories.end()) { - os << getPrefix() << "" << stateId << ":\"" << portEscape(eventName) << "\" -> " << _histories[destId] << " [" << transitionEdgeStyle << "]" << std::endl; - } else if(InterpreterImpl::isFinal(_graph[destId].node)) { - os << getPrefix() << stateId << ":\"" << portEscape(eventName) << "\" -> " << destId << " [" << transitionEdgeStyle << "]" << std::endl; - } else { - os << getPrefix() << "" << stateId << ":\"" << portEscape(eventName) << "\" -> " << destId << ":__name [" << transitionEdgeStyle << "]" << std::endl; - } - } - } - } -#else - // iterate all *targets* and make connections - for(destIterF = state.targets.begin(); destIterF != state.targets.end(); destIterF = destIterB) { - std::string eventName = destIterF->first; - - // all these react to the same event - std::pair <DotState::mmap_s_e_t::const_iterator,DotState::mmap_s_e_t::const_iterator> keyRange = state.targets.equal_range(eventName); - std::set<Arabica::DOM::Element<std::string> > targetSet; - for (destIterB = keyRange.first; destIterB != keyRange.second; ++destIterB) { - const Arabica::DOM::Element<std::string>& transElem = destIterB->second; - Arabica::XPath::NodeSet<std::string> targetStates = _interpreter.getImpl()->getTargetStates(transElem); - for (int i = 0; i < targetStates.size(); i++) { - targetSet.insert(Arabica::DOM::Element<std::string>(targetStates[i])); - } - } - for (std::set<Arabica::DOM::Element<std::string> >::iterator stateIter = targetSet.begin(); stateIter != targetSet.end(); stateIter++) { - std::string destId = idForNode(*stateIter); - if (_histories.find(destId) != _histories.end()) { - os << getPrefix() << "" << stateId << ":\"" << portEscape(eventName) << "\" -> " << _histories[destId] << " [" << transitionEdgeStyle << "]" << std::endl; - } else if(InterpreterImpl::isFinal(_graph[destId].node)) { - os << getPrefix() << stateId << ":\"" << portEscape(eventName) << "\" -> " << destId << " [" << transitionEdgeStyle << "]" << std::endl; - } else { - os << getPrefix() << "" << stateId << ":\"" << portEscape(eventName) << "\" -> " << destId << ":__name [" << transitionEdgeStyle << "]" << std::endl; - } - writeStateElement(os, _graph[destId]); - } } -#endif - -#endif - } -std::string SCXMLDotWriter::getDetailedLabel(const Arabica::DOM::Element<std::string>& elem, int indentation) { +std::string SCXMLDotWriter::getDetailedLabel(const Element<std::string>& elem, int indentation) { /* <table> @@ -525,6 +578,12 @@ std::string SCXMLDotWriter::getDetailedLabel(const Arabica::DOM::Element<std::st } // send --------- + if (iequals(TAGNAME(childElems.item(i)), "raise")) { + if (HAS_ATTR(childElems.item(i), "event ")) + details.name += "<br />event = " + ATTR(childElems.item(i), "event"); + } + + // send --------- if (iequals(TAGNAME(childElems.item(i)), "send")) { if (HAS_ATTR(childElems.item(i), "id")) details.name += "<br />id = " + ATTR(childElems.item(i), "id"); @@ -597,7 +656,7 @@ std::string SCXMLDotWriter::getDetailedLabel(const Arabica::DOM::Element<std::st } // recurse - details.content = getDetailedLabel((Arabica::DOM::Element<std::string>)childElems.item(i), indentation + 1); + details.content = getDetailedLabel((Element<std::string>)childElems.item(i), indentation + 1); content.push_back(details); } @@ -631,7 +690,7 @@ std::string SCXMLDotWriter::portEscape(const std::string& text) { std::string escaped(text); boost::replace_all(escaped, ".", "-"); - return escaped; + return text; } std::string SCXMLDotWriter::dotEscape(const std::string& text) { @@ -655,14 +714,22 @@ std::string SCXMLDotWriter::colorForIndent(int indent) { return ss.str(); } -std::string SCXMLDotWriter::nameForNode(const Arabica::DOM::Node<std::string>& node) { +std::string SCXMLDotWriter::nameForNode(const Node<std::string>& node) { std::string elemName; if (node.getNodeType() == Node_base::ELEMENT_NODE) { - Arabica::DOM::Element<std::string> elem = (Arabica::DOM::Element<std::string>)node; - if (InterpreterImpl::isParallel(elem)) - elemName += "<i>Parallel</i><br />"; - if (elem.hasAttribute("name")) { - elemName += elem.getAttribute("name"); + Element<std::string> elem = (Element<std::string>)node; + + if (false) { + } else if (elem.getTagName() == "scxml") { + if (elem.hasAttribute("name") && !UUID::isUUID(elem.getAttribute("name"))) { + elemName += elem.getAttribute("name"); + } else if (elem.hasAttribute("id") && !UUID::isUUID(elem.getAttribute("id"))) { + elemName += elem.getAttribute("id"); + } + } else if (InterpreterImpl::isCompound(elem)) { + elemName = "<i>Compound: </i>" + elem.getAttribute("id"); + } else if (InterpreterImpl::isParallel(elem)) { + elemName = "<i>Parallel: </i>" + elem.getAttribute("id"); } else if (elem.hasAttribute("id")) { elemName += elem.getAttribute("id"); } @@ -674,12 +741,12 @@ std::string SCXMLDotWriter::nameForNode(const Arabica::DOM::Node<std::string>& n } -std::string SCXMLDotWriter::idForNode(const Arabica::DOM::Node<std::string>& node) { +std::string SCXMLDotWriter::idForNode(const Node<std::string>& node) { std::string elemId; // try to get the id as the name or id attribute if (node.getNodeType() == Node_base::ELEMENT_NODE) { - Arabica::DOM::Element<std::string> elem = (Arabica::DOM::Element<std::string>)node; + Element<std::string> elem = (Element<std::string>)node; if (elem.hasAttribute("name")) { elemId = elem.getAttribute("name"); } else if (elem.hasAttribute("id")) { @@ -689,8 +756,8 @@ std::string SCXMLDotWriter::idForNode(const Arabica::DOM::Node<std::string>& nod // no luck, create id from position in tree if (elemId.size() == 0) { - Arabica::DOM::Node<std::string> tmpParent = node; - Arabica::DOM::Node<std::string> tmpIndex; + Node<std::string> tmpParent = node; + Node<std::string> tmpIndex; do { if (tmpParent.getNodeType() != Node_base::ELEMENT_NODE) continue; diff --git a/src/uscxml/debug/SCXMLDotWriter.h b/src/uscxml/debug/SCXMLDotWriter.h index 30d5fcf..79ea6c1 100644 --- a/src/uscxml/debug/SCXMLDotWriter.h +++ b/src/uscxml/debug/SCXMLDotWriter.h @@ -57,10 +57,23 @@ class Interpreter; class USCXML_API SCXMLDotWriter : public InterpreterMonitor { public: + enum PortType { + PORT_TARGET, + PORT_EVENT, + PORT_TRANSITION + }; + struct StateAnchor { - StateAnchor() : depth(std::numeric_limits<int32_t>::max()) {} + StateAnchor() : childDepth(-1), transDepth(-1), type(PORT_TARGET) {} Arabica::DOM::Element<std::string> element; - uint32_t depth; + int32_t childDepth; + int32_t transDepth; + + PortType type; + + operator bool() const { + return childDepth != -1 || transDepth != -1 || element; + } }; struct ElemDetails { @@ -70,17 +83,45 @@ public: }; struct DotState { - DotState() : isBorder(false) {} + DotState() : isBorder(false), portType(PORT_TARGET) {} Arabica::DOM::Element<std::string> node; + Arabica::XPath::NodeSet<std::string> transitions; std::multimap<std::string, Arabica::DOM::Element<std::string> > targets; // key is remote node, transition is element std::multimap<std::string, Arabica::DOM::Element<std::string> > events; // key is event name, value is transitions that react bool isBorder; + PortType portType; + std::set<std::string> childs; std::set<std::string> initialchilds; + typedef std::multimap<std::string, Arabica::DOM::Element<std::string> > mmap_s_e_t; }; + enum EdgeType { + EDGE_INITAL, + EDGE_TRANSIION, + EDGE_SPONTANEOUS + }; + + struct DotEdge { + DotEdge() : type(EDGE_TRANSIION) {} + DotEdge(const std::string& from, const std::string& to) : type(EDGE_TRANSIION), from(from), to(to) { + } + + bool operator< (const DotEdge& other) const { + return from + fromPort + to + toPort > other.from + other.fromPort + other.to + other.toPort; + } + + EdgeType type; + std::string from; + std::string fromCompass; + std::string fromPort; + std::string to; + std::string toPort; + std::string toCompass; + }; + SCXMLDotWriter(); ~SCXMLDotWriter(); @@ -120,8 +161,15 @@ protected: const std::list<SCXMLDotWriter::StateAnchor>& stateAnchors, const Arabica::DOM::Element<std::string>& transition); - void assembleGraph(const Arabica::DOM::Element<std::string>& start, uint32_t depth = std::numeric_limits<int32_t>::max()); - void writeStateElement(std::ostream& os, const DotState& elem); + void assembleGraph(const Arabica::DOM::Element<std::string>& start, + int32_t childDepth = std::numeric_limits<int32_t>::max(), + int32_t transDepth = std::numeric_limits<int32_t>::max()); + void writeStateElement(std::ostream& os, const Arabica::DOM::Element<std::string>& state); + + void writePerTransitionPorts(std::ostream& os, const std::list<std::string>& outPorts, const DotState& dotState); + void writePerEventPorts(std::ostream& os, const std::list<std::string>& outPorts, const DotState& dotState); + void writePerTargetPorts(std::ostream& os, const std::list<std::string>& outPorts, const DotState& dotState); + void writeUnknownNode(std::ostream& os, const std::string& targetId); int _iteration; @@ -130,13 +178,18 @@ protected: int _indentation; std::map<std::string, DotState> _graph; - + std::set<DotEdge> _edges; + // these are only set in ephemeral instances per monitor call Arabica::DOM::Element<std::string> _transition; + Arabica::DOM::Element<std::string> _scxml; Interpreter _interpreter; std::string _xmlNSPrefix; std::list<StateAnchor> _anchors; - std::map<std::string, std::string> _histories; + std::map<std::string, DotEdge> _histories; + + PortType _portType; + }; } diff --git a/src/uscxml/plugins/ioprocessor/modality/MMIMessages.cpp b/src/uscxml/plugins/ioprocessor/modality/MMIMessages.cpp index 8ab95ea..6db5ac4 100644 --- a/src/uscxml/plugins/ioprocessor/modality/MMIMessages.cpp +++ b/src/uscxml/plugins/ioprocessor/modality/MMIMessages.cpp @@ -24,7 +24,7 @@ #include <DOM/SAX2DOM/SAX2DOM.hpp> #include <SAX/helpers/InputSourceResolver.hpp> -#include <uscxml/NameSpacingParser.h> +#include <uscxml/DOMUtils.h> #include <boost/algorithm/string.hpp> @@ -109,7 +109,7 @@ Arabica::DOM::Node<std::string> MMIEvent::getEventNode(Arabica::DOM::Node<std::s Arabica::DOM::Document<std::string> MMIEvent::toXML() const { Arabica::DOM::DOMImplementation<std::string> domFactory = Arabica::SimpleDOM::DOMImplementation<std::string>::getDOMImplementation(); Document<std::string> doc = domFactory.createDocument(nameSpace, "", 0); - Element<std::string> mmiElem = doc.createElementNS(nameSpace, "mmi"); +// Element<std::string> mmiElem = doc.createElementNS(nameSpace, "mmi"); Element<std::string> msgElem = doc.createElementNS(nameSpace, tagName); msgElem.setAttributeNS(nameSpace, "Source", source); msgElem.setAttributeNS(nameSpace, "Target", target); @@ -136,8 +136,9 @@ Arabica::DOM::Document<std::string> MMIEvent::toXML() const { msgElem.appendChild(dataElem); } - mmiElem.appendChild(msgElem); - doc.appendChild(mmiElem); +// mmiElem.appendChild(msgElem); +// doc.appendChild(mmiElem); + doc.appendChild(msgElem); return doc; } diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 62b8749..436f52e 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -134,6 +134,11 @@ target_link_libraries(test-sockets uscxml) # add_test(test-datamodel ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test-sockets) set_target_properties(test-sockets PROPERTIES FOLDER "Tests") +add_executable(test-vxml-mmi-socket src/test-vxml-mmi-socket.cpp) +target_link_libraries(test-vxml-mmi-socket uscxml) +# add_test(test-datamodel ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test-sockets) +set_target_properties(test-vxml-mmi-socket PROPERTIES FOLDER "Tests") + # if (NOT WIN32) # add_executable(test-mmi src/test-mmi.cpp) # target_link_libraries(test-mmi uscxml) diff --git a/test/src/test-vxml-mmi-socket.cpp b/test/src/test-vxml-mmi-socket.cpp new file mode 100644 index 0000000..b57b7f6 --- /dev/null +++ b/test/src/test-vxml-mmi-socket.cpp @@ -0,0 +1,72 @@ +#include "uscxml/config.h" +#include "uscxml/server/Socket.h" +#include <iostream> +#include <stdexcept> + +#include <event2/event.h> +#include "event2/thread.h" + +#ifdef HAS_SIGNAL_H +#include <signal.h> +#endif + +#include "uscxml/concurrency/tinythread.h" +#include "uscxml/plugins/ioprocessor/modality/MMIMessages.h" +#include <DOM/io/Stream.hpp> + +#include "uscxml/plugins/ioprocessor/modality/MMIMessages.cpp" + + +using namespace uscxml; + +class TestServer : public ServerSocket { +public: + TestServer(int domain, int type, int protocol) : ServerSocket(domain, type, protocol) {} + virtual void readCallback(const char* data, size_t size, Connection& conn) { + std::string content(data, size); +// std::cout << "Server got: " << content << std::endl; + std::string urghs("hi!"); + conn.reply(urghs.data(), urghs.size()); + }; +}; + +class TestClient : public ClientSocket { +public: + TestClient(int domain, int type, int protocol) : ClientSocket(domain, type, protocol) {} + virtual void readCallback(const char* data, size_t size) { + std::string content(data, size); + }; +}; + +int main(int argc, char** argv) { + +#if defined(HAS_SIGNAL_H) && !defined(WIN32) + signal(SIGPIPE, SIG_IGN); +#endif + +#ifndef _WIN32 + evthread_use_pthreads(); +#else + evthread_use_windows_threads(); +#endif + +// TestClient client(PF_INET, SOCK_STREAM, 0); +// client.connect("epikur.local", 4343); + + StartRequest startReq; + startReq.source = "undefined.source"; + startReq.target = "epikur.local:4343"; + startReq.requestId = "131234141234"; + startReq.data = + "<vxml xmlns=\"http://www.w3.org/2001/vxml\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" version=\"2.1\" xml:lang=\"en\"" + "xsi:schematicLocation=\"http://www.w3.org/2001/vxml http://www.w3.org/TR/voicexml20/vxml.xsd\">" + " <prompt>Goodbye!</prompt>" + "</vxml>"; + + Arabica::DOM::Document<std::string> reqXML = startReq.toXML(); + std::stringstream xmlSS; + xmlSS << reqXML; + std::cout << reqXML; + +// client.write(xmlSS.str().data(), xmlSS.str().size()); +}
\ No newline at end of file |