summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--apps/mmi-browser.cpp30
-rw-r--r--apps/samples/vrml-server.scxml396
-rw-r--r--src/uscxml/Interpreter.cpp194
-rw-r--r--src/uscxml/URL.cpp4
-rw-r--r--src/uscxml/plugins/datamodel/ecmascript/v8/V8DataModel.cpp6
-rw-r--r--src/uscxml/plugins/element/postpone/PostponeElement.cpp69
-rw-r--r--src/uscxml/plugins/element/postpone/PostponeElement.h12
-rw-r--r--src/uscxml/plugins/element/response/ResponseElement.cpp133
-rw-r--r--src/uscxml/plugins/invoker/filesystem/dirmon/DirMonInvoker.cpp423
-rw-r--r--src/uscxml/plugins/invoker/filesystem/dirmon/DirMonInvoker.h59
-rw-r--r--src/uscxml/plugins/invoker/filesystem/dirmon/FileWatcher/FileWatcherOSX.cpp62
-rw-r--r--src/uscxml/plugins/invoker/filesystem/dirmon/FileWatcher/FileWatcherOSX.h3
-rw-r--r--src/uscxml/plugins/invoker/graphics/openscenegraph/OSGConverter.cpp316
-rw-r--r--src/uscxml/plugins/invoker/graphics/openscenegraph/OSGConverter.h15
-rw-r--r--src/uscxml/plugins/invoker/heartbeat/HeartbeatInvoker.cpp2
-rw-r--r--src/uscxml/server/HTTPServer.cpp62
-rw-r--r--src/uscxml/server/HTTPServer.h4
-rw-r--r--test/CMakeLists.txt5
-rw-r--r--test/src/test-dirmon.cpp26
19 files changed, 1160 insertions, 661 deletions
diff --git a/apps/mmi-browser.cpp b/apps/mmi-browser.cpp
index 1cb82e3..8d3a12f 100644
--- a/apps/mmi-browser.cpp
+++ b/apps/mmi-browser.cpp
@@ -12,7 +12,7 @@
#ifdef HAS_SIGNAL_H
void handler(int s) {
- printf("Caught SIGPIPE ############\n");
+ printf("Caught SIGPIPE ############\n");
}
#endif
@@ -34,24 +34,24 @@ int main(int argc, char** argv) {
using namespace uscxml;
#ifdef HAS_SIGNAL_H
- // disable SIGPIPE
+ // disable SIGPIPE
// struct sigaction act;
// act.sa_handler=SIG_IGN;
// sigemptyset(&act.sa_mask);
// act.sa_flags=0;
// sigaction(SIGPIPE, &act, NULL);
-
- // signal(SIGPIPE, handler);
-
- signal(SIGPIPE, SIG_IGN);
-
- // struct sigaction act;
- // int r;
- // memset(&act, 0, sizeof(act));
- // act.sa_handler = SIG_IGN;
- // act.sa_flags = SA_RESTART;
- // r = sigaction(SIGPIPE, &act, NULL);
-
+
+ // signal(SIGPIPE, handler);
+
+ signal(SIGPIPE, SIG_IGN);
+
+ // struct sigaction act;
+ // int r;
+ // memset(&act, 0, sizeof(act));
+ // act.sa_handler = SIG_IGN;
+ // act.sa_flags = SA_RESTART;
+ // r = sigaction(SIGPIPE, &act, NULL);
+
#endif
if (argc < 2) {
@@ -87,7 +87,7 @@ int main(int argc, char** argv) {
interpreter->setCmdLineOptions(argc, argv);
interpreter->start();
while(interpreter->runOnMainThread(25));
- // interpreter->interpret();
+ // interpreter->interpret();
delete interpreter;
}
diff --git a/apps/samples/vrml-server.scxml b/apps/samples/vrml-server.scxml
index 1d356be..bf0e4f6 100644
--- a/apps/samples/vrml-server.scxml
+++ b/apps/samples/vrml-server.scxml
@@ -1,23 +1,139 @@
<scxml datamodel="ecmascript" name="vrml">
<script src="http://uscxml.tk.informatik.tu-darmstadt.de/scripts/dump.js" />
<script>
+ var wrls = {}; // information of the wrl, vrml files
var models = {}; // information of the osgb files
var processed = {}; // information about processed files
- // this pattern matches the query string we use as part of generated image filenames
- var numPattern = '([0-9]+\.?[0-9]*)';
+ var pathDelim = ':'; // we need to flatten directories - this will seperate them in filenames
+
+ /**
+ * This pattern matches the query string we use as part of generated image filenames
+ */
+ var numPattern = '(-?[0-9]+\.?[0-9]*)';
var formatPattern = new RegExp(
'-(' + numPattern + // pitch
'_' + numPattern + // roll
'_' + numPattern + // yaw
'_' + numPattern + // zoom
+ '_' + numPattern + // x
+ '_' + numPattern + // y
+ '_' + numPattern + // z
'_' + numPattern + // width
- '_' + numPattern + ')\\..*$'); // height
+ '_' + numPattern + // height
+ '_(off|on)' + ')\\..*$'); // autorotate
+
+ /**
+ * Transform a file we found into a processed or model struct
+ */
+ function fileToStruct(file) {
+ var struct = {};
+ var formatMatch = formatPattern.exec(file.name);
+ // is this a processed file?
+ if (formatMatch &amp;&amp; formatMatch.length == 12) {
+ struct.key = file.relDir.replace(/\//g, pathDelim).substr(1) + file.name.substr(0, file.name.length - formatMatch[0].length);
+ struct.format = formatMatch[1] + '.' + file.extension;
+ struct.pitch = parseFloat(formatMatch[2]);
+ struct.roll = parseFloat(formatMatch[3]);
+ struct.yaw = parseFloat(formatMatch[4]);
+ struct.zoom = parseFloat(formatMatch[5]);
+ struct.x = parseFloat(formatMatch[6]);
+ struct.y = parseFloat(formatMatch[7]);
+ struct.z = parseFloat(formatMatch[8]);
+ struct.width = parseFloat(formatMatch[9]);
+ struct.height = parseFloat(formatMatch[10]);
+ struct.autorotate = parseFloat(formatMatch[11]);
+ } else {
+ struct.key = file.relDir.replace(/\//g, pathDelim).substr(1) + file.strippedName;
+ }
+ return struct;
+ }
+
+ /**
+ * Transform a http request into something to look up in the processed structure
+ */
+ function reqToStruct(req) {
+ var struct = {};
+
+ var query = (('query' in req) ? req.query : {});
+
+ struct.pitch = (('pitch' in query &amp;&amp; isNumber(query.pitch)) ? query.pitch : 0);
+ struct.roll = (('roll' in query &amp;&amp; isNumber(query.roll)) ? query.roll : 0);
+ struct.yaw = (('yaw' in query &amp;&amp; isNumber(query.yaw)) ? query.yaw : 0);
+ struct.zoom = (('zoom' in query &amp;&amp; isNumber(query.zoom)) ? query.zoom : 1);
+ struct.x = (('x' in query &amp;&amp; isNumber(query.x)) ? query.x : 0);
+ struct.y = (('y' in query &amp;&amp; isNumber(query.y)) ? query.y : 0);
+ struct.z = (('z' in query &amp;&amp; isNumber(query.z)) ? query.z : 0);
+ struct.width = (('width' in query &amp;&amp; isNumber(query.width)) ? query.width : 640);
+ struct.height = (('height' in query &amp;&amp; isNumber(query.height)) ? query.height : 480);
+ struct.autorotate = (('autorotate' in query &amp;&amp; (query.autorotate === 'on' || query.autorotate === 'off')) ? query.autorotate : 'on');
+
+ var fileComp = req.pathComponent[req.pathComponent.length - 1];
+ struct.file = fileComp.substr(0, fileComp.indexOf('.'));
+ struct.ext = fileComp.substr(fileComp.indexOf('.') + 1);
+
+ struct.key = _event.data.pathComponent.slice(1, _event.data.pathComponent.length - 1).join(pathDelim);
+ if (struct.key.length > 0)
+ struct.key += pathDelim;
+ struct.key += struct.file;
+
+ struct.format =
+ struct.pitch + '_' +
+ struct.roll + '_' +
+ struct.yaw + '_' +
+ struct.zoom + '_' +
+ struct.x + '_' +
+ struct.y + '_' +
+ struct.z + '_' +
+ struct.width + '_' +
+ struct.height + '_' +
+ struct.autorotate + '.' +
+ struct.ext;
+ return struct;
+ }
+
+ function isSupportedFormat(extension) {
+ if (extension === "gif")
+ return true;
+ if (extension === "jpg")
+ return true;
+ if (extension === "jpeg")
+ return true;
+ if (extension === "png")
+ return true;
+ if (extension === "tif")
+ return true;
+ if (extension === "tiff")
+ return true;
+ if (extension === "bmp")
+ return true;
+ return false;
+ }
+
+ // list all available models in a summary format for the topmost request
+ function overviewList() {
+ var struct = {};
+ struct.models = {};
+ for (key in models) {
+ var model = models[key];
+ struct.models[model.relDir + model.strippedName] = {};
+ struct.models[model.relDir + model.strippedName].url = _ioprocessors['http'].location + model.relDir + model.strippedName.replace(pathDelim, '/') + '.png';
+ }
+ return struct;
+ }
+
+ /**
+ * Provide an endsWith function for the string prototype
+ */
+ String.prototype.endsWith = function(suffix) {
+ return this.indexOf(suffix, this.length - suffix.length) !== -1;
+ };
// check whether a given string represents a number
function isNumber(n) {
return !isNaN(parseFloat(n)) &amp;&amp; isFinite(n);
}
+
</script>
<state id="main">
<!-- Stop processing if no vrml-path was given on command line -->
@@ -35,28 +151,7 @@
<log expr="'An error occured:'" />
<script>dump(_event);</script>
</transition>
-
- <!-- Start the directory monitor for wrl files -->
- <!--invoke type="dirmon" id="dirmon.vrml">
- <param name="dir" expr="_x['args']['vrml-path']" />
- <param name="recurse" expr="true" />
- <param name="suffix" expr="'vrml wrl'" />
- <finalize>
- <script>
- // use filename relative to the vrml dir as the key
- var key = _event.data.file.relDir + _event.data.file.strippedName;
- if (!(key in models)) {
- models[key] = {};
- }
- models[key][_event.data.file.extension] = _event.data.file;
- </script>
- <send target="#_osgvonvert.osgb">
- <param name="source" expr="_event.data.file.path" />
- <param name="dest" expr="_x['args']['tmp-path'] + _event.data.file.relDir + _event.data.file.strippedName + '.osgb'" />
- </send>
- </finalize>
- </invoke -->
-
+
<!-- Start the directory monitor for generated files -->
<invoke type="dirmon" id="dirmon.processed">
<param name="dir" expr="_x['args']['tmp-path']" />
@@ -65,174 +160,189 @@
<!-- Called for every file we found -->
<finalize>
<script>
- var formatMatch = formatPattern.exec(_event.data.file.name);
- if (formatMatch &amp;&amp; formatMatch.length == 8) {
- // this is a generated file with an encoded query string
- var file = _event.data.file.relDir.replace("/","-").substr(1) +
- _event.data.file.name.substr(0, _event.data.file.name.length - formatMatch[0].length);
- var format = formatMatch[1] + '.' + _event.data.file.extension;
-
- // format: "2_0.1_0_1_640_480.png"
- // file: "simulation-dir-model"
-
- print("Found a new processed file at [" + file + "][" + format + "]\n");
+ _event.fileStruct = fileToStruct(_event.data.file);
+
+ if (_event.data.file.extension === "osgb") {
+ // this is a binary 3D file converted from the wrls
+ if (_event.name === "file.deleted") {
+ delete models[_event.fileStruct.key];
+ print("Removed a vanished osgb file at " + _event.fileStruct.key + "\n");
+ } else {
+ models[_event.fileStruct.key] = _event.data.file;
+ print("Inserted a new osgb file at " + _event.fileStruct.key + "\n");
+ }
+
+ } else if ('format' in _event.fileStruct) {
+ // this is a processed file generated for some request
- if (_event.name === "file.existing" || _event.name === "file.added") {
- if (!(file in processed)) {
- processed[file] = {}
- }
- processed[file][format] = _event.data.file;
- processed[file][format].pitch = parseFloat(formatMatch[2]);
- processed[file][format].roll = parseFloat(formatMatch[3]);
- processed[file][format].yaw = parseFloat(formatMatch[4]);
- processed[file][format].zoom = parseFloat(formatMatch[5]);
- processed[file][format].width = parseFloat(formatMatch[6]);
- processed[file][format].height = parseFloat(formatMatch[7]);
+ if (_event.name === "file.deleted") {
+ delete processed[_event.fileStruct.key][_event.fileStruct.format];
+ print("Removed a vanished processed file at " + _event.fileStruct.key + "\n");
} else {
- if (file in processed &amp;&amp; key in processed[file]) {
- delete processed[file][key];
+ if (!(_event.fileStruct.key in processed)) {
+ processed[_event.fileStruct.key] = {}
}
+ processed[_event.fileStruct.key][_event.fileStruct.format] = _event.data.file;
+ print("Inserted a new processed file at " + _event.fileStruct.key + "\n");
}
-
- } else if (_event.data.file.extension === "osgb") {
- // this is a binary 3D file converted from the wrls
- var key = _event.data.file.relDir.replace("/","-").substr(1) + _event.data.file.strippedName;
- models[key] = _event.data.file;
- print("Inserted a new osgb file at " + key + "\n");
-
} else {
- print("Found something unrelated " + _event.data.file.name + "\n");
+ print("Ignoring " + _event.data.file.name + "\n");
}
</script>
</finalize>
</invoke>
+ <!-- Start the directory monitor for wrl files -->
+ <invoke type="dirmon" id="dirmon.vrml">
+ <param name="dir" expr="_x['args']['vrml-path']" />
+ <param name="recurse" expr="true" />
+ <param name="suffix" expr="'vrml wrl'" />
+ <finalize>
+ <script>
+ _event.fileStruct = fileToStruct(_event.data.file);
+ if (_event.name === "file.existing" || _event.name === "file.added") {
+ wrls[_event.fileStruct.key] = _event.data.file;
+ print("Inserting wrl " + _event.data.file.path + " from " +_event.data.file.relDir + " at " + _event.fileStruct.key + "\n");
+ }
+ if (_event.name === "file.deleted") {
+ delete wrls[_event.fileStruct.key];
+ print("Deleting wrl " + _event.data.file.path + " from " +_event.data.file.relDir + " at " + _event.fileStruct.key + "\n");
+ }
+ </script>
+ <if cond="models &amp;&amp;
+ (!(_event.fileStruct.key in models) ||
+ wrls[_event.fileStruct.key].mtime > models[_event.fileStruct.key].mtime)
+ ">
+ <send target="#_osgvonvert.osgb">
+ <param name="source" expr="_event.data.file.path" />
+ <param name="dest" expr="_x['args']['tmp-path'] + '/' + _event.fileStruct.key + '.osgb'" />
+ </send>
+ </if>
+ </finalize>
+ </invoke>
+
<!-- Start the osgconvert invoker to transform 3D files -->
<invoke type="osgconvert" id="osgvonvert.osgb">
<param name="threads" expr="4" />
+ <!--finalize>
+ <script>
+ // we could put the file into the osgbs or processed here, but we rely on the directory monitors for now
+ print("Received " + _event.name + " regarding " + _event.data.dest + "\n");
+ </script>
+ </finalize-->
</invoke>
<!-- Idle here -->
<state id="idle">
- <transition event="http" target="idle" cond="_event.data.pathComponent.length > 2">
+ <transition event="http" target="idle" cond="
+ _event.data.pathComponent.length >= 2 &amp;&amp;
+ _event.data.pathComponent[_event.data.pathComponent.length - 1].indexOf('.') !== -1">
<!-- request for a specific format
http://host/vrml/relative/path/format?query=string -->
<script>
- print("HTTP Request\n");
- _event["file"] = _event.data.pathComponent.slice(1, _event.data.pathComponent.length - 1).join('-');
- _event["ext"] = _event.data.pathComponent[_event.data.pathComponent.length - 1];
-
- // normalize request
- if (!('query' in _event.data)) _event.data.query = {};
- var query = _event.data.query;
- if (!('pitch' in query) || !isNumber(query.pitch)) query.pitch = 0;
- if (!('roll' in query) || !isNumber(query.roll)) query.roll = 0;
- if (!('yaw' in query) || !isNumber(query.yaw)) query.yaw = 0;
- if (!('zoom' in query) || !isNumber(query.zoom)) query.zoom = 1;
- if (!('width' in query) || !isNumber(query.width)) query.width = 640;
- if (!('height' in query) || !isNumber(query.height)) query.height = 480;
+ _event.fileStruct = reqToStruct(_event.data);
+ _event.dest = _x['args']['tmp-path'] + '/' + _event['fileStruct'].key + '-' + _event['fileStruct'].format;
- _event["format"] = query.pitch + '_' + query.roll + '_' + query.yaw + '_' + query.zoom + '_' + query.width + '_' + query.height + '.' + _event["ext"];
- _event["dest"] = _x['args']['tmp-path'] + '/' + _event["file"] + '-' + _event["format"];
-
- print("Got a request for [" + _event["file"] + "][" + _event['format'] + "]\n");
-
-// print("---------------\n");
-// print("Event:\n");
+ print("Got a request for [" + _event['fileStruct'].key + '-' + _event['fileStruct'].format + "]\n");
// dump(_event);
-// print("Models:\n");
-// dump(models);
-// print("Processed:\n");
-// dump(processed);
-// print("---------------\n");
-
</script>
- <if cond="_event['file'] in models">
+ <if cond="_event['fileStruct'].key in models &amp;&amp; isSupportedFormat(_event['fileStruct'].ext)">
<!-- There is such a file available as osgb -->
- <if cond="_event['file'] in processed &amp;&amp; _event['format'] in processed[_event['file']]">
+ <if cond="
+ _event['fileStruct'].key in processed &amp;&amp;
+ _event['fileStruct'].format in processed[_event['fileStruct'].key]">
<script>
- print("Already processed!\n");
- print("Sending " + processed[_event['file']][_event['format']].path + "\n");
+ print("Sending " + processed[_event['fileStruct'].key][_event['fileStruct'].format].path + "\n");
</script>
<response status="200" requestexpr="_event.origin">
- <content fileexpr="processed[_event['file']][_event['format']].path" />
+ <content fileexpr="processed[_event['fileStruct'].key][_event['fileStruct'].format].path" />
</response>
<else>
- <script>
-// print(_event['querySuffix'] + '.' + _event['fileFormat'] + " not in \n");
-// dump(processed[_event['fileKey']]);
- print("Processing!\n");
- print("Outfile " + _event['dest'] + " from model " + _event['file'] + "\n");
- </script>
- <send target="#_osgvonvert.osgb">
- <param name="source" expr="models[_event['file']].path" />
- <param name="dest" expr="_event['dest']" />
- <param name="pitch" expr="_event.data.query.pitch" />
- <param name="roll" expr="_event.data.query.roll" />
- <param name="yaw" expr="_event.data.query.yaw" />
- <param name="zoom" expr="_event.data.query.zoom" />
- <param name="width" expr="_event.data.query.width" />
- <param name="height" expr="_event.data.query.height" />
- </send>
- <!--
- Redeliver the event once the untilexpr is true. The untilexpr has to evaluate
- into another valid expression that we will check again on stable configurations.
- -->
- <postpone untilexpr="'\'' + _event['file'] + '\' in processed &amp;&amp; \'' + _event['format'] + '\'' + ' in processed[\'' + _event['file'] + '\']'" />
+ <if cond="_event.name.endsWith('postponed')">
+ <!--
+ A postponed event we couldn't answer
+ -->
+ <response status="404" requestexpr="_event.origin" />
+ <else>
+ <script>
+ print("Processing outfile " + _event['dest'] + " from model " + _event['file'] + "\n");
+ </script>
+ <send target="#_osgvonvert.osgb">
+ <param name="source" expr="models[_event['fileStruct'].key].path" />
+ <param name="dest" expr="_event['dest']" />
+ <param name="pitch" expr="_event.fileStruct.pitch" />
+ <param name="roll" expr="_event.fileStruct.roll" />
+ <param name="yaw" expr="_event.fileStruct.yaw" />
+ <param name="zoom" expr="_event.fileStruct.zoom" />
+ <param name="x" expr="_event.fileStruct.x" />
+ <param name="y" expr="_event.fileStruct.y" />
+ <param name="z" expr="_event.fileStruct.z" />
+ <param name="width" expr="_event.fileStruct.width" />
+ <param name="height" expr="_event.fileStruct.height" />
+ <param name="autorotate" expr="_event.fileStruct.autorotate" />
+ </send>
+ <!--
+ Redeliver the event once the untilexpr is true. The untilexpr has to evaluate
+ into another valid expression that we will check again on stable configurations.
+ -->
+ <postpone
+ untilexpr="
+ '\'' + _event['fileStruct'].key + '\' in processed &amp;&amp;
+ \'' + _event['fileStruct'].format + '\'' + ' in processed[\'' + _event['fileStruct'].key + '\'] ||
+ _event.name === \'convert.failure\' &amp;&amp; _event.data.dest === \'' + _event['dest'] + '\''
+ "/>
+ </else>
+ </if>
</else>
</if>
<else>
- <script>
- print("No such file!\n");
- print("File " + _event['file'] + "\n");
- </script>
<!-- There is no such model -->
<response status="404" requestexpr="_event.origin" />
</else>
</if>
</transition>
-
- <!-- request for info on a special file -->
- <!--transition event="http" target="idle" cond="
- _event.data.pathComponent.length == 2 &amp;&amp;
- _event.data.pathComponent[0] == 'vrml' &amp;&amp;
- _event.data.pathComponent[1] in files">
- <script>dump(_event);</script>
+ <!--
+ process request for JSON datastructures
+ -->
+ <transition event="http" target="idle" cond="
+ _event.data.pathComponent.length == 2 &amp;&amp;
+ _event.data.pathComponent[1] === 'models'">
+ <script>//dump(_event)</script>
<response status="200" requestexpr="_event.origin">
- <content expr="files[_event.data.pathComponent[1]]" />
+ <header name="Content-Type" value="application/json" />
+ <content expr="models" />
</response>
- </transition -->
+ </transition>
- <!-- request for topmost list of all files -->
<transition event="http" target="idle" cond="
- _event.data.pathComponent.length == 2">
- <script>dump(_event);</script>
- <if cond="_event.data.pathComponent[1] === 'models'">
- <response status="200" requestexpr="_event.origin">
- <header name="Content-Type" value="application/json" />
- <content expr="models" />
- </response>
- <elseif cond="_event.data.pathComponent[1] === 'processed'">
- <response status="200" requestexpr="_event.origin">
- <header name="Content-Type" value="application/json" />
- <content expr="processed" />
- </response>
- </elseif>
- <else>
- <response status="404" requestexpr="_event.origin" />
- </else>
- </if>
+ _event.data.pathComponent.length == 2 &amp;&amp;
+ _event.data.pathComponent[1] === 'processed'">
+ <script>//dump(_event)</script>
+ <response status="200" requestexpr="_event.origin">
+ <header name="Content-Type" value="application/json" />
+ <content expr="processed" />
+ </response>
+ </transition>
+
+ <transition event="http" target="idle" cond="
+ _event.data.pathComponent.length == 2 &amp;&amp;
+ _event.data.pathComponent[1] === 'wrls'">
+ <script>//dump(_event)</script>
+ <response status="200" requestexpr="_event.origin">
+ <header name="Content-Type" value="application/json" />
+ <content expr="wrls" />
+ </response>
</transition>
<!-- request for topmost list of all files -->
<transition event="http" target="idle" cond="
_event.data.pathComponent.length == 1">
- <script>dump(_event);</script>
+ <script>//dump(_event);</script>
<response status="200" requestexpr="_event.origin">
<header name="Content-Type" value="application/json" />
- <content expr="models" />
+ <content expr="overviewList()" />
</response>
</transition>
diff --git a/src/uscxml/Interpreter.cpp b/src/uscxml/Interpreter.cpp
index eeeff0c..5324b9e 100644
--- a/src/uscxml/Interpreter.cpp
+++ b/src/uscxml/Interpreter.cpp
@@ -275,8 +275,10 @@ void Interpreter::interpret() {
LOG(ERROR) << "No datamodel for " << datamodelName << " registered";
}
- if (_dataModel)
+ if (_dataModel) {
_dataModel.assign("_x.args", _cmdLineOptions);
+ _dataModel.assign("_ioprocessors['http']", _httpServlet->getDataModelVariables());
+ }
setupIOProcessors();
@@ -456,13 +458,13 @@ void Interpreter::mainEventLoop() {
#endif
monIter = _monitors.begin();
while(monIter != _monitors.end()) {
- try {
- (*monIter)->beforeMicroStep(this);
- } catch (Event e) {
- LOG(ERROR) << "Syntax error when calling beforeMicroStep on monitors: " << std::endl << e << std::endl;
- } catch (...) {
- LOG(ERROR) << "An exception occured when calling beforeMicroStep on monitors";
- }
+ try {
+ (*monIter)->beforeMicroStep(this);
+ } catch (Event e) {
+ LOG(ERROR) << "Syntax error when calling beforeMicroStep on monitors: " << std::endl << e << std::endl;
+ } catch (...) {
+ LOG(ERROR) << "An exception occured when calling beforeMicroStep on monitors";
+ }
monIter++;
}
@@ -484,13 +486,13 @@ void Interpreter::mainEventLoop() {
if (!enabledTransitions.empty()) {
monIter = _monitors.begin();
while(monIter != _monitors.end()) {
- try {
- (*monIter)->beforeTakingTransitions(this, enabledTransitions);
- } catch (Event e) {
- LOG(ERROR) << "Syntax error when calling beforeTakingTransitions on monitors: " << std::endl << e << std::endl;
- } catch (...) {
- LOG(ERROR) << "An exception occured when calling beforeTakingTransitions on monitors";
- }
+ try {
+ (*monIter)->beforeTakingTransitions(this, enabledTransitions);
+ } catch (Event e) {
+ LOG(ERROR) << "Syntax error when calling beforeTakingTransitions on monitors: " << std::endl << e << std::endl;
+ } catch (...) {
+ LOG(ERROR) << "An exception occured when calling beforeTakingTransitions on monitors";
+ }
monIter++;
}
microstep(enabledTransitions);
@@ -514,13 +516,13 @@ void Interpreter::mainEventLoop() {
monIter = _monitors.begin();
// if (!_sendQueue || _sendQueue->isEmpty()) {
while(monIter != _monitors.end()) {
- try {
- (*monIter)->onStableConfiguration(this);
- } catch (Event e) {
- LOG(ERROR) << "Syntax error when calling onStableConfiguration on monitors: " << std::endl << e << std::endl;
- } catch (...) {
- LOG(ERROR) << "An exception occured when calling onStableConfiguration on monitors";
- }
+ try {
+ (*monIter)->onStableConfiguration(this);
+ } catch (Event e) {
+ LOG(ERROR) << "Syntax error when calling onStableConfiguration on monitors: " << std::endl << e << std::endl;
+ } catch (...) {
+ LOG(ERROR) << "An exception occured when calling onStableConfiguration on monitors";
+ }
monIter++;
}
// }
@@ -582,13 +584,13 @@ void Interpreter::mainEventLoop() {
}
monIter = _monitors.begin();
while(monIter != _monitors.end()) {
- try {
- (*monIter)->beforeCompletion(this);
- } catch (Event e) {
- LOG(ERROR) << "Syntax error when calling beforeCompletion on monitors: " << std::endl << e << std::endl;
- } catch (...) {
- LOG(ERROR) << "An exception occured when calling beforeCompletion on monitors";
- }
+ try {
+ (*monIter)->beforeCompletion(this);
+ } catch (Event e) {
+ LOG(ERROR) << "Syntax error when calling beforeCompletion on monitors: " << std::endl << e << std::endl;
+ } catch (...) {
+ LOG(ERROR) << "An exception occured when calling beforeCompletion on monitors";
+ }
monIter++;
}
@@ -596,13 +598,13 @@ void Interpreter::mainEventLoop() {
monIter = _monitors.begin();
while(monIter != _monitors.end()) {
- try {
- (*monIter)->afterCompletion(this);
- } catch (Event e) {
- LOG(ERROR) << "Syntax error when calling afterCompletion on monitors: " << std::endl << e << std::endl;
- } catch (...) {
- LOG(ERROR) << "An exception occured when calling afterCompletion on monitors";
- }
+ try {
+ (*monIter)->afterCompletion(this);
+ } catch (Event e) {
+ LOG(ERROR) << "Syntax error when calling afterCompletion on monitors: " << std::endl << e << std::endl;
+ } catch (...) {
+ LOG(ERROR) << "An exception occured when calling afterCompletion on monitors";
+ }
monIter++;
}
@@ -674,9 +676,9 @@ void Interpreter::send(const Arabica::DOM::Node<std::string>& element) {
}
} catch (Event e) {
LOG(ERROR) << "Syntax error in send element eventexpr:" << std::endl << e << std::endl;
- return;
+ return;
}
- try {
+ try {
// target
if (HAS_ATTR(element, "targetexpr") && _dataModel) {
sendReq.target = _dataModel.evalAsString(ATTR(element, "targetexpr"));
@@ -685,9 +687,9 @@ void Interpreter::send(const Arabica::DOM::Node<std::string>& element) {
}
} catch (Event e) {
LOG(ERROR) << "Syntax error in send element targetexpr:" << std::endl << e << std::endl;
- return;
+ return;
}
- try {
+ try {
// type
if (HAS_ATTR(element, "typeexpr") && _dataModel) {
sendReq.type = _dataModel.evalAsString(ATTR(element, "typeexpr"));
@@ -698,9 +700,9 @@ void Interpreter::send(const Arabica::DOM::Node<std::string>& element) {
}
} catch (Event e) {
LOG(ERROR) << "Syntax error in send element typeexpr:" << std::endl << e << std::endl;
- return;
+ return;
}
- try {
+ try {
// id
if (HAS_ATTR(element, "idlocation") && _dataModel) {
sendReq.sendid = _dataModel.evalAsString(ATTR(element, "idlocation"));
@@ -729,10 +731,10 @@ void Interpreter::send(const Arabica::DOM::Node<std::string>& element) {
*/
} catch (Event e) {
LOG(ERROR) << "Syntax error in send element idlocation:" << std::endl << e << std::endl;
- return;
+ return;
}
- try {
+ try {
// delay
std::string delay;
sendReq.delayMs = 0;
@@ -757,10 +759,10 @@ void Interpreter::send(const Arabica::DOM::Node<std::string>& element) {
}
} catch (Event e) {
LOG(ERROR) << "Syntax error in send element delayexpr:" << std::endl << e << std::endl;
- return;
+ return;
}
-
- try {
+
+ try {
// namelist
if (HAS_ATTR(element, "namelist")) {
if (_dataModel) {
@@ -776,10 +778,10 @@ void Interpreter::send(const Arabica::DOM::Node<std::string>& element) {
}
} catch (Event e) {
LOG(ERROR) << "Syntax error in send element namelist:" << std::endl << e << std::endl;
- return;
+ return;
}
- try {
+ try {
// params
NodeSet<std::string> params = filterChildElements(_xmlNSPrefix + "param", element);
for (int i = 0; i < params.size(); i++) {
@@ -803,9 +805,9 @@ void Interpreter::send(const Arabica::DOM::Node<std::string>& element) {
}
} catch (Event e) {
LOG(ERROR) << "Syntax error in send element param expr:" << std::endl << e << std::endl;
- return;
+ return;
}
- try {
+ try {
// content
NodeSet<std::string> contents = filterChildElements(_xmlNSPrefix + "content", element);
@@ -831,16 +833,16 @@ void Interpreter::send(const Arabica::DOM::Node<std::string>& element) {
}
} catch (Event e) {
LOG(ERROR) << "Syntax error in send element content:" << std::endl << e << std::endl;
- return;
+ return;
}
- assert(_sendIds.find(sendReq.sendid) == _sendIds.end());
- _sendIds[sendReq.sendid] = std::make_pair(this, sendReq);
- if (sendReq.delayMs > 0) {
- _sendQueue->addEvent(sendReq.sendid, Interpreter::delayedSend, sendReq.delayMs, &_sendIds[sendReq.sendid]);
- } else {
- delayedSend(&_sendIds[sendReq.sendid], sendReq.name);
- }
+ assert(_sendIds.find(sendReq.sendid) == _sendIds.end());
+ _sendIds[sendReq.sendid] = std::make_pair(this, sendReq);
+ if (sendReq.delayMs > 0) {
+ _sendQueue->addEvent(sendReq.sendid, Interpreter::delayedSend, sendReq.delayMs, &_sendIds[sendReq.sendid]);
+ } else {
+ delayedSend(&_sendIds[sendReq.sendid], sendReq.name);
+ }
}
void Interpreter::delayedSend(void* userdata, std::string eventName) {
@@ -1368,20 +1370,20 @@ void Interpreter::executeContent(const Arabica::DOM::Node<std::string>& content)
} else {
// condition does not match - do we have an elsif?
if (ifElem.hasChildNodes()) {
- NodeList<std::string> elseifElem = ifElem.getElementsByTagNameNS(_nsURL, "elseif");
- for (unsigned int i = 0; i < elseifElem.getLength(); i++) {
+ NodeSet<std::string> elseifElem = filterChildElements(_xmlNSPrefix + "elseif", ifElem);
+ for (unsigned int i = 0; i < elseifElem.size(); i++) {
#if 0
- if (HAS_ATTR(elseifElem.item(i), "cond"))
- std::cout << ATTR(elseifElem.item(i), "cond") << std::endl;
+ if (HAS_ATTR(elseifElem[i], "cond"))
+ std::cout << ATTR(elseifElem[i], "cond") << std::endl;
#endif
- if (hasConditionMatch(elseifElem.item(i))) {
- executeContent(elseifElem.item(i).getChildNodes());
+ if (hasConditionMatch(elseifElem[i])) {
+ executeContent(elseifElem[i].getChildNodes());
goto ELSIF_ELEM_MATCH;
}
}
- NodeList<std::string> elseElem = ifElem.getElementsByTagNameNS(_nsURL, "else");
- if (elseElem.getLength() > 0)
- executeContent(elseElem.item(0).getChildNodes());
+ NodeSet<std::string> elseElem = filterChildElements(_xmlNSPrefix + "else", ifElem);
+ if (elseElem.size() > 0)
+ executeContent(elseElem[0].getChildNodes());
}
}
ELSIF_ELEM_MATCH:
@@ -1619,13 +1621,13 @@ void Interpreter::exitStates(const Arabica::XPath::NodeSet<std::string>& enabled
monIter = _monitors.begin();
while(monIter != _monitors.end()) {
- try {
- (*monIter)->beforeExitingStates(this, statesToExit);
- } catch (Event e) {
- LOG(ERROR) << "Syntax error when calling beforeExitingStates on monitors: " << std::endl << e << std::endl;
- } catch (...) {
- LOG(ERROR) << "An exception occured when calling beforeExitingStates on monitors";
- }
+ try {
+ (*monIter)->beforeExitingStates(this, statesToExit);
+ } catch (Event e) {
+ LOG(ERROR) << "Syntax error when calling beforeExitingStates on monitors: " << std::endl << e << std::endl;
+ } catch (...) {
+ LOG(ERROR) << "An exception occured when calling beforeExitingStates on monitors";
+ }
monIter++;
}
@@ -1682,13 +1684,13 @@ void Interpreter::exitStates(const Arabica::XPath::NodeSet<std::string>& enabled
monIter = _monitors.begin();
while(monIter != _monitors.end()) {
- try {
- (*monIter)->afterExitingStates(this);
- } catch (Event e) {
- LOG(ERROR) << "Syntax error when calling afterExitingStates on monitors: " << std::endl << e << std::endl;
- } catch (...) {
- LOG(ERROR) << "An exception occured when calling afterExitingStates on monitors";
- }
+ try {
+ (*monIter)->afterExitingStates(this);
+ } catch (Event e) {
+ LOG(ERROR) << "Syntax error when calling afterExitingStates on monitors: " << std::endl << e << std::endl;
+ } catch (...) {
+ LOG(ERROR) << "An exception occured when calling afterExitingStates on monitors";
+ }
monIter++;
}
@@ -1800,13 +1802,13 @@ void Interpreter::enterStates(const Arabica::XPath::NodeSet<std::string>& enable
monIter = _monitors.begin();
while(monIter != _monitors.end()) {
- try {
- (*monIter)->beforeEnteringStates(this, statesToEnter);
- } catch (Event e) {
- LOG(ERROR) << "Syntax error when calling beforeEnteringStates on monitors: " << std::endl << e << std::endl;
- } catch (...) {
- LOG(ERROR) << "An exception occured when calling beforeEnteringStates on monitors";
- }
+ try {
+ (*monIter)->beforeEnteringStates(this, statesToEnter);
+ } catch (Event e) {
+ LOG(ERROR) << "Syntax error when calling beforeEnteringStates on monitors: " << std::endl << e << std::endl;
+ } catch (...) {
+ LOG(ERROR) << "An exception occured when calling beforeEnteringStates on monitors";
+ }
monIter++;
}
@@ -1867,13 +1869,13 @@ void Interpreter::enterStates(const Arabica::XPath::NodeSet<std::string>& enable
monIter = _monitors.begin();
while(monIter != _monitors.end()) {
- try {
- (*monIter)->afterEnteringStates(this);
- } catch (Event e) {
- LOG(ERROR) << "Syntax error when calling afterEnteringStates on monitors: " << std::endl << e << std::endl;
- } catch (...) {
- LOG(ERROR) << "An exception occured when calling afterEnteringStates on monitors";
- }
+ try {
+ (*monIter)->afterEnteringStates(this);
+ } catch (Event e) {
+ LOG(ERROR) << "Syntax error when calling afterEnteringStates on monitors: " << std::endl << e << std::endl;
+ } catch (...) {
+ LOG(ERROR) << "An exception occured when calling afterEnteringStates on monitors";
+ }
monIter++;
}
diff --git a/src/uscxml/URL.cpp b/src/uscxml/URL.cpp
index d410bcf..1f180e4 100644
--- a/src/uscxml/URL.cpp
+++ b/src/uscxml/URL.cpp
@@ -64,7 +64,7 @@ size_t URLImpl::headerHandler(void *ptr, size_t size, size_t nmemb, void *userda
}
void URLImpl::downloadStarted() {
- LOG(INFO) << "Starting download of " << asString() << std::endl;
+// LOG(INFO) << "Starting download of " << asString() << std::endl;
_inContent.str("");
_inContent.clear();
_inHeader.str("");
@@ -80,7 +80,7 @@ void URLImpl::downloadStarted() {
void URLImpl::downloadCompleted() {
tthread::lock_guard<tthread::recursive_mutex> lock(_mutex);
- LOG(INFO) << "Finished downloading " << asString() << " with " << _inContent.str().size() << " bytes";
+// LOG(INFO) << "Finished downloading " << asString() << " with " << _inContent.str().size() << " bytes";
_hasFailed = false;
_isDownloaded = true;
diff --git a/src/uscxml/plugins/datamodel/ecmascript/v8/V8DataModel.cpp b/src/uscxml/plugins/datamodel/ecmascript/v8/V8DataModel.cpp
index d791b9e..d00ad43 100644
--- a/src/uscxml/plugins/datamodel/ecmascript/v8/V8DataModel.cpp
+++ b/src/uscxml/plugins/datamodel/ecmascript/v8/V8DataModel.cpp
@@ -320,9 +320,9 @@ v8::Handle<v8::Value> V8DataModel::evalAsValue(const std::string& expr) {
exceptionEvent.data.compound["filename"] = Data(filename, Data::VERBATIM);
std::string sourceLine(*v8::String::AsciiValue(message->GetSourceLine()));
- size_t startpos = sourceLine.find_first_not_of(" \t");
- if(std::string::npos != startpos) // removoe leading white space
- sourceLine = sourceLine.substr(startpos);
+ size_t startpos = sourceLine.find_first_not_of(" \t");
+ if(std::string::npos != startpos) // removoe leading white space
+ sourceLine = sourceLine.substr(startpos);
exceptionEvent.data.compound["sourceline"] = Data(sourceLine, Data::VERBATIM);
diff --git a/src/uscxml/plugins/element/postpone/PostponeElement.cpp b/src/uscxml/plugins/element/postpone/PostponeElement.cpp
index 5dc1c60..b50b5c2 100644
--- a/src/uscxml/plugins/element/postpone/PostponeElement.cpp
+++ b/src/uscxml/plugins/element/postpone/PostponeElement.cpp
@@ -41,16 +41,16 @@ void PostponeElement::enterElement(const Arabica::DOM::Node<std::string>& node)
}
// when will we refire the event?
- std::string until;
- try {
- if (HAS_ATTR(node, "untilexpr")) {
- until = _interpreter->getDataModel().evalAsString(ATTR(node, "untilexpr"));
- } else if (HAS_ATTR(node, "until")) {
- until = ATTR(node, "until");
- }
+ std::string until;
+ try {
+ if (HAS_ATTR(node, "untilexpr")) {
+ until = _interpreter->getDataModel().evalAsString(ATTR(node, "untilexpr"));
+ } else if (HAS_ATTR(node, "until")) {
+ until = ATTR(node, "until");
+ }
} catch (Event e) {
LOG(ERROR) << "Syntax error in postpone element untilexpr:" << std::endl << e << std::endl;
- return;
+ return;
}
if (until.length() == 0) {
@@ -58,30 +58,65 @@ void PostponeElement::enterElement(const Arabica::DOM::Node<std::string>& node)
return;
}
+ LOG(INFO) << until;
+
+#if 0
+ std::string timeoutStr = "0s";
+ try {
+ if (HAS_ATTR(node, "timeoutexpr")) {
+ timeoutStr = _interpreter->getDataModel().evalAsString(ATTR(node, "timeoutexpr"));
+ } else if (HAS_ATTR(node, "timeout")) {
+ timeoutStr = ATTR(node, "timeout");
+ }
+ } catch (Event e) {
+ LOG(ERROR) << "Syntax error in postpone element timeoutexpr:" << std::endl << e << std::endl;
+ return;
+ }
+
+ uint64_t timeout = 0;
+ NumAttr timeoutAttr(timeoutStr);
+ if (boost::iequals(timeoutAttr.unit, "s")) {
+ timeout = strTo<int>(timeoutAttr.value) * 1000;
+ } else if (boost::iequals(timeoutAttr.unit, "ms")) {
+ timeout = strTo<int>(timeoutAttr.value);
+ }
+ if (timeout > 0) {
+ timeout += tthread::chrono::system_clock::now();
+ }
+#endif
Event currEvent = _interpreter->getCurrentEvent();
- Resubmitter::postpone(currEvent, until, _interpreter);
+ Resubmitter::postpone(currEvent, until, 0, _interpreter);
}
void PostponeElement::exitElement(const Arabica::DOM::Node<std::string>& node) {
}
-void PostponeElement::Resubmitter::postpone(const Event& event, std::string until, Interpreter* interpreter) {
+void PostponeElement::Resubmitter::postpone(const Event& event, std::string until, uint64_t timeout, Interpreter* interpreter) {
Resubmitter* resubmitter = getInstance(interpreter);
- resubmitter->_postponedEvents.push_back(std::make_pair(until, event));
+ resubmitter->_postponedEvents.push_back(Postponed(event, until, timeout));
}
void PostponeElement::Resubmitter::onStableConfiguration(Interpreter* interpreter) {
- std::list<std::pair<std::string, Event> >::iterator eventIter = _postponedEvents.begin();
+ std::list<Postponed>::iterator eventIter = _postponedEvents.begin();
while(eventIter != _postponedEvents.end()) {
try {
- LOG(INFO) << "Reevaluating: >> " << eventIter->first << " <<";
- if (interpreter->getDataModel().evalAsBool(eventIter->first)) {
- LOG(INFO) << " -> is TRUE";
- interpreter->receive(eventIter->second, true);
+// LOG(INFO) << "Reevaluating: >> " << eventIter->first << " <<";
+ if (eventIter->timeout > 0 && tthread::chrono::system_clock::now() < eventIter->timeout) {
+ // TODO: We should use an event queue
+// LOG(INFO) << " -> Timeout";
+ eventIter->event.name += ".timeout";
+ interpreter->receive(eventIter->event, true);
+ _postponedEvents.erase(eventIter);
+ break;
+ }
+ if (interpreter->getDataModel().evalAsBool(eventIter->until)) {
+// LOG(INFO) << " -> is TRUE";
+ eventIter->event.name += ".postponed";
+ interpreter->receive(eventIter->event, true);
_postponedEvents.erase(eventIter);
break;
}
- LOG(INFO) << " -> is FALSE";
+// LOG(INFO) << " -> is FALSE";
} catch (Event e) {
LOG(ERROR) << "Syntax error while evaluating until attribute of postpone element:" << std::endl << e << std::endl;
_postponedEvents.erase(eventIter++);
diff --git a/src/uscxml/plugins/element/postpone/PostponeElement.h b/src/uscxml/plugins/element/postpone/PostponeElement.h
index 03aafde..eb7a738 100644
--- a/src/uscxml/plugins/element/postpone/PostponeElement.h
+++ b/src/uscxml/plugins/element/postpone/PostponeElement.h
@@ -12,6 +12,14 @@ namespace uscxml {
class PostponeElement : public ExecutableContentImpl {
public:
+ struct Postponed {
+ Postponed(const Event& event, const std::string& until, long timeout) :
+ event(event), until(until), timeout(timeout) {}
+ Event event;
+ std::string until;
+ uint64_t timeout;
+ };
+
PostponeElement() {}
virtual ~PostponeElement() {}
boost::shared_ptr<ExecutableContentImpl> create(Interpreter* interpreter);
@@ -40,13 +48,13 @@ protected:
}
static Resubmitter* getInstance(Interpreter* interpreter);
- static void postpone(const Event& event, std::string until, Interpreter* interpreter);
+ static void postpone(const Event& event, std::string until, uint64_t timeout, Interpreter* interpreter);
// InterpreterMonitor
void onStableConfiguration(Interpreter* interpreter);
void afterCompletion(Interpreter* interpreter);
- std::list<std::pair<std::string, Event> > _postponedEvents;
+ std::list<Postponed> _postponedEvents;
static std::map<Interpreter*, Resubmitter*> _instances;
static tthread::recursive_mutex _accessLock;
diff --git a/src/uscxml/plugins/element/response/ResponseElement.cpp b/src/uscxml/plugins/element/response/ResponseElement.cpp
index 62ebf34..2e25b27 100644
--- a/src/uscxml/plugins/element/response/ResponseElement.cpp
+++ b/src/uscxml/plugins/element/response/ResponseElement.cpp
@@ -52,7 +52,7 @@ void ResponseElement::enterElement(const Arabica::DOM::Node<std::string>& node)
return;
}
httpReply.status = strTo<int>(statusStr);;
-
+
// extract the content
Arabica::XPath::NodeSet<std::string> contents = Interpreter::filterChildElements(_interpreter->getXMLPrefixForNS(getNamespace()) + "content", node);
if (contents.size() > 0) {
@@ -63,91 +63,90 @@ void ResponseElement::enterElement(const Arabica::DOM::Node<std::string>& node)
httpReply.content = contentValue;
} catch (Event e) {
LOG(ERROR) << "Syntax error with expr in content child of response element:" << std::endl << e << std::endl;
- return;
+ return;
}
} else {
LOG(ERROR) << "content element has expr attribute but no datamodel is specified.";
- return;
+ return;
}
} else if (HAS_ATTR(contents[0], "file") || HAS_ATTR(contents[0], "fileexpr")) { // -- content is from file ------
- URL file;
+ URL file;
if (HAS_ATTR(contents[0], "fileexpr")) {
- if (_interpreter->getDataModel()) {
- try {
- file = "file://" + _interpreter->getDataModel().evalAsString(ATTR(contents[0], "fileexpr"));
- } catch (Event e) {
- LOG(ERROR) << "Syntax error with fileexpr in content child of response element:" << std::endl << e << std::endl;
- return;
- }
+ if (_interpreter->getDataModel()) {
+ try {
+ file = "file://" + _interpreter->getDataModel().evalAsString(ATTR(contents[0], "fileexpr"));
+ } catch (Event e) {
+ LOG(ERROR) << "Syntax error with fileexpr in content child of response element:" << std::endl << e << std::endl;
+ return;
+ }
}
} else {
- file = "file://" + ATTR(contents[0], "fileexpr");
+ file = "file://" + ATTR(contents[0], "fileexpr");
+ }
+ if (file) {
+ httpReply.content = file.getInContent();
+ size_t lastDot;
+ if ((lastDot = file.path().find_last_of(".")) != std::string::npos) {
+ std::string extension = file.path().substr(lastDot + 1);
+ std::string mimeType = HTTPServer::mimeTypeForExtension(extension);
+ if (mimeType.length() > 0) {
+ httpReply.headers["Content-Type"] = mimeType;
+ }
+ }
}
- if (file) {
- httpReply.content = file.getInContent();
- size_t lastDot;
- if ((lastDot = file.path().find_last_of(".")) != std::string::npos) {
- std::string extension = file.path().substr(lastDot + 1);
- std::string mimeType = HTTPServer::mimeTypeForExtension(extension);
- if (mimeType.length() > 0) {
- httpReply.headers["Content-Type"] = mimeType;
- }
- }
- }
} else if (contents[0].hasChildNodes()) { // -- content embedded as child nodes ------
httpReply.content = contents[0].getFirstChild().getNodeValue();
} else {
LOG(ERROR) << "content element does not specify any content.";
- return;
+ return;
}
}
- // process headers
+ // process headers
Arabica::XPath::NodeSet<std::string> headers = Interpreter::filterChildElements(_interpreter->getXMLPrefixForNS(getNamespace()) + "header", node);
- for (int i = 0; i < headers.size(); i++) {
- std::string name;
- if (HAS_ATTR(headers[i], "name")) {
- name = ATTR(headers[i], "name");
- } else if(HAS_ATTR(headers[i], "nameexpr")) {
- if (_interpreter->getDataModel()) {
- try {
- name = _interpreter->getDataModel().evalAsString(ATTR(headers[i], "nameexpr"));
- } catch (Event e) {
- LOG(ERROR) << "Syntax error with nameexpr in header child of response element:" << std::endl << e << std::endl;
- return;
- }
- } else {
+ for (int i = 0; i < headers.size(); i++) {
+ std::string name;
+ if (HAS_ATTR(headers[i], "name")) {
+ name = ATTR(headers[i], "name");
+ } else if(HAS_ATTR(headers[i], "nameexpr")) {
+ if (_interpreter->getDataModel()) {
+ try {
+ name = _interpreter->getDataModel().evalAsString(ATTR(headers[i], "nameexpr"));
+ } catch (Event e) {
+ LOG(ERROR) << "Syntax error with nameexpr in header child of response element:" << std::endl << e << std::endl;
+ return;
+ }
+ } else {
LOG(ERROR) << "header element has nameexpr attribute but no datamodel is specified.";
- return;
- }
- } else {
- LOG(ERROR) << "header element has no name or nameexpr attribute.";
- return;
- }
-
- std::string value;
- if (HAS_ATTR(headers[i], "value")) {
- value = ATTR(headers[i], "value");
- } else if(HAS_ATTR(headers[i], "expr")) {
- if (_interpreter->getDataModel()) {
- try {
- value = _interpreter->getDataModel().evalAsString(ATTR(headers[i], "expr"));
- } catch (Event e) {
- LOG(ERROR) << "Syntax error with expr in header child of response element:" << std::endl << e << std::endl;
- return;
- }
- } else {
+ return;
+ }
+ } else {
+ LOG(ERROR) << "header element has no name or nameexpr attribute.";
+ return;
+ }
+
+ std::string value;
+ if (HAS_ATTR(headers[i], "value")) {
+ value = ATTR(headers[i], "value");
+ } else if(HAS_ATTR(headers[i], "expr")) {
+ if (_interpreter->getDataModel()) {
+ try {
+ value = _interpreter->getDataModel().evalAsString(ATTR(headers[i], "expr"));
+ } catch (Event e) {
+ LOG(ERROR) << "Syntax error with expr in header child of response element:" << std::endl << e << std::endl;
+ return;
+ }
+ } else {
LOG(ERROR) << "header element has expr attribute but no datamodel is specified.";
- return;
- }
- } else {
- LOG(ERROR) << "header element has no value or expr attribute.";
- return;
- }
-
- httpReply.headers[name] = value;
- }
-
+ return;
+ }
+ } else {
+ LOG(ERROR) << "header element has no value or expr attribute.";
+ return;
+ }
+
+ httpReply.headers[name] = value;
+ }
// send the reply
HTTPServer::reply(httpReply);
diff --git a/src/uscxml/plugins/invoker/filesystem/dirmon/DirMonInvoker.cpp b/src/uscxml/plugins/invoker/filesystem/dirmon/DirMonInvoker.cpp
index 37be3e0..675135a 100644
--- a/src/uscxml/plugins/invoker/filesystem/dirmon/DirMonInvoker.cpp
+++ b/src/uscxml/plugins/invoker/filesystem/dirmon/DirMonInvoker.cpp
@@ -25,10 +25,10 @@ bool connect(pluma::Host& host) {
#endif
DirMonInvoker::DirMonInvoker() :
- _reportExisting(true),
- _reportHidden(false),
- _recurse(false),
- _thread(NULL) {
+ _reportExisting(true),
+ _reportHidden(false),
+ _recurse(false),
+ _thread(NULL) {
}
DirMonInvoker::~DirMonInvoker() {
@@ -55,222 +55,329 @@ void DirMonInvoker::cancel(const std::string sendId) {
}
void DirMonInvoker::invoke(const InvokeRequest& req) {
- if (req.params.find("dir") != req.params.end() && boost::iequals(req.params.find("reportexisting")->second, "false"))
+ if (req.params.find("dir") == req.params.end()) {
+ LOG(ERROR) << "No dir param given";
+ return;
+ }
+
+ if (boost::iequals(req.params.find("reportexisting")->second, "false"))
_reportExisting = false;
- if (req.params.find("recurse") != req.params.end() && boost::iequals(req.params.find("recurse")->second, "true"))
+ if (req.params.find("recurse") != req.params.end() &&
+ boost::iequals(req.params.find("recurse")->second, "true"))
_recurse = true;
- if (req.params.find("reporthidden") != req.params.end() && boost::iequals(req.params.find("reporthidden")->second, "true"))
+ if (req.params.find("reporthidden") != req.params.end() &&
+ boost::iequals(req.params.find("reporthidden")->second, "true"))
_reportHidden = true;
-
- std::string suffixList;
- if (req.params.find("suffix") != req.params.end()) {
- suffixList = req.params.find("suffix")->second;
- } else if (req.params.find("suffixes") != req.params.end()) {
- suffixList = req.params.find("suffixes")->second;
- }
-
- if (suffixList.size() > 0) {
- // seperate path into components
- std::stringstream ss(suffixList);
- std::string item;
- while(std::getline(ss, item, ' ')) {
- if (item.length() == 0)
- continue;
- _suffixes.insert(item);
- }
- }
-
+
+ std::string suffixList;
+ if (req.params.find("suffix") != req.params.end()) {
+ suffixList = req.params.find("suffix")->second;
+ } else if (req.params.find("suffixes") != req.params.end()) {
+ suffixList = req.params.find("suffixes")->second;
+ }
+
+ if (suffixList.size() > 0) {
+ // seperate path into components
+ std::stringstream ss(suffixList);
+ std::string item;
+ while(std::getline(ss, item, ' ')) {
+ if (item.length() == 0)
+ continue;
+ _suffixes.insert(item);
+ }
+ }
+
std::multimap<std::string, std::string>::const_iterator dirIter = req.params.find("dir");
while(dirIter != req.params.upper_bound("dir")) {
URL url(dirIter->second);
if (!_interpreter->toAbsoluteURI(url) || !boost::iequals(url.scheme(), "file")) {
LOG(ERROR) << "Given directory '" << dirIter->second << "' cannot be transformed to absolute path";
} else {
- _watchIds.insert(std::make_pair(url.path(), _fileWatcher.addWatch(url.path(), this, _recurse)));
+ _dir = url.path();
}
- dirIter++;
+ break;
}
+
+ _watcher = new DirectoryWatch(_dir, _recurse);
+ _watcher->addMonitor(this);
+ _watcher->updateEntries(true);
+
_isRunning = true;
_thread = new tthread::thread(DirMonInvoker::run, this);
}
void DirMonInvoker::run(void* instance) {
- if (((DirMonInvoker*)instance)->_reportExisting)
- ((DirMonInvoker*)instance)->reportExisting();
-
- while(((DirMonInvoker*)instance)->_isRunning)
- ((DirMonInvoker*)instance)->_fileWatcher.update();
+ while(((DirMonInvoker*)instance)->_isRunning) {
+ ((DirMonInvoker*)instance)->_watcher->updateEntries();
+ tthread::this_thread::sleep_for(tthread::chrono::milliseconds(20));
+ }
}
-void DirMonInvoker::reportExisting() {
- std::multimap<std::string, FW::WatchID>::iterator watchIter = _watchIds.begin();
- while(watchIter != _watchIds.end()) {
- reportExistingIn(watchIter->first, watchIter->second);
- watchIter++;
+void DirMonInvoker::handleChanges(DirectoryWatch::Action action, const std::string reportedDir, const std::string reportedFilename, struct stat fileStat) {
+
+// std::cout << action << " on " << reportedFilename << std::endl;
+
+ std::string path; // complete path to the file including filename
+ std::string relPath; // path relative to monitored directory including filename
+ std::string dir; // the name of the directory we monitor
+ std::string relDir; // the directory from dir to the actual directory where we found a file
+ std::string basename; // filename including suffix
+ std::string strippedName; // filename without the suffix
+ std::string extension; // the extension
+
+ dir = reportedDir;
+
+ path = dir + reportedFilename;
+ boost::algorithm::replace_all(path, "\\", "/");
+ boost::algorithm::replace_all(path, "//", "/");
+
+ assert(boost::algorithm::starts_with(path, dir));
+ relPath = path.substr(dir.length());
+ assert(boost::equal(path, dir + relPath));
+
+ size_t lastSep;
+ if ((lastSep = path.find_last_of(PATH_SEPERATOR)) != std::string::npos) {
+ lastSep++;
+ basename = path.substr(lastSep, path.length() - lastSep);
+ } else {
+ assert(false);
}
-}
+ assert(boost::algorithm::ends_with(relPath, basename));
+
+ // extension is the suffix and strippedName the basename without the suffix
+ size_t lastDot;
+ if ((lastDot = basename.find_last_of(".")) != std::string::npos) {
+ if (lastDot == 0) {
+ // hidden file
+ strippedName = basename;
+ } else {
+ extension = basename.substr(lastDot + 1);
+ strippedName = basename.substr(0, lastDot);
+ }
+ } else {
+ strippedName = basename;
+ }
+
+ relDir = relPath.substr(0, relPath.length() - basename.length());
+ assert(boost::equal(path, dir + relDir + basename));
-void DirMonInvoker::handleFileAction(FW::WatchID watchid, const FW::String& dir, const FW::String& filename, FW::Action action) {
-
- std::string path;
- if (!boost::algorithm::starts_with(filename, dir)) {
- path = dir + filename;
- } else {
- path = filename;
- }
-
- if (_suffixes.size() > 0) {
- bool validSuffix = false;
- std::set<std::string>::iterator suffixIter = _suffixes.begin();
- while(suffixIter != _suffixes.end()) {
- if (boost::algorithm::ends_with(path, *suffixIter)) {
- validSuffix = true;
- break;
- }
- suffixIter++;
- }
- if (!validSuffix)
- return;
- }
+ // return if this is a hidden file
+ if (boost::algorithm::starts_with(basename, ".") && !_reportHidden)
+ return;
+
+ // ilter suffixes
+ if (_suffixes.size() > 0) {
+ bool validSuffix = false;
+ std::set<std::string>::iterator suffixIter = _suffixes.begin();
+ while(suffixIter != _suffixes.end()) {
+ if (boost::algorithm::ends_with(path, *suffixIter)) {
+ validSuffix = true;
+ break;
+ }
+ suffixIter++;
+ }
+ if (!validSuffix)
+ return;
+ }
Event event;
event.invokeid = _invokeId;
+
switch (action) {
- case FW::Actions::Existing:
+ case DirectoryWatch::EXISTING:
event.name = "file.existing";
break;
- case FW::Actions::Add:
+ case DirectoryWatch::ADDED:
event.name = "file.added";
break;
- case FW::Actions::Delete:
+ case DirectoryWatch::DELETED:
event.name = "file.deleted";
break;
- case FW::Actions::Modified:
+ case DirectoryWatch::MODIFIED:
event.name = "file.modified";
break;
default:
break;
}
- // basename is the filename with suffix
- std::string basename;
- size_t lastSep;
- if ((lastSep = path.find_last_of(PATH_SEPERATOR)) != std::string::npos) {
- lastSep++;
- basename = path.substr(lastSep, path.length() - lastSep);
- event.data.compound["file"].compound["name"] = Data(basename, Data::VERBATIM);
- }
-
- // return if this is a hidden file
- if (boost::algorithm::starts_with(basename, ".") && !_reportHidden)
- return;
-
- struct stat fileStat;
- if (action != FW::Actions::Delete) {
- if (stat(path.c_str(), &fileStat) != 0) {
- LOG(ERROR) << "Error with stat on directory entry " << path << ": " << strerror(errno);
- return;
- } else {
- event.data.compound["file"].compound["mtime"] = toStr(fileStat.st_mtime);
- event.data.compound["file"].compound["ctime"] = toStr(fileStat.st_ctime);
- event.data.compound["file"].compound["atime"] = toStr(fileStat.st_atime);
- event.data.compound["file"].compound["size"] = toStr(fileStat.st_size);
- }
- }
-
- // extension is the suffix and strippedName the basename without the suffix
- size_t lastDot;
- if ((lastDot = basename.find_last_of(".")) != std::string::npos) {
- std::string extension = basename.substr(lastDot + 1);
- event.data.compound["file"].compound["extension"] = Data(extension, Data::VERBATIM);
- std::string strippedName = basename.substr(0, lastDot);
- event.data.compound["file"].compound["strippedName"] = Data(strippedName, Data::VERBATIM);
- }
-
- // relpath is the path to the file relative to the dir
- if (boost::algorithm::starts_with(filename, dir)) {
- std::string relPath = filename.substr(dir.length());
- event.data.compound["file"].compound["relPath"] = Data(relPath, Data::VERBATIM);
-
- // relDir is the relpath without the basename
- if ((lastSep = relPath.find_last_of(PATH_SEPERATOR)) != std::string::npos) {
- lastSep++;
- std::string relDir = relPath.substr(0, lastSep);
- event.data.compound["file"].compound["relDir"] = Data(relDir, Data::VERBATIM);
- }
+ if (action != DirectoryWatch::DELETED) {
+ event.data.compound["file"].compound["mtime"] = toStr(fileStat.st_mtime);
+ event.data.compound["file"].compound["ctime"] = toStr(fileStat.st_ctime);
+ event.data.compound["file"].compound["atime"] = toStr(fileStat.st_atime);
+ event.data.compound["file"].compound["size"] = toStr(fileStat.st_size);
}
+ event.data.compound["file"].compound["name"] = Data(basename, Data::VERBATIM);
+ event.data.compound["file"].compound["extension"] = Data(extension, Data::VERBATIM);
+ event.data.compound["file"].compound["strippedName"] = Data(strippedName, Data::VERBATIM);
+ event.data.compound["file"].compound["relPath"] = Data(relPath, Data::VERBATIM);
+ event.data.compound["file"].compound["relDir"] = Data(relDir, Data::VERBATIM);
event.data.compound["file"].compound["path"] = Data(path, Data::VERBATIM);
event.data.compound["file"].compound["dir"] = Data(dir, Data::VERBATIM);
returnEvent(event);
}
-bool DirMonInvoker::filter(const std::string filename) {
- return true;
+void DirectoryWatch::reportAsDeleted() {
+ std::map<std::string, struct stat>::iterator fileIter = _knownEntries.begin();
+ while(fileIter != _knownEntries.end()) {
+ if (fileIter->second.st_mode & S_IFDIR) {
+ _knownDirs[fileIter->first]->reportAsDeleted();
+ delete _knownDirs[fileIter->first];
+ _knownDirs.erase(fileIter->first);
+ } else {
+ _monitors_t::iterator monIter = _monitors.begin();
+ while(monIter != _monitors.end()) {
+ (*monIter)->handleChanges(DELETED, _dir, _relDir + PATH_SEPERATOR + fileIter->first, fileIter->second);
+ monIter++;
+ }
+ }
+ _knownEntries.erase(fileIter->first);
+ fileIter++;
+ }
+ assert(_knownDirs.size() == 0);
+ assert(_knownEntries.size() == 0);
}
-void DirMonInvoker::reportExistingIn(const std::string dir, FW::WatchID watchid) {
-#ifndef WIN32
- DIR *dp;
- dp = opendir(dir.c_str());
- if (dp == NULL) {
- LOG(ERROR) << "Error opening directory " << dir << ": " << strerror(errno);
+void DirectoryWatch::updateEntries(bool reportAsExisting) {
+ _monitors_t::iterator monIter;
+ if (_dir[_dir.length() - 1] == PATH_SEPERATOR)
+ _dir = _dir.substr(0, _dir.length() - 1);
+
+ // stat directory for modification date
+ struct stat dirStat;
+ if (stat((_dir + _relDir).c_str(), &dirStat) != 0) {
+ LOG(ERROR) << "Error with stat on directory " << _dir << ": " << strerror(errno);
return;
}
- // iterate all entries and see what changed
- struct dirent* entry;
- while((entry = readdir(dp))) {
- std::string dname = entry->d_name;
+
+ if ((unsigned)dirStat.st_mtime >= (unsigned)_lastChecked) {
+ // there are changes in the directory
+ std::set<std::string> currEntries;
+
+#ifndef WIN32
+ DIR *dp;
+ dp = opendir((_dir + _relDir).c_str());
+ if (dp == NULL) {
+ LOG(ERROR) << "Error opening directory " << _dir + _relDir << ": " << strerror(errno);
+ return;
+ }
+ // iterate all entries and see what changed
+ struct dirent* entry;
+ while((entry = readdir(dp))) {
+ std::string dname = entry->d_name;
#else
- WIN32_FIND_DATA ffd;
- HANDLE hFind = INVALID_HANDLE_VALUE;
- TCHAR szDir[MAX_PATH];
- StringCchCopy(szDir, MAX_PATH, dir.c_str());
- StringCchCat(szDir, MAX_PATH, TEXT("\\*"));
-
- hFind = FindFirstFile(szDir, &ffd);
- do {
- std::string dname = ffd.cFileName;
+ WIN32_FIND_DATA ffd;
+ HANDLE hFind = INVALID_HANDLE_VALUE;
+ TCHAR szDir[MAX_PATH];
+ StringCchCopy(szDir, MAX_PATH, _dir.c_str());
+ StringCchCat(szDir, MAX_PATH, TEXT("\\*"));
+
+ hFind = FindFirstFile(szDir, &ffd);
+ do {
+ string dname = ffd.cFileName;
#endif
- if (boost::iequals(dname, ".") || boost::iequals(dname, ".."))
- continue;
+ // see if the file was changed
+ char* filename;
+ asprintf(&filename, "%s/%s", (_dir + _relDir).c_str(), dname.c_str());
- char* filename = (char*)malloc(dir.size() + dname.size() + 2);
- sprintf(filename, "%s/%s", dir.c_str(), dname.c_str());
+ struct stat fileStat;
+ if (stat(filename, &fileStat) != 0) {
+ LOG(ERROR) << "Error with stat on directory entry: " << filename << ": " << strerror(errno);
+ free(filename);
+ continue;
+ }
- struct stat fileStat;
- if (stat(filename, &fileStat) != 0) {
- LOG(ERROR) << "Error with stat on directory entry " << filename << ": " << strerror(errno);
- free(filename);
- continue;
- }
+ if (fileStat.st_mode & S_IFDIR) {
+ if (boost::equals(dname, ".") || boost::equals(dname, "..")) {
+ free(filename);
+ continue; // do not report . or ..
+ }
+ }
- if (fileStat.st_mode & S_IFDIR) {
- if (_recurse) {
- reportExistingIn(filename, watchid);
+ currEntries.insert(dname);
+
+ if (_knownEntries.find(dname) != _knownEntries.end()) {
+ // we have seen this entry before
+ struct stat oldStat = _knownEntries[dname];
+ if (oldStat.st_mtime < fileStat.st_mtime) {
+ monIter = _monitors.begin();
+ while(monIter != _monitors.end()) {
+ (*monIter)->handleChanges(MODIFIED, _dir, _relDir + PATH_SEPERATOR + dname, fileStat);
+ monIter++;
+ }
+ }
} else {
- free(filename);
- continue;
+ // we have not yet seen this entry
+ if (fileStat.st_mode & S_IFDIR) {
+ _knownDirs[dname] = new DirectoryWatch(_dir, _relDir + PATH_SEPERATOR + dname);
+ monIter = _monitors.begin();
+ while(monIter != _monitors.end()) {
+ _knownDirs[dname]->addMonitor(*monIter);
+ monIter++;
+ }
+ } else {
+ monIter = _monitors.begin();
+ while(monIter != _monitors.end()) {
+ if (reportAsExisting) {
+ (*monIter)->handleChanges(EXISTING, _dir, _relDir + PATH_SEPERATOR + dname, fileStat);
+ } else {
+ (*monIter)->handleChanges(ADDED, _dir, _relDir + PATH_SEPERATOR + dname, fileStat);
+ }
+ monIter++;
+ }
+ }
}
- }
- if (!filter(dname)) {
free(filename);
- continue;
+ _knownEntries[dname] = fileStat; // gets copied on insertion
+#ifndef WIN32
}
-
- handleFileAction(watchid, dir, filename, FW::Actions::Existing);
+ closedir(dp);
+#else
+ }
+ while (FindNextFile(hFind, &ffd) != 0);
+ FindClose(hFind);
+#endif
+ // are there any known entries we have not seen this time around?
+ std::map<std::string, struct stat>::iterator fileIter = _knownEntries.begin();
+ while(fileIter != _knownEntries.end()) {
+ if (currEntries.find(fileIter->first) == currEntries.end()) {
+ // we used to know this file
+ if (fileIter->second.st_mode & S_IFDIR) {
+ if (_recurse) {
+ _knownDirs[fileIter->first]->reportAsDeleted();
+ delete _knownDirs[fileIter->first];
+ _knownDirs.erase(fileIter->first);
+ }
+ } else {
+ monIter = _monitors.begin();
+ while(monIter != _monitors.end()) {
+ (*monIter)->handleChanges(DELETED, _dir, _relDir + PATH_SEPERATOR + fileIter->first, fileIter->second);
+ monIter++;
+ }
+ }
+ _knownEntries.erase(fileIter->first);
+ }
+ fileIter++;
+ }
+ // remember when we last checked the directory for modifications
#ifndef WIN32
- }
- closedir(dp);
+ time(&_lastChecked);
#else
- }
- while (FindNextFile(hFind, &ffd) != 0);
- FindClose(hFind);
+ // TODO: this will fail with sub-millisecond updates to the directory
+ _lastChecked = dirStat.st_mtime + 1;
#endif
-
+ // update all directories
+ }
+ if (_recurse) {
+ std::map<std::string, DirectoryWatch*>::iterator dirIter = _knownDirs.begin();
+ while(dirIter != _knownDirs.end()) {
+ dirIter->second->updateEntries();
+ dirIter++;
+ }
+ }
}
} \ No newline at end of file
diff --git a/src/uscxml/plugins/invoker/filesystem/dirmon/DirMonInvoker.h b/src/uscxml/plugins/invoker/filesystem/dirmon/DirMonInvoker.h
index 04e670d..3fd9258 100644
--- a/src/uscxml/plugins/invoker/filesystem/dirmon/DirMonInvoker.h
+++ b/src/uscxml/plugins/invoker/filesystem/dirmon/DirMonInvoker.h
@@ -2,8 +2,8 @@
#define DIRMONINVOKER_H_W09J90F0
#include <uscxml/Interpreter.h>
-#include "FileWatcher/FileWatcher.h"
#include <map>
+#include <sys/stat.h>
#ifdef BUILD_AS_PLUGINS
#include "uscxml/plugins/Plugins.h"
@@ -11,7 +11,48 @@
namespace uscxml {
-class DirMonInvoker : public InvokerImpl, public FW::FileWatchListener {
+class DirectoryWatchMonitor;
+
+class DirectoryWatch {
+public:
+ enum Action {
+ ADDED = 1,
+ MODIFIED = 2,
+ DELETED = 4,
+ EXISTING = 8
+ };
+
+ DirectoryWatch(const std::string& dir, bool recurse = false) : _dir(dir), _recurse(recurse), _lastChecked(0) {}
+
+ void addMonitor(DirectoryWatchMonitor* monitor) {
+ _monitors.insert(monitor);
+ }
+ void removeMonitor(DirectoryWatchMonitor* monitor) {
+ _monitors.erase(monitor);
+ }
+ void updateEntries(bool reportAsExisting = false);
+ void reportAsDeleted();
+
+protected:
+ DirectoryWatch(const std::string& dir, const std::string& relDir) : _dir(dir), _relDir(relDir), _recurse(true), _lastChecked(0) {}
+
+ std::string _dir;
+ std::string _relDir;
+
+ bool _recurse;
+ std::map<std::string, struct stat> _knownEntries;
+ std::map<std::string, DirectoryWatch*> _knownDirs;
+ std::set<DirectoryWatchMonitor*> _monitors;
+ typedef std::set<DirectoryWatchMonitor*> _monitors_t;
+ time_t _lastChecked;
+};
+
+class DirectoryWatchMonitor {
+public:
+ virtual void handleChanges(DirectoryWatch::Action action, const std::string dir, const std::string file, struct stat fileStat) = 0;
+};
+
+class DirMonInvoker : public InvokerImpl, public DirectoryWatchMonitor {
public:
DirMonInvoker();
virtual ~DirMonInvoker();
@@ -30,10 +71,7 @@ public:
virtual void cancel(const std::string sendId);
virtual void invoke(const InvokeRequest& req);
- void handleFileAction(FW::WatchID watchid, const FW::String& dir, const FW::String& filename, FW::Action action);
- void reportExisting();
- void reportExistingIn(const std::string dir, FW::WatchID watchid);
- virtual bool filter(const std::string filename);
+ virtual void handleChanges(DirectoryWatch::Action action, const std::string dir, const std::string file, struct stat fileStat);
static void run(void* instance);
@@ -41,12 +79,15 @@ protected:
bool _reportExisting;
bool _reportHidden;
bool _recurse;
- std::set<std::string> _suffixes;
+
+ std::string _dir;
+ std::set<std::string> _suffixes;
bool _isRunning;
tthread::thread* _thread;
- FW::FileWatcher _fileWatcher;
- std::multimap<std::string, FW::WatchID> _watchIds;
+ tthread::recursive_mutex _mutex;
+
+ DirectoryWatch* _watcher;
};
#ifdef BUILD_AS_PLUGINS
diff --git a/src/uscxml/plugins/invoker/filesystem/dirmon/FileWatcher/FileWatcherOSX.cpp b/src/uscxml/plugins/invoker/filesystem/dirmon/FileWatcher/FileWatcherOSX.cpp
index e1634b0..a47d635 100644
--- a/src/uscxml/plugins/invoker/filesystem/dirmon/FileWatcher/FileWatcherOSX.cpp
+++ b/src/uscxml/plugins/invoker/filesystem/dirmon/FileWatcher/FileWatcherOSX.cpp
@@ -35,6 +35,7 @@
#include <stdlib.h>
#include <dirent.h>
#include <string.h>
+#include <iostream>
// this is more suited:
// https://developer.apple.com/library/mac/#documentation/Darwin/Conceptual/FSEvents_ProgGuide/UsingtheFSEventsFramework/UsingtheFSEventsFramework.html
@@ -71,18 +72,20 @@ struct WatchStruct {
WatchID mWatchID;
String mDirName;
FileWatchListener* mListener;
+ FileWatcherOSX* mWatcher;
+ bool mRecursive;
// index 0 is always the directory
KEvent mChangeList[MAX_CHANGE_EVENT_SIZE];
size_t mChangeListCount;
- WatchStruct(WatchID watchid, const String& dirname, FileWatchListener* listener)
- : mWatchID(watchid), mDirName(dirname), mListener(listener) {
+ WatchStruct(WatchID watchid, const String& dirname, FileWatchListener* listener, FileWatcherOSX* watcher, bool recursive = false)
+ : mWatchID(watchid), mDirName(dirname), mListener(listener), mWatcher(watcher), mRecursive(recursive) {
mChangeListCount = 0;
addAll();
}
- void addFile(const String& name, bool imitEvents = true) {
+ void addFile(const String& name, bool emitEvents = true) {
//fprintf(stderr, "ADDED: %s\n", name.c_str());
// create entry
@@ -104,18 +107,18 @@ struct WatchStruct {
// set the event data at the end of the list
EV_SET(&mChangeList[mChangeListCount], fd, EVFILT_VNODE,
EV_ADD | EV_ENABLE | EV_ONESHOT,
- NOTE_DELETE | NOTE_EXTEND | NOTE_WRITE | NOTE_ATTRIB,
+ NOTE_DELETE | NOTE_EXTEND | NOTE_WRITE | NOTE_ATTRIB | NOTE_RENAME,
0, (void*)entry);
// qsort
qsort(mChangeList + 1, mChangeListCount, sizeof(KEvent), comparator);
// handle action
- if(imitEvents)
+ if(emitEvents)
handleAction(name, Actions::Add);
}
- void removeFile(const String& name, bool imitEvents = true) {
+ void removeFile(const String& name, bool emitEvents = true) {
// bsearch
KEvent target;
EntryStruct tempEntry(name.c_str(), 0);
@@ -140,7 +143,7 @@ struct WatchStruct {
qsort(mChangeList + 1, mChangeListCount, sizeof(KEvent), comparator);
// handle action
- if(imitEvents)
+ if(emitEvents)
handleAction(name, Actions::Delete);
}
@@ -207,7 +210,7 @@ struct WatchStruct {
int fd = open(mDirName.c_str(), O_RDONLY);
EV_SET(&mChangeList[0], fd, EVFILT_VNODE,
EV_ADD | EV_ENABLE | EV_ONESHOT,
- NOTE_DELETE | NOTE_EXTEND | NOTE_WRITE | NOTE_ATTRIB,
+ NOTE_DELETE | NOTE_EXTEND | NOTE_WRITE | NOTE_ATTRIB | NOTE_RENAME,
0, 0);
//fprintf(stderr, "ADDED: %s\n", mDirName.c_str());
@@ -222,11 +225,13 @@ struct WatchStruct {
while((entry = readdir(dir)) != NULL) {
String fname = (mDirName + "/" + String(entry->d_name));
stat(fname.c_str(), &attrib);
- if(S_ISREG(attrib.st_mode))
+ if(S_ISREG(attrib.st_mode)) {
addFile(fname, false);
- //else
- // fprintf(stderr, "NOT ADDED: %s (%d)\n", fname.c_str(), attrib.st_mode);
-
+ } else if(S_IFDIR && mRecursive && entry->d_name[0] != '.') {
+ mWatcher->addWatch(fname, mListener, mRecursive);
+ } else {
+ fprintf(stderr, "NOT ADDED: %s (%d)\n", fname.c_str(), attrib.st_mode);
+ }
}//end while
closedir(dir);
@@ -263,19 +268,33 @@ void FileWatcherOSX::update() {
if(nev == -1)
perror("kevent");
else {
+ if (event.fflags & NOTE_DELETE) {
+ fprintf(stderr, "NOTE_DELETE ");
+ }
+ if (event.fflags & NOTE_EXTEND) {
+ fprintf(stderr, "NOTE_EXTEND ");
+ }
+ if (event.fflags & NOTE_WRITE) {
+ fprintf(stderr, "NOTE_WRITE ");
+ }
+ if (event.fflags & NOTE_ATTRIB) {
+ fprintf(stderr, "NOTE_ATTRIB ");
+ }
+ if (event.fflags & NOTE_RENAME) {
+ fprintf(stderr, "NOTE_RENAME ");
+ }
+
EntryStruct* entry = 0;
if((entry = (EntryStruct*)event.udata) != 0) {
- //fprintf(stderr, "File: %s -- ", (char*)entry->mFilename);
+ fprintf(stderr, " to %s -- \n", (char*)entry->mFilename);
if(event.fflags & NOTE_DELETE) {
- //fprintf(stderr, "File deleted\n");
//watch->handleAction(entry->mFilename, Action::Delete);
watch->removeFile(entry->mFilename);
}
if(event.fflags & NOTE_EXTEND ||
event.fflags & NOTE_WRITE ||
event.fflags & NOTE_ATTRIB) {
- //fprintf(stderr, "modified\n");
//watch->rescan();
struct stat attrib;
stat(entry->mFilename, &attrib);
@@ -283,7 +302,7 @@ void FileWatcherOSX::update() {
watch->handleAction(entry->mFilename, FW::Actions::Modified);
}
} else {
- //fprintf(stderr, "Dir: %s -- rescanning\n", watch->mDirName.c_str());
+ fprintf(stderr, " in %s -- rescanning\n", watch->mDirName.c_str());
watch->rescan();
}
}
@@ -295,7 +314,7 @@ void FileWatcherOSX::update() {
FileWatcherOSX::FileWatcherOSX() {
mDescriptor = kqueue();
mTimeOut.tv_sec = 0;
- mTimeOut.tv_nsec = 2000000;
+ mTimeOut.tv_nsec = 20000000;
}
//--------
@@ -322,9 +341,12 @@ WatchID FileWatcherOSX::addWatch(const String& directory, FileWatchListener* wat
0, (void*)"testing");
*/
- WatchStruct* watch = new WatchStruct(++mLastWatchID, directory, watcher);
- mWatches.insert(std::make_pair(mLastWatchID, watch));
- return mLastWatchID;
+ std::cout << "Adding watch for " << directory << std::endl;
+
+ WatchID currWatch = ++mLastWatchID;
+ WatchStruct* watch = new WatchStruct(currWatch, directory, watcher, this, recursive);
+ mWatches.insert(std::make_pair(currWatch, watch));
+ return currWatch;
}
//--------
diff --git a/src/uscxml/plugins/invoker/filesystem/dirmon/FileWatcher/FileWatcherOSX.h b/src/uscxml/plugins/invoker/filesystem/dirmon/FileWatcher/FileWatcherOSX.h
index 39f411c..92e3957 100644
--- a/src/uscxml/plugins/invoker/filesystem/dirmon/FileWatcher/FileWatcherOSX.h
+++ b/src/uscxml/plugins/invoker/filesystem/dirmon/FileWatcher/FileWatcherOSX.h
@@ -54,7 +54,7 @@ public:
/// Add a directory watch
/// @exception FileNotFoundException Thrown when the requested directory does not exist
- WatchID addWatch(const String& directory, FileWatchListener* watcher, bool recursive);
+ WatchID addWatch(const String& directory, FileWatchListener* watcher, bool recursive = false);
/// Remove a directory watch. This is a brute force lazy search O(nlogn).
void removeWatch(const String& directory);
@@ -78,6 +78,7 @@ private:
/// WatchID allocator
int mLastWatchID;
+ friend class WatchStruct;
};//end FileWatcherOSX
};//namespace FW
diff --git a/src/uscxml/plugins/invoker/graphics/openscenegraph/OSGConverter.cpp b/src/uscxml/plugins/invoker/graphics/openscenegraph/OSGConverter.cpp
index ea249e4..2fa2877 100644
--- a/src/uscxml/plugins/invoker/graphics/openscenegraph/OSGConverter.cpp
+++ b/src/uscxml/plugins/invoker/graphics/openscenegraph/OSGConverter.cpp
@@ -5,10 +5,12 @@
#include <osg/MatrixTransform>
#include <osg/Node>
#include <osg/Group>
+#include <osg/ComputeBoundsVisitor>
#include <osgDB/ReadFile>
#include <osgDB/WriteFile>
#include <osgDB/Registry>
#include <osgGA/TrackballManipulator>
+#include <osgGA/AnimationPathManipulator>
#include <osg/ShapeDrawable>
#include <boost/lexical_cast.hpp>
@@ -91,6 +93,20 @@ void OSGConverter::send(const SendRequest& req) {
}
}
+ boost::algorithm::replace_all(actualReq.params.find("dest")->second, "//", "/");
+ boost::algorithm::replace_all(actualReq.params.find("dest")->second, "\\\\", "\\");
+
+ if (actualReq.params.find("autorotate") == actualReq.params.end()) {
+ if (actualReq.params.find("autorotateexpr") != actualReq.params.end()) {
+ if (_interpreter->getDataModel()) {
+ actualReq.params.insert(std::make_pair("autorotate", _interpreter->getDataModel().evalAsString(actualReq.params.find("autorotateexpr")->second)));
+ } else {
+ LOG(ERROR) << "SendRequests for osginvoker ncludes autorotateexpr but no datamodel is specified";
+ return;
+ }
+ }
+ }
+
if (actualReq.params.find("format") == actualReq.params.end()) {
// no explicit format
if (actualReq.params.find("formatexpr") != actualReq.params.end() && _interpreter->getDataModel()) {
@@ -111,12 +127,26 @@ void OSGConverter::send(const SendRequest& req) {
}
}
- EVAL_PARAM_EXPR(actualReq.params, "heightexpr", "height");
- EVAL_PARAM_EXPR(actualReq.params, "widthexpr", "width");
- EVAL_PARAM_EXPR(actualReq.params, "pitchexpr", "pitch");
- EVAL_PARAM_EXPR(actualReq.params, "rollexpr", "roll");
- EVAL_PARAM_EXPR(actualReq.params, "yawexpr", "yaw");
- EVAL_PARAM_EXPR(actualReq.params, "zoomexpr", "zoom");
+// assert(osgDB::Registry::instance()->getReaderWriterForExtension("png"));
+// osgDB::Registry::ReaderWriterList formatList = osgDB::Registry::instance()->getReaderWriterList();
+// for (int i = 0; i < formatList.size(); i++) {
+// std::map<std::string, std::string> funcDesc = formatList[i]->supportedProtocols();
+// std::map<std::string, std::string>::iterator funcDescIter = funcDesc.begin();
+// while(funcDescIter != funcDesc.end()) {
+// std::cout << funcDescIter->first << ": " << funcDescIter->second << std::endl;
+// funcDescIter++;
+// }
+// }
+
+ EVAL_PARAM_EXPR(actualReq.params, "heightexpr", "height");
+ EVAL_PARAM_EXPR(actualReq.params, "widthexpr", "width");
+ EVAL_PARAM_EXPR(actualReq.params, "pitchexpr", "pitch");
+ EVAL_PARAM_EXPR(actualReq.params, "rollexpr", "roll");
+ EVAL_PARAM_EXPR(actualReq.params, "yawexpr", "yaw");
+ EVAL_PARAM_EXPR(actualReq.params, "zoomexpr", "zoom");
+ EVAL_PARAM_EXPR(actualReq.params, "xexpr", "x");
+ EVAL_PARAM_EXPR(actualReq.params, "yexpr", "y");
+ EVAL_PARAM_EXPR(actualReq.params, "zexpr", "z");
// process(actualReq);
_workQueue.push(actualReq);
@@ -150,13 +180,13 @@ void OSGConverter::run(void* instance) {
}
void OSGConverter::process(const SendRequest& req) {
-
+
// std::cout << req;
-
- int width = 640;
+
+ int width = 640;
int height = 480;
- CAST_PARAM(req.params, width, "width", int);
- CAST_PARAM(req.params, height, "height", int);
+ CAST_PARAM(req.params, width, "width", int);
+ CAST_PARAM(req.params, height, "height", int);
assert(req.params.find("source") != req.params.end());
assert(req.params.find("dest") != req.params.end());
@@ -166,80 +196,131 @@ void OSGConverter::process(const SendRequest& req) {
std::string dest = req.params.find("dest")->second;
std::string format = req.params.find("format")->second;
- osg::ref_ptr<osg::Node> sceneGraph = setupGraph(source);
+ bool autoRotate = true;
+ if (req.params.find("autorotate") != req.params.end()) {
+ if (boost::iequals(req.params.find("autorotate")->second, "off") ||
+ boost::iequals(req.params.find("autorotate")->second, "0") ||
+ boost::iequals(req.params.find("autorotate")->second, "false")) {
+ autoRotate = false;
+ }
+ }
+
+ osg::ref_ptr<osg::Node> model = setupGraph(source, autoRotate);
+ if (model->asGroup()->getNumChildren() == 0) {
+ reportFailure(req);
+ return;
+ }
+
+ osg::ref_ptr<osg::Group> sceneGraph = new osg::Group();
+ sceneGraph->addChild(model);
osgDB::ReaderWriter::WriteResult result;
if (osgDB::Registry::instance()->getReaderWriterForExtension(format) != NULL) {
// write as another 3D file
result = osgDB::Registry::instance()->writeNode(*sceneGraph, dest, osgDB::Registry::instance()->getOptions());
+ if (result.success()) {
+ // we can know about success right here
+ reportSuccess(req);
+ return;
+ }
}
- if (result.error()) {
- // make a screenshot
- osgViewer::ScreenCaptureHandler::CaptureOperation* cOp = new NameRespectingWriteToFile(dest,
- format,
- osgViewer::ScreenCaptureHandler::WriteToFile::OVERWRITE
- );
-
- osgViewer::ScreenCaptureHandler* captureHandler = new osgViewer::ScreenCaptureHandler(cOp, -1);
-
- osgViewer::Viewer viewer;
- viewer.setSceneData(sceneGraph);
- viewer.setCameraManipulator(new osgGA::TrackballManipulator());
- viewer.addEventHandler(captureHandler);
- captureHandler->startCapture();
-
- osg::DisplaySettings* ds = osg::DisplaySettings::instance().get();
- osg::ref_ptr<osg::GraphicsContext::Traits> traits = new osg::GraphicsContext::Traits(ds);
- traits->width = width;
- traits->height = height;
- traits->pbuffer = true;
- osg::ref_ptr<osg::GraphicsContext> gc = osg::GraphicsContext::createGraphicsContext(traits.get());
-
- if (!gc.valid()) {
- LOG(ERROR) << "Cannot create GraphicsContext!";
- return;
- }
-
-
- GLenum pbuffer = gc->getTraits()->doubleBuffer ? GL_BACK : GL_FRONT;
-
- viewer.getCamera()->setGraphicsContext(gc.get());
- viewer.getCamera()->setViewport(new osg::Viewport(0,0,traits->width,traits->height));
- viewer.getCamera()->setDrawBuffer(pbuffer);
- viewer.getCamera()->setReadBuffer(pbuffer);
-
- // set background color
- viewer.getCamera()->setClearColor(osg::Vec4f(1.0f,1.0f,1.0f,1.0f));
- viewer.getCamera()->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
-
-// viewer.getCamera()->setViewMatrix(requestToCamPose(req));
-
- viewer.home();
- ((osg::MatrixTransform*)sceneGraph.get())->setMatrix(requestToModelPose(req));
-
- // perform one viewer iteration
- viewer.realize();
- viewer.frame();
+ /**
+ * If we failed to interpret the extension as another 3D file, try to make a screenshot.
+ */
+
+ ((osg::MatrixTransform*)model.get())->setMatrix(requestToModelPose(req));
+ osg::BoundingSphere bs = model->getBound();
+
+// osg::ref_ptr<osg::MatrixTransform> scale = new osg::MatrixTransform();
+// scale->setMatrix(osg::Matrix::scale(bs.radius() / 5, bs.radius() / 5, bs.radius() / 5));
+// scale->addChild(getOrigin());
+// sceneGraph->addChild(scale);
+
+ osgViewer::ScreenCaptureHandler::CaptureOperation* cOp = new NameRespectingWriteToFile(
+ dest,
+ format,
+ osgViewer::ScreenCaptureHandler::WriteToFile::OVERWRITE,
+ req, this);
+
+ osgViewer::ScreenCaptureHandler* captureHandler = new osgViewer::ScreenCaptureHandler(cOp, -1);
+
+ osgViewer::Viewer viewer;
+ viewer.setSceneData(sceneGraph);
+ viewer.setCameraManipulator(new osgGA::TrackballManipulator());
+ viewer.addEventHandler(captureHandler);
+ captureHandler->startCapture();
+
+ osg::DisplaySettings* ds = osg::DisplaySettings::instance().get();
+ osg::ref_ptr<osg::GraphicsContext::Traits> traits = new osg::GraphicsContext::Traits(ds);
+ traits->width = width;
+ traits->height = height;
+ traits->pbuffer = true;
+ osg::ref_ptr<osg::GraphicsContext> gc = osg::GraphicsContext::createGraphicsContext(traits.get());
+
+ if (!gc.valid()) {
+ LOG(ERROR) << "Cannot create GraphicsContext!";
+ return;
}
+
+ GLenum pbuffer = gc->getTraits()->doubleBuffer ? GL_BACK : GL_FRONT;
+
+ viewer.getCamera()->setGraphicsContext(gc.get());
+ viewer.getCamera()->setViewport(new osg::Viewport(0,0,traits->width,traits->height));
+ viewer.getCamera()->setDrawBuffer(pbuffer);
+ viewer.getCamera()->setReadBuffer(pbuffer);
+
+ // set background color
+ viewer.getCamera()->setClearColor(osg::Vec4f(1.0f,1.0f,1.0f,1.0f));
+ viewer.getCamera()->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+ viewer.getCameraManipulator()->setByMatrix(osg::Matrix::lookAt(osg::Vec3d(0,0,bs.radius() * -4), // eye
+ (osg::Vec3d)bs.center(), // center
+ osg::Vec3d(0,1,0))); // up
+
+// viewer.home();
+
+ // perform one viewer iteration
+ viewer.realize();
+ viewer.frame();
+}
+
+void OSGConverter::reportSuccess(const SendRequest& req) {
+ Event event(req);
+ if (event.name.length() == 0)
+ event.name = "convert";
+ event.name += ".success";
+ returnEvent(event);
}
-osg::Matrix OSGConverter::requestToModelPose(const SendRequest& req) {
- double pitch = 0;
- double roll = 0;
- double yaw = 0;
- double zoom = 1;
- CAST_PARAM(req.params, pitch, "pitch", double);
- CAST_PARAM(req.params, roll, "roll", double);
- CAST_PARAM(req.params, yaw, "yaw", double);
- CAST_PARAM(req.params, zoom, "zoom", double);
-
- osg::Matrix m = osg::Matrix::scale(zoom, zoom, zoom) * eulerToMatrix(pitch, roll, yaw);
-
+void OSGConverter::reportFailure(const SendRequest& req) {
+ Event event(req);
+ if (event.name.length() == 0)
+ event.name = "convert";
+ event.name += ".failure";
+ returnEvent(event);
+}
+
+osg::Matrix OSGConverter::requestToModelPose(const SendRequest& req) {
+ double pitch = 0;
+ double roll = 0;
+ double yaw = 0;
+ double zoom = 1;
+ double x = 0;
+ double y = 0;
+ double z = 0;
+ CAST_PARAM(req.params, pitch, "pitch", double);
+ CAST_PARAM(req.params, roll, "roll", double);
+ CAST_PARAM(req.params, yaw, "yaw", double);
+ CAST_PARAM(req.params, zoom, "zoom", double);
+ CAST_PARAM(req.params, x, "x", double);
+ CAST_PARAM(req.params, y, "y", double);
+ CAST_PARAM(req.params, z, "z", double);
+
+ osg::Matrix m = osg::Matrix::scale(zoom, zoom, zoom) * eulerToMatrix(pitch, roll, yaw) * osg::Matrix::translate(-1 * x, -1 * y, -1 * z);
#if 0
- dumpMatrix(m);
+ dumpMatrix(m);
#endif
- return m;
+ return m;
}
osg::Matrix OSGConverter::requestToCamPose(const SendRequest& req) {
@@ -247,17 +328,18 @@ osg::Matrix OSGConverter::requestToCamPose(const SendRequest& req) {
// CAST_PARAM(req.params, zoom, "zoom", double);
// osg::Matrix scale = osg::Matrix::scale(zoom, zoom, zoom);
// return scale;
- osg::Matrix identity;
- identity.makeIdentity();
- return identity;
+ osg::Matrix identity;
+ identity.makeIdentity();
+ return identity;
}
-osg::ref_ptr<osg::Node> OSGConverter::setupGraph(const std::string filename) {
+osg::ref_ptr<osg::Node> OSGConverter::setupGraph(const std::string filename, bool autoRotate) {
/**
* root (model pose)
- * - modelCenter (center model)
- * - model (actual model)
+ * - rotate (autoRotate to face largest side)
+ * - modelCenter (center model)
+ * - model (actual model)
*/
long now = tthread::chrono::system_clock::now();
@@ -275,7 +357,7 @@ osg::ref_ptr<osg::Node> OSGConverter::setupGraph(const std::string filename) {
}
_models[filename] = std::make_pair(now, model);
}
- _models[filename].first = now;
+ _models[filename].first = now;
#if 1
// remove old models from cache
@@ -291,25 +373,66 @@ osg::ref_ptr<osg::Node> OSGConverter::setupGraph(const std::string filename) {
#endif
}
-
- osg::ref_ptr<osg::MatrixTransform> root = new osg::MatrixTransform();
+ osg::ref_ptr<osg::MatrixTransform> root = new osg::MatrixTransform();
+ osg::ref_ptr<osg::MatrixTransform> rotate = new osg::MatrixTransform();
osg::ref_ptr<osg::Node> model = _models[filename].second;
// translation matrix to move model into center
osg::ref_ptr<osg::MatrixTransform> modelCenter = new osg::MatrixTransform();
modelCenter->addChild(model);
+ rotate->addChild(modelCenter);
// move bounding sphere center into origin
osg::BoundingSphere bs = model->getBound();
modelCenter->setMatrix(osg::Matrix::translate(bs.center() *= -1));
- // add to model pose matrix
- root->addChild(modelCenter);
-
+ // get bounding box
+ osg::ComputeBoundsVisitor cbv;
+ osg::BoundingBox& bb(cbv.getBoundingBox());
+ modelCenter->accept(cbv);
+
+ if (autoRotate) {
+ double depth = bb.zMax() - bb.zMin();
+ double width = bb.xMax() - bb.xMin();
+ double height = bb.yMax() - bb.yMin();
+
+ double frontArea = width * height;
+ double sideArea = depth * height;
+ double topArea = depth * width;
+
+ // rotate by multiples of 90deg to face largest area
+ if (frontArea < sideArea || frontArea < topArea) {
+ if (sideArea < topArea) {
+ // top needs to come to front -> rotate on x
+ rotate->setMatrix(osg::Matrix::rotate(M_PI_2, osg::Vec3f(1.0,0,0)));
+ } else {
+ // side needs to come to front
+ rotate->setMatrix(osg::Matrix::rotate(M_PI_2, osg::Vec3f(0,1.0,0)));
+ }
+ }
+ }
+
+ // add rotation to root
+ root->addChild(rotate);
return root;
}
+osg::ref_ptr<osg::Node> OSGConverter::getOrigin() {
+ osg::Geode* geode = new osg::Geode();
+// osg::StateSet* stateset = new osg::StateSet();
+// stateset->setMode(GL_LIGHTING, osg::StateAttribute::ON);
+// geode->setStateSet(stateset);
+
+ geode->addDrawable(new osg::ShapeDrawable(new osg::Sphere(osg::Vec3(0.0f,0.0f,0.0f),1)));
+ geode->addDrawable(new osg::ShapeDrawable(new osg::Box(osg::Vec3(10.0f,0.0f,0.0f),0.5)));
+ geode->addDrawable(new osg::ShapeDrawable(new osg::Box(osg::Vec3(0.0f,10.0f,0.0f),2)));
+ geode->addDrawable(new osg::ShapeDrawable(new osg::Box(osg::Vec3(0.0f,0.0f,10.0f),4)));
+ // geode->addDrawable(new osg::ShapeDrawable(new osg::Cone(osg::Vec3(4.0f,0.0f,0.0f),radius,height),hints));
+
+ return geode;
+}
+
osg::Matrix OSGConverter::eulerToMatrix(double pitch, double roll, double yaw) {
// see http://www.flipcode.com/documents/matrfaq.html#Q36
osg::Matrix m;
@@ -337,7 +460,7 @@ osg::Matrix OSGConverter::eulerToMatrix(double pitch, double roll, double yaw) {
m(0,3) = m(1,3) = m(2,3) = m(3,0) = m(3,1) = m(3,2) = 0;
m(3,3) = 1;
-
+
return m;
}
@@ -370,16 +493,21 @@ void OSGConverter::matrixToEuler(const osg::Matrix& m, double& pitch, double& ro
}
void OSGConverter::dumpMatrix(const osg::Matrix& m) {
- for (int i = 0; i < 4; i++) {
- for (int j = 0; j < 4; j++) {
- std::cout << ", " << m(i, j);
- }
- std::cout << std::endl;
- }
+ for (int i = 0; i < 4; i++) {
+ for (int j = 0; j < 4; j++) {
+ std::cout << ", " << m(i, j);
+ }
+ std::cout << std::endl;
+ }
}
-
+
void OSGConverter::NameRespectingWriteToFile::operator()(const osg::Image& image, const unsigned int context_id) {
- osgDB::writeImageFile(image, _filename);
+ bool success = osgDB::writeImageFile(image, _filename);
+ if (success) {
+ _converter->reportSuccess(_req);
+ } else {
+ _converter->reportFailure(_req);
+ }
}
} \ No newline at end of file
diff --git a/src/uscxml/plugins/invoker/graphics/openscenegraph/OSGConverter.h b/src/uscxml/plugins/invoker/graphics/openscenegraph/OSGConverter.h
index 67b0da6..f493e73 100644
--- a/src/uscxml/plugins/invoker/graphics/openscenegraph/OSGConverter.h
+++ b/src/uscxml/plugins/invoker/graphics/openscenegraph/OSGConverter.h
@@ -31,10 +31,13 @@ public:
virtual void cancel(const std::string sendId);
virtual void invoke(const InvokeRequest& req);
+ void reportSuccess(const SendRequest& req);
+ void reportFailure(const SendRequest& req);
+
osg::Matrix requestToModelPose(const SendRequest& req);
osg::Matrix requestToCamPose(const SendRequest& req);
- static void dumpMatrix(const osg::Matrix& m);
+ static void dumpMatrix(const osg::Matrix& m);
static osg::Matrix eulerToMatrix(double pitch, double roll, double yaw);
static void matrixToEuler(const osg::Matrix& m, double& pitch, double& roll, double& yaw);
@@ -43,14 +46,20 @@ protected:
public:
NameRespectingWriteToFile(const std::string& filename,
const std::string& extension,
- SavePolicy savePolicy) : osgViewer::ScreenCaptureHandler::WriteToFile(filename, extension, savePolicy) {
+ SavePolicy savePolicy,
+ const SendRequest& req,
+ OSGConverter* converter) : osgViewer::ScreenCaptureHandler::WriteToFile(filename, extension, savePolicy),
+ _req(req), _converter(converter) {
}
virtual void operator()(const osg::Image& image, const unsigned int context_id);
+ SendRequest _req;
+ OSGConverter* _converter;
};
uscxml::concurrency::BlockingQueue<SendRequest> _workQueue;
- osg::ref_ptr<osg::Node> setupGraph(const std::string filename);
+ osg::ref_ptr<osg::Node> setupGraph(const std::string filename, bool autoRotate = false);
+ osg::ref_ptr<osg::Node> getOrigin();
std::map<std::string, std::pair<long, osg::ref_ptr<osg::Node> > > _models;
tthread::recursive_mutex _cacheMutex;
diff --git a/src/uscxml/plugins/invoker/heartbeat/HeartbeatInvoker.cpp b/src/uscxml/plugins/invoker/heartbeat/HeartbeatInvoker.cpp
index 16947a7..6371a43 100644
--- a/src/uscxml/plugins/invoker/heartbeat/HeartbeatInvoker.cpp
+++ b/src/uscxml/plugins/invoker/heartbeat/HeartbeatInvoker.cpp
@@ -41,7 +41,7 @@ void HeartbeatInvoker::cancel(const std::string sendId) {
}
void HeartbeatInvoker::invoke(const InvokeRequest& req) {
- _invokeId = req.invokeid;
+ _invokeId = req.invokeid;
_event.invokeid = _invokeId;
std::string intervalStr;
double interval = 0;
diff --git a/src/uscxml/server/HTTPServer.cpp b/src/uscxml/server/HTTPServer.cpp
index a7957ea..ebc5b91 100644
--- a/src/uscxml/server/HTTPServer.cpp
+++ b/src/uscxml/server/HTTPServer.cpp
@@ -33,9 +33,9 @@
#endif
namespace uscxml {
-
+
HTTPServer::HTTPServer(unsigned short port) {
- _port = port;
+ _port = port;
_base = event_base_new();
_http = evhttp_new(_base);
_handle = NULL;
@@ -58,24 +58,25 @@ std::map<std::string, std::string> HTTPServer::mimeTypes;
HTTPServer* HTTPServer::getInstance(int port) {
tthread::lock_guard<tthread::recursive_mutex> lock(_instanceMutex);
if (_instance == NULL) {
-
- // this is but a tiny list, supply a content-type <header> yourself
- mimeTypes["txt"] = "text/plain";
- mimeTypes["c"] = "text/plain";
- mimeTypes["h"] = "text/plain";
- mimeTypes["html"] = "text/html";
- mimeTypes["htm"] = "text/htm";
- mimeTypes["css"] = "text/css";
- mimeTypes["gif"] = "image/gif";
- mimeTypes["jpg"] = "image/jpeg";
- mimeTypes["jpeg"] = "image/jpeg";
- mimeTypes["mpg"] = "video/mpeg";
- mimeTypes["mov"] = "video/quicktime";
- mimeTypes["png"] = "image/png";
- mimeTypes["pdf"] = "application/pdf";
- mimeTypes["ps"] = "application/postscript";
- mimeTypes["tif"] = "image/tiff";
- mimeTypes["tiff"] = "image/tiff";
+
+ // this is but a tiny list, supply a content-type <header> yourself
+ mimeTypes["txt"] = "text/plain";
+ mimeTypes["c"] = "text/plain";
+ mimeTypes["h"] = "text/plain";
+ mimeTypes["html"] = "text/html";
+ mimeTypes["htm"] = "text/htm";
+ mimeTypes["css"] = "text/css";
+ mimeTypes["bmp"] = "image/bmp";
+ mimeTypes["gif"] = "image/gif";
+ mimeTypes["jpg"] = "image/jpeg";
+ mimeTypes["jpeg"] = "image/jpeg";
+ mimeTypes["mpg"] = "video/mpeg";
+ mimeTypes["mov"] = "video/quicktime";
+ mimeTypes["png"] = "image/png";
+ mimeTypes["pdf"] = "application/pdf";
+ mimeTypes["ps"] = "application/postscript";
+ mimeTypes["tif"] = "image/tiff";
+ mimeTypes["tiff"] = "image/tiff";
#ifndef _WIN32
evthread_use_pthreads();
@@ -89,9 +90,9 @@ HTTPServer* HTTPServer::getInstance(int port) {
}
std::string HTTPServer::mimeTypeForExtension(const std::string& ext) {
- if (mimeTypes.find(ext) != mimeTypes.end())
- return mimeTypes[ext];
- return "";
+ if (mimeTypes.find(ext) != mimeTypes.end())
+ return mimeTypes[ext];
+ return "";
}
void HTTPServer::httpRecvReqCallback(struct evhttp_request *req, void *callbackData) {
@@ -220,18 +221,23 @@ void HTTPServer::processByMatchingServlet(const Request& request) {
}
void HTTPServer::reply(const Reply& reply) {
- struct evbuffer *evb = evbuffer_new();
+ if (reply.content.size() > 0 && reply.headers.find("Content-Type") == reply.headers.end()) {
+ LOG(INFO) << "Sending content without Content-Type header";
+ }
- if (reply.content.size() > 0 && reply.headers.find("Content-Type") == reply.headers.end()) {
- LOG(INFO) << "Sending content without Content-Type header";
- }
-
std::map<std::string, std::string>::const_iterator headerIter = reply.headers.begin();
while(headerIter != reply.headers.end()) {
evhttp_add_header(evhttp_request_get_output_headers(reply.curlReq), headerIter->first.c_str(), headerIter->second.c_str());
headerIter++;
}
+ if (reply.status >= 400) {
+ evhttp_send_error(reply.curlReq, reply.status, NULL);
+ return;
+ }
+
+ struct evbuffer *evb = evbuffer_new();
+
if (!boost::iequals(reply.type, "HEAD"))
evbuffer_add(evb, reply.content.data(), reply.content.size());
diff --git a/src/uscxml/server/HTTPServer.h b/src/uscxml/server/HTTPServer.h
index 9bab1a1..c573422 100644
--- a/src/uscxml/server/HTTPServer.h
+++ b/src/uscxml/server/HTTPServer.h
@@ -41,7 +41,7 @@ public:
static std::string getBaseURL();
static void reply(const Reply& reply);
- static std::string mimeTypeForExtension(const std::string& ext);
+ static std::string mimeTypeForExtension(const std::string& ext);
static bool registerServlet(const std::string& path, HTTPServlet* servlet); ///< Register a servlet, returns false if path is already taken
static void unregisterServlet(HTTPServlet* servlet);
@@ -58,7 +58,7 @@ private:
static void httpRecvReqCallback(struct evhttp_request *req, void *callbackData);
void processByMatchingServlet(const Request& request);
- static std::map<std::string, std::string> mimeTypes;
+ static std::map<std::string, std::string> mimeTypes;
std::map<std::string, HTTPServlet*> _servlets;
typedef std::map<std::string, HTTPServlet*>::iterator servlet_iter_t;
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index b6cbdc0..d7f30ed 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -33,6 +33,11 @@ target_link_libraries(test-eventdelay uscxml)
add_test(test-eventdelay ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test-eventdelay)
set_target_properties(test-eventdelay PROPERTIES FOLDER "Tests")
+add_executable(test-dirmon src/test-dirmon.cpp)
+target_link_libraries(test-dirmon uscxml)
+add_test(test-dirmon ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test-dirmon)
+set_target_properties(test-dirmon PROPERTIES FOLDER "Tests")
+
if (NOT WIN32)
add_executable(test-arabica-events src/test-arabica-events.cpp)
target_link_libraries(test-arabica-events uscxml)
diff --git a/test/src/test-dirmon.cpp b/test/src/test-dirmon.cpp
new file mode 100644
index 0000000..f3a83b3
--- /dev/null
+++ b/test/src/test-dirmon.cpp
@@ -0,0 +1,26 @@
+#include "uscxml/config.h"
+#include "uscxml/Message.h"
+#include "uscxml/concurrency/tinythread.h"
+#include <assert.h>
+#include <boost/algorithm/string.hpp>
+#include <iostream>
+#include "uscxml/plugins/invoker/filesystem/dirmon/DirMonInvoker.h"
+
+using namespace uscxml;
+using namespace boost;
+
+class Watcher : public DirectoryWatchMonitor {
+ void handleChanges(DirectoryWatch::Action action, const std::string dir, const std::string file, struct stat fileStat) {
+ std::cout << "Monitor on " << dir << ": " << action << " for " << file << std::endl;
+ }
+};
+
+int main(int argc, char** argv) {
+
+ Watcher watcher;
+ DirectoryWatch* dw = new DirectoryWatch("/Users/sradomski/Desktop/tmp", true);
+ dw->addMonitor(&watcher);
+ while(true) {
+ dw->updateEntries();
+ }
+} \ No newline at end of file