summaryrefslogtreecommitdiffstats
path: root/apps/samples/vrml/vrml-server.caching.scxml
diff options
context:
space:
mode:
Diffstat (limited to 'apps/samples/vrml/vrml-server.caching.scxml')
-rw-r--r--apps/samples/vrml/vrml-server.caching.scxml416
1 files changed, 416 insertions, 0 deletions
diff --git a/apps/samples/vrml/vrml-server.caching.scxml b/apps/samples/vrml/vrml-server.caching.scxml
new file mode 100644
index 0000000..70a7c3a
--- /dev/null
+++ b/apps/samples/vrml/vrml-server.caching.scxml
@@ -0,0 +1,416 @@
+<scxml datamodel="ecmascript" name="vrml">
+ <script src="http://uscxml.tk.informatik.tu-darmstadt.de/scripts/dump.js" />
+ <script src="http://uscxml.tk.informatik.tu-darmstadt.de/scripts/string.endsWith.js" />
+ <script src="http://uscxml.tk.informatik.tu-darmstadt.de/scripts/array.last.js" />
+ <script>
+ var wrls = {}; // information of the wrl, vrml files
+ var models = {}; // information of the osgb files
+ var processed = {}; // information about processed files
+
+ 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
+ '_(off|on)' + // autorotate
+ ')\\.\\w+$'); // end
+
+ /**
+ * 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];
+ var group = models[key].group
+ var name = model.strippedName.split(pathDelim).last();
+ var entry = assign(struct, ['models'].concat(group.substr(1).split('/')).concat(name), {});
+ entry.url =
+ _ioprocessors['http'].location + model.relDir + model.strippedName.split(pathDelim).join('/') + '.png';
+ entry.path = model.relDir + model.strippedName.split(pathDelim).join('/') + '.png';
+ }
+ return struct;
+ }
+
+ // check whether a given string represents a number
+ function isNumber(n) {
+ return !isNaN(parseFloat(n)) &amp;&amp; isFinite(n);
+ }
+
+ // allow to set deep keys in an object
+ function assign(obj, path, value) {
+ if (typeof path === 'string')
+ path = path.split('.');
+ if (!(path instanceof Array))
+ return undefined;
+
+ lastKeyIndex = path.length-1;
+ for (var i = 0; i &lt; lastKeyIndex; ++ i) {
+ key = path[i];
+ if (key.length == 0)
+ continue;
+ if (!(key in obj))
+ obj[key] = {}
+ obj = obj[key];
+ }
+ obj[path[lastKeyIndex]] = value;
+ return obj[path[lastKeyIndex]];
+ }
+ </script>
+ <state id="main">
+ <!-- Stop processing if no vrml-path was given on command line -->
+ <transition target="final" cond="_x['args']['vrml-path'] == undefined || _x['args']['vrml-path'].length == 0">
+ <log expr="'No --vrml-path given'" />
+ </transition>
+
+ <!-- Stop processing if no tmp-path was given on command line -->
+ <transition target="final" cond="_x['args']['tmp-path'] == undefined || _x['args']['tmp-path'].length == 0">
+ <log expr="'No --tmp-path given'" />
+ </transition>
+
+ <!-- Stop processing if any error occurs -->
+ <transition target="final" event="error">
+ <log expr="'An error occured:'" />
+ <script>dump(_event);</script>
+ </transition>
+
+ <!-- Start the directory monitor for generated files -->
+ <invoke type="dirmon" id="dirmon.processed">
+ <param name="dir" expr="_x['args']['tmp-path']" />
+ <param name="recurse" expr="false" />
+ <param name="reportExisting" expr="true" />
+ <!-- Called for every file we found -->
+ <finalize>
+ <script>
+ _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;
+ models[_event.fileStruct.key].group = '/' + _event.data.file.name.split(pathDelim).slice(0,-1).join('/');
+ 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.deleted") {
+ delete processed[_event.fileStruct.key][_event.fileStruct.format];
+ print("Removed a vanished processed file at " + _event.fileStruct.key + "\n");
+ } else {
+ 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 {
+ 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>
+ //dump(_event);
+ </script>
+ <!-- <file operation="write" contentexpr="_event.data.content" sandbox="off" urlexpr="_event.data.dest" /> -->
+ </finalize>
+ <!--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>
+
+ <!-- Start a nested SCXML interpreter to create movies from the images -->
+ <!-- <invoke type="scxml" id="scxml.ffmpeg" src="ffmpeg-server.scxml" autoforward="true">
+ <param name="modelDir" expr="_x['args']['tmp-path']" />
+ </invoke> -->
+
+ <!-- Idle here -->
+ <state id="idle">
+ <!--onentry>
+ <log expr="_event" />
+ </onentry -->
+ <transition event="http.get" 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>
+ _event.fileStruct = reqToStruct(_event.data);
+ _event.dest = _x['args']['tmp-path'] + '/' + _event['fileStruct'].key + '-' + _event['fileStruct'].format;
+ print("Got a request for [" + _event['fileStruct'].key + '-' + _event['fileStruct'].format + "]\n");
+// dump(_event);
+ </script>
+ <if cond="_event['fileStruct'].key in models &amp;&amp; isSupportedFormat(_event['fileStruct'].ext)">
+ <!-- There is such a file available as osgb -->
+ <if cond="
+ _event['fileStruct'].key in processed &amp;&amp;
+ _event['fileStruct'].format in processed[_event['fileStruct'].key]">
+ <script>
+ //print("Sending " + processed[_event['fileStruct'].key][_event['fileStruct'].format].path + "\n");
+ </script>
+ <respond status="200" to="_event.origin">
+ <header name="Connection" value="close" />
+ <header name="Access-Control-Allow-Origin" value="*" />
+ <content fileexpr="processed[_event['fileStruct'].key][_event['fileStruct'].format].path" />
+ </respond>
+ <else />
+ <if cond="_event.name.endsWith('postponed')">
+ <!--
+ A postponed event we couldn't answer
+ -->
+ <respond status="404" to="_event.origin">
+ <header name="Connection" value="close" />
+ </respond>
+ <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'] + '\''
+ "/>
+ </if>
+ </if>
+ <else />
+ <!-- There is no such model -->
+ <respond status="404" to="_event.origin">
+ <header name="Connection" value="close" />
+ </respond>
+ </if>
+ </transition>
+
+ <!--
+ process request for JSON datastructures
+ -->
+ <transition event="http.get" target="idle" cond="
+ _event.data.pathComponent.length == 2 &amp;&amp;
+ _event.data.pathComponent[1] === 'models'">
+ <script>//dump(_event)</script>
+ <respond status="200" to="_event.origin">
+ <header name="Connection" value="close" />
+ <header name="Content-Type" value="application/json" />
+ <header name="Access-Control-Allow-Origin" value="*" />
+ <content expr="models" />
+ </respond>
+ </transition>
+
+ <transition event="http.get" target="idle" cond="
+ _event.data.pathComponent.length == 2 &amp;&amp;
+ _event.data.pathComponent[1] === 'processed'">
+ <script>//dump(_event)</script>
+ <respond status="200" to="_event.origin">
+ <header name="Connection" value="close" />
+ <header name="Content-Type" value="application/json" />
+ <header name="Access-Control-Allow-Origin" value="*" />
+ <content expr="processed" />
+ </respond>
+ </transition>
+
+ <transition event="http.get" target="idle" cond="
+ _event.data.pathComponent.length == 2 &amp;&amp;
+ _event.data.pathComponent[1] === 'wrls'">
+ <script>//dump(_event)</script>
+ <respond status="200" to="_event.origin">
+ <header name="Connection" value="close" />
+ <header name="Content-Type" value="application/json" />
+ <header name="Access-Control-Allow-Origin" value="*" />
+ <content expr="wrls" />
+ </respond>
+ </transition>
+
+ <!-- request for topmost list of all files -->
+ <transition event="http.get" target="idle" cond="
+ _event.data.pathComponent.length == 1">
+ <script>//dump(_event);</script>
+ <respond status="200" to="_event.origin">
+ <header name="Connection" value="close" />
+ <header name="Content-Type" value="application/json" />
+ <header name="Access-Control-Allow-Origin" value="*" />
+ <content expr="overviewList()" />
+ </respond>
+ </transition>
+
+ <!-- XHR CORS preflight response -->
+ <transition event="http.options" target="idle">
+ <script>//dump(_event);</script>
+ <respond status="200" to="_event.origin">
+ <header name="Access-Control-Allow-Origin" value="*" />
+ <header name="Access-Control-Allow-Methods" value="GET, OPTIONS" />
+ <header name="Access-Control-Allow-Headers" value="X-Requested-With" />
+ </respond>
+ </transition>
+
+ <transition event="render.done" target="idle">
+ <script>dump(_event);</script>
+ <respond status="200" to="_event.data.context">
+ <header name="Connection" value="close" />
+ <header name="Content-Type" valueexpr="_event.data.mimetype" />
+ <header name="Content-Disposition" valueexpr="'attachment; filename=' + _event.data.filename" />
+ <content expr="_event.data.movie" />
+ </respond>
+ </transition>
+
+ </state>
+ </state>
+ <state id="final" final="true" />
+</scxml> \ No newline at end of file