From 02f52d15e7df2500c0c6e96660a43a985add16e8 Mon Sep 17 00:00:00 2001 From: Stefan Radomski Date: Wed, 13 Aug 2014 13:48:26 +0200 Subject: Builds for Raspberry and started VoiceXML HTTP integration --- CMakeLists.txt | 7 +- README.md | 2 +- contrib/build-scripts/build-libevent-linux.sh | 2 +- contrib/cmake/FindLua.cmake | 171 +++++++++++++++++++++ contrib/cmake/FindSWI.cmake | 2 +- src/uscxml/URL.cpp | 11 +- .../plugins/ioprocessor/modality/MMIMessages.cpp | 135 ++++++++-------- .../plugins/ioprocessor/modality/MMIMessages.h | 15 +- src/uscxml/server/Socket.cpp | 109 +++++++++++-- src/uscxml/server/Socket.h | 22 +++ src/uscxml/transform/ChartToFSM.cpp | 136 +++++++++------- src/uscxml/transform/ChartToFSM.h | 9 +- test/CMakeLists.txt | 5 + test/src/test-sockets.cpp | 67 +++++++- test/src/test-vxml-mmi-http.cpp | 165 ++++++++++++++++++++ test/src/test-vxml-mmi-socket.cpp | 133 +++++++++++++--- 16 files changed, 804 insertions(+), 187 deletions(-) create mode 100644 contrib/cmake/FindLua.cmake create mode 100644 test/src/test-vxml-mmi-http.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 4905554..ccc4a11 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,7 +14,7 @@ endif() # specify USCXML version SET(USCXML_VERSION_MAJOR "0") SET(USCXML_VERSION_MINOR "3") -SET(USCXML_VERSION_PATCH "0") +SET(USCXML_VERSION_PATCH "1") SET(USCXML_VERSION ${USCXML_VERSION_MAJOR}.${USCXML_VERSION_MINOR}.${USCXML_VERSION_PATCH}) # build type has to be set before the project definition4 @@ -164,7 +164,7 @@ else () endif() endif() -SET(USCXML_LIBRARY_HOST_URL_PREFIX "http://uscxml.mintwerk.de/prebuilt" CACHE STRING "The root path of an URL where to look for prebuilt libraries.") +SET(USCXML_LIBRARY_HOST_URL_PREFIX "http://uscxml.tk.informatik.tu-darmstadt.de/prebuilt" CACHE STRING "The root path of an URL where to look for prebuilt libraries.") if (CMAKE_CROSSCOMPILING) if (IOS) @@ -238,7 +238,8 @@ if (NOT EXISTS ${USCXML_PREBUILT_LIBRARY_PATH}) ) file(WRITE ${PROJECT_SOURCE_DIR}/contrib/prebuilt/${USCXML_PLATFORM_ID}/VERSION.txt "${USCXML_VERSION}") else() - message("Downloading prebuilt libraries failed with ${STATUS_STRING} - maybe this platform is not supported?") + message("Downloading prebuilt libraries failed with ${STATUS_STRING}") + message("Provide prebuilts in ${PROJECT_SOURCE_DIR}/contrib/prebuilt/${USCXML_PLATFORM_ID}/") endif() endif() diff --git a/README.md b/README.md index 14bd901..76a4eff 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ ## General uSCXML is a SCXML interpreter written in C/C++. It is [standards compliant](#test-reports) and [easily extended](#extending-uscxml) -even in C# and Java. It runs on Linux, Windows and Mac OSX, each 32- as well as 64Bits as well as iOS. +even in C# and Java. It runs on Linux, Windows, Raspberry Pi and Mac OSX, each 32- as well as 64Bits as well as iOS. * Datamodels * Full [ECMAScript datamodel](https://github.com/tklab-tud/uscxml/tree/master/src/uscxml/plugins/datamodel/ecmascript) using Google's v8 (and JavaScriptCore on MacOSX and iOS) diff --git a/contrib/build-scripts/build-libevent-linux.sh b/contrib/build-scripts/build-libevent-linux.sh index cdb6115..c57ad52 100755 --- a/contrib/build-scripts/build-libevent-linux.sh +++ b/contrib/build-scripts/build-libevent-linux.sh @@ -20,7 +20,7 @@ if [ ! -f event.c ]; then exit fi -rm lib*.a +#rm lib*.a if [ -f Makefile ]; then make clean diff --git a/contrib/cmake/FindLua.cmake b/contrib/cmake/FindLua.cmake new file mode 100644 index 0000000..f8b68b2 --- /dev/null +++ b/contrib/cmake/FindLua.cmake @@ -0,0 +1,171 @@ +#.rst: +# FindLua +# ------- +# +# +# +# Locate Lua library This module defines +# +# :: +# +# LUA_FOUND - if false, do not try to link to Lua +# LUA_LIBRARIES - both lua and lualib +# LUA_INCLUDE_DIR - where to find lua.h +# LUA_VERSION_STRING - the version of Lua found +# LUA_VERSION_MAJOR - the major version of Lua +# LUA_VERSION_MINOR - the minor version of Lua +# LUA_VERSION_PATCH - the patch version of Lua +# +# +# +# Note that the expected include convention is +# +# :: +# +# #include "lua.h" +# +# and not +# +# :: +# +# #include +# +# This is because, the lua location is not standardized and may exist in +# locations other than lua/ + +#============================================================================= +# Copyright 2007-2009 Kitware, Inc. +# Copyright 2013 Rolf Eike Beer +# +# Distributed under the OSI-approved BSD License (the "License"); +# see accompanying file Copyright.txt for details. +# +# This software is distributed WITHOUT ANY WARRANTY; without even the +# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the License for more information. +#============================================================================= +# (To distribute this file outside of CMake, substitute the full +# License text for the above reference.) + +unset(_lua_include_subdirs) +unset(_lua_library_names) + +# this is a function only to have all the variables inside go away automatically +function(set_lua_version_vars) + set(LUA_VERSIONS5 5.3 5.2 5.1 5.0) + + if (Lua_FIND_VERSION_EXACT) + if (Lua_FIND_VERSION_COUNT GREATER 1) + set(lua_append_versions ${Lua_FIND_VERSION_MAJOR} ${Lua_FIND_VERSION_MINOR}) + endif () + elseif (Lua_FIND_VERSION) + # once there is a different major version supported this should become a loop + if (NOT Lua_FIND_VERSION_MAJOR GREATER 5) + if (Lua_FIND_VERSION_COUNT EQUAL 1) + set(lua_append_versions ${LUA_VERSIONS5}) + else () + foreach (subver IN LISTS LUA_VERSIONS5) + if (NOT subver VERSION_LESS ${Lua_FIND_VERSION}) + list(APPEND lua_append_versions ${subver}) + endif () + endforeach () + endif () + endif () + else () + # once there is a different major version supported this should become a loop + set(lua_append_versions ${LUA_VERSIONS5}) + endif () + + foreach (ver IN LISTS lua_append_versions) + string(REGEX MATCH "^([0-9]+)\\.([0-9]+)$" _ver "${ver}") + list(APPEND _lua_include_subdirs + include/lua${CMAKE_MATCH_1}${CMAKE_MATCH_2} + include/lua${CMAKE_MATCH_1}.${CMAKE_MATCH_2} + include/lua-${CMAKE_MATCH_1}.${CMAKE_MATCH_2} + ) + list(APPEND _lua_library_names + lua${CMAKE_MATCH_1}${CMAKE_MATCH_2} + lua${CMAKE_MATCH_1}.${CMAKE_MATCH_2} + lua-${CMAKE_MATCH_1}.${CMAKE_MATCH_2} + ) + endforeach () + + set(_lua_include_subdirs "${_lua_include_subdirs}" PARENT_SCOPE) + set(_lua_library_names "${_lua_library_names}" PARENT_SCOPE) +endfunction(set_lua_version_vars) + +set_lua_version_vars() + +find_path(LUA_INCLUDE_DIR lua.h + HINTS + ENV LUA_DIR + PATH_SUFFIXES ${_lua_include_subdirs} include/lua include + PATHS + ~/Library/Frameworks + /Library/Frameworks + /sw # Fink + /opt/local # DarwinPorts + /opt/csw # Blastwave + /opt +) +unset(_lua_include_subdirs) + +find_library(LUA_LIBRARY + NAMES ${_lua_library_names} lua + HINTS + ENV LUA_DIR + PATH_SUFFIXES lib + PATHS + ~/Library/Frameworks + /Library/Frameworks + /sw + /opt/local + /opt/csw + /opt +) +unset(_lua_library_names) + +if (LUA_LIBRARY) + # include the math library for Unix + if (UNIX AND NOT APPLE AND NOT BEOS) + find_library(LUA_MATH_LIBRARY m) + set(LUA_LIBRARIES "${LUA_LIBRARY};${LUA_MATH_LIBRARY}") + # For Windows and Mac, don't need to explicitly include the math library + else () + set(LUA_LIBRARIES "${LUA_LIBRARY}") + endif () +endif () + +if (LUA_INCLUDE_DIR AND EXISTS "${LUA_INCLUDE_DIR}/lua.h") + # At least 5.[012] have different ways to express the version + # so all of them need to be tested. Lua 5.2 defines LUA_VERSION + # and LUA_RELEASE as joined by the C preprocessor, so avoid those. + file(STRINGS "${LUA_INCLUDE_DIR}/lua.h" lua_version_strings + REGEX "^#define[ \t]+LUA_(RELEASE[ \t]+\"Lua [0-9]|VERSION([ \t]+\"Lua [0-9]|_[MR])).*") + + string(REGEX REPLACE ".*;#define[ \t]+LUA_VERSION_MAJOR[ \t]+\"([0-9])\"[ \t]*;.*" "\\1" LUA_VERSION_MAJOR ";${lua_version_strings};") + if (LUA_VERSION_MAJOR MATCHES "^[0-9]+$") + string(REGEX REPLACE ".*;#define[ \t]+LUA_VERSION_MINOR[ \t]+\"([0-9])\"[ \t]*;.*" "\\1" LUA_VERSION_MINOR ";${lua_version_strings};") + string(REGEX REPLACE ".*;#define[ \t]+LUA_VERSION_RELEASE[ \t]+\"([0-9])\"[ \t]*;.*" "\\1" LUA_VERSION_PATCH ";${lua_version_strings};") + set(LUA_VERSION_STRING "${LUA_VERSION_MAJOR}.${LUA_VERSION_MINOR}.${LUA_VERSION_PATCH}") + else () + string(REGEX REPLACE ".*;#define[ \t]+LUA_RELEASE[ \t]+\"Lua ([0-9.]+)\"[ \t]*;.*" "\\1" LUA_VERSION_STRING ";${lua_version_strings};") + if (NOT LUA_VERSION_STRING MATCHES "^[0-9.]+$") + string(REGEX REPLACE ".*;#define[ \t]+LUA_VERSION[ \t]+\"Lua ([0-9.]+)\"[ \t]*;.*" "\\1" LUA_VERSION_STRING ";${lua_version_strings};") + endif () + string(REGEX REPLACE "^([0-9]+)\\.[0-9.]*$" "\\1" LUA_VERSION_MAJOR "${LUA_VERSION_STRING}") + string(REGEX REPLACE "^[0-9]+\\.([0-9]+)[0-9.]*$" "\\1" LUA_VERSION_MINOR "${LUA_VERSION_STRING}") + string(REGEX REPLACE "^[0-9]+\\.[0-9]+\\.([0-9]).*" "\\1" LUA_VERSION_PATCH "${LUA_VERSION_STRING}") + endif () + + unset(lua_version_strings) +endif() + +include(FindPackageHandleStandardArgs) +# handle the QUIETLY and REQUIRED arguments and set LUA_FOUND to TRUE if +# all listed variables are TRUE +FIND_PACKAGE_HANDLE_STANDARD_ARGS(Lua + REQUIRED_VARS LUA_LIBRARIES LUA_INCLUDE_DIR + VERSION_VAR LUA_VERSION_STRING) + +mark_as_advanced(LUA_INCLUDE_DIR LUA_LIBRARY LUA_MATH_LIBRARY) diff --git a/contrib/cmake/FindSWI.cmake b/contrib/cmake/FindSWI.cmake index 7aa5f32..f89f705 100644 --- a/contrib/cmake/FindSWI.cmake +++ b/contrib/cmake/FindSWI.cmake @@ -186,7 +186,7 @@ endif() #message(FATAL_ERROR "SWI_BINARY: ${SWI_BINARY} / SWI_LIBRARY_RELEASE: ${SWI_LIBRARY_RELEASE} / SWI_LIBRARY_DEBUG: ${SWI_LIBRARY_DEBUG} / SWI_INCLUDE_DIR: ${SWI_INCLUDE_DIR} / SWI_CPP_INCLUDE_DIR: ${SWI_CPP_INCLUDE_DIR}") INCLUDE(FindPackageHandleStandardArgs) -FIND_PACKAGE_HANDLE_STANDARD_ARGS(SWI DEFAULT_MSG SWI_LIBRARY SWI_BINARY SWI_INCLUDE_DIR SWI_CPP_INCLUDE_DIR) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(SWI DEFAULT_MSG SWI_LIBRARY SWI_BINARY SWI_INCLUDE_DIR) if (SWI_FOUND) diff --git a/src/uscxml/URL.cpp b/src/uscxml/URL.cpp index 4e97faa..f5ba85c 100644 --- a/src/uscxml/URL.cpp +++ b/src/uscxml/URL.cpp @@ -341,6 +341,7 @@ void URLImpl::setRequestType(const std::string& requestType) { void URLImpl::setOutContent(const std::string& content) { _outContent = content; + _requestType = "POST"; } const std::string URLImpl::getInContent(bool forceReload) { @@ -602,15 +603,17 @@ void URLFetcher::fetchURL(URL& url) { struct curl_slist* headers = NULL; std::map::iterator paramIter = url._impl->_outHeader.begin(); while(paramIter != url._impl->_outHeader.end()) { - char* key = curl_easy_escape(handle, paramIter->first.c_str(), paramIter->first.length()); - char* value = curl_easy_escape(handle, paramIter->second.c_str(), paramIter->second.length()); +// char* key = curl_easy_escape(handle, paramIter->first.c_str(), paramIter->first.length()); +// char* value = curl_easy_escape(handle, paramIter->second.c_str(), paramIter->second.length()); + + const char* value = paramIter->second.c_str(); char* header = (char*)malloc(paramIter->first.size() + strlen(value) + 3); sprintf(header,"%s: %s", paramIter->first.c_str(), value); headers = curl_slist_append(headers, header); - curl_free(key); - curl_free(value); +// curl_free(key); +// curl_free(value); paramIter++; } diff --git a/src/uscxml/plugins/ioprocessor/modality/MMIMessages.cpp b/src/uscxml/plugins/ioprocessor/modality/MMIMessages.cpp index 6db5ac4..67a2371 100644 --- a/src/uscxml/plugins/ioprocessor/modality/MMIMessages.cpp +++ b/src/uscxml/plugins/ioprocessor/modality/MMIMessages.cpp @@ -34,6 +34,19 @@ (element.hasAttributeNS(nameSpace, #name) ? element.getAttributeNS(nameSpace, #name) : "") \ ) +#define FIND_EVENT_NODE(node)\ +while (node) {\ + if (node.getNodeType() == Node_base::ELEMENT_NODE) {\ + if (boost::iequals(node.getLocalName(), "MMI")) {\ + node = node.getFirstChild();\ + continue;\ + } else {\ + break;\ + }\ + }\ + node = node.getNextSibling();\ +}\ + namespace uscxml { @@ -42,9 +55,21 @@ using namespace Arabica::DOM; std::string MMIEvent::nameSpace = "http://www.w3.org/2008/04/mmi-arch"; MMIEvent::Type MMIEvent::getType(Arabica::DOM::Node node) { - if (!node) + if (!node || node.getNodeType() != Arabica::DOM::Node_base::ELEMENT_NODE) return INVALID; + // MMI container? + if (boost::iequals(node.getLocalName(), "MMI")) { + node = node.getFirstChild(); + if (!node) + return INVALID; + while(node.getNodeType() != Arabica::DOM::Node_base::ELEMENT_NODE) { + node = node.getNextSibling(); + if (!node) + return INVALID; + } + } + if (boost::iequals(node.getLocalName(), "NEWCONTEXTREQUEST")) return NEWCONTEXTREQUEST; if (boost::iequals(node.getLocalName(), "NEWCONTEXTRESPONSE")) @@ -84,32 +109,9 @@ MMIEvent::Type MMIEvent::getType(Arabica::DOM::Node node) { return INVALID; } -Arabica::DOM::Node MMIEvent::getEventNode(Arabica::DOM::Node node) { - if (!node) - return node; - - if (node.getNodeType() == Node_base::DOCUMENT_NODE) - node = Arabica::DOM::Document(node).getDocumentElement(); - - // get the first element - while (node && node.getNodeType() != Node_base::ELEMENT_NODE) { - node = node.getNextSibling(); - } - // get the contained message - if (node && getType(node) == INVALID) { - node = node.getFirstChild(); - while (node && node.getNodeType() != Node_base::ELEMENT_NODE && getType(node) == INVALID) { - node = node.getNextSibling(); - } - } - return node; -} - - -Arabica::DOM::Document MMIEvent::toXML() const { +Arabica::DOM::Document MMIEvent::toXML(bool encapsulateInMMI) const { Arabica::DOM::DOMImplementation domFactory = Arabica::SimpleDOM::DOMImplementation::getDOMImplementation(); Document doc = domFactory.createDocument(nameSpace, "", 0); -// Element mmiElem = doc.createElementNS(nameSpace, "mmi"); Element msgElem = doc.createElementNS(nameSpace, tagName); msgElem.setAttributeNS(nameSpace, "Source", source); msgElem.setAttributeNS(nameSpace, "Target", target); @@ -136,21 +138,25 @@ Arabica::DOM::Document MMIEvent::toXML() const { msgElem.appendChild(dataElem); } -// mmiElem.appendChild(msgElem); -// doc.appendChild(mmiElem); - doc.appendChild(msgElem); + if (encapsulateInMMI) { + Element mmiElem = doc.createElementNS(nameSpace, "mmi"); + mmiElem.appendChild(msgElem); + doc.appendChild(mmiElem); + } else { + doc.appendChild(msgElem); + } return doc; } -Arabica::DOM::Document ContextualizedRequest::toXML() const { - Document doc = MMIEvent::toXML(); +Arabica::DOM::Document ContextualizedRequest::toXML(bool encapsulateInMMI) const { + Document doc = MMIEvent::toXML(encapsulateInMMI); Element msgElem = Element(doc.getDocumentElement().getFirstChild()); msgElem.setAttributeNS(nameSpace, "Context", context); return doc; } -Arabica::DOM::Document ContentRequest::toXML() const { - Document doc = ContextualizedRequest::toXML(); +Arabica::DOM::Document ContentRequest::toXML(bool encapsulateInMMI) const { + Document doc = ContextualizedRequest::toXML(encapsulateInMMI); Element msgElem = Element(doc.getDocumentElement().getFirstChild()); if (contentURL.href.size() > 0) { @@ -185,15 +191,15 @@ Arabica::DOM::Document ContentRequest::toXML() const { return doc; } -Arabica::DOM::Document ExtensionNotification::toXML() const { - Document doc = ContextualizedRequest::toXML(); +Arabica::DOM::Document ExtensionNotification::toXML(bool encapsulateInMMI) const { + Document doc = ContextualizedRequest::toXML(encapsulateInMMI); Element msgElem = Element(doc.getDocumentElement().getFirstChild()); msgElem.setAttributeNS(nameSpace, "Name", name); return doc; } -Arabica::DOM::Document StatusResponse::toXML() const { - Document doc = ContextualizedRequest::toXML(); +Arabica::DOM::Document StatusResponse::toXML(bool encapsulateInMMI) const { + Document doc = ContextualizedRequest::toXML(encapsulateInMMI); Element msgElem = Element(doc.getDocumentElement().getFirstChild()); if (status == ALIVE) { msgElem.setAttributeNS(nameSpace, "Status", "alive"); @@ -207,8 +213,8 @@ Arabica::DOM::Document StatusResponse::toXML() const { return doc; } -Arabica::DOM::Document StatusInfoResponse::toXML() const { - Document doc = StatusResponse::toXML(); +Arabica::DOM::Document StatusInfoResponse::toXML(bool encapsulateInMMI) const { + Document doc = StatusResponse::toXML(encapsulateInMMI); Element msgElem = Element(doc.getDocumentElement().getFirstChild()); Element statusInfoElem = doc.createElementNS(nameSpace, "StatusInfo"); @@ -219,8 +225,8 @@ Arabica::DOM::Document StatusInfoResponse::toXML() const { return doc; } -Arabica::DOM::Document StatusRequest::toXML() const { - Document doc = ContextualizedRequest::toXML(); +Arabica::DOM::Document StatusRequest::toXML(bool encapsulateInMMI) const { + Document doc = ContextualizedRequest::toXML(encapsulateInMMI); Element msgElem = Element(doc.getDocumentElement().getFirstChild()); if (automaticUpdate) { @@ -234,11 +240,8 @@ Arabica::DOM::Document StatusRequest::toXML() const { MMIEvent MMIEvent::fromXML(Arabica::DOM::Node node, InterpreterImpl* interpreter) { MMIEvent msg; - while (node) { - if (node.getNodeType() == Node_base::ELEMENT_NODE) - break; - node = node.getNextSibling(); - } + FIND_EVENT_NODE(node); + Element msgElem(node); msg.source = STRING_ATTR_OR_EXPR(msgElem, Source); msg.target = STRING_ATTR_OR_EXPR(msgElem, Target); @@ -281,11 +284,8 @@ MMIEvent::operator Event() const { ContextualizedRequest ContextualizedRequest::fromXML(Arabica::DOM::Node node, InterpreterImpl* interpreter) { ContextualizedRequest msg(MMIEvent::fromXML(node, interpreter)); - while (node) { - if (node.getNodeType() == Node_base::ELEMENT_NODE) - break; - node = node.getNextSibling(); - } + FIND_EVENT_NODE(node); + Element msgElem(node); msg.context = STRING_ATTR_OR_EXPR(msgElem, Context); return msg; @@ -300,11 +300,8 @@ ContextualizedRequest::operator Event() const { ContentRequest ContentRequest::fromXML(Arabica::DOM::Node node, InterpreterImpl* interpreter) { ContentRequest msg(ContextualizedRequest::fromXML(node, interpreter)); - while (node) { - if (node.getNodeType() == Node_base::ELEMENT_NODE) - break; - node = node.getNextSibling(); - } + FIND_EVENT_NODE(node); + Element msgElem(node); Element contentElem; @@ -344,11 +341,8 @@ ContentRequest ContentRequest::fromXML(Arabica::DOM::Node node, Int ExtensionNotification ExtensionNotification::fromXML(Arabica::DOM::Node node, InterpreterImpl* interpreter) { ExtensionNotification msg(ContextualizedRequest::fromXML(node, interpreter)); - while (node) { - if (node.getNodeType() == Node_base::ELEMENT_NODE) - break; - node = node.getNextSibling(); - } + FIND_EVENT_NODE(node); + Element msgElem(node); msg.name = STRING_ATTR_OR_EXPR(msgElem, Name); msg.type = EXTENSIONNOTIFICATION; @@ -367,11 +361,8 @@ ExtensionNotification::operator Event() const { StatusResponse StatusResponse::fromXML(Arabica::DOM::Node node, InterpreterImpl* interpreter) { StatusResponse msg(ContextualizedRequest::fromXML(node, interpreter)); - while (node) { - if (node.getNodeType() == Node_base::ELEMENT_NODE) - break; - node = node.getNextSibling(); - } + FIND_EVENT_NODE(node); + Element msgElem(node); std::string status = STRING_ATTR_OR_EXPR(msgElem, Status); @@ -390,11 +381,8 @@ StatusResponse StatusResponse::fromXML(Arabica::DOM::Node node, Int StatusInfoResponse StatusInfoResponse::fromXML(Arabica::DOM::Node node, InterpreterImpl* interpreter) { StatusInfoResponse msg(StatusResponse::fromXML(node, interpreter)); - while (node) { - if (node.getNodeType() == Node_base::ELEMENT_NODE) - break; - node = node.getNextSibling(); - } + FIND_EVENT_NODE(node); + Element msgElem(node); Element statusInfoElem; @@ -424,11 +412,8 @@ StatusInfoResponse StatusInfoResponse::fromXML(Arabica::DOM::Node n StatusRequest StatusRequest::fromXML(Arabica::DOM::Node node, InterpreterImpl* interpreter) { StatusRequest msg(ContextualizedRequest::fromXML(node, interpreter)); - while (node) { - if (node.getNodeType() == Node_base::ELEMENT_NODE) - break; - node = node.getNextSibling(); - } + FIND_EVENT_NODE(node); + Element msgElem(node); std::string autoUpdate = STRING_ATTR_OR_EXPR(msgElem, RequestAutomaticUpdate); diff --git a/src/uscxml/plugins/ioprocessor/modality/MMIMessages.h b/src/uscxml/plugins/ioprocessor/modality/MMIMessages.h index fc9142e..e4456f8 100644 --- a/src/uscxml/plugins/ioprocessor/modality/MMIMessages.h +++ b/src/uscxml/plugins/ioprocessor/modality/MMIMessages.h @@ -51,9 +51,8 @@ public: }; static Type getType(Arabica::DOM::Node node); - static Arabica::DOM::Node getEventNode(Arabica::DOM::Node node); - virtual Arabica::DOM::Document toXML() const; + virtual Arabica::DOM::Document toXML(bool encapsulateInMMI = false) const; static MMIEvent fromXML(Arabica::DOM::Node node, InterpreterImpl* interpreter = NULL); @@ -107,7 +106,7 @@ public: class ContextualizedRequest : public MMIEvent { public: - virtual Arabica::DOM::Document toXML() const; + virtual Arabica::DOM::Document toXML(bool encapsulateInMMI = false) const; static ContextualizedRequest fromXML(Arabica::DOM::Node node, InterpreterImpl* interpreter = NULL); operator Event() const; @@ -207,7 +206,7 @@ public: tagName = "StatusRequest"; type = STARTREQUEST; } - virtual Arabica::DOM::Document toXML() const; + virtual Arabica::DOM::Document toXML(bool encapsulateInMMI = false) const; static StatusRequest fromXML(Arabica::DOM::Node node, InterpreterImpl* interpreter = NULL); operator Event() const; @@ -224,7 +223,7 @@ public: std::string fetchTimeout; }; - virtual Arabica::DOM::Document toXML() const; + virtual Arabica::DOM::Document toXML(bool encapsulateInMMI = false) const; static ContentRequest fromXML(Arabica::DOM::Node node, InterpreterImpl* interpreter = NULL); operator Event() const; @@ -284,7 +283,7 @@ public: tagName = "ExtensionNotification"; type = EXTENSIONNOTIFICATION; } - virtual Arabica::DOM::Document toXML() const; + virtual Arabica::DOM::Document toXML(bool encapsulateInMMI = false) const; static ExtensionNotification fromXML(Arabica::DOM::Node node, InterpreterImpl* interpreter = NULL); operator Event() const; @@ -307,7 +306,7 @@ public: tagName = "StatusResponse"; type = STATUSRESPONSE; } - virtual Arabica::DOM::Document toXML() const; + virtual Arabica::DOM::Document toXML(bool encapsulateInMMI = false) const; static StatusResponse fromXML(Arabica::DOM::Node node, InterpreterImpl* interpreter = NULL); Status status; @@ -317,7 +316,7 @@ protected: class StatusInfoResponse : public StatusResponse { public: - virtual Arabica::DOM::Document toXML() const; + virtual Arabica::DOM::Document toXML(bool encapsulateInMMI = false) const; StatusInfoResponse(const StatusResponse& father) : StatusResponse(father) {} static StatusInfoResponse fromXML(Arabica::DOM::Node node, InterpreterImpl* interpreter = NULL); diff --git a/src/uscxml/server/Socket.cpp b/src/uscxml/server/Socket.cpp index 9c844e5..2d474ea 100644 --- a/src/uscxml/server/Socket.cpp +++ b/src/uscxml/server/Socket.cpp @@ -67,10 +67,16 @@ Socket::~Socket() { } void Socket::setupSockAddr(const std::string& address, int port) { + if (address == "*") { _sin.sin_addr.s_addr = 0; } else { - _sin.sin_addr.s_addr = inet_addr(address.c_str()); + struct hostent *he = NULL; + if ( (he = gethostbyname(address.c_str()) ) != NULL ) { + memcpy(&_sin.sin_addr, he->h_addr_list[0], he->h_length); + } else { + _sin.sin_addr.s_addr = inet_addr(address.c_str()); + } if (_sin.sin_addr.s_addr == INADDR_NONE) throw std::runtime_error(std::string("inet_addr: ") + strerror(errno)); } @@ -83,6 +89,34 @@ void Socket::setBlockSizeRead(size_t size) { _blockSizeRead = size; } +void Socket::parseAddress(const std::string& address, std::string& protocol, std::string& hostName, uint16_t& port) { + // tcp://hostname:port + size_t protEnd = address.find("://"); + if (protEnd != std::string::npos) { + protocol = address.substr(0, protEnd); + protEnd += 3; + } else { + protocol = "tcp"; + protEnd = 0; + } + + size_t hostEnd = address.find(":", protEnd); + if (hostEnd != std::string::npos) { + hostName = address.substr(protEnd, hostEnd - protEnd); + hostEnd += 1; + } else { + hostName = "127.0.0.1"; + hostEnd = protEnd; + } + + if (hostEnd < address.size()) { + port = strTo(address.substr(hostEnd)); + } else { + port = 0; + } +} + + ClientSocket::ClientSocket(int domain, int type, int protocol) : Socket(domain, type, protocol), _clientEvent(NULL) { } @@ -95,7 +129,7 @@ ClientSocket::~ClientSocket() { } void ClientSocket::errorCallback(struct bufferevent *bev, short error, void *ctx) { - ClientSocket* instance = (ClientSocket*)ctx; +// ClientSocket* instance = (ClientSocket*)ctx; // tthread::lock_guard lock(instance->_mutex); if (error & BEV_EVENT_READING) { @@ -115,6 +149,14 @@ void ClientSocket::errorCallback(struct bufferevent *bev, short error, void *ctx // bufferevent_free(bev); } +void ClientSocket::connect(const std::string& address) { + std::string _prot; + std::string _address; + uint16_t _port; + parseAddress(address, _prot, _address, _port); + connect(_address, _port); +} + void ClientSocket::connect(const std::string& address, int port) { // tthread::lock_guard lock(_mutex); @@ -123,11 +165,15 @@ void ClientSocket::connect(const std::string& address, int port) { throw std::runtime_error(std::string("connect: ") + strerror(errno)); } - _clientEvent = bufferevent_socket_new(_base->base, _socketFD, 0); //BEV_OPT_THREADSAFE); + _clientEvent = bufferevent_socket_new(_base->base, _socketFD, BEV_OPT_THREADSAFE); //BEV_OPT_THREADSAFE); bufferevent_setcb(_clientEvent, ClientSocket::readCallback, NULL, ClientSocket::errorCallback, this); bufferevent_enable(_clientEvent, EV_READ|EV_WRITE); } +int ClientSocket::write(const std::string& data) { + return write(data.data(), data.size()); +} + int ClientSocket::write(const char* data, size_t size) { // tthread::lock_guard lock(_mutex); bufferevent_write(_clientEvent, data, size); @@ -138,14 +184,15 @@ void ClientSocket::readCallback(struct bufferevent *bev, void *ctx) { ClientSocket* instance = (ClientSocket*)ctx; // tthread::lock_guard lock(instance->_mutex); - size_t n; + int n; struct evbuffer* input; char* data = (char*)malloc(instance->_blockSizeRead); input = bufferevent_get_input(bev); - n = evbuffer_remove(input, data, instance->_blockSizeRead); - - instance->readCallback(data, n); + + while((n = evbuffer_remove(input, data, instance->_blockSizeRead)) > 0) { + instance->readCallback(data, n); + } free(data); } @@ -201,7 +248,6 @@ void ServerSocket::errorCallback(struct bufferevent *bev, short error, void *ctx #else close(conn->second.fd); #endif - instance->_connections.erase(conn); } } else if (error & BEV_EVENT_EOF) { @@ -229,9 +275,9 @@ void ServerSocket::readCallback(struct bufferevent *bev, void *ctx) { char* data = (char*)malloc(instance->_blockSizeRead); input = bufferevent_get_input(bev); - n = evbuffer_remove(input, data, instance->_blockSizeRead); - - instance->readCallback(data, n, instance->_connections[bev]); + while((n = evbuffer_remove(input, data, instance->_blockSizeRead)) > 0) { + instance->readCallback(data, n, instance->_connections[bev]); + } free(data); } @@ -241,11 +287,30 @@ void ServerSocket::bind() { } } +void ServerSocket::listen(const std::string& address) { + std::string _prot; + std::string _address; + uint16_t _port; + parseAddress(address, _prot, _address, _port); + listen(_address, _port); +} + void ServerSocket::listen(const std::string& address, int port) { // tthread::lock_guard lock(_mutex); setupSockAddr(address, port); bind(); + int reuseaddr_on = 1; + setsockopt(_socketFD, SOL_SOCKET, SO_REUSEADDR, &reuseaddr_on, sizeof(reuseaddr_on)); + + int flags = fcntl(_socketFD, F_GETFL); + if (flags >= 0) { + flags |= O_NONBLOCK; + if (fcntl(_socketFD, F_SETFL, flags) < 0) { + // could not set to non-blocj + } + } + _listenerEvent = event_new(_base->base, _socketFD, EV_READ|EV_PERSIST, acceptCallback, (void*)this); /*XXX check it */ event_add(_listenerEvent, NULL); @@ -275,7 +340,7 @@ void ServerSocket::acceptCallback(evutil_socket_t listener, short event, void *c } else { struct bufferevent *bev; evutil_make_socket_nonblocking(fd); - bev = bufferevent_socket_new(instance->_base->base, fd, BEV_OPT_THREADSAFE); + bev = bufferevent_socket_new(instance->_base->base, fd, BEV_OPT_THREADSAFE); //BEV_OPT_THREADSAFE bufferevent_setcb(bev, ServerSocket::readCallback, NULL, ServerSocket::errorCallback, ctx); bufferevent_enable(bev, EV_READ|EV_WRITE); @@ -288,4 +353,24 @@ void ServerSocket::Connection::reply(const char* data, size_t size) { bufferevent_write(bufferEvent, data, size); } +void PacketServerSocket::readCallback(const char* data, size_t size, Connection& conn) { + std::stringstream& fragment = _fragments[conn]; + fragment << std::string(data, size); + + size_t startPos = 0; + size_t endPos; + const std::string& buffer = fragment.str(); + while((endPos = buffer.find(_sep, startPos)) != std::string::npos) { +// std::cout << ">" << buffer.substr(startPos, endPos - startPos) << "<" << std::endl; + readCallback(buffer.substr(startPos, endPos - startPos), conn); + startPos = endPos + _sep.size(); + } + if (startPos != 0 && startPos < buffer.size() + 1) { + std::string rest = buffer.substr(startPos); + fragment.str(std::string()); + fragment.clear(); + fragment << rest; + } +} + } diff --git a/src/uscxml/server/Socket.h b/src/uscxml/server/Socket.h index 5854a46..9330c4b 100644 --- a/src/uscxml/server/Socket.h +++ b/src/uscxml/server/Socket.h @@ -23,6 +23,7 @@ #include "uscxml/Common.h" // for USCXML_API #include "uscxml/concurrency/EventBase.h" #include +#include #include #include @@ -49,6 +50,7 @@ public: virtual ~Socket(); void setBlockSizeRead(size_t size); + static void parseAddress(const std::string& address, std::string& protocol, std::string& hostName, uint16_t& port); protected: @@ -67,6 +69,10 @@ class USCXML_API ServerSocket : public Socket { public: class Connection { public: + bool operator<(const Connection& other) const { + return bufferEvent < other.bufferEvent; + } + struct bufferevent* bufferEvent; int fd; @@ -77,6 +83,7 @@ public: virtual ~ServerSocket(); void listen(const std::string& address, int port); + void listen(const std::string& address); virtual void readCallback(const char* data, size_t size, Connection& conn) {}; @@ -93,6 +100,19 @@ protected: }; +class USCXML_API PacketServerSocket : public ServerSocket { +public: + PacketServerSocket(int domain, int type, int protocol, const std::string& sep) : ServerSocket(domain, type, protocol), _sep(sep) {} + virtual ~PacketServerSocket() {} + + void readCallback(const char* data, size_t size, Connection& conn); + virtual void readCallback(const std::string& packet, Connection& conn) = 0; + +protected: + std::string _sep; + std::map _fragments; +}; + class USCXML_API ClientSocket : public Socket { public: ClientSocket(int domain, int type, int protocol); @@ -100,6 +120,8 @@ public: virtual void readCallback(const char* data, size_t size) {}; void connect(const std::string& address, int port); + void connect(const std::string& address); + int write(const std::string& data); int write(const char* data, size_t size); diff --git a/src/uscxml/transform/ChartToFSM.cpp b/src/uscxml/transform/ChartToFSM.cpp index 0a04771..c31853c 100644 --- a/src/uscxml/transform/ChartToFSM.cpp +++ b/src/uscxml/transform/ChartToFSM.cpp @@ -163,9 +163,9 @@ FlatteningInterpreter::~FlatteningInterpreter() { } Document FlatteningInterpreter::getDocument() const { -// std::cout << "######################" << std::endl; -// std::cout << _flatDoc << std::endl; -// std::cout << "######################" << std::endl; +// std::cerr << "######################" << std::endl; +// std::cerr << _flatDoc << std::endl; +// std::cerr << "######################" << std::endl; return _flatDoc; } @@ -175,7 +175,7 @@ InterpreterState FlatteningInterpreter::interpret() { setupIOProcessors(); uint64_t complexity = ChartToFSM::stateMachineComplexity(_scxml) + 1; - std::cout << "Approximate Complexity: " << complexity << std::endl; + std::cerr << "Approximate Complexity: " << complexity << std::endl; // initialize the datamodel std::string datamodelName; @@ -227,10 +227,10 @@ InterpreterState FlatteningInterpreter::interpret() { initialTransitions.push_back(transitionElem); } labelTransitions(); - weightTransitions(); +// weightTransitions(); indexTransitions(_scxml); -// std::cout << _scxml << std::endl; +// std::cerr << _scxml << std::endl; GlobalTransition* globalTransition = new GlobalTransition(initialTransitions, _dataModel, this); _start->outgoing[globalTransition->transitionId] = globalTransition; @@ -245,9 +245,9 @@ InterpreterState FlatteningInterpreter::interpret() { for(std::map::iterator globalConfIter = _globalConf.begin(); globalConfIter != _globalConf.end(); globalConfIter++) { - std::cout << globalConfIter->first << std::endl; + std::cerr << globalConfIter->first << std::endl; } - std::cout << _globalConf.size() << std::endl; + std::cerr << _globalConf.size() << std::endl; #endif createDocument(); @@ -263,13 +263,13 @@ InterpreterState FlatteningInterpreter::interpret() { } } - std::cout << "Actual Complexity: " << nrStates << std::endl; + std::cerr << "Actual Complexity: " << nrStates << std::endl; return _state; } void FlatteningInterpreter::executeContent(const Arabica::DOM::Element& content, bool rethrow) { -// std::cout << content << std::endl; -// std::cout << TAGNAME(content) << std::endl; +// std::cerr << content << std::endl; +// std::cerr << TAGNAME(content) << std::endl; GlobalTransition::Action action; @@ -306,7 +306,7 @@ void FlatteningInterpreter::internalDoneSend(const Arabica::DOM::Element onentry = _flatDoc.createElementNS(_nsInfo.nsURL, "onentry"); @@ -551,23 +551,23 @@ void FlatteningInterpreter::explode() { break; NodeSet transitions; -// std::cout << globalState->stateId << " [" << nrElements << "]: " << std::endl; +// std::cerr << globalState->stateId << " [" << nrElements << "]: " << std::endl; for (int i = 1; i <= k; i++) { -// std::cout << stack[i] - 1 << ", "; +// std::cerr << stack[i] - 1 << ", "; transitions.push_back(allTransitions[stack[i] - 1]); } -// std::cout << std::endl; +// std::cerr << std::endl; _perfTotal++; _perfProcessed++; if (tthread::chrono::system_clock::now() - _lastTimeStamp > 1000) { _lastTimeStamp = tthread::chrono::system_clock::now(); -// std::cout << globalState->stateId << " [" << nrElements << "]: " << std::endl; - std::cout << "States: " << _globalConf.size() << " - "; - std::cout << "Tested: " << _perfTotal << " [" << _perfProcessed << "/sec] - "; - std::cout << "Current Complexity: 2**" << nrElements << " = " << pow(2.0, static_cast(nrElements)); - std::cout << std::endl; +// std::cerr << globalState->stateId << " [" << nrElements << "]: " << std::endl; + std::cerr << "States: " << _globalConf.size() << " - "; + std::cerr << "Tested: " << _perfTotal << " [" << _perfProcessed << "/sec] - "; + std::cerr << "Current Complexity: 2**" << nrElements << " = " << pow(2.0, static_cast(nrElements)); + std::cerr << std::endl; _perfProcessed = 0; } @@ -595,11 +595,12 @@ void FlatteningInterpreter::explode() { // two combinations might have projected onto the same conflict-free set if (transitionSets.find(transition->transitionId) != transitionSets.end()) { -// std::cout << "skipping as projected onto existing conflict-free subset" << std::endl; +// std::cerr << "skipping as projected onto existing conflict-free subset" << std::endl; delete transition; continue; } +#if 0 for (int currDepth = 0; currDepth <= maxDepth; currDepth++) { int lowestOrder = std::numeric_limits::max(); int nrDepth = 0; @@ -618,7 +619,7 @@ void FlatteningInterpreter::explode() { transition->firstElemPerLevel.push_back(lowestOrder); transition->prioPerLevel.push_back(prioPerLevel); } - +#endif #if 0 // calculate priority transition->priority = 0; @@ -643,7 +644,7 @@ NEXT_DEPTH: } #endif // remember this conflict-free set -// std::cout << "New conflict-free subset: " << transition->transitionId << ":" << transition->eventDesc << std::endl; +// std::cerr << "New conflict-free subset: " << transition->transitionId << ":" << transition->eventDesc << std::endl; transitionSets[transition->transitionId] = transition; } @@ -746,9 +747,9 @@ void FlatteningInterpreter::createDocument() { int index = 0; for (std::list >::reverse_iterator transIter = indexedTransitions.rbegin(); transIter != indexedTransitions.rend(); transIter++) { const Element& refTrans = *transIter; - std::cout << index++ << ": " << refTrans << std::endl; + std::cerr << index++ << ": " << refTrans << std::endl; } - std::cout << std::endl; + std::cerr << std::endl; for (std::vector >::iterator confIter = sortedStates.begin(); confIter != sortedStates.end(); @@ -766,9 +767,11 @@ template bool PtrComp(const T * const & a, const T * const & b) } -bool isRedundantSubset (GlobalTransition* first, GlobalTransition* second) { +/** + * subset only removes transitions without cond -> superset will always be enabled + */ +bool hasUnconditionalSuperset (GlobalTransition* first, GlobalTransition* second) { if (isSuperset(second, first)) { -// std::cout << second->transitions.size() << " / " << first->transitions.size() << std::endl; for (int i = 0; i < first->transitions.size(); i++) { if (!InterpreterImpl::isMember(first->transitions[i], second->transitions)) { if (HAS_ATTR_CAST(first->transitions[i], "cond")) { @@ -781,7 +784,16 @@ bool isRedundantSubset (GlobalTransition* first, GlobalTransition* second) { return false; //second can't be removed } -std::list filterRedundantSubset(std::list list) { +bool hasEarlierUnconditionalMatch(GlobalTransition* first, GlobalTransition* second) { + if (first->eventDesc == second->eventDesc) { + if (first->condition.size() == 0) + return true; + } + return false; +} + +// for some reason, unique is not quite up to the task +std::list reapplyUniquePredicates(std::list list) { for (std::list::iterator outerIter = list.begin(); outerIter != list.end(); @@ -796,12 +808,17 @@ std::list filterRedundantSubset(std::list GlobalTransition* t1 = *outerIter; GlobalTransition* t2 = *innerIter; - if (isRedundantSubset(t1, t2)) { + if (hasUnconditionalSuperset(t1, t2)) { list.erase(outerIter++); - } else if (isRedundantSubset(t2, t1)) { + continue; + } else if (hasUnconditionalSuperset(t2, t1)) { list.erase(innerIter++); + continue; + } + if (hasEarlierUnconditionalMatch(t1, t2)) { + list.erase(innerIter++); + continue; } - } } @@ -827,9 +844,10 @@ void FlatteningInterpreter::appendGlobalStateNode(GlobalState* globalState) { // transitionList = sortTransitions(transitionList); transitionList.sort(PtrComp); - transitionList.unique(isRedundantSubset); + transitionList.unique(hasUnconditionalSuperset); + transitionList.unique(hasEarlierUnconditionalMatch); // unique is not quite like what we need, but it was a start - transitionList = filterRedundantSubset(transitionList); + transitionList = reapplyUniquePredicates(transitionList); // apend here, for transient state chains to trail the state _scxml.appendChild(state); @@ -854,23 +872,7 @@ Node FlatteningInterpreter::globalTransitionToNode(GlobalTransition // transition.setAttribute("ref", globalTransition->index); #if 1 - std::string members; - int index = 0; - std::string seperator; - for (std::list >::reverse_iterator transIter = indexedTransitions.rbegin(); transIter != indexedTransitions.rend(); transIter++) { - const Element& refTrans = *transIter; - if (isMember(refTrans, globalTransition->transitions)) { - members += seperator + toStr(index); - } else { - members += seperator; - for (int i = 0; i < toStr(index).size(); i++) { - members += " "; - } - } - seperator = " "; - index++; - } - transition.setAttribute("members", members); + transition.setAttribute("members", globalTransition->members); #endif if (!globalTransition->isEventless) { @@ -910,9 +912,9 @@ Node FlatteningInterpreter::globalTransitionToNode(GlobalTransition #endif -// std::cout << " firstPerLevel:" << feSS.str() << " " << globalTransition->transitionId << std::endl; -// std::cout << "event: " << globalTransition->eventDesc << " firstPerLevel:" << feSS.str() << " numberPerLevel:" << nrSS.str() << " prioPerLevel:" << prSS.str() << " " << globalTransition->transitionId << std::endl; -// std::cout << globalTransition->transitionId << std::endl; +// std::cerr << " firstPerLevel:" << feSS.str() << " " << globalTransition->transitionId << std::endl; +// std::cerr << "event: " << globalTransition->eventDesc << " firstPerLevel:" << feSS.str() << " numberPerLevel:" << nrSS.str() << " prioPerLevel:" << prSS.str() << " " << globalTransition->transitionId << std::endl; +// std::cerr << globalTransition->transitionId << std::endl; NodeSet transientStateChain; @@ -1031,6 +1033,7 @@ Node FlatteningInterpreter::globalTransitionToNode(GlobalTransition return transition; } +#if 0 void FlatteningInterpreter::weightTransitions() { maxDepth = 0; maxOrder = 0; @@ -1055,6 +1058,7 @@ void FlatteningInterpreter::weightTransitions() { states = getChildStates(states); } } +#endif void FlatteningInterpreter::labelTransitions() { // put a unique id on each transition @@ -1134,11 +1138,11 @@ GlobalTransition::GlobalTransition(const Arabica::XPath::NodeSet& t isEventless = true; #if 0 - std::cout << "################" << std::endl; + std::cerr << "################" << std::endl; for (int i = 0; i < transitions.size(); i++) { - std::cout << transitions[i] << std::endl; + std::cerr << transitions[i] << std::endl; } - std::cout << "################" << std::endl; + std::cerr << "################" << std::endl; #endif std::list conditions; @@ -1156,6 +1160,22 @@ GlobalTransition::GlobalTransition(const Arabica::XPath::NodeSet& t } transitionId = setId.str(); + int index = 0; + std::string seperator; + for (std::list >::iterator transIter = interpreter->indexedTransitions.begin(); transIter != interpreter->indexedTransitions.end(); transIter++) { + const Element& refTrans = *transIter; + if (InterpreterImpl::isMember(refTrans, transitions)) { + members += seperator + toStr(index); + } else { + members += seperator; + for (int i = 0; i < toStr(index).size(); i++) { + members += " "; + } + } + seperator = " "; + index++; + } + /** * Can these events event occur together? They can't if: * 1. event / eventless is mixed @@ -1230,11 +1250,13 @@ GlobalTransition::GlobalTransition(const Arabica::XPath::NodeSet& t eventDesc = "*"; } - if (conditions.size() > 0) { + if (conditions.size() > 1) { condition = dataModel.andExpressions(conditions); if (condition.size() == 0) { LOG(ERROR) << "Datamodel does not support to conjungate expressions!" << std::endl; } + } else if (conditions.size() == 1) { + condition = conditions.front(); } } diff --git a/src/uscxml/transform/ChartToFSM.h b/src/uscxml/transform/ChartToFSM.h index 923304c..a60985d 100644 --- a/src/uscxml/transform/ChartToFSM.h +++ b/src/uscxml/transform/ChartToFSM.h @@ -77,15 +77,16 @@ public: bool isTargetless; // whether or not all our transitions are eventless bool isSubset; // there is a superset to this set - std::vector firstElemPerLevel; - std::vector nrElemPerLevel; - std::vector prioPerLevel; +// std::vector firstElemPerLevel; +// std::vector nrElemPerLevel; +// std::vector prioPerLevel; Arabica::XPath::NodeSet transitions; // constituting transitions std::list eventNames; // the list of longest event names that will enable this set std::string eventDesc; // space-seperated eventnames for convenience std::string condition; // conjunction of all the set's conditions + std::string members; // a convenience string listing all constituting transitions // executable content we gathered when we took the transition std::list actions; @@ -140,7 +141,7 @@ protected: void explode(); void labelTransitions(); - void weightTransitions(); +// void weightTransitions(); void createDocument(); void indexTransitions(const Arabica::DOM::Element& root); diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index dc827d3..dbc5a15 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -146,6 +146,11 @@ 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") +add_executable(test-vxml-mmi-http src/test-vxml-mmi-http.cpp) +target_link_libraries(test-vxml-mmi-http uscxml) +# add_test(test-datamodel ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test-sockets) +set_target_properties(test-vxml-mmi-http 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-sockets.cpp b/test/src/test-sockets.cpp index a02eb9c..ad567f7 100644 --- a/test/src/test-sockets.cpp +++ b/test/src/test-sockets.cpp @@ -1,4 +1,5 @@ #include "uscxml/config.h" +#include "uscxml/Convenience.h" #include "uscxml/server/Socket.h" #include #include @@ -25,6 +26,28 @@ public: }; }; +int packetSeq = 0; + +class LogServer : public ServerSocket { +public: + LogServer(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; + }; +}; + +class CountingPacketServer : public PacketServerSocket { +public: + CountingPacketServer(int domain, int type, int protocol, const std::string& sep) : PacketServerSocket(domain, type, protocol, sep) {} + virtual void readCallback(const std::string& packet, Connection& conn) { +// std::cout << "-- " << packet << std::endl; + size_t seq = strTo(packet); + assert(seq == packetSeq); + packetSeq++; + }; +}; + class TestClient : public ClientSocket { public: TestClient(int domain, int type, int protocol) : ClientSocket(domain, type, protocol) {} @@ -45,7 +68,49 @@ int main(int argc, char** argv) { evthread_use_windows_threads(); #endif - if (0) { + if (1) { + packetSeq = 0; + CountingPacketServer server(PF_INET, SOCK_STREAM, 0, std::string("tadaa!")); +// LogServer server(PF_INET, SOCK_STREAM, 0); + server.listen("*", 1235); + server.setBlockSizeRead(1); + + TestClient client(PF_INET, SOCK_STREAM, 0); + client.connect("127.0.0.1", 1235); + + int iterations = 1000; + std::stringstream contentSS; + for (int i = 0; i < iterations; i++) { + contentSS << toStr(i); + contentSS << "tadaa!"; + } + client.write(contentSS.str()); + + while(packetSeq != iterations) + usleep(10000); + } + + if (1) { + packetSeq = 0; + CountingPacketServer server(PF_INET, SOCK_STREAM, 0, std::string("\0", 1)); + server.listen("*", 1235); + + TestClient client(PF_INET, SOCK_STREAM, 0); + client.connect("127.0.0.1", 1235); + + int iterations = 1000; + for (int i = 0; i < iterations; i++) { + client.write(toStr(i)); + client.write("\0", 1); + } + + while(packetSeq != iterations) + usleep(10000); + } + + exit(0); + + if (1) { // start server socket and connect int iterations = 100; diff --git a/test/src/test-vxml-mmi-http.cpp b/test/src/test-vxml-mmi-http.cpp new file mode 100644 index 0000000..50a8dd0 --- /dev/null +++ b/test/src/test-vxml-mmi-http.cpp @@ -0,0 +1,165 @@ +#include "uscxml/config.h" +#include "uscxml/server/Socket.h" +#include "uscxml/UUID.h" +#include +#include + +#include + +#include +#include "event2/thread.h" + +#ifdef HAS_SIGNAL_H +#include +#endif + +#include "uscxml/server/HTTPServer.h" +#include "uscxml/URL.h" +#include "uscxml/concurrency/tinythread.h" +#include "uscxml/plugins/ioprocessor/modality/MMIMessages.h" +#include + +#ifdef _WIN32 +#include "XGetopt.h" +#endif + +#include "uscxml/plugins/ioprocessor/modality/MMIMessages.cpp" + +#define ISSUE_REQUEST(name) {\ + Arabica::DOM::Document name##XML = name.toXML(true);\ + name##XML.getDocumentElement().setPrefix("mmi");\ + std::stringstream name##XMLSS;\ + name##XMLSS << name##XML;\ + URL name##URL(target);\ + name##URL.setOutContent(name##XMLSS.str());\ + name##URL.addOutHeader("Content-type", "application/xml");\ + name##URL.download(true);\ +} + +using namespace uscxml; + +std::map Requests; +std::map Replies; + +tthread::condition_variable Cond; +tthread::mutex Mutex; + +class MMIServlet : public HTTPServlet { +public: + bool httpRecvRequest(const HTTPServer::Request& request) { + tthread::lock_guard lock(Mutex); + + NameSpacingParser parser = NameSpacingParser::fromXML(request.content); + switch(MMIEvent::getType(parser.getDocument().getDocumentElement())) { + case MMIEvent::NEWCONTEXTRESPONSE: { + NewContextResponse* resp = new NewContextResponse(NewContextResponse::fromXML(parser.getDocument().getDocumentElement())); + Replies[resp->requestId] = resp; + } + case MMIEvent::STARTRESPONSE: { + StartResponse* resp = new StartResponse(StartResponse::fromXML(parser.getDocument().getDocumentElement())); + Replies[resp->requestId] = resp; + } + default: ; + } + + Cond.notify_all(); + return true; + } + void setURL(const std::string& url) { + this->url = url; + } + std::string url; +}; + +void printUsageAndExit(const char* progName) { + // remove path from program name + std::string progStr(progName); + if (progStr.find_last_of(PATH_SEPERATOR) != std::string::npos) { + progStr = progStr.substr(progStr.find_last_of(PATH_SEPERATOR) + 1, progStr.length() - (progStr.find_last_of(PATH_SEPERATOR) + 1)); + } + + 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(" [-tURL] URL"); + printf("\n"); + printf("Options\n"); + printf("\t-tURL : URL of VoiceXML HTTP server\n"); + printf("\tURL : URL of a VoiceXML document\n"); + printf("\n"); + exit(1); +} + +int main(int argc, char** argv) { + try { + tthread::lock_guard lock(Mutex); + + std::string target; + std::string document; + + if (argc < 2) + printUsageAndExit(argv[0]); + + int option; + while ((option = getopt(argc, argv, "t:")) != -1) { + switch(option) { + case 't': + target = optarg; + break; + default: + printUsageAndExit(argv[0]); + } + } + + if (argc < optind) + printUsageAndExit(argv[0]); + + document = argv[optind]; + + if (!boost::starts_with(document, "http")) + document = "http://" + document; + + if (!boost::starts_with(target, "http")) + document = "http://" + target; + + // target = "http://130.83.163.167:9090/mmi"; + // target = "http://localhost:9090/mmi"; + + + MMIServlet servlet; + HTTPServer::getInstance(4344, 0); + HTTPServer::getInstance()->registerServlet("/mmi", &servlet); + + std::string source = servlet.url; + + NewContextRequest newCtxReq; + newCtxReq.source = source; + newCtxReq.target = target; + newCtxReq.requestId = UUID::getUUID(); + + Requests[newCtxReq.requestId] = &newCtxReq; + ISSUE_REQUEST(newCtxReq); + + while(Replies.find(newCtxReq.requestId) == Replies.end()) + Cond.wait(Mutex); + + StartRequest startReq; + startReq.source = source; + startReq.target = target; + startReq.requestId = UUID::getUUID(); + startReq.contentURL.href = document; + //"https://raw.githubusercontent.com/Roland-Taizun-Azhar/TaskAssistance-Project/master/WebContent/hello.vxml"; + + Requests[startReq.requestId] = &startReq; + ISSUE_REQUEST(startReq); + + while(Replies.find(startReq.requestId) == Replies.end()) + Cond.wait(Mutex); + } catch (Event e) { + std::cout << e << std::endl; + } catch (std::exception e) { + std::cout << e.what() << std::endl; + } + + +} \ No newline at end of file diff --git a/test/src/test-vxml-mmi-socket.cpp b/test/src/test-vxml-mmi-socket.cpp index a79cec7..8246c3b 100644 --- a/test/src/test-vxml-mmi-socket.cpp +++ b/test/src/test-vxml-mmi-socket.cpp @@ -1,5 +1,6 @@ #include "uscxml/config.h" #include "uscxml/server/Socket.h" +#include "uscxml/UUID.h" #include #include @@ -19,15 +20,70 @@ using namespace uscxml; -class TestServer : public ServerSocket { +bool testAddressParsing() { + std::string protocol; + std::string hostName; + uint16_t port; + + { + Socket::parseAddress("4343", protocol, hostName, port); + assert(protocol == "tcp"); + assert(hostName == "127.0.0.1"); + assert(port == 4343); + + Socket::parseAddress("localhost:4343", protocol, hostName, port); + assert(protocol == "tcp"); + assert(hostName == "localhost"); + assert(port == 4343); + + Socket::parseAddress("tcp://localhost:4343", protocol, hostName, port); + assert(protocol == "tcp"); + assert(hostName == "localhost"); + assert(port == 4343); + } + return true; +} + +bool testMMIEvents() { + { + NewContextRequest newCtxReq; + newCtxReq.source = "localhost:3434"; + newCtxReq.target = "localhost:1212"; + newCtxReq.requestId = "requestId"; + + Arabica::DOM::Document newCtxReqXML1 = newCtxReq.toXML(); + Arabica::DOM::Document newCtxReqXML2 = newCtxReq.toXML(true); + +// std::cout << newCtxReqXML1 << std::endl; +// std::cout << newCtxReqXML2 << std::endl; + + NewContextRequest newCtxReq1 = NewContextRequest::fromXML(newCtxReqXML1.getDocumentElement()); + NewContextRequest newCtxReq2 = NewContextRequest::fromXML(newCtxReqXML2.getDocumentElement()); + + assert(MMIEvent::getType(newCtxReqXML1.getDocumentElement()) == MMIEvent::NEWCONTEXTREQUEST); + assert(MMIEvent::getType(newCtxReqXML2.getDocumentElement()) == MMIEvent::NEWCONTEXTREQUEST); + + assert(newCtxReq1.source == "localhost:3434"); + assert(newCtxReq2.source == "localhost:3434"); + assert(newCtxReq1.target == "localhost:1212"); + assert(newCtxReq2.target == "localhost:1212"); + assert(newCtxReq1.requestId == "requestId"); + assert(newCtxReq2.requestId == "requestId"); + + } + return true; +} + +class TestServer : public PacketServerSocket { 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; + TestServer(int domain, int type, int protocol) : PacketServerSocket(domain, type, protocol, std::string("\0", 1)) {} + virtual void readCallback(const std::string& packet, Connection& conn) { + std::cout << "Server got: " << packet << std::endl; std::string urghs("hi!"); conn.reply(urghs.data(), urghs.size()); }; + + std::stringstream fragment; }; class TestClient : public ClientSocket { @@ -38,6 +94,9 @@ public: }; }; +std::map _requests; +std::map _replies; + int main(int argc, char** argv) { #if defined(HAS_SIGNAL_H) && !defined(WIN32) @@ -49,24 +108,58 @@ int main(int argc, char** argv) { #else evthread_use_windows_threads(); #endif - + testAddressParsing(); + testMMIEvents(); + // TestClient client(PF_INET, SOCK_STREAM, 0); // client.connect("epikur.local", 4343); + std::string target = "localhost:4343"; + std::string source = "localhost:4344"; + + TestServer server(PF_INET, SOCK_STREAM, 0); + server.listen(source); + +// while(true) +// sleep(1000); + + TestClient client(PF_INET, SOCK_STREAM, 0); + client.connect(source); + + NewContextRequest newCtxReq; + newCtxReq.source = source; + newCtxReq.target = target; + newCtxReq.requestId = UUID::getUUID(); + + _requests[newCtxReq.requestId] = &newCtxReq; + + Arabica::DOM::Document newCtxReqXML = newCtxReq.toXML(true); + std::stringstream newCtxReqXMLSS; + newCtxReqXMLSS << newCtxReqXML; + + for (int i = 0; i < 100000; i++) { + std::string index = toStr(i); + client.write(index.c_str(), index.size() + 1); +// client.write(newCtxReqXMLSS.str().data(), newCtxReqXMLSS.str().size()); +// client.write("\0", 1); + } + + while(true) + sleep(1000); - StartRequest startReq; - startReq.source = "undefined.source"; - startReq.target = "epikur.local:4343"; - startReq.requestId = "131234141234"; - startReq.data = - "" - " Goodbye!" - ""; - - Arabica::DOM::Document reqXML = startReq.toXML(); - std::stringstream xmlSS; - xmlSS << reqXML; - std::cout << reqXML; +// StartRequest startReq; +// startReq.source = "localhost:4344"; +// startReq.target = "localhost:4343"; +// startReq.requestId = "131234141234"; +// startReq.data = +// "" +// " Goodbye!" +// ""; +// +// Arabica::DOM::Document 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 -- cgit v0.12