summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore2
-rw-r--r--CMakeLists.txt15
-rw-r--r--apps/w3c-mmi/MMIEventServlet.cpp4
-rw-r--r--src/uscxml/Factory.cpp56
-rw-r--r--src/uscxml/Factory.h12
-rw-r--r--src/uscxml/Interpreter.cpp12
-rw-r--r--src/uscxml/Message.h2
-rw-r--r--src/uscxml/NameSpacingParser.h7
-rw-r--r--src/uscxml/plugins/datamodel/ecmascript/JavaScriptCore/JSCDataModel.cpp102
-rw-r--r--src/uscxml/plugins/datamodel/ecmascript/JavaScriptCore/JSCDataModel.h5
-rw-r--r--src/uscxml/plugins/datamodel/prolog/swi/SWIDataModel.cpp16
-rw-r--r--src/uscxml/plugins/invoker/CMakeLists.txt19
-rw-r--r--src/uscxml/plugins/invoker/location/LocationInvoker.cpp44
-rw-r--r--src/uscxml/plugins/invoker/location/LocationInvoker.h40
-rw-r--r--src/uscxml/plugins/invoker/miles/SpatialAudio.cpp43
-rw-r--r--src/uscxml/plugins/invoker/miles/SpatialAudio.h1
-rw-r--r--src/uscxml/plugins/invoker/umundo/UmundoInvoker.cpp14
-rw-r--r--src/uscxml/plugins/invoker/vxml/VoiceXMLInvoker.cpp14
-rw-r--r--src/uscxml/plugins/invoker/xhtml/XHTMLInvoker.cpp63
-rw-r--r--test/CMakeLists.txt12
-rw-r--r--test/samples/uscxml/applications/SpatialMapTicker.java153
-rw-r--r--test/samples/uscxml/applications/spoken-map-ticker.scxml64
-rw-r--r--test/samples/uscxml/applications/spoken-map-ticker.xhtml236
-rw-r--r--test/samples/uscxml/arabica/test-arabica-parsing.xml6
-rw-r--r--test/samples/uscxml/test-prolog.scxml15
-rw-r--r--test/src/test-arabica-parsing.cpp48
-rw-r--r--test/src/test-datamodel.cpp80
-rw-r--r--test/src/test-predicates.cpp3
28 files changed, 1010 insertions, 78 deletions
diff --git a/.gitignore b/.gitignore
index c1665be..649e14d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -14,3 +14,5 @@ package/darwin*
package/windows*
*.tgz
+
+*.class
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 1fa22f8..4c27508 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -287,10 +287,13 @@ if (APPLE)
endif()
if (IOS)
- set(CMAKE_OSX_DEPLOYMENT_TARGET 4.3)
- foreach(FLAGS CMAKE_C_FLAGS CMAKE_CXX_FLAGS CMAKE_EXE_LINKER_FLAGS CMAKE_SHARED_LINKER_FLAGS CMAKE_MODULE_LINKER_FLAGS)
- set(${FLAGS} "${${FLAGS}} -miphoneos-version-min=${CMAKE_OSX_DEPLOYMENT_TARGET}")
- endforeach()
+ if (${CMAKE_GENERATOR} STREQUAL "Xcode")
+ else()
+ set(CMAKE_OSX_DEPLOYMENT_TARGET 4.3)
+ foreach(FLAGS CMAKE_C_FLAGS CMAKE_CXX_FLAGS CMAKE_EXE_LINKER_FLAGS CMAKE_SHARED_LINKER_FLAGS CMAKE_MODULE_LINKER_FLAGS)
+ set(${FLAGS} "${${FLAGS}} -miphoneos-version-min=${CMAKE_OSX_DEPLOYMENT_TARGET}")
+ endforeach()
+ endif()
endif()
############################################################
@@ -636,6 +639,10 @@ if (NOT WIN32)
add_subdirectory(src/bindings)
endif()
+#if (IOS)
+ add_subdirectory(apps/ios)
+#endif()
+
############################################################
# Header Files
############################################################
diff --git a/apps/w3c-mmi/MMIEventServlet.cpp b/apps/w3c-mmi/MMIEventServlet.cpp
index cb5136a..22b8d91 100644
--- a/apps/w3c-mmi/MMIEventServlet.cpp
+++ b/apps/w3c-mmi/MMIEventServlet.cpp
@@ -68,7 +68,7 @@ bool MMIEventServlet::httpRecvRequest(const HTTPServer::Request& req) {
return true;
}
- std::cout << mmiDoc.getNamespaceURI() << std::endl;
+// std::cout << mmiDoc.getNamespaceURI() << std::endl;
Node<std::string> mmiEvent = mmiDoc.getFirstChild();
// get the first element
while (mmiEvent && mmiEvent.getNodeType() != Node_base::ELEMENT_NODE) {
@@ -81,7 +81,7 @@ bool MMIEventServlet::httpRecvRequest(const HTTPServer::Request& req) {
mmiEvent = mmiEvent.getNextSibling();
}
}
- std::cout << mmiEvent << std::endl;
+// std::cout << mmiEvent << std::endl;
if (!mmiEvent) {
evhttp_send_error(req.curlReq, 402, NULL);
diff --git a/src/uscxml/Factory.cpp b/src/uscxml/Factory.cpp
index b3ad09e..7ac3b98 100644
--- a/src/uscxml/Factory.cpp
+++ b/src/uscxml/Factory.cpp
@@ -392,6 +392,62 @@ boost::shared_ptr<ExecutableContentImpl> Factory::createExecutableContent(const
}
+size_t DataModelImpl::replaceExpressions(std::string& content) {
+ std::stringstream ss;
+ size_t replacements = 0;
+ size_t indent = 0;
+ size_t pos = 0;
+ size_t start = std::string::npos;
+ size_t end = 0;
+ while (true) {
+ // find any of ${}
+ pos = content.find_first_of("${}", pos);
+ if (pos == std::string::npos) {
+ ss << content.substr(end, content.length() - end);
+ break;
+ }
+ if (content[pos] == '$') {
+ if (content.size() > pos && content[pos+1] == '{') {
+ pos++;
+ start = pos + 1;
+ // copy everything in between
+ ss << content.substr(end, (start - 2) - end);
+ }
+ } else if (content[pos] == '{' && start != std::string::npos) {
+ indent++;
+ } else if (content[pos] == '}' && start != std::string::npos) {
+ if (!indent) {
+ end = pos;
+ // we found a token to substitute
+ std::string expr = content.substr(start, end - start);
+ end++;
+ try {
+ Data data = getStringAsData(expr);
+// if (data.type == Data::VERBATIM) {
+// ss << "\"" << data.atom << "\"";
+// } else {
+// ss << data.atom;
+// }
+ ss << Data::toJSON(data);
+ replacements++;
+ } catch (Event e) {
+ // insert unsubstituted
+ start -= 2;
+ ss << content.substr(start, end - start);
+ }
+ start = std::string::npos;
+ } else {
+ indent--;
+ }
+ }
+ pos++;
+ }
+ if (replacements)
+ content = ss.str();
+
+ return replacements;
+}
+
Factory* Factory::getInstance() {
if (_instance == NULL) {
diff --git a/src/uscxml/Factory.h b/src/uscxml/Factory.h
index 7b08982..2a0ec43 100644
--- a/src/uscxml/Factory.h
+++ b/src/uscxml/Factory.h
@@ -10,12 +10,14 @@
#include <string>
#include <set>
#include <boost/shared_ptr.hpp>
+#include <limits>
namespace uscxml {
// see http://stackoverflow.com/questions/228005/alternative-to-itoa-for-converting-integer-to-string-c
template <typename T> std::string toStr(T tmp) {
std::ostringstream out;
+ out.precision(std::numeric_limits<double>::digits10 + 1);
out << tmp;
return out.str();
}
@@ -28,9 +30,9 @@ template <typename T> T strTo(std::string tmp) {
}
inline bool isNumeric( const char* pszInput, int nNumberBase) {
- std::string base = "0123456789ABCDEF";
+ std::string base = ".0123456789ABCDEF";
std::string input = pszInput;
- return (input.find_first_not_of(base.substr(0, nNumberBase)) == std::string::npos);
+ return (input.find_first_not_of(base.substr(0, nNumberBase + 1)) == std::string::npos);
}
class InterpreterImpl;
@@ -252,6 +254,8 @@ public:
virtual void setEvent(const Event& event) = 0;
virtual Data getStringAsData(const std::string& content) = 0;
+ size_t replaceExpressions(std::string& content);
+
// foreach
virtual uint32_t getLength(const std::string& expr) = 0;
virtual void setForeach(const std::string& item,
@@ -363,6 +367,10 @@ public:
return _impl->isDeclared(expr);
}
+ size_t replaceExpressions(std::string& content) {
+ return _impl->replaceExpressions(content);
+ }
+
protected:
boost::shared_ptr<DataModelImpl> _impl;
};
diff --git a/src/uscxml/Interpreter.cpp b/src/uscxml/Interpreter.cpp
index 158ea96..8c1b4a6 100644
--- a/src/uscxml/Interpreter.cpp
+++ b/src/uscxml/Interpreter.cpp
@@ -436,7 +436,7 @@ void InterpreterImpl::processContentElement(const Arabica::DOM::Node<std::string
std::string& expr) {
if (HAS_ATTR(content, "expr")) {
expr = ATTR(content, "expr");
- } else if (content.hasChildNodes()) {
+ } else if (content.hasChildNodes() || HAS_ATTR(content, "src") || HAS_ATTR(content, "srcexpr")) {
processDOMorText(content, dom, text);
} else {
LOG(ERROR) << "content element does not specify any content.";
@@ -455,7 +455,7 @@ void InterpreterImpl::processDOMorText(const Arabica::DOM::Node<std::string>& no
LOG(ERROR) << LOCALNAME(node) << " element has relative src or srcexpr URI with no baseURI set.";
return;
}
- if (_cachedURLs.find(sourceURL.asString()) != _cachedURLs.end()) {
+ if (_cachedURLs.find(sourceURL.asString()) != _cachedURLs.end() && false) {
srcContent << _cachedURLs[sourceURL.asString()];
} else {
srcContent << sourceURL;
@@ -473,8 +473,12 @@ void InterpreterImpl::processDOMorText(const Arabica::DOM::Node<std::string>& no
std::auto_ptr<std::istream> ssPtr(ss);
Arabica::SAX::InputSource<std::string> inputSource;
inputSource.setByteStream(ssPtr);
+
+// parser.setFeature(Arabica::SAX::FeatureNames<std::string>().external_general, true);
+
if (parser.parse(inputSource) && parser.getDocument()) {
dom = parser.getDocument();
+ std::cout << dom;
Node<std::string> content = dom.getDocumentElement();
assert(content.getNodeType() == Node_base::ELEMENT_NODE);
Node<std::string> container = dom.createElement("container");
@@ -1106,7 +1110,7 @@ void InterpreterImpl::executeContent(const Arabica::DOM::Node<std::string>& cont
}
std::stringstream srcContent;
- if (_cachedURLs.find(scriptUrl.asString()) != _cachedURLs.end()) {
+ if (_cachedURLs.find(scriptUrl.asString()) != _cachedURLs.end() && false) {
srcContent << _cachedURLs[scriptUrl.asString()];
} else {
srcContent << scriptUrl;
@@ -1313,7 +1317,7 @@ Arabica::XPath::NodeSet<std::string> InterpreterImpl::getStates(const std::vecto
Arabica::DOM::Node<std::string> InterpreterImpl::getState(const std::string& stateId) {
- if (_cachedStates.find(stateId) != _cachedStates.end()) {
+ if (_cachedStates.find(stateId) != _cachedStates.end() && false) {
return _cachedStates[stateId];
}
diff --git a/src/uscxml/Message.h b/src/uscxml/Message.h
index dd62702..dda2279 100644
--- a/src/uscxml/Message.h
+++ b/src/uscxml/Message.h
@@ -26,7 +26,7 @@ public:
INTERPRETED
};
- Data() {}
+ Data() : type(INTERPRETED) {}
Data(const std::string& atom_, Type type_ = INTERPRETED) : atom(atom_), type(type_) {}
explicit Data(const Arabica::DOM::Node<std::string>& dom);
virtual ~Data() {}
diff --git a/src/uscxml/NameSpacingParser.h b/src/uscxml/NameSpacingParser.h
index fddf306..50cc260 100644
--- a/src/uscxml/NameSpacingParser.h
+++ b/src/uscxml/NameSpacingParser.h
@@ -6,6 +6,13 @@
namespace uscxml {
+class ScriptEntityResolver : public Arabica::SAX::EntityResolver<std::string> {
+ virtual InputSourceT resolveEntity(const std::string& publicId, const std::string& systemId) {
+ Arabica::SAX::InputSource<std::string> is;
+ return is;
+ }
+};
+
class NameSpacingParser : public Arabica::SAX2DOM::Parser<std::string> {
public:
NameSpacingParser();
diff --git a/src/uscxml/plugins/datamodel/ecmascript/JavaScriptCore/JSCDataModel.cpp b/src/uscxml/plugins/datamodel/ecmascript/JavaScriptCore/JSCDataModel.cpp
index 8feb73a..47ec21d 100644
--- a/src/uscxml/plugins/datamodel/ecmascript/JavaScriptCore/JSCDataModel.cpp
+++ b/src/uscxml/plugins/datamodel/ecmascript/JavaScriptCore/JSCDataModel.cpp
@@ -24,10 +24,37 @@ bool connect(pluma::Host& host) {
JSCDataModel::JSCDataModel() {
}
+#if 0
+typedef struct {
+ int version; /* current (and only) version is 0 */
+ JSClassAttributes attributes;
+
+ const char* className;
+ JSClassRef parentClass;
+
+ const JSStaticValue* staticValues;
+ const JSStaticFunction* staticFunctions;
+
+ JSObjectInitializeCallback initialize;
+ JSObjectFinalizeCallback finalize;
+ JSObjectHasPropertyCallback hasProperty;
+ JSObjectGetPropertyCallback getProperty;
+ JSObjectSetPropertyCallback setProperty;
+ JSObjectDeletePropertyCallback deleteProperty;
+ JSObjectGetPropertyNamesCallback getPropertyNames;
+ JSObjectCallAsFunctionCallback callAsFunction;
+ JSObjectCallAsConstructorCallback callAsConstructor;
+ JSObjectHasInstanceCallback hasInstance;
+ JSObjectConvertToTypeCallback convertToType;
+} JSClassDefinition;
+#endif
+
// functions need to be objects to hold private data in JSC
JSClassDefinition JSCDataModel::jsInClassDef = { 0, 0, "In", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, jsIn, 0, 0, 0 };
JSClassDefinition JSCDataModel::jsPrintClassDef = { 0, 0, "print", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, jsPrint, 0, 0, 0 };
+JSClassDefinition JSCDataModel::jsIOProcessorsClassDef = { 0, 0, "ioProcessors", 0, 0, 0, 0, 0, jsIOProcessorHasProp, jsIOProcessorGetProp, 0, 0, jsIOProcessorListProps, 0, 0, 0, 0 };
+
boost::shared_ptr<DataModelImpl> JSCDataModel::create(InterpreterImpl* interpreter) {
boost::shared_ptr<JSCDataModel> dm = boost::shared_ptr<JSCDataModel>(new JSCDataModel());
@@ -51,15 +78,33 @@ boost::shared_ptr<DataModelImpl> JSCDataModel::create(InterpreterImpl* interpret
JSObjectSetProperty(dm->_ctx, JSContextGetGlobalObject(dm->_ctx), printName, jsPrint, kJSPropertyAttributeNone, NULL);
JSStringRelease(inName);
- dm->eval("_ioprocessors = {};");
+ JSClassRef jsIOProcClassRef = JSClassCreate(&jsIOProcessorsClassDef);
+ JSObjectRef jsIOProc = JSObjectMake(dm->_ctx, jsIOProcClassRef, dm.get());
+ JSStringRef ioProcName = JSStringCreateWithUTF8CString("_ioprocessors");
+ JSObjectSetProperty(dm->_ctx, JSContextGetGlobalObject(dm->_ctx), ioProcName, jsIOProc, kJSPropertyAttributeNone, NULL);
+ JSStringRelease(ioProcName);
+
+ JSStringRef nameName = JSStringCreateWithUTF8CString("_name");
+ JSStringRef name = JSStringCreateWithUTF8CString(dm->_interpreter->getName().c_str());
+ JSObjectSetProperty(dm->_ctx, JSContextGetGlobalObject(dm->_ctx), nameName, JSValueMakeString(dm->_ctx, name), kJSPropertyAttributeNone, NULL);
+ JSStringRelease(nameName);
+ JSStringRelease(name);
+
+ JSStringRef sessionIdName = JSStringCreateWithUTF8CString("_sessionid");
+ JSStringRef sessionId = JSStringCreateWithUTF8CString(dm->_interpreter->getSessionId().c_str());
+ JSObjectSetProperty(dm->_ctx, JSContextGetGlobalObject(dm->_ctx), sessionIdName, JSValueMakeString(dm->_ctx, sessionId), kJSPropertyAttributeNone, NULL);
+ JSStringRelease(sessionIdName);
+ JSStringRelease(sessionId);
Arabica::DOM::JSCDocument::JSCDocumentPrivate* privData = new Arabica::DOM::JSCDocument::JSCDocumentPrivate();
- privData->nativeObj = new Arabica::DOM::Document<std::string>(interpreter->getDocument());
- privData->dom = dm->_dom;
+ if (interpreter) {
+ privData->nativeObj = new Arabica::DOM::Document<std::string>(interpreter->getDocument());
+ privData->dom = dm->_dom;
- JSObjectRef documentObject = JSObjectMake(dm->_ctx, Arabica::DOM::JSCDocument::getTmpl(), privData);
- JSObjectRef globalObject = JSContextGetGlobalObject(dm->_ctx);
- JSObjectSetProperty(dm->_ctx, globalObject, JSStringCreateWithUTF8CString("document"), documentObject, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete, NULL);
+ JSObjectRef documentObject = JSObjectMake(dm->_ctx, Arabica::DOM::JSCDocument::getTmpl(), privData);
+ JSObjectRef globalObject = JSContextGetGlobalObject(dm->_ctx);
+ JSObjectSetProperty(dm->_ctx, globalObject, JSStringCreateWithUTF8CString("document"), documentObject, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete, NULL);
+ }
dm->eval("_invokers = {};");
dm->eval("_x = {};");
@@ -202,10 +247,9 @@ Data JSCDataModel::getValueAsData(const JSValueRef value) {
JSValueRef exception = NULL;
switch(JSValueGetType(_ctx, value)) {
case kJSTypeUndefined:
- LOG(ERROR) << "IsUndefined is unimplemented";
- break;
case kJSTypeNull:
- LOG(ERROR) << "IsNull is unimplemented";
+ data.atom = "null";
+ data.type = Data::INTERPRETED;
break;
case kJSTypeBoolean:
data.atom = (JSValueToBoolean(_ctx, value) ? "true" : "false");
@@ -225,6 +269,7 @@ Data JSCDataModel::getValueAsData(const JSValueRef value) {
JSStringGetUTF8CString(stringValue, buf, maxSize);
data.atom = std::string(buf);
+ data.type = Data::VERBATIM;
JSStringRelease(stringValue);
free(buf);
break;
@@ -488,4 +533,43 @@ JSValueRef JSCDataModel::jsIn(JSContextRef ctx, JSObjectRef function, JSObjectRe
}
+bool JSCDataModel::jsIOProcessorHasProp(JSContextRef ctx, JSObjectRef object, JSStringRef propertyName) {
+ JSCDataModel* INSTANCE = (JSCDataModel*)JSObjectGetPrivate(object);
+ std::map<std::string, IOProcessor> ioProcessors = INSTANCE->_interpreter->getIOProcessors();
+
+ size_t maxSize = JSStringGetMaximumUTF8CStringSize(propertyName);
+ char buffer[maxSize];
+ JSStringGetUTF8CString(propertyName, buffer, maxSize);
+ std::string prop(buffer);
+
+ return ioProcessors.find(prop) != ioProcessors.end();
+}
+
+JSValueRef JSCDataModel::jsIOProcessorGetProp(JSContextRef ctx, JSObjectRef object, JSStringRef propertyName, JSValueRef* exception) {
+ JSCDataModel* INSTANCE = (JSCDataModel*)JSObjectGetPrivate(object);
+ std::map<std::string, IOProcessor> ioProcessors = INSTANCE->_interpreter->getIOProcessors();
+
+ size_t maxSize = JSStringGetMaximumUTF8CStringSize(propertyName);
+ char buffer[maxSize];
+ JSStringGetUTF8CString(propertyName, buffer, maxSize);
+ std::string prop(buffer);
+
+ if (ioProcessors.find(prop) != ioProcessors.end()) {
+ return INSTANCE->getDataAsValue(ioProcessors.find(prop)->second.getDataModelVariables());
+ }
+ return JSValueMakeUndefined(ctx);
+}
+
+void JSCDataModel::jsIOProcessorListProps(JSContextRef ctx, JSObjectRef object, JSPropertyNameAccumulatorRef propertyNames) {
+ JSCDataModel* INSTANCE = (JSCDataModel*)JSObjectGetPrivate(object);
+ std::map<std::string, IOProcessor> ioProcessors = INSTANCE->_interpreter->getIOProcessors();
+
+ std::map<std::string, IOProcessor>::const_iterator ioProcIter = ioProcessors.begin();
+ while(ioProcIter != ioProcessors.end()) {
+ JSStringRef ioProcName = JSStringCreateWithUTF8CString(ioProcIter->first.c_str());
+ JSPropertyNameAccumulatorAddName(propertyNames, ioProcName);
+ ioProcIter++;
+ }
+}
+
} \ No newline at end of file
diff --git a/src/uscxml/plugins/datamodel/ecmascript/JavaScriptCore/JSCDataModel.h b/src/uscxml/plugins/datamodel/ecmascript/JavaScriptCore/JSCDataModel.h
index 28a313e..36765c2 100644
--- a/src/uscxml/plugins/datamodel/ecmascript/JavaScriptCore/JSCDataModel.h
+++ b/src/uscxml/plugins/datamodel/ecmascript/JavaScriptCore/JSCDataModel.h
@@ -66,6 +66,11 @@ protected:
static JSClassDefinition jsPrintClassDef;
static JSValueRef jsPrint(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception);
+ static JSClassDefinition jsIOProcessorsClassDef;
+ static bool jsIOProcessorHasProp(JSContextRef ctx, JSObjectRef object, JSStringRef propertyName);
+ static JSValueRef jsIOProcessorGetProp(JSContextRef ctx, JSObjectRef object, JSStringRef propertyName, JSValueRef* exception);
+ static void jsIOProcessorListProps(JSContextRef ctx, JSObjectRef object, JSPropertyNameAccumulatorRef propertyNames);
+
JSValueRef getDocumentAsValue(const Arabica::DOM::Document<std::string>& doc);
JSValueRef getDataAsValue(const Data& data);
Data getValueAsData(const JSValueRef value);
diff --git a/src/uscxml/plugins/datamodel/prolog/swi/SWIDataModel.cpp b/src/uscxml/plugins/datamodel/prolog/swi/SWIDataModel.cpp
index c92274c..2e37b29 100644
--- a/src/uscxml/plugins/datamodel/prolog/swi/SWIDataModel.cpp
+++ b/src/uscxml/plugins/datamodel/prolog/swi/SWIDataModel.cpp
@@ -264,13 +264,17 @@ void SWIDataModel::eval(const std::string& expr) {
}
bool SWIDataModel::evalAsBool(const std::string& expr) {
- PlCompound compound(expr.c_str());
- PlTermv termv(compound.arity());
- for (int i = 0; i < compound.arity(); i++) {
- termv[i] = compound[i + 1];
+ try {
+ PlCompound compound(expr.c_str());
+ PlTermv termv(compound.arity());
+ for (int i = 0; i < compound.arity(); i++) {
+ termv[i] = compound[i + 1];
+ }
+ PlQuery query(compound.name(), termv);
+ return query.next_solution() > 0;
+ } catch(...) {
+ return false;
}
- PlQuery query(compound.name(), termv);
- return query.next_solution() > 0;
}
std::string SWIDataModel::evalAsString(const std::string& expr) {
diff --git a/src/uscxml/plugins/invoker/CMakeLists.txt b/src/uscxml/plugins/invoker/CMakeLists.txt
index 34d8918..e836275 100644
--- a/src/uscxml/plugins/invoker/CMakeLists.txt
+++ b/src/uscxml/plugins/invoker/CMakeLists.txt
@@ -32,6 +32,25 @@ else()
list (APPEND USCXML_FILES ${DIRMON_INVOKER})
endif()
+
+# Location invoker to watch for proximity to GPS coordinates
+
+file(GLOB_RECURSE LOCATION_INVOKER
+ location/*.cpp
+ location/*.h
+)
+source_group("Invoker\\location" FILES ${LOCATION_INVOKER})
+if (BUILD_AS_PLUGINS)
+ add_library(
+ invoker_location SHARED
+ ${LOCATION_INVOKER})
+ target_link_libraries(invoker_location uscxml)
+ set_target_properties(invoker_location PROPERTIES FOLDER "Plugin Invoker")
+else()
+ list (APPEND USCXML_FILES ${LOCATION_INVOKER})
+endif()
+
+
# System invoker to open a native command
file(GLOB_RECURSE XHTML_INVOKER
diff --git a/src/uscxml/plugins/invoker/location/LocationInvoker.cpp b/src/uscxml/plugins/invoker/location/LocationInvoker.cpp
new file mode 100644
index 0000000..9aeb6b4
--- /dev/null
+++ b/src/uscxml/plugins/invoker/location/LocationInvoker.cpp
@@ -0,0 +1,44 @@
+#include "LocationInvoker.h"
+#include <glog/logging.h>
+
+#ifdef BUILD_AS_PLUGINS
+#include <Pluma/Connector.hpp>
+#endif
+
+namespace uscxml {
+
+#ifdef BUILD_AS_PLUGINS
+PLUMA_CONNECTOR
+bool connect(pluma::Host& host) {
+ host.add( new LocationInvokerProvider() );
+ return true;
+}
+#endif
+
+LocationInvoker::LocationInvoker() {
+}
+
+LocationInvoker::~LocationInvoker() {
+};
+
+boost::shared_ptr<InvokerImpl> LocationInvoker::create(InterpreterImpl* interpreter) {
+ boost::shared_ptr<LocationInvoker> invoker = boost::shared_ptr<LocationInvoker>(new LocationInvoker());
+ invoker->_interpreter = interpreter;
+ return invoker;
+}
+
+Data LocationInvoker::getDataModelVariables() {
+ Data data;
+ return data;
+}
+
+void LocationInvoker::send(const SendRequest& req) {
+}
+
+void LocationInvoker::cancel(const std::string sendId) {
+}
+
+void LocationInvoker::invoke(const InvokeRequest& req) {
+}
+
+} \ No newline at end of file
diff --git a/src/uscxml/plugins/invoker/location/LocationInvoker.h b/src/uscxml/plugins/invoker/location/LocationInvoker.h
new file mode 100644
index 0000000..2eb4833
--- /dev/null
+++ b/src/uscxml/plugins/invoker/location/LocationInvoker.h
@@ -0,0 +1,40 @@
+#ifndef LOCATIONINVOKER_H_W09J90F0
+#define LOCATIONINVOKER_H_W09J90F0
+
+#include <uscxml/Interpreter.h>
+
+#ifdef BUILD_AS_PLUGINS
+#include "uscxml/plugins/Plugins.h"
+#endif
+
+namespace uscxml {
+
+class LocationInvoker : public InvokerImpl {
+public:
+ LocationInvoker();
+ virtual ~LocationInvoker();
+ virtual boost::shared_ptr<InvokerImpl> create(InterpreterImpl* interpreter);
+
+ virtual std::set<std::string> getNames() {
+ std::set<std::string> names;
+ names.insert("location");
+ names.insert("http://uscxml.tk.informatik.tu-darmstadt.de/#location");
+ return names;
+ }
+
+ virtual Data getDataModelVariables();
+ virtual void send(const SendRequest& req);
+ virtual void cancel(const std::string sendId);
+ virtual void invoke(const InvokeRequest& req);
+
+protected:
+};
+
+#ifdef BUILD_AS_PLUGINS
+PLUMA_INHERIT_PROVIDER(LocationInvoker, InvokerImpl);
+#endif
+
+}
+
+
+#endif /* end of include guard: LOCATIONINVOKER_H_W09J90F0 */
diff --git a/src/uscxml/plugins/invoker/miles/SpatialAudio.cpp b/src/uscxml/plugins/invoker/miles/SpatialAudio.cpp
index 3826ebb..298b15f 100644
--- a/src/uscxml/plugins/invoker/miles/SpatialAudio.cpp
+++ b/src/uscxml/plugins/invoker/miles/SpatialAudio.cpp
@@ -28,11 +28,11 @@ SpatialAudio::SpatialAudio() {
_pos[0] = _pos[1] = _pos[2] = 0.0;
_listener = new float[3];
_listener[0] = _listener[1] = _listener[2] = 0.0;
+ _maxPos = new float[3];
+ _maxPos[0] = _maxPos[1] = _maxPos[2] = 1.0;
miles_init();
-
}
-
SpatialAudio::~SpatialAudio() {
};
@@ -53,8 +53,8 @@ void SpatialAudio::send(const SendRequest& req) {
_audioDev = miles_audio_device_open(MILES_AUDIO_IO_OPENAL, _audioDevIndex, 0, 22050, 2, 1, 1024, false);
if (_audioDev != NULL) {
_audioDevOpen = true;
- float rolloffFactor = 0.2;
- miles_audio_device_control(MILES_AUDIO_IO_OPENAL, _audioDev, MILES_AUDIO_DEVICE_CTRL_SET_ROLLOFF_FACTOR, &rolloffFactor);
+// float rolloffFactor = 1.0;
+// miles_audio_device_control(MILES_AUDIO_IO_OPENAL, _audioDev, MILES_AUDIO_DEVICE_CTRL_SET_ROLLOFF_FACTOR, &rolloffFactor);
}
}
@@ -70,24 +70,30 @@ void SpatialAudio::send(const SendRequest& req) {
miles_audio_device_control(MILES_AUDIO_IO_OPENAL, _audioDev, MILES_AUDIO_DEVICE_CTRL_SET_POSITION, _pos);
+
char* buffer = (char*)malloc(_audioDev->chunk_size);
// skip wav header
_dataStream.seekg(44);
-
+
while(_dataStream.readsome(buffer, _audioDev->chunk_size) != 0) {
- miles_audio_device_write(MILES_AUDIO_IO_OPENAL, _audioDev, buffer, _audioDev->chunk_size);
+ int written = 0;
+ while(written < _audioDev->chunk_size) {
+ written += miles_audio_device_write(MILES_AUDIO_IO_OPENAL, _audioDev, buffer + written, _audioDev->chunk_size - written);
+ tthread::this_thread::sleep_for(tthread::chrono::milliseconds(10));
+ }
}
+ _dataStream.seekg(0);
free(buffer);
}
} else if (boost::iequals(req.name, "move.listener")) {
if (_audioDevOpen) {
getPosFromParams(req.params, _listener);
- std::cout << "Listener: ";
- for (int i = 0; i < 3; i++) {
- std::cout << _listener[i] << " ";
- }
- std::cout << std::endl;
+// std::cout << "Listener: ";
+// for (int i = 0; i < 3; i++) {
+// std::cout << _listener[i] << " ";
+// }
+// std::cout << std::endl;
miles_audio_device_control(MILES_AUDIO_IO_OPENAL, _audioDev, MILES_AUDIO_DEVICE_CTRL_SET_LISTENER_POS, _listener);
@@ -119,6 +125,17 @@ void SpatialAudio::invoke(const InvokeRequest& req) {
getPosFromParams(req.params, _pos);
+ std::multimap<std::string, std::string>::const_iterator paramIter = req.params.begin();
+ while(paramIter != req.params.end()) {
+ if (boost::iequals(paramIter->first, "maxX"))
+ _maxPos[0] = strTo<float>(paramIter->second);
+ if (boost::iequals(paramIter->first, "maxY"))
+ _maxPos[1] = strTo<float>(paramIter->second);
+ if (boost::iequals(paramIter->first, "maxZ"))
+ _maxPos[2] = strTo<float>(paramIter->second);
+ paramIter++;
+ }
+
struct miles_audio_device_description *devices;
int ndevs;
@@ -170,6 +187,10 @@ void SpatialAudio::getPosFromParams(const std::multimap<std::string, std::string
} catch (boost::bad_lexical_cast& e) {
LOG(ERROR) << "Cannot interpret circle as float value in params: " << e.what();
}
+
+ position[0] = position[0] / _maxPos[0];
+ position[1] = position[1] / _maxPos[1];
+ position[2] = position[2] / _maxPos[2];
// std::cout << _pos[0] << ":" << _pos[1] << ":" << _pos[2] << std::endl;
}
diff --git a/src/uscxml/plugins/invoker/miles/SpatialAudio.h b/src/uscxml/plugins/invoker/miles/SpatialAudio.h
index 082d796..59ecfdb 100644
--- a/src/uscxml/plugins/invoker/miles/SpatialAudio.h
+++ b/src/uscxml/plugins/invoker/miles/SpatialAudio.h
@@ -45,6 +45,7 @@ protected:
float* _pos;
float* _listener;
+ float* _maxPos;
bool _audioDevOpen;
int _audioDevIndex;
struct miles_audio_device* _audioDev;
diff --git a/src/uscxml/plugins/invoker/umundo/UmundoInvoker.cpp b/src/uscxml/plugins/invoker/umundo/UmundoInvoker.cpp
index 2b3377e..081842f 100644
--- a/src/uscxml/plugins/invoker/umundo/UmundoInvoker.cpp
+++ b/src/uscxml/plugins/invoker/umundo/UmundoInvoker.cpp
@@ -247,7 +247,11 @@ void UmundoInvoker::receive(void* object, umundo::Message* msg) {
// get meta fields into event
std::map<std::string, std::string>::const_iterator metaIter = msg->getMeta().begin();
while(metaIter != msg->getMeta().end()) {
- event.data.compound[metaIter->first] = Data(metaIter->second, Data::VERBATIM);
+ if (isNumeric(metaIter->second.c_str(), 10)) {
+ event.data.compound[metaIter->first] = Data(metaIter->second, Data::INTERPRETED);
+ } else {
+ event.data.compound[metaIter->first] = Data(metaIter->second, Data::VERBATIM);
+ }
metaIter++;
}
@@ -309,9 +313,13 @@ std::multimap<std::string, std::pair<std::string, boost::weak_ptr<umundo::Node>
boost::shared_ptr<umundo::Node> UmundoInvoker::getNode(InterpreterImpl* interpreter, const std::string& domain) {
std::pair<_nodes_t::iterator, _nodes_t::iterator> range = _nodes.equal_range(interpreter->getName());
for (_nodes_t::iterator it = range.first; it != range.second; it++) {
- if (it->second.first.compare(domain) == 0)
- return it->second.second.lock();
+ if (it->second.first.compare(domain) == 0) {
+ boost::shared_ptr<umundo::Node> node = it->second.second.lock();
+ if (node)
+ return node;
+ }
}
+ // create a new node
boost::shared_ptr<umundo::Node> node = boost::shared_ptr<umundo::Node>(new umundo::Node(domain));
std::pair<std::string, std::pair<std::string, boost::weak_ptr<umundo::Node> > > pair = std::make_pair(interpreter->getName(), std::make_pair(domain, node));
_nodes.insert(pair);
diff --git a/src/uscxml/plugins/invoker/vxml/VoiceXMLInvoker.cpp b/src/uscxml/plugins/invoker/vxml/VoiceXMLInvoker.cpp
index a71d21a..a18be8e 100644
--- a/src/uscxml/plugins/invoker/vxml/VoiceXMLInvoker.cpp
+++ b/src/uscxml/plugins/invoker/vxml/VoiceXMLInvoker.cpp
@@ -48,10 +48,20 @@ Data VoiceXMLInvoker::getDataModelVariables() {
void VoiceXMLInvoker::send(const SendRequest& req) {
StartRequest start;
std::stringstream domSS;
+// if (req.dom) {
+// // hack until jVoiceXML supports XML
+// std::cout << req.dom;
+// Arabica::DOM::NodeList<std::string> prompts = req.dom.getElementsByTagName("vxml:prompt");
+// for (int i = 0; i < prompts.getLength(); i++) {
+// if (prompts.item(i).hasChildNodes()) {
+// domSS << prompts.item(i).getFirstChild().getNodeValue() << ".";
+// }
+// }
+// }
domSS << req.getFirstDOMElement();
start.content = domSS.str();
-
- start.contentURL.href = "http://localhost/~sradomski/hello.vxml";
+ _interpreter->getDataModel().replaceExpressions(start.content);
+
start.requestId = "asdf";
start.source = "asdf";
start.target = "umundo://mmi/jvoicexml";
diff --git a/src/uscxml/plugins/invoker/xhtml/XHTMLInvoker.cpp b/src/uscxml/plugins/invoker/xhtml/XHTMLInvoker.cpp
index 0c7ef08..4e2c01a 100644
--- a/src/uscxml/plugins/invoker/xhtml/XHTMLInvoker.cpp
+++ b/src/uscxml/plugins/invoker/xhtml/XHTMLInvoker.cpp
@@ -7,9 +7,12 @@
#include <Pluma/Connector.hpp>
#endif
-#if defined(__APPLE__) and defined(TARGET_OS_MAC)
-#include <CoreFoundation/CFBundle.h>
-#include <ApplicationServices/ApplicationServices.h>
+#if __APPLE__
+# if TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR
+# else
+# include <CoreFoundation/CFBundle.h>
+# include <ApplicationServices/ApplicationServices.h>
+# endif
#endif
namespace uscxml {
@@ -41,6 +44,7 @@ bool XHTMLInvoker::httpRecvRequest(const HTTPServer::Request& req) {
// these are the XHR requests
if (boost::iequals(req.data["header"]["X-Requested-With"].atom, "XMLHttpRequest")) {
if (boost::iequals(req.data["type"].atom, "get")) {
+ // the long-polling GET
if (_longPoll) {
evhttp_send_error(_longPoll.curlReq, 204, NULL);
_longPoll.curlReq = NULL;
@@ -52,9 +56,12 @@ bool XHTMLInvoker::httpRecvRequest(const HTTPServer::Request& req) {
}
return true;
} else {
+ // a POST request
Event ev(req);
if (ev.data["header"]["X-SCXML-Name"]) {
ev.name = ev.data["header"]["X-SCXML-Name"].atom;
+ } else {
+ ev.name = req.data["type"].atom;
}
ev.origin = _invokeId;
ev.initContent(req.data["content"].atom);
@@ -62,6 +69,9 @@ bool XHTMLInvoker::httpRecvRequest(const HTTPServer::Request& req) {
// content is already on ev.raw
ev.data.compound["Connection"].compound.erase("content");
+ HTTPServer::Reply reply(req);
+ HTTPServer::reply(reply);
+
returnEvent(ev);
return true;
}
@@ -70,17 +80,9 @@ bool XHTMLInvoker::httpRecvRequest(const HTTPServer::Request& req) {
// initial request for a document
if (!req.data["query"] && // no query parameters
boost::iequals(req.data["type"].atom, "get") && // request type is GET
- (boost::icontains(req.data["header"]["Accept"].atom, "text/html") || // accepts html or
- boost::contains(req.data["header"]["User-Agent"].atom, "MSIE")) && // is the internet explorer
req.content.length() == 0) { // no content
HTTPServer::Reply reply(req);
- URL templateURL("templates/xhtml-invoker.html");
- templateURL.toAbsolute(_interpreter->getBaseURI());
- templateURL.download(true);
- std::string templateContent = templateURL.getInContent();
- boost::replace_all(templateContent, "${scxml.server}", _url);
- boost::replace_all(templateContent, "${scxml.invokeId}", _invokeId);
std::string content;
std::stringstream ss;
@@ -90,15 +92,25 @@ bool XHTMLInvoker::httpRecvRequest(const HTTPServer::Request& req) {
} else if(_invokeReq.data) {
ss << _invokeReq.data;
content = ss.str();
- } else {
+ } else if (_invokeReq.content.length() > 0){
content = _invokeReq.content;
+ } else {
+ URL templateURL("templates/xhtml-invoker.html");
+ templateURL.toAbsolute(_interpreter->getBaseURI());
+ templateURL.download(true);
+ content = templateURL.getInContent();
}
- boost::replace_all(templateContent, "${scxml.content}", content);
- reply.content = templateContent;
- reply.headers["Content-Type"] = "text/html";
+
+ _interpreter->getDataModel().replaceExpressions(content);
+ reply.content = content;
+
+ // application/xhtml+xml
+ reply.headers["Content-Type"] = "text/html; charset=utf-8";
+// reply.headers["Content-Type"] = "application/xhtml+xml; charset=utf-8";
// aggressive caching in IE will return all XHR get requests instantenously with the template otherwise
reply.headers["Cache-Control"] = "no-cache";
+// reply.headers["Cache-Control"] = "max-age=3600";
HTTPServer::reply(reply);
@@ -122,17 +134,27 @@ Data XHTMLInvoker::getDataModelVariables() {
void XHTMLInvoker::send(const SendRequest& req) {
tthread::lock_guard<tthread::recursive_mutex> lock(_mutex);
+ SendRequest reqCopy(req);
+ _interpreter->getDataModel().replaceExpressions(reqCopy.content);
+ Data json = Data::fromJSON(reqCopy.content);
+ if (json) {
+ reqCopy.data = json;
+ }
+
if (!_longPoll) {
- _outQueue.push_back(req);
+ _outQueue.push_back(reqCopy);
return;
}
- reply(req, _longPoll);
+ reply(reqCopy, _longPoll);
_longPoll.curlReq = NULL;
}
void XHTMLInvoker::reply(const SendRequest& req, const HTTPServer::Request& longPoll) {
HTTPServer::Reply reply(longPoll);
+ // is there JSON in the content?
+ std::string content = req.content;
+
if (req.dom) {
std::stringstream ss;
Arabica::DOM::Node<std::string> content = req.dom.getDocumentElement();
@@ -164,8 +186,10 @@ void XHTMLInvoker::cancel(const std::string sendId) {
void XHTMLInvoker::invoke(const InvokeRequest& req) {
_invokeReq = req;
- HTTPServer::registerServlet(_interpreter->getName() + "/xhtml/" + req.invokeid, this);
-#if defined(__APPLE__) and defined(TARGET_OS_MAC)
+ HTTPServer::registerServlet(_interpreter->getName() + "/xhtml/" + req.invokeid + ".html", this);
+#if __APPLE__
+# if TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR
+# else
// see http://stackoverflow.com/questions/4177744/c-osx-open-default-browser
CFURLRef url = CFURLCreateWithBytes (
NULL, // allocator
@@ -177,6 +201,7 @@ void XHTMLInvoker::invoke(const InvokeRequest& req) {
LSOpenCFURLRef(url,0);
CFRelease(url);
return;
+# endif
#endif
#ifdef _WIN32
// see http://support.microsoft.com/kb/224816
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index 6e00d52..ba332c2 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -40,7 +40,7 @@ if (OFF)
set_target_properties(test-dirmon PROPERTIES FOLDER "Tests")
endif()
-if (OFF)
+if (NOT WIN32)
add_executable(test-arabica-events src/test-arabica-events.cpp)
target_link_libraries(test-arabica-events uscxml)
add_test(test-arabica-events ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test-arabica-events ${CMAKE_SOURCE_DIR}/test/samples/uscxml/arabica/test-arabica-events.xml)
@@ -51,6 +51,11 @@ if (OFF)
add_test(test-arabica-xpath ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test-arabica-xpath)
set_target_properties(test-arabica-xpath PROPERTIES FOLDER "Tests")
+ add_executable(test-arabica-parsing src/test-arabica-parsing.cpp)
+ target_link_libraries(test-arabica-parsing uscxml)
+ add_test(test-arabica-xpath ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test-arabica-parsing)
+ set_target_properties(test-arabica-parsing PROPERTIES FOLDER "Tests")
+
endif()
add_executable(test-url src/test-url.cpp)
@@ -58,6 +63,11 @@ target_link_libraries(test-url uscxml)
add_test(test-url ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test-url)
set_target_properties(test-url PROPERTIES FOLDER "Tests")
+add_executable(test-datamodel src/test-datamodel.cpp)
+target_link_libraries(test-datamodel uscxml)
+add_test(test-datamodel ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test-datamodel)
+set_target_properties(test-datamodel PROPERTIES FOLDER "Tests")
+
# if (NOT WIN32)
# add_executable(test-mmi src/test-mmi.cpp)
# target_link_libraries(test-mmi uscxml)
diff --git a/test/samples/uscxml/applications/SpatialMapTicker.java b/test/samples/uscxml/applications/SpatialMapTicker.java
new file mode 100644
index 0000000..d161264
--- /dev/null
+++ b/test/samples/uscxml/applications/SpatialMapTicker.java
@@ -0,0 +1,153 @@
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.LinkedList;
+import java.util.Random;
+
+import org.umundo.core.Greeter;
+import org.umundo.core.Message;
+import org.umundo.core.Node;
+import org.umundo.core.Publisher;
+
+public class SpatialMapTicker {
+
+ Node node;
+ Publisher pub;
+ Greeter greeter;
+ ArrayList<Sensor> sensors = new ArrayList<Sensor>();
+ static ArrayList<SensorMessage> messages = new ArrayList<SensorMessage>();
+ static Random random = new Random(System.currentTimeMillis());
+
+ public class SensorMessage {
+ public String message;
+ public String severity;
+ public SensorMessage(String message, String severity) {
+ this.message = message;
+ this.severity = severity;
+ }
+ public SensorMessage(String message) {
+ this.message = message;
+ this.severity = "Notice";
+ }
+ }
+
+ public class Sensor {
+ @Override
+ public String toString() {
+ return "Sensor [id=" + id + ", lat=" + lat + ", lon=" + lon
+ + ", html=" + getHTML() + "]";
+ }
+ public String id = "";
+ public Double lat = new Double(0);
+ public Double lon = new Double(0);
+ LinkedList<SensorMessage> messages = new LinkedList<SensorMessage>();
+
+ public void addMessage(SensorMessage message) {
+ if (messages.size() > 15)
+ messages.removeLast();
+ messages.addFirst(message);
+ }
+
+ public String getHTML() {
+ StringBuilder sb = new StringBuilder();
+ for (SensorMessage message : messages) {
+ sb.append(message.severity);
+ sb.append(": ");
+ sb.append(message.message);
+ sb.append("<br />");
+ }
+ return sb.toString();
+ }
+ }
+
+ public class SensorGreeter extends Greeter {
+ public void welcome(Publisher publisher, String nodeId, String subId) {
+ // send all sensors to new subscribers
+ for (Sensor sensor : sensors) {
+ Message msg = new Message(); //Message.toSubscriber(subId);
+ msg.putMeta("id", sensor.id);
+ msg.putMeta("lat", sensor.lat.toString());
+ msg.putMeta("lon", sensor.lon.toString());
+ msg.putMeta("html", sensor.getHTML());
+ pub.send(msg);
+ }
+ }
+
+ @Override
+ public void farewell(Publisher arg0, String nodeId, String subId) {
+ }
+
+ }
+
+ public SpatialMapTicker() {
+ node = new Node();
+ pub = new Publisher("map/tick");
+ greeter = new SensorGreeter();
+ pub.setGreeter(greeter);
+ node.addPublisher(pub);
+
+ double latCenter = 59.32;
+ double lonCenter = 18.08;
+
+ int nrSensors = 15; //(int) (Math.random() * 20);
+ for (int i = 0; i < nrSensors; i++) {
+ Sensor sensor = new Sensor();
+ double latOffset = (Math.random() - 0.5) * 0.3;
+ double lonOffset = (Math.random() - 0.5) * 0.3;
+
+ sensor.id = "Sensor #" + i;
+ sensor.lat = latCenter + latOffset;
+ sensor.lon = lonCenter + lonOffset;
+ sensors.add(sensor);
+ }
+ }
+
+ public static void main(String[] args) {
+ SpatialMapTicker ticker = new SpatialMapTicker();
+ ticker.run();
+ }
+
+ private void run() {
+ messages.add(new SensorMessage("Oil pressure threshold exceeded", "Error"));
+ messages.add(new SensorMessage("Error #245 in diagnostics unit"));
+ messages.add(new SensorMessage("Error #32 in diagnostics unit"));
+ messages.add(new SensorMessage("Error #81 in diagnostics unit"));
+ messages.add(new SensorMessage("Error #15 in diagnostics unit"));
+ messages.add(new SensorMessage("Error #145 in diagnostics unit"));
+ messages.add(new SensorMessage("Unit was moved out of construction site area"));
+ messages.add(new SensorMessage("Hydraulic pressure exceeding safety limits"));
+ messages.add(new SensorMessage("Drivers seat belts are not fastened!"));
+ messages.add(new SensorMessage("Battery recharge cycles exceeded"));
+ messages.add(new SensorMessage("Unit operated outside recommended paramters"));
+
+ while (true) {
+ try {
+ Thread.sleep((long) (Math.random() * 1000) + 200);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+
+ Sensor sensor = sensors.get(random.nextInt(sensors.size()));
+ SensorMessage fault = messages.get(random.nextInt(messages.size()));
+
+ Date now = new Date();
+ SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss z");
+ String nowString = sdf.format(now);
+ sensor.addMessage(fault);
+
+// System.out.println("Publishing " + sensor);
+
+ Message msg = new Message();
+ msg.putMeta("id", sensor.id);
+ msg.putMeta("lat", sensor.lat.toString());
+ msg.putMeta("lon", sensor.lon.toString());
+ msg.putMeta("html", sensor.getHTML());
+ msg.putMeta("time", nowString);
+ msg.putMeta("timeStamp", Long.toString(now.getTime()));
+ msg.putMeta("lastMsg", sensor.messages.getFirst().message);
+ msg.putMeta("severity", sensor.messages.getFirst().severity);
+ pub.send(msg);
+ }
+ }
+
+}
diff --git a/test/samples/uscxml/applications/spoken-map-ticker.scxml b/test/samples/uscxml/applications/spoken-map-ticker.scxml
new file mode 100644
index 0000000..682429d
--- /dev/null
+++ b/test/samples/uscxml/applications/spoken-map-ticker.scxml
@@ -0,0 +1,64 @@
+<scxml datamodel="ecmascript" name="comet-test"
+ xmlns:html="http://www.w3.org/1999/xhtml"
+ xmlns:vxml="http://www.w3.org/2001/vxml"
+ xmlns="http://www.w3.org/2005/07/scxml">
+
+ <script src="http://uscxml.tk.informatik.tu-darmstadt.de/scripts/dump.js" />
+
+ <state id="start">
+ <invoke type="xhtml" id="xhtml1">
+ <content src="spoken-map-ticker.xhtml" />
+ <finalize>
+ <!-- <script>dump(_event);</script> -->
+ </finalize>
+ </invoke>
+
+ <invoke type="vxml" id="vxml" />
+
+ <invoke type="umundo" id="umundo">
+ <param name="channel" expr="'map/tick'" />
+ <finalize>
+ <script>dump(_event);</script>
+ </finalize>
+ </invoke>
+
+ <invoke type="http://www.smartvortex.eu/mmi/spatial-audio/" src="../audio/click.wav" id="audio">
+ <!-- <param name="maxX" expr="180" />
+ <param name="maxY" expr="18" />
+ <param name="maxZ" expr="85" /> -->
+ </invoke>
+
+ <state id="idle">
+ <transition target="idle" event="map.center">
+ <send event="move.listener" target="#_audio">
+ <param name="x" expr="_event.data.lon" />
+ <param name="y" expr="18 - _event.data.zoom" />
+ <!-- <param name="y" expr="0" /> -->
+ <param name="z" expr="_event.data.lat" />
+
+ </send>
+ </transition>
+ <transition target="idle" event="umundo.rcvd">
+ <send target="#_xhtml1">
+ <content>${_event.data}</content>
+ </send>
+ <send target="#_audio" event="play" delay="0ms">
+ <param name="x" expr="_event.data.lon" />
+ <param name="y" expr="0" />
+ <param name="z" expr="_event.data.lat" />
+ </send>
+
+ <if cond="_event.data.lastMsg">
+ <send target="#_vxml">
+ <content>
+ <vxml:prompt>
+ ${_event.data.lastMsg}
+ </vxml:prompt>
+ </content>
+ </send>
+ </if>
+
+ </transition>
+ </state>
+ </state>
+</scxml> \ No newline at end of file
diff --git a/test/samples/uscxml/applications/spoken-map-ticker.xhtml b/test/samples/uscxml/applications/spoken-map-ticker.xhtml
new file mode 100644
index 0000000..e656be3
--- /dev/null
+++ b/test/samples/uscxml/applications/spoken-map-ticker.xhtml
@@ -0,0 +1,236 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <!-- space in between script is required as <script/> is not valid HTML to most browsers -->
+ <script type="text/javascript" src="http://code.jquery.com/jquery-2.0.3.js"> </script>
+ <script type="text/javascript" src="http://www.openlayers.org/api/OpenLayers.js"> </script>
+ <script type="text/javascript" src="http://epikur.local/~sradomski/CometSession.js"> </script>
+ <script type="text/javascript" src="https://raw.github.com/jchavannes/jquery-timer/master/jquery.timer.js"> </script>
+
+ <style type="text/css">
+ html, body, #map {
+ width: 100%;
+ height: 100%;
+ margin: 0;
+ }
+ .olPopup {
+ border-radius: 8px;
+ }
+ div.olControlOverviewMapElement {
+ background-color: #666;
+ }
+ div.olMap {
+ width: 180px;
+ height: 120px;
+ margin: 0;
+ }
+ div.timeRuler {
+ font-family:Courier,Arial,Helvetica,sans-serif;
+ font-size: 1.2em;
+ font-weight: bold;
+ background-color: #eee;
+ }
+ div#message {
+ overflow: auto;
+ height: 100%;
+ font-family:Courier,Arial,Helvetica,sans-serif;
+ font-size: 0.6em;
+ }
+
+ div#message p {
+ padding: 0px;
+ margin: 4px 2px;
+ }
+
+ </style>
+
+ <!-- script type="text/javascript" src="http://localhost/~sradomski/CometSession.js"></script -->
+ <script type="text/javascript">
+//<![CDATA[
+ $(document).ready(function() {
+ var scxml = new CometSession({
+ server: document.URL,
+ onEvent: function(response, message) {
+ if (message) {
+ console.log(message);
+
+ if (!markers[message.id]) {
+ addMarker(message);
+ } else {
+ for (i = 0; i < map.popups.length; i++) {
+ if (map.popups[i].feature.id === message.id) {
+ map.popups[i].setContentHTML("<h3>" + message.id + "</h3>" + message.html);
+ }
+ }
+ markers[message.id].marker.opacity = 1.0;
+ markers[message.id].marker.setOpacity(1.0);
+
+ markers[message.id].marker.timer.play();
+ markers[message.id].feature.popupContentHTML = "<h3>" + message.id + "</h3>" + message.html;
+ }
+
+ if (message.lastMsg && message.lastMsg.length > 0) {
+ var messageDiv = $("#message")[0];
+ if (message.timeStamp > lastUpdateTime + 3000) {
+ var timeRuler = document.createElement("div");
+ timeRuler.setAttribute("class", "timeRuler");
+ timeRuler.innerHTML = message.time; // + messageDiv.innerHTML;
+ messageDiv.appendChild(timeRuler);
+ lastUpdateTime = message.timeStamp;
+ }
+
+ console.log(message.timeStamp);
+ console.log(lastUpdateTime);
+
+ var messagePara = document.createElement("p");
+ messagePara.innerHTML += message.lastMsg; // + messageDiv.innerHTML;
+ messageDiv.appendChild(messagePara);
+ messagePara.addEventListener("mousedown", function() {
+ var marker = markers[message.id].marker;
+ marker.events.triggerEvent("mousedown");
+ });
+ messagePara.addEventListener("mouseover", function() {
+ messagePara.style.backgroundColor = "#eee";
+ });
+ messagePara.addEventListener("mouseout", function() {
+ messagePara.style.backgroundColor = "#fff";
+ });
+
+ if (!stopScrolling) {
+ messageDiv.scrollTop = messageDiv.scrollHeight;
+ }
+ }
+ }
+ }
+ });
+
+ // var map = new OpenLayers.Map("demoMap");
+ // map.addLayer(new OpenLayers.Layer.OSM());
+ // map.zoomToMaxExtent();
+
+ var markers = {};
+ var lastUpdateTime = 0;
+ var stopScrolling = false;
+
+ var fromProjection = new OpenLayers.Projection("EPSG:4326"); // Transform from WGS 1984
+ var toProjection = new OpenLayers.Projection("EPSG:900913"); // to Spherical Mercator Projection
+
+ function onMoveEvent() {
+ var position = map.getCenter().transform(toProjection, fromProjection);
+ if (Math.random() > 0.7) { // do not publish every move event
+ scxml.send('map.center', {
+ 'zoom': map.getZoom(),
+ 'lon': position.lon,
+ 'lat': position.lat });
+ }
+ }
+
+ function onMoveEndEvent() {
+ var position = map.getCenter().transform(toProjection, fromProjection);
+ scxml.send('map.center', {
+ 'zoom': map.getZoom(),
+ 'lon': position.lon,
+ 'lat': position.lat });
+ }
+
+ function addMarker(message) {
+ var feature = new OpenLayers.Feature(
+ markerLayer,
+ new OpenLayers.LonLat(message.lon, message.lat).transform(fromProjection, toProjection));
+ feature.closeBox = true;
+ feature.popupClass =  OpenLayers.Class(OpenLayers.Popup.Anchored, {
+ 'autoSize': true,
+// 'maxSize': new OpenLayers.Size(300,200)
+ });
+ feature.data.popupContentHTML = "<h3>" + message.id + "</h3>" + message.html;
+ feature.data.overflow = "auto";
+ feature.data.id = message.id;
+ var marker = feature.createMarker();
+
+ marker.timer = $.timer(function() {
+ if (marker.opacity > 0.5) {
+ marker.opacity -= 0.02;
+ marker.setOpacity(marker.opacity);
+ } else {
+ marker.timer.stop();
+ }
+ }, 100);
+
+ marker.opacity = 1.0;
+ marker.setOpacity(marker.opacity);
+ marker.timer.play();
+
+ markerClick = function(evt) {
+ if (this.popup == null) {
+ this.popup = this.createPopup(this.closeBox);
+ map.addPopup(this.popup);
+ this.popup.show();
+ } else {
+ this.popup.toggle();
+ }
+ currentPopup = this.popup;
+ OpenLayers.Event.stop(evt);
+ };
+
+ marker.events.register("mousedown", feature, markerClick);
+ markers[message.id] = {
+ 'marker': marker,
+ 'feature': feature
+ };
+ markerLayer.addMarker(marker);
+ }
+ // see also: http://php-baustelle.de/openlayers/step-by-step/
+ var map = new OpenLayers.Map('map', {
+ eventListeners: {
+ "move": onMoveEvent,
+ "moveend": onMoveEndEvent,
+ }
+ });
+
+ var mapnik = new OpenLayers.Layer.OSM();
+ var markerLayer = new OpenLayers.Layer.Markers("Markers");
+// var attribution = new OpenLayers.Layer.Vector("Attribution", {attribution:"for Smart Vortex"});
+
+ map.addLayer(mapnik);
+// map.addLayer(attribution);
+ map.addLayer(markerLayer); // has to be the last layer to receive events!
+
+ map.addControl(new OpenLayers.Control.LayerSwitcher());
+ // see also http://dev.openlayers.org/releases/OpenLayers-2.12/examples/mousewheel-interval.html
+ map.addControl(new OpenLayers.Control.Navigation(
+ {dragPanOptions: {enableKinetic: true}}
+ ));
+
+ var overview = new OpenLayers.Control.OverviewMap({
+ maximized: true,
+ minRatio: 200,
+ maxRatio: 300,
+ });
+ map.addControl(overview);
+
+ // stockholm
+ var position = new OpenLayers.LonLat(18.08,59.32).transform(fromProjection, toProjection);
+// var position = new OpenLayers.LonLat(0,0).transform(fromProjection, toProjection);
+ var zoom = 11;
+ map.setCenter(position, zoom);
+
+ // var message = {
+ // 'id': "12",
+ // 'lat': 59.454441065280534,
+ // 'lon': 18.062356332194543,
+ // 'html': '<p>asdf</p>'
+ // };
+
+// addMarker(message);
+ });
+//]]>
+ </script>
+ </head>
+ <body>
+ <table width="100%" height="100%">
+ <tr>
+ <td width="20%" height="100%"><div id="message" onmouseover="stopScrolling = true" onmouseout="stopScrolling = false"></div></td>
+ <td width="80%" height="100%"><div id="map"></div></td>
+ </tr>
+ </table>
+ </body>
+</html> \ No newline at end of file
diff --git a/test/samples/uscxml/arabica/test-arabica-parsing.xml b/test/samples/uscxml/arabica/test-arabica-parsing.xml
new file mode 100644
index 0000000..f4e46fb
--- /dev/null
+++ b/test/samples/uscxml/arabica/test-arabica-parsing.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0"?>
+<root>
+<![CDATA[
+< " ' < > &
+]]>
+</root> \ No newline at end of file
diff --git a/test/samples/uscxml/test-prolog.scxml b/test/samples/uscxml/test-prolog.scxml
index 8ccfd6b..def23db 100644
--- a/test/samples/uscxml/test-prolog.scxml
+++ b/test/samples/uscxml/test-prolog.scxml
@@ -36,7 +36,7 @@
</foreach>
</onentry>
<transition target="s2"
- cond="mother(martha, X), father(bob, X)"/>
+ cond="mother(martha, X), father(bob, X), in(s1)"/>
</state>
<state id="s2">
<onentry>
@@ -49,21 +49,10 @@
</send>
</onentry>
<transition
- target="idle"
+ target="end"
event="foo"
cond="member(element('p',_,_), X), event(data(X))" />
</state>
- <state id="idle">
- <transition event="http.post" target="idle">
- <log expr="listing." />
- <respond status="200" to="event(origin(X))" />
- </transition>
- <transition event="http.post" cond="" target="idle">
- <log expr="listing." />
- <respond status="200" to="event(origin(X))" />
- </transition>
- </state>
-
<state id="end" final="true" />
</scxml>
diff --git a/test/src/test-arabica-parsing.cpp b/test/src/test-arabica-parsing.cpp
new file mode 100644
index 0000000..2a09dd6
--- /dev/null
+++ b/test/src/test-arabica-parsing.cpp
@@ -0,0 +1,48 @@
+#include "uscxml/config.h"
+#include "uscxml/Common.h"
+#include <DOM/Document.hpp>
+#include <XPath/XPath.hpp>
+#include <DOM/SAX2DOM/SAX2DOM.hpp>
+#include <DOM/io/Stream.hpp>
+#include <SAX/helpers/CatchErrorHandler.hpp>
+#include <DOM/Events/EventTarget.hpp>
+#include <DOM/Events/EventListener.hpp>
+
+using namespace Arabica::DOM;
+
+int main(int argc, char** argv) {
+
+ {
+ std::stringstream* ss = new std::stringstream();
+ (*ss) << "<root>\n<![CDATA[\n< \" ' < > &\n]]>\n</root>";
+ // we need an auto_ptr for arabica to assume ownership
+ std::auto_ptr<std::istream> ssPtr(ss);
+ Arabica::SAX::InputSource<std::string> inputSource(ssPtr);
+
+ Arabica::SAX2DOM::Parser<std::string> domParser;
+ Arabica::SAX::CatchErrorHandler<std::string> errorHandler;
+ domParser.setErrorHandler(errorHandler);
+
+ if(!domParser.parse(inputSource)) {
+ std::cout << errorHandler.errors();
+ return -1;
+ }
+ std::cout << domParser.getDocument().getDocumentElement().getFirstChild().getNodeValue() << std::endl;
+ std::cout << domParser.getDocument() << std::endl;
+ }
+ {
+ Arabica::SAX::InputSource<std::string> inputSource;
+ inputSource.setSystemId("/Users/sradomski/Documents/TK/Code/uscxml/test/samples/uscxml/arabica/test-arabica-parsing.xml");
+
+ Arabica::SAX2DOM::Parser<std::string> domParser;
+ Arabica::SAX::CatchErrorHandler<std::string> errorHandler;
+ domParser.setErrorHandler(errorHandler);
+
+ if(!domParser.parse(inputSource)) {
+ std::cout << errorHandler.errors();
+ return -1;
+ }
+ std::cout << domParser.getDocument() << std::endl;
+ }
+
+} \ No newline at end of file
diff --git a/test/src/test-datamodel.cpp b/test/src/test-datamodel.cpp
new file mode 100644
index 0000000..77f0851
--- /dev/null
+++ b/test/src/test-datamodel.cpp
@@ -0,0 +1,80 @@
+#include "uscxml/URL.h"
+#include "uscxml/Message.h"
+#include "uscxml/Interpreter.h"
+#include "uscxml/server/HTTPServer.h"
+
+#include <SAX/helpers/InputSourceResolver.hpp>
+
+#include <assert.h>
+#include <boost/algorithm/string.hpp>
+#include <iostream>
+
+using namespace uscxml;
+using namespace boost;
+
+
+int main(int argc, char** argv) {
+#ifdef _WIN32
+ WSADATA wsaData;
+ WSAStartup(MAKEWORD(2, 2), &wsaData);
+#endif
+
+ DataModel dm(Factory::getInstance()->createDataModel("ecmascript", NULL));
+ dm.evalAsString("var foo = 12");
+
+ {
+ std::string content = "$";
+ int rplc = dm.replaceExpressions(content);
+ assert(rplc == 0);
+ std::cout << content << std::endl;
+ assert(boost::equals(content, "$"));
+ }
+
+ {
+ std::string content = "$sadf ${foo}";
+ int rplc = dm.replaceExpressions(content);
+ assert(rplc == 1);
+ std::cout << content << std::endl;
+ assert(boost::equals(content, "$sadf 12"));
+ }
+
+ {
+ std::string content = "${";
+ int rplc = dm.replaceExpressions(content);
+ assert(rplc == 0);
+ std::cout << content << std::endl;
+ assert(boost::equals(content, "${"));
+ }
+
+ {
+ std::string content = "${foo}";
+ int rplc = dm.replaceExpressions(content);
+ assert(rplc == 1);
+ std::cout << content << std::endl;
+ assert(boost::equals(content, "12"));
+ }
+
+ {
+ std::string content = "${bar}";
+ int rplc = dm.replaceExpressions(content);
+ assert(rplc == 0);
+ std::cout << content << std::endl;
+ assert(boost::equals(content, "${bar}"));
+ }
+
+ {
+ std::string content = "There are ${bar} monkeys! Really ${foo} monkeys!";
+ int rplc = dm.replaceExpressions(content);
+ assert(rplc == 1);
+ std::cout << content << std::endl;
+ assert(boost::equals(content, "There are ${bar} monkeys! Really 12 monkeys!"));
+ }
+
+ {
+ std::string content = "There are ${foo} monkeys! Really ${foo} monkeys!";
+ int rplc = dm.replaceExpressions(content);
+ assert(rplc == 2);
+ std::cout << content << std::endl;
+ assert(boost::equals(content, "There are 12 monkeys! Really 12 monkeys!"));
+ }
+} \ No newline at end of file
diff --git a/test/src/test-predicates.cpp b/test/src/test-predicates.cpp
index 954afb2..ff61f92 100644
--- a/test/src/test-predicates.cpp
+++ b/test/src/test-predicates.cpp
@@ -56,6 +56,7 @@ int main(int argc, char** argv) {
assert(InterpreterImpl::nameMatch(transEvents, "foo.bar"));
assert(!InterpreterImpl::nameMatch(transEvents, "errors.my.custom"));
assert(!InterpreterImpl::nameMatch(transEvents, "errorhandler.mistake"));
- assert(!InterpreterImpl::nameMatch(transEvents, "errOr.send"));
+ // is the event name case sensitive?
+ // assert(!InterpreterImpl::nameMatch(transEvents, "errOr.send"));
assert(!InterpreterImpl::nameMatch(transEvents, "foobar"));
} \ No newline at end of file