diff options
Diffstat (limited to 'apps/samples/vrml/vrml-server.caching.scxml')
-rw-r--r-- | apps/samples/vrml/vrml-server.caching.scxml | 416 |
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 && 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 && isNumber(query.pitch)) ? query.pitch : 0); + struct.roll = (('roll' in query && isNumber(query.roll)) ? query.roll : 0); + struct.yaw = (('yaw' in query && isNumber(query.yaw)) ? query.yaw : 0); + struct.zoom = (('zoom' in query && isNumber(query.zoom)) ? query.zoom : 1); + struct.x = (('x' in query && isNumber(query.x)) ? query.x : 0); + struct.y = (('y' in query && isNumber(query.y)) ? query.y : 0); + struct.z = (('z' in query && isNumber(query.z)) ? query.z : 0); + struct.width = (('width' in query && isNumber(query.width)) ? query.width : 640); + struct.height = (('height' in query && isNumber(query.height)) ? query.height : 480); + struct.autorotate = (('autorotate' in query && (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)) && 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 < 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 && + (!(_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 && + _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 && isSupportedFormat(_event['fileStruct'].ext)"> + <!-- There is such a file available as osgb --> + <if cond=" + _event['fileStruct'].key in processed && + _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 && + \'' + _event['fileStruct'].format + '\'' + ' in processed[\'' + _event['fileStruct'].key + '\'] || + _event.name === \'convert.failure\' && _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 && + _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 && + _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 && + _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 |