diff options
author | Stefan Radomski <radomski@tk.informatik.tu-darmstadt.de> | 2013-03-17 12:11:03 (GMT) |
---|---|---|
committer | Stefan Radomski <radomski@tk.informatik.tu-darmstadt.de> | 2013-03-17 12:11:03 (GMT) |
commit | ccbf595c52fd705ec70abc774a29b153a7281334 (patch) | |
tree | 81b698cf9b2290a6905d12c71ef62e88a8b1c1cc | |
parent | 81079295b8be14128b7e532d504b32280360532e (diff) | |
download | uscxml-ccbf595c52fd705ec70abc774a29b153a7281334.zip uscxml-ccbf595c52fd705ec70abc774a29b153a7281334.tar.gz uscxml-ccbf595c52fd705ec70abc774a29b153a7281334.tar.bz2 |
Fixed http responses and added miles invoker
22 files changed, 1402 insertions, 87 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index d4697a7..04261f2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -806,16 +806,20 @@ endif() # MILES modality components -#find_package(MILES COMPONENTS core audio debug) -if (MILES_FOUND AND OFF) +find_package(MILES) +if (MILES_FOUND) include_directories(${MILES_INCLUDE_DIR}) # openal is only needed for miles find_package(OpenAL REQUIRED) + find_package(JPEG REQUIRED) + list (APPEND MILES_LIBRARIES "iconv") + list (APPEND MILES_LIBRARIES ${JPEG_LIBRARIES}) include_directories(${OPENAL_INCLUDE_DIR}) - file(GLOB_RECURSE MILES_INVOKER src/uscxml/invoker/modality/miles/*.cpp src/uscxml/invoker/modality/miles/*.h) + file(GLOB_RECURSE MILES_INVOKER src/uscxml/plugins/invoker/miles/*.cpp src/uscxml/plugins/invoker/miles/*.h) source_group("Invoker\\miles" FILES ${MILES_INVOKER}) +# message("MILES_INVOKER ${MILES_INVOKER}") if (BUILD_AS_PLUGINS) add_library( diff --git a/apps/samples/miles-connect.html b/apps/samples/miles-connect.html new file mode 100644 index 0000000..ae5f77e --- /dev/null +++ b/apps/samples/miles-connect.html @@ -0,0 +1,52 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html> +<head> + <meta http-equiv="Content-Type" content="text/html;charset=utf-8" /> + <link rel="stylesheet" href="http://ajax.googleapis.com/ajax/libs/dojo/1.8.3/dijit/themes/tundra/tundra.css"> + <link rel="stylesheet" href="http://ajax.googleapis.com/ajax/libs/dojo/1.8.3/dojox/layout/resources/FloatingPane.css"> + <link rel="stylesheet" href="http://ajax.googleapis.com/ajax/libs/dojo/1.8.3/dojox/layout/resources/ResizeHandle.css"> + + <style type="text/css"> + </style> + + <script type="text/javascript"> + // dojoConfig = { + // async : false, + // isDebug : true, + // debugAtAllCosts : true, + // } + </script> + + <script src="http://ajax.googleapis.com/ajax/libs/dojo/1.8.3/dojo/dojo.js"></script> + <script type="text/javascript" src="viewer.js"></script> + + <script type="text/javascript"> + require(["dojo/domReady!", "dojo"], function(dom, dojo) { + var jsonHere = { + "email": "someone@here.com", + "reflectorIP": "88.131.107.12", + "problemName": "Fancy problem" + } + var xhr = dojo.require("dojo/_base/xhr"); + xhr.post({ + // The URL to request + url: "http://localhost:8080/miles/connect", + handleAs:"json", + postData: dojo.toJson(jsonHere), + headers:{ + "X-Requested-With": null, + "Content-Type": "application/json", + }, + load: function(result) { + + } + }); + }); + </script> + </head> + <body class="tundra"> + <div style="width: 600px; height: 400px"> + <div id="scene1"></div> + </div> + </body> +</html> diff --git a/apps/samples/miles-disconnect.html b/apps/samples/miles-disconnect.html new file mode 100644 index 0000000..30f5b39 --- /dev/null +++ b/apps/samples/miles-disconnect.html @@ -0,0 +1,52 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html> +<head> + <meta http-equiv="Content-Type" content="text/html;charset=utf-8" /> + <link rel="stylesheet" href="http://ajax.googleapis.com/ajax/libs/dojo/1.8.3/dijit/themes/tundra/tundra.css"> + <link rel="stylesheet" href="http://ajax.googleapis.com/ajax/libs/dojo/1.8.3/dojox/layout/resources/FloatingPane.css"> + <link rel="stylesheet" href="http://ajax.googleapis.com/ajax/libs/dojo/1.8.3/dojox/layout/resources/ResizeHandle.css"> + + <style type="text/css"> + </style> + + <script type="text/javascript"> + // dojoConfig = { + // async : false, + // isDebug : true, + // debugAtAllCosts : true, + // } + </script> + + <script src="http://ajax.googleapis.com/ajax/libs/dojo/1.8.3/dojo/dojo.js"></script> + <script type="text/javascript" src="viewer.js"></script> + + <script type="text/javascript"> + require(["dojo/domReady!", "dojo"], function(dom, dojo) { + var jsonHere = { + "email": "someone@here.com", + "reflectorIP": "88.131.107.12", + "problemName": "Fancy problem" + } + var xhr = dojo.require("dojo/_base/xhr"); + xhr.post({ + // The URL to request + url: "http://localhost:8080/miles/disconnect", + handleAs:"json", + postData: dojo.toJson(jsonHere), + headers:{ + "X-Requested-With": null, + "Content-Type": "application/json", + }, + load: function(result) { + + } + }); + }); + </script> + </head> + <body class="tundra"> + <div style="width: 600px; height: 400px"> + <div id="scene1"></div> + </div> + </body> +</html> diff --git a/apps/samples/miles.scxml b/apps/samples/miles.scxml new file mode 100644 index 0000000..4eef0eb --- /dev/null +++ b/apps/samples/miles.scxml @@ -0,0 +1,43 @@ +<scxml name="miles" datamodel="ecmascript"> + <script src="http://uscxml.tk.informatik.tu-darmstadt.de/scripts/dump.js" /> + <state id="main"> + <invoke type="miles" id="miles"> + <param name="foo" expr="'asdf'" /> + <finalize> + <script> + </script> + </finalize> + </invoke> + + <state id="idle"> + <transition event="http.post" target="idle"> + <script>dump(_event);</script> + <if cond="_event.data.pathComponent[1] === 'session'"> + <response status="200" requestexpr="_event.origin" /> + + <elseif cond="_event.data.pathComponent[1] === 'connect'"> + <send target="#_miles" event="connect"> + <param name="reflectorIP" expr="_event.data.content.reflectorIP" /> + <param name="email" expr="_event.data.content.email" /> + <param name="problemName" expr="_event.data.content.problemName" /> + </send> + <response status="200" requestexpr="_event.origin" /> + + </elseif> + <elseif cond="_event.data.pathComponent[1] === 'disconnect'"> + <send target="#_miles" event="disconnect"> + <param name="reflectorIP" expr="_event.data.content.reflectorIP" /> + <param name="problemName" expr="_event.data.content.problemName" /> + </send> + <response status="200" requestexpr="_event.origin" /> + + </elseif> + <else> + <response status="404" requestexpr="_event.origin" /> + + </else> + </if> + </transition> + </state> + </state> +</scxml>
\ No newline at end of file diff --git a/apps/samples/viewer.html b/apps/samples/viewer.html new file mode 100644 index 0000000..4cf971b --- /dev/null +++ b/apps/samples/viewer.html @@ -0,0 +1,48 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html> +<head> + <meta http-equiv="Content-Type" content="text/html;charset=utf-8" /> + <link rel="stylesheet" href="http://ajax.googleapis.com/ajax/libs/dojo/1.8.3/dijit/themes/tundra/tundra.css"> + <link rel="stylesheet" href="http://ajax.googleapis.com/ajax/libs/dojo/1.8.3/dojox/layout/resources/FloatingPane.css"> + <link rel="stylesheet" href="http://ajax.googleapis.com/ajax/libs/dojo/1.8.3/dojox/layout/resources/ResizeHandle.css"> + + <style type="text/css"> + .alternateDock { + position:absolute; + background-color:#ededed; + right:0px; top:0px; + border-left:1px solid #ccc; + height:100%; + + } + #alternateDock ul.dojoxDockList { display:block; } + .testFixedSize { + width:300px; + height:200px; + padding:7px; + } + </style> + + <script type="text/javascript"> + // dojoConfig = { + // async : false, + // isDebug : true, + // debugAtAllCosts : true, + // } + </script> + + <script src="http://ajax.googleapis.com/ajax/libs/dojo/1.8.3/dojo/dojo.js"></script> + <script type="text/javascript" src="viewer.js"></script> + + <script type="text/javascript"> + require(["dojo/domReady!", "dojo"], function(dom, dojo) { + var viewer = new VRMLViewer(dojo.byId("scene1"), { 'dojo': dojo }); + }); + </script> + </head> + <body class="tundra"> + <div style="width: 600px; height: 400px"> + <div id="scene1"></div> + </div> + </body> +</html> diff --git a/apps/samples/viewer.js b/apps/samples/viewer.js new file mode 100644 index 0000000..dac3a96 --- /dev/null +++ b/apps/samples/viewer.js @@ -0,0 +1,386 @@ +function VRMLViewer(element, params) { + + // private attributes + var self = this; + var dojo = require("dojo"); + var domConst = dojo.require('dojo/dom-construct'); + var xhr = dojo.require("dojo/_base/xhr"); + + require(["dojox/storage"], function (storage) { + self.localStorage = dojox.storage.manager.getProvider(); + self.localStorage.initialize(); + var savedServerURL = self.localStorage.get("vrmlServer"); + if (savedServerURL) { + self.serverURL = savedServerURL; + self.serverBox.set('value', savedServerURL); + } + }); + + /** + * Why can't I fetch dijits via CommonJS? + */ + // var Button = require('dijit/form/Button'); + // var HorizontalSlider = require('dijit/form/HorizontalSlider'); + // var VerticalSlider = require('dijit/form/VerticalSlider'); + + + // private instanceId + if (!VRMLViewer.instances) + VRMLViewer.instances = 0; + var instanceId = VRMLViewer.instances++; + + // public attributes + this.pitch = 0; + this.roll = 0; + this.yaw = 0; + this.zoom = 1; + this.x = 0; + this.y = 0; + this.z = 0; + this.width = 640; + this.height = 480; + this.autorotate = false; + + this.serverURL = "http://88.69.49.213:8080/vrml"; + this.imageURL; + + view = "normal"; + if (params.view == "maximized") { + view = "maximized"; + } + + // if (this.view == "maximized") { + // this.width = Math.min(element.clientWidth - 150, 800); + // this.height = (this.width * 3) / 4; + // } else { + // this.width = Math.min(element.clientWidth - 50, 800); + // this.height = (this.width * 3) / 4; + // } + + // privileged public methods + this.updateScene = function() { + if (self.imageURL) { + self.imgElem.src = self.imageURL + + '?width=' + self.width + + '&height=' + self.height + + '&pitch=' + self.pitch + + '&roll=' + self.roll + + '&yaw=' + self.yaw + + '&x=' + self.x + + '&y=' + self.y + + '&z=' + self.z + + '&zoom=' + self.zoom + + '&autorotate=' + (self.autorotate ? '1' : '0'); + } + } + + this.refreshServer = function(server) { + serverURL = server; + self.localStorage.put("vrmlServer", serverURL, null); + xhr.get({ + // The URL to request + url: server, + handleAs:"json", + headers:{"X-Requested-With":null}, + load: function(result) { + (function fillstore(tree, parentId) { + for (key in tree) { + if ('url' in tree[key]) { + self.fileStore.add({id:parentId+key, name:key, url:self.serverURL + tree[key].path, parent:parentId}); +// self.messageBox.innerHTML += '<pre>' + self.serverURL + tree[key].path + '</pre>'; +// self.messageBox.innerHTML += '<pre>' + tree[key].url + '?width=200&height=150</pre>' + '<img src="' + tree[key].url + '?width=200&height=150" />'; + } else { + self.fileStore.add({id:parentId+key, name:key, parent:parentId}); + fillstore(tree[key], parentId+key); + } + } + } (result.models, "root", "")); + } + }); + } + + // establish our dom + element.appendChild(domConst.toDom('\ + <div id="floatPane">\ + <div style="text-align: right"><div class="server" /></div><button type="button" class="browseButton"></button></div>\ + <div style="height: 100%; overflow: auto" class="fileList"></div>\ + </div>\ + <table>\ + <tr>\ + <td valign="top">\ + <div style="position: relative; padding: 0px">\ + <img class="model" style="z-index: -1; min-width: ' + self.width + 'px; min-height: ' + self.height + 'px"></img>\ + <div style="position: absolute; left: 10px; top: 7%; height: 100%">\ + <div class="pitchSlide"></div>\ + </div>\ + <div style="position: absolute; right: 10px; top: 15%; height: 50%">\ + <div class="zoomSlide"></div>\ + </div>\ + <div style="position: absolute; left: 7%; top: 10px; width: 100%">\ + <div class="rollSlide"></div>\ + </div>\ + <div style="position: absolute; left: 7%; bottom: 15px;">\ + <div class="yawSlide"></div>\ + </div>\ + <table cellspacing="0" style="position: absolute; right: 5px; bottom: 25px">\ + <tr>\ + <td align="right">x: <input type="text" class="xSpinner"></input></td>\ + </tr>\ + <tr>\ + <td align="right">y: <input type="text" class="ySpinner"></input></td>\ + </tr>\ + <tr>\ + <td align="right"><button type="button" class="resetButton"></button></td>\ + </tr>\ + </table>\ + </div>\ + </td>\ + <td valign="top" height="100%">\ + </td>\ + </tr>\ + <tr>\ + <td colspan="2"><div class="messages"></div></td>\ + </tr>\ + </table>\ + ')); + + // fetch special dom nodes for content + this.messageBox = dojo.query("div.messages", element)[0]; + this.imgElem = dojo.query("img.model", element)[0]; + this.serverBoxElem = dojo.query("div.server", element)[0]; + this.browseButtonElem = dojo.query("button.browseButton", element)[0]; + this.fileListElem = dojo.query("div.fileList", element)[0]; + + this.resetButtonElem = dojo.query("button.resetButton", element)[0]; + this.xSpinnerElem = dojo.query("input.xSpinner", element)[0]; + this.ySpinnerElem = dojo.query("input.ySpinner", element)[0]; + this.pitchSlideElem = dojo.query("div.pitchSlide", element)[0]; + this.rollSlideElem = dojo.query("div.rollSlide", element)[0]; + this.yawSlideElem = dojo.query("div.yawSlide", element)[0]; + this.zoomSlideElem = dojo.query("div.zoomSlide", element)[0]; + + require(["dojox/layout/FloatingPane"], function(FloatingPane) { + self.floatPane = new FloatingPane({ + title: "VRML Viewer", + resizable: true, dockable: false, closable: false, + style: "position:absolute;top:10;left:10;width:250px;height:300px;z-index: 2", + id: "floatPane", + }, dojo.byId("floatPane")); + + self.floatPane.startup(); + }); + + // require(['dijit/form/Button','dijit/Dialog'], + // function (Button) { + // var d = new Dialog({ + // 'title':'I am nonmodal', + // 'class':'nonModal' + // }); + // }); + + // setup fileStore for tree list + require(["dojo/store/Memory", "dojo/store/Observable", "dijit/tree/ObjectStoreModel"], + function(Memory, Observable, ObjectStoreModel){ + self.fileStore = new Observable(new Memory({ + data: [ { id: 'root', name:'3D Models'} ], + getChildren: function(object){ + return this.query({parent: object.id}); + } + })); + self.fileTreeModel = new ObjectStoreModel({ + store: self.fileStore, + query: { id: "root" } + }); + }); + + // setup actual tree dijit + require(["dojo/dom", "dojo/store/Memory", "dojo/store/Observable", "dijit/tree/ObjectStoreModel", "dijit/Tree"], + function(dom, Memory, Observable, ObjectStoreModel, Tree) { + self.fileList = new dijit.Tree({ + id: "fileList", + model: self.fileTreeModel, + persist: false, + showRoot: false, + onClick: function(item){ +// self.messageBox.innerHTML = '<pre>' + item.url + '?width=200&height=150</pre>' + '<img src="' + item.url + '?width=200&height=150" />'; + if ('url' in item) { + self.imageURL = item.url; + self.updateScene(); + } + }, + getIconClass: function(item, opened) { + return (!item || !('url' in item)) ? (opened ? "dijitFolderOpened" : "dijitFolderClosed") : "dijitLeaf" + }, + getIconStyle: function(item, opened){ + if('url' in item) { + return { backgroundImage: "url('" + item.url + "?width=16&height=16')"}; + } + } + //return {backgroundImage: "url('" + item.url + "?width=16&height=16')"}; + + }).placeAt(self.fileListElem); +// tree.dndController.singular = true; + }); + + require(["dijit/form/TextBox"], function(TextBox) { + self.serverBox = new TextBox({ + name: "Server", + value: self.serverURL, + style: "width: 70%", + + onKeyDown: function(e) { + var code = e.keyCode || e.which; + if( code === 13 ) { + e.preventDefault(); + self.refreshServer(this.get("value")); + return false; + } + }, + }, self.serverBoxElem); + }); + + require(["dijit/form/Button"], function(Button) { + self.resetButton = new Button({ + label: "Reset", + onClick: function(){ + self.xSpinner.set('value',0); + self.ySpinner.set('value',0); + self.zSpinner.set('value',0); + self.pitchSlide.attr('value',0); + self.rollSlide.attr('value',0); + self.yawSlide.attr('value',0); + self.zoomSlide.attr('value',1); + + self.floatPane.startup(); + self.floatPane.show(); + } + }, self.resetButtonElem); + }); + + require(["dijit/form/NumberSpinner"], function(NumberSpinner) { + self.xSpinner = new NumberSpinner({ + value: 0, + smallDelta: 1, + constraints: { places:0 }, + style: "width:60px", + onChange: function(value){ + self.x = value; + self.updateScene(); + } + }, self.xSpinnerElem ); + }); + + require(["dijit/form/NumberSpinner"], function(NumberSpinner) { + self.ySpinner = new NumberSpinner({ + value: 0, + smallDelta: 1, + constraints: { places:0 }, + style: "width:60px", + onChange: function(value){ + self.y = value; + self.updateScene(); + } + }, self.ySpinnerElem ); + }); + + require(["dijit/form/NumberSpinner"], function(NumberSpinner) { + self.zSpinner = new NumberSpinner({ + value: 0, + smallDelta: 1, + constraints: { places:0 }, + style: "width:60px", + onChange: function(value){ + self.z = value; + self.updateScene(); + } + }, self.zSpinnerElem ); + }); + + require(["dijit/form/Button"], function(Button) { + self.browseButton = new Button({ + label: "Browse", + onClick: function(){ + self.refreshServer(self.serverBox.get("value")); + } + }, self.browseButtonElem); + }); + + // add zoom slider + require(["dijit/form/VerticalSlider"], function(VerticalSlider) { + self.zoomSlide = new VerticalSlider({ + minimum: 0.001, + showButtons: false, + maximum: 1, + value: 1, + intermediateChanges: false, + style: "height: 90%", + onChange: function(value){ + self.zoom = Math.ceil(value * 1000) / 1000; + self.updateScene(); + } + }, self.zoomSlideElem); + }); + + // add pitch slider + require(["dijit/form/VerticalSlider", "dijit/form/VerticalRuleLabels", "dijit/form/VerticalRule"], function(VerticalSlider, VerticalRuleLabels, VerticalRule) { + // Create the rules + // var rulesNode = dojo.create("div", {}, self.pitchSlideElem, "first"); + // var sliderRules = new VerticalRule({ + // container: "leftDecoration", + // count: 11, + // style: "width: 5px;"}, rulesNode); + + // Create the labels + // var labelsNode = dojo.create( + // "div", {}, self.pitchSlideElem, "first"); + // var sliderLabels = new VerticalRuleLabels({ + // labels: ["Pitch", ""], + // container: "rightDecoration", + // labelStyle: "-webkit-transform: rotate(-90deg); -moz-transform: rotate(-90deg); padding-left: -3px; font-size: 0.75em" + // }, labelsNode); + + self.pitchSlide = new VerticalSlider({ + minimum: 0, + showButtons: false, + maximum: 2 * 3.14159, + value: 0, + intermediateChanges: false, + style: "height: 90%", + onChange: function(value){ + self.pitch = Math.ceil(value * 100) / 100; + self.updateScene(); + } + }, self.pitchSlideElem); + }); + + // add roll slider + require(["dijit/form/HorizontalSlider"], function(HorizontalSlider) { + self.rollSlide = new HorizontalSlider({ + minimum: 0, + showButtons: false, + maximum: 2 * 3.14159, + intermediateChanges: false, + style: "width: 90%", + onChange: function(value){ + self.roll = Math.ceil(value * 100) / 100; + self.updateScene(); + } + }, self.rollSlideElem); + }); + + // add yaw slider + require(["dijit/form/HorizontalSlider"], function(HorizontalSlider) { + self.yawSlide = new HorizontalSlider({ + minimum: 0, + showButtons: false, + maximum: 2 * 3.14159, + intermediateChanges: false, + style: "width: 90%", + onChange: function(value){ + self.yaw = Math.ceil(value * 100) / 100; + self.updateScene(); + } + }, self.yawSlideElem); + }); + +} diff --git a/apps/samples/vrml-server.scxml b/apps/samples/vrml-server.scxml index 7c257f5..c5647b0 100644 --- a/apps/samples/vrml-server.scxml +++ b/apps/samples/vrml-server.scxml @@ -117,8 +117,12 @@ 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'; + 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; } @@ -126,15 +130,45 @@ /** * Provide an endsWith function for the string prototype */ - String.prototype.endsWith = function(suffix) { - return this.indexOf(suffix, this.length - suffix.length) !== -1; - }; + if(!Array.prototype.last) { + String.prototype.endsWith = function(suffix) { + return this.indexOf(suffix, this.length - suffix.length) !== -1; + } + } + + /** + * Provide last() for arrays + */ + if(!Array.prototype.last) { + Array.prototype.last = function() { + return this[this.length - 1]; + } + } // 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 --> @@ -171,6 +205,7 @@ 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"); } @@ -236,7 +271,7 @@ <!-- Idle here --> <state id="idle"> - <transition event="http" target="idle" cond=" + <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 @@ -254,9 +289,11 @@ _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"); + //print("Sending " + processed[_event['fileStruct'].key][_event['fileStruct'].format].path + "\n"); </script> <response status="200" requestexpr="_event.origin"> + <header name="Connection" value="close" /> + <header name="Access-Control-Allow-Origin" value="*" /> <content fileexpr="processed[_event['fileStruct'].key][_event['fileStruct'].format].path" /> </response> <else> @@ -264,7 +301,9 @@ <!-- A postponed event we couldn't answer --> - <response status="404" requestexpr="_event.origin" /> + <response status="404" requestexpr="_event.origin"> + <header name="Connection" value="close" /> + </response> <else> <script> print("Processing outfile " + _event['dest'] + " from model " + _event['file'] + "\n"); @@ -299,7 +338,9 @@ </if> <else> <!-- There is no such model --> - <response status="404" requestexpr="_event.origin" /> + <response status="404" requestexpr="_event.origin"> + <header name="Connection" value="close" /> + </response> </else> </if> </transition> @@ -307,45 +348,63 @@ <!-- process request for JSON datastructures --> - <transition event="http" target="idle" cond=" + <transition event="http.get" target="idle" cond=" _event.data.pathComponent.length == 2 && _event.data.pathComponent[1] === 'models'"> <script>//dump(_event)</script> <response status="200" requestexpr="_event.origin"> + <header name="Connection" value="close" /> <header name="Content-Type" value="application/json" /> + <header name="Access-Control-Allow-Origin" value="*" /> <content expr="models" /> </response> </transition> - <transition event="http" target="idle" cond=" + <transition event="http.get" target="idle" cond=" _event.data.pathComponent.length == 2 && _event.data.pathComponent[1] === 'processed'"> <script>//dump(_event)</script> <response status="200" requestexpr="_event.origin"> + <header name="Connection" value="close" /> <header name="Content-Type" value="application/json" /> + <header name="Access-Control-Allow-Origin" value="*" /> <content expr="processed" /> </response> </transition> - <transition event="http" target="idle" cond=" + <transition event="http.get" target="idle" cond=" _event.data.pathComponent.length == 2 && _event.data.pathComponent[1] === 'wrls'"> <script>//dump(_event)</script> <response status="200" requestexpr="_event.origin"> + <header name="Connection" value="close" /> <header name="Content-Type" value="application/json" /> + <header name="Access-Control-Allow-Origin" value="*" /> <content expr="wrls" /> </response> </transition> <!-- request for topmost list of all files --> - <transition event="http" target="idle" cond=" + <transition event="http.get" target="idle" cond=" _event.data.pathComponent.length == 1"> <script>//dump(_event);</script> <response status="200" requestexpr="_event.origin"> + <header name="Connection" value="close" /> <header name="Content-Type" value="application/json" /> + <header name="Access-Control-Allow-Origin" value="*" /> <content expr="overviewList()" /> </response> </transition> + + <!-- XHR CORS preflight response --> + <transition event="http.options" target="idle"> + <script>dump(_event);</script> + <response status="200" requestexpr="_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" /> + </response> + </transition> </state> </state> diff --git a/contrib/cmake/FindMiles.cmake b/contrib/cmake/FindMiles.cmake index d868794..f86e695 100644 --- a/contrib/cmake/FindMiles.cmake +++ b/contrib/cmake/FindMiles.cmake @@ -57,54 +57,100 @@ FIND_PATH(MILES_INCLUDE_DIR miles/miles.h # RelWithDebInfo. ################################################### SET(MILES_LIBRARIES) -foreach (_MILES_COMPONENT ${_MILES_COMPONENTS_TO_PROCESS}) - SET(_CURR_COMPONENT "MILES_${_MILES_COMPONENT}_LIBRARY") - STRING(TOLOWER ${_MILES_COMPONENT}${_MILES_64BIT_LIB_POSTFIX} _MILES_COMPONENT_LC) - # prefer MinSizeRel libraries - FIND_LIBRARY(${_CURR_COMPONENT} +list(LENGTH MILES_FIND_COMPONENTS MILES_NR_COMPONENTS) +if (MILES_NR_COMPONENTS GREATER 0) + foreach (_MILES_COMPONENT ${_MILES_COMPONENTS_TO_PROCESS}) + SET(_CURR_COMPONENT "MILES_${_MILES_COMPONENT}_LIBRARY") + STRING(TOLOWER ${_MILES_COMPONENT}${_MILES_64BIT_LIB_POSTFIX} _MILES_COMPONENT_LC) + + # prefer MinSizeRel libraries + FIND_LIBRARY(${_CURR_COMPONENT} + PATH_SUFFIXES lib + NAMES miles_${_MILES_COMPONENT_LC}_s + PATHS ${_MILES_LIB_SEARCHPATH} + ENV MILES_LIB_DIR + ) + if (${_CURR_COMPONENT}) + list(APPEND MILES_LIBRARIES optimized ${${_CURR_COMPONENT}}) + else() + # if no minsize libraries were found try normal release + FIND_LIBRARY(${_CURR_COMPONENT} + PATH_SUFFIXES lib + NAMES miles_${_MILES_COMPONENT_LC} + PATHS ${_MILES_LIB_SEARCHPATH} + ENV MILES_LIB_DIR + ) + if (${_CURR_COMPONENT}) + list(APPEND MILES_LIBRARIES optimized ${${_CURR_COMPONENT}}) + endif() + endif() + + # prefer RelWithDebInfo libraries + FIND_LIBRARY(${_CURR_COMPONENT}_DEBUG + PATH_SUFFIXES lib + NAMES miles_${_MILES_COMPONENT_LC}_rd + PATHS ${_MILES_LIB_SEARCHPATH} + ENV MILES_LIB_DIR + ) + if (${_CURR_COMPONENT}_DEBUG) + list(APPEND MILES_LIBRARIES debug ${${_CURR_COMPONENT}_DEBUG}) + else() + FIND_LIBRARY(${_CURR_COMPONENT}_DEBUG + PATH_SUFFIXES lib + NAMES miles_${_MILES_COMPONENT_LC}_d + PATHS ${_MILES_LIB_SEARCHPATH} + ENV MILES_LIB_DIR + ) + if (${_CURR_COMPONENT}_DEBUG) + list(APPEND MILES_LIBRARIES debug ${${_CURR_COMPONENT}_DEBUG}) + endif() + endif() + endforeach() +else() + FIND_LIBRARY(MILES_RELEASE PATH_SUFFIXES lib - NAMES miles_${_MILES_COMPONENT_LC}_s + NAMES miles_s PATHS ${_MILES_LIB_SEARCHPATH} ENV MILES_LIB_DIR ) - if (${_CURR_COMPONENT}) - list(APPEND MILES_LIBRARIES optimized ${${_CURR_COMPONENT}}) + if (MILES_RELEASE) + list(APPEND MILES_LIBRARIES optimized ${MILES_RELEASE}) else() # if no minsize libraries were found try normal release - FIND_LIBRARY(${_CURR_COMPONENT} + FIND_LIBRARY(MILES_RELEASE PATH_SUFFIXES lib - NAMES miles_${_MILES_COMPONENT_LC} + NAMES miles PATHS ${_MILES_LIB_SEARCHPATH} ENV MILES_LIB_DIR ) - if (${_CURR_COMPONENT}) - list(APPEND MILES_LIBRARIES optimized ${${_CURR_COMPONENT}}) + if (MILES_RELEASE) + list(APPEND MILES_LIBRARIES optimized ${MILES_RELEASE}) endif() endif() - + # prefer RelWithDebInfo libraries - FIND_LIBRARY(${_CURR_COMPONENT}_DEBUG + FIND_LIBRARY(MILES_DEBUG PATH_SUFFIXES lib - NAMES miles_${_MILES_COMPONENT_LC}_rd + NAMES miles_rd PATHS ${_MILES_LIB_SEARCHPATH} ENV MILES_LIB_DIR ) - if (${_CURR_COMPONENT}_DEBUG) - list(APPEND MILES_LIBRARIES debug ${${_CURR_COMPONENT}_DEBUG}) + if (MILES_DEBUG) + list(APPEND MILES_LIBRARIES debug ${MILES_DEBUG}) else() - FIND_LIBRARY(${_CURR_COMPONENT}_DEBUG + FIND_LIBRARY(MILES_DEBUG PATH_SUFFIXES lib - NAMES miles_${_MILES_COMPONENT_LC}_d + NAMES miles_d PATHS ${_MILES_LIB_SEARCHPATH} ENV MILES_LIB_DIR ) - if (${_CURR_COMPONENT}_DEBUG) - list(APPEND MILES_LIBRARIES debug ${${_CURR_COMPONENT}_DEBUG}) + if (MILES_DEBUG) + list(APPEND MILES_LIBRARIES debug ${MILES_DEBUG}) endif() endif() -endforeach() +endif() INCLUDE(FindPackageHandleStandardArgs) FIND_PACKAGE_HANDLE_STANDARD_ARGS(MILES DEFAULT_MSG MILES_LIBRARIES MILES_INCLUDE_DIR) diff --git a/src/uscxml/Factory.cpp b/src/uscxml/Factory.cpp index 94049ea..4011446 100644 --- a/src/uscxml/Factory.cpp +++ b/src/uscxml/Factory.cpp @@ -3,6 +3,7 @@ #include "uscxml/Factory.h" #include "uscxml/Message.h" +#include "uscxml/Interpreter.h" #include <glog/logging.h> #ifdef BUILD_AS_PLUGINS @@ -25,7 +26,7 @@ # endif # ifdef MILES_FOUND -# include "uscxml/plugins/invoker/modality/miles/SpatialAudio.h" +# include "uscxml/plugins/invoker/miles/MilesSessionInvoker.h" # endif # ifdef FFMPEG_FOUND @@ -60,29 +61,29 @@ Factory::Factory() { pluginPath = (getenv("USCXML_PLUGIN_PATH") != NULL ? getenv("USCXML_PLUGIN_PATH") : ""); } if (pluginPath.length() > 0) { - pluma.acceptProviderType<InvokerProvider>(); - pluma.acceptProviderType<IOProcessorProvider>(); - pluma.acceptProviderType<DataModelProvider>(); + pluma.acceptProviderType<InvokerImplProvider>(); + pluma.acceptProviderType<IOProcessorImplProvider>(); + pluma.acceptProviderType<DataModelImplProvider>(); pluma.loadFromFolder(pluginPath); - std::vector<InvokerProvider*> invokerProviders; + std::vector<InvokerImplProvider*> invokerProviders; pluma.getProviders(invokerProviders); - for (std::vector<InvokerProvider*>::iterator it = invokerProviders.begin() ; it != invokerProviders.end() ; ++it) { - Invoker* invoker = (*it)->create(); + for (std::vector<InvokerImplProvider*>::iterator it = invokerProviders.begin() ; it != invokerProviders.end() ; ++it) { + InvokerImpl* invoker = (*it)->create(); registerInvoker(invoker); } - std::vector<IOProcessorProvider*> ioProcessorProviders; + std::vector<IOProcessorImplProvider*> ioProcessorProviders; pluma.getProviders(ioProcessorProviders); - for (std::vector<IOProcessorProvider*>::iterator it = ioProcessorProviders.begin() ; it != ioProcessorProviders.end() ; ++it) { - IOProcessor* ioProcessor = (*it)->create(); + for (std::vector<IOProcessorImplProvider*>::iterator it = ioProcessorProviders.begin() ; it != ioProcessorProviders.end() ; ++it) { + IOProcessorImpl* ioProcessor = (*it)->create(); registerIOProcessor(ioProcessor); } - std::vector<DataModelProvider*> dataModelProviders; + std::vector<DataModelImplProvider*> dataModelProviders; pluma.getProviders(dataModelProviders); - for (std::vector<DataModelProvider*>::iterator it = dataModelProviders.begin() ; it != dataModelProviders.end() ; ++it) { - DataModel* dataModel = (*it)->create(); + for (std::vector<DataModelImplProvider*>::iterator it = dataModelProviders.begin() ; it != dataModelProviders.end() ; ++it) { + DataModelImpl* dataModel = (*it)->create(); registerDataModel(dataModel); } } @@ -96,7 +97,7 @@ Factory::Factory() { #ifdef MILES_FOUND { - SpatialAudio* invoker = new SpatialAudio(); + MilesSessionInvoker* invoker = new MilesSessionInvoker(); registerInvoker(invoker); } #endif diff --git a/src/uscxml/Interpreter.cpp b/src/uscxml/Interpreter.cpp index 5324b9e..95f4658 100644 --- a/src/uscxml/Interpreter.cpp +++ b/src/uscxml/Interpreter.cpp @@ -37,9 +37,9 @@ Interpreter::Interpreter() : Arabica::SAX2DOM::Parser<std::string>() { _thread = NULL; _sendQueue = NULL; _parentQueue = NULL; - _httpServlet = NULL; _running = false; _done = false; + _httpServlet = NULL; #ifdef _WIN32 WSADATA wsaData; @@ -277,7 +277,8 @@ void Interpreter::interpret() { if (_dataModel) { _dataModel.assign("_x.args", _cmdLineOptions); - _dataModel.assign("_ioprocessors['http']", _httpServlet->getDataModelVariables()); + if (_httpServlet) + _dataModel.assign("_ioprocessors['http']", _httpServlet->getDataModelVariables()); } setupIOProcessors(); diff --git a/src/uscxml/Interpreter.h b/src/uscxml/Interpreter.h index 318af95..1a44f80 100644 --- a/src/uscxml/Interpreter.h +++ b/src/uscxml/Interpreter.h @@ -114,6 +114,10 @@ public: return _cmdLineOptions; } + HTTPServletInvoker* getHTTPServlet() { + return _httpServlet; + } + DataModel getDataModel() { return _dataModel; } @@ -166,10 +170,6 @@ public: return _sessionId; } - HTTPServletInvoker* getHTTPServlet() { - return _httpServlet; - } - bool runOnMainThread(int fps, bool blocking = true); static bool isMember(const Arabica::DOM::Node<std::string>& node, const Arabica::XPath::NodeSet<std::string>& set); diff --git a/src/uscxml/plugins/datamodel/ecmascript/v8/V8DataModel.cpp b/src/uscxml/plugins/datamodel/ecmascript/v8/V8DataModel.cpp index d00ad43..407988e 100644 --- a/src/uscxml/plugins/datamodel/ecmascript/v8/V8DataModel.cpp +++ b/src/uscxml/plugins/datamodel/ecmascript/v8/V8DataModel.cpp @@ -178,6 +178,7 @@ Data V8DataModel::getValueAsData(const v8::Handle<v8::Value>& value) { } else if(value->IsString()) { v8::String::AsciiValue property(v8::Handle<v8::String>::Cast(value)); data.atom = *property; + data.type = Data::VERBATIM; } else if(value->IsStringObject()) { LOG(ERROR) << "IsStringObject is unimplemented" << std::endl; } else if(value->IsTrue()) { diff --git a/src/uscxml/plugins/element/postpone/PostponeElement.cpp b/src/uscxml/plugins/element/postpone/PostponeElement.cpp index b50b5c2..96cda6f 100644 --- a/src/uscxml/plugins/element/postpone/PostponeElement.cpp +++ b/src/uscxml/plugins/element/postpone/PostponeElement.cpp @@ -58,7 +58,7 @@ void PostponeElement::enterElement(const Arabica::DOM::Node<std::string>& node) return; } - LOG(INFO) << until; +// LOG(INFO) << until; #if 0 std::string timeoutStr = "0s"; @@ -124,6 +124,8 @@ void PostponeElement::Resubmitter::onStableConfiguration(Interpreter* interprete } eventIter++; } +// LOG(ERROR) << _postponedEvents.size() << " Postponess remaining"; + } void PostponeElement::Resubmitter::afterCompletion(Interpreter* interpreter) { diff --git a/src/uscxml/plugins/element/response/ResponseElement.cpp b/src/uscxml/plugins/element/response/ResponseElement.cpp index 2e25b27..2da0d07 100644 --- a/src/uscxml/plugins/element/response/ResponseElement.cpp +++ b/src/uscxml/plugins/element/response/ResponseElement.cpp @@ -42,8 +42,12 @@ void ResponseElement::enterElement(const Arabica::DOM::Node<std::string>& node) LOG(ERROR) << "No matching HTTP request for response element"; return; } + + assert(servlet->getRequests().find(requestId) != servlet->getRequests().end()); HTTPServer::Request httpReq = servlet->getRequests()[requestId]; + assert(httpReq.curlReq != NULL); HTTPServer::Reply httpReply(httpReq); + servlet->getRequests().erase(requestId); // get the status or default to 200 std::string statusStr = (HAS_ATTR(node, "status") ? ATTR(node, "status") : "200"); diff --git a/src/uscxml/plugins/invoker/filesystem/dirmon/DirMonInvoker.cpp b/src/uscxml/plugins/invoker/filesystem/dirmon/DirMonInvoker.cpp index 675135a..65ea531 100644 --- a/src/uscxml/plugins/invoker/filesystem/dirmon/DirMonInvoker.cpp +++ b/src/uscxml/plugins/invoker/filesystem/dirmon/DirMonInvoker.cpp @@ -358,9 +358,10 @@ void DirectoryWatch::updateEntries(bool reportAsExisting) { monIter++; } } - _knownEntries.erase(fileIter->first); + _knownEntries.erase(fileIter++); + } else { + fileIter++; } - fileIter++; } // remember when we last checked the directory for modifications #ifndef WIN32 diff --git a/src/uscxml/plugins/invoker/graphics/openscenegraph/OSGConverter.cpp b/src/uscxml/plugins/invoker/graphics/openscenegraph/OSGConverter.cpp index 09cf663..93a238c 100644 --- a/src/uscxml/plugins/invoker/graphics/openscenegraph/OSGConverter.cpp +++ b/src/uscxml/plugins/invoker/graphics/openscenegraph/OSGConverter.cpp @@ -270,10 +270,13 @@ void OSGConverter::process(const SendRequest& req) { viewer.getCamera()->setDrawBuffer(pbuffer); viewer.getCamera()->setReadBuffer(pbuffer); + double zoom = 1; + CAST_PARAM(req.params, zoom, "zoom", double); + // 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 + viewer.getCameraManipulator()->setByMatrix(osg::Matrix::lookAt(osg::Vec3d(0,0,bs.radius() * (-3.4 * zoom)), // eye (osg::Vec3d)bs.center(), // center osg::Vec3d(0,1,0))); // up @@ -304,19 +307,17 @@ 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); + osg::Matrix m = eulerToMatrix(pitch, roll, yaw) * osg::Matrix::translate(-1 * x, -1 * y, -1 * z); #if 0 dumpMatrix(m); #endif @@ -335,6 +336,9 @@ osg::Matrix OSGConverter::requestToCamPose(const SendRequest& req) { osg::ref_ptr<osg::Node> OSGConverter::setupGraph(const std::string filename, bool autoRotate) { + // get some privacy + tthread::lock_guard<tthread::recursive_mutex> lock(_cacheMutex); + /** * root (model pose) * - rotate (autoRotate to face largest side) @@ -345,8 +349,6 @@ osg::ref_ptr<osg::Node> OSGConverter::setupGraph(const std::string filename, boo long now = tthread::chrono::system_clock::now(); { - // get some privacy - tthread::lock_guard<tthread::recursive_mutex> lock(_cacheMutex); // do we have it in the cache? if (_models.find(filename) == _models.end()) { diff --git a/src/uscxml/plugins/invoker/http/HTTPServletInvoker.h b/src/uscxml/plugins/invoker/http/HTTPServletInvoker.h index c9fe844..5d2d4b9 100644 --- a/src/uscxml/plugins/invoker/http/HTTPServletInvoker.h +++ b/src/uscxml/plugins/invoker/http/HTTPServletInvoker.h @@ -53,7 +53,7 @@ protected: }; #ifdef BUILD_AS_PLUGINS -PLUMA_INHERIT_PROVIDER(HTTPServletInvoker, Invoker); +PLUMA_INHERIT_PROVIDER(HTTPServletInvoker, InvokerImpl); #endif } diff --git a/src/uscxml/plugins/invoker/miles/MilesSessionInvoker.cpp b/src/uscxml/plugins/invoker/miles/MilesSessionInvoker.cpp new file mode 100644 index 0000000..1a1416a --- /dev/null +++ b/src/uscxml/plugins/invoker/miles/MilesSessionInvoker.cpp @@ -0,0 +1,407 @@ +#include "MilesSessionInvoker.h" +#include <glog/logging.h> + +#ifdef BUILD_AS_PLUGINS +#include <Pluma/Connector.hpp> +#endif + +#include <inttypes.h> + +namespace uscxml { + +#ifdef BUILD_AS_PLUGINS +PLUMA_CONNECTOR +bool connect(pluma::Host& host) { + host.add( new MilesSessionInvokerProvider() ); + return true; +} +#endif + +MilesSessionInvoker::MilesSessionInvoker() { + /* Initalize Miles */ + miles_init(); +} + +MilesSessionInvoker::~MilesSessionInvoker() { +}; + +boost::shared_ptr<IOProcessorImpl> MilesSessionInvoker::create(Interpreter* interpreter) { + boost::shared_ptr<MilesSessionInvoker> invoker = boost::shared_ptr<MilesSessionInvoker>(new MilesSessionInvoker()); + invoker->_interpreter = interpreter; + return invoker; +} + +Data MilesSessionInvoker::getDataModelVariables() { + Data data; + return data; +} + +void MilesSessionInvoker::send(const SendRequest& req) { + std::cout << req; + if (boost::iequals(req.name, "disconnect")) { + std::string reflectorIP = req.params.find("reflectorip")->second; + std::string problemName = req.params.find("problemname")->second; + int rv; + rv = miles_disconnect_reflector_session((char*)reflectorIP.c_str(), (char*)problemName.c_str()); + if (!rv) { + LOG(ERROR) << "Could not disconnect from reflector session"; + return; + } + + } + if (boost::iequals(req.name, "connect")) { + std::string email = req.params.find("email")->second; + std::string reflectorIP = req.params.find("reflectorip")->second; + std::string problemName = req.params.find("problemname")->second; + + int rv; + rv = miles_connect_reflector_session((char*)reflectorIP.c_str(), (char*)problemName.c_str()); + if (!rv) { + LOG(ERROR) << "Could not setup reflector session"; + return; + } + + /* Set up audio and video RTP sockets */ + video_rtp_in_socket = miles_net_setup_udp_socket((char*)reflectorIP.c_str(), video_port, video_port, 10, 16000); + audio_rtp_in_socket = miles_net_setup_udp_socket((char*)reflectorIP.c_str(), audio_port, audio_port, 10, 16000); + video_rtp_out_socket = video_rtp_in_socket; //miles_net_setup_udp_socket((char*)reflectorIP.c_str(), video_port, 0, 10, 16000); + audio_rtp_out_socket = audio_rtp_in_socket; //miles_net_setup_udp_socket((char*)reflectorIP.c_str(), audio_port, 0, 10, 16000); + + /* Set up audio and video RTCP sockets */ + video_rtcp_in_socket = miles_net_setup_udp_socket((char*)reflectorIP.c_str(), video_port+1, video_port+1, 10, 16000); + audio_rtcp_in_socket = miles_net_setup_udp_socket((char*)reflectorIP.c_str(), audio_port+1, audio_port+1, 10, 16000); + video_rtcp_out_socket = video_rtcp_in_socket; //miles_net_setup_udp_socket((char*)reflectorIP.c_str(), video_port+1, 0, 10, 16000); + audio_rtcp_out_socket = audio_rtcp_in_socket; //miles_net_setup_udp_socket((char*)reflectorIP.c_str(), audio_port+1, 0, 10, 16000); + + /* Set up RTP audio and video sessions */ + video_session = miles_rtp_setup_session(video_rtp_in_socket, MILES_RTP_MEDIA_TYPE_VIDEO); + audio_session = miles_rtp_setup_session(audio_rtp_in_socket, MILES_RTP_MEDIA_TYPE_AUDIO); + + /* Set up RTCP audio and video sessions */ + video_session->rtcp_session = miles_rtp_setup_rtcp_session(video_session, video_rtcp_in_socket); + audio_session->rtcp_session = miles_rtp_setup_rtcp_session(audio_session, audio_rtcp_in_socket); + + /* Initialize and configure video encoder */ + video_encoder = miles_video_codec_init_encoder(); + video_encoder->codec_id = miles_video_codec_get_encoder_for_rtp_payload_type(MILES_RTP_PAYLOAD_TYPE_JPEG); + video_encoder->width = 320; + video_encoder->height = 240; + video_encoder->qfactor = 50; + rv = miles_video_codec_setup_encoder(video_encoder); + if (!rv) { + LOG(ERROR) << "Could not setup video encoder"; + return; + } + + /* Set up video grabber */ + rv = miles_video_grabber_get_supported_grabbers(&supported_video_grabbers); + if(rv<=0) { + /* No video grabber available */ + exit(-1); + } + video_grabber = miles_video_grabber_create_context(supported_video_grabbers[0]); + video_grabber->width = video_encoder->width; + video_grabber->height = video_encoder->height; + miles_video_grabber_setup(video_grabber); + free(supported_video_grabbers); + + /* Set up outgoing RTP stream for video */ + out_rtp_video_stream = miles_rtp_setup_outgoing_stream(video_session, video_rtp_out_socket, 0, MILES_RTP_PAYLOAD_TYPE_JPEG); + + /* Initialize and configure audio encoder */ + audio_encoder = miles_audio_codec_init_encoder(); + audio_encoder->codec_id = miles_audio_codec_get_encoder_for_rtp_payload_type(MILES_RTP_PAYLOAD_TYPE_L16); + audio_encoder->sample_rate = 16000; + audio_encoder->bytes_per_sample = 2; + audio_encoder->chunk_size = 320; /* 20 ms */ + audio_encoder->input_format = MILES_AUDIO_FORMAT_PCM; + rv = miles_audio_codec_setup_encoder(audio_encoder); + if(rv == 0) { + /* Couldn't set up audio codec */ + exit(-1); + } + + + /* Set up audio grabber */ + int n = miles_audio_device_get_supported_devices(&supported_audio_devices); + if(n<=0) { + /* No audio device available */ + exit(-1); + } + /* Use first device that supports capture */ + for(int i=0; i<n; i++) { + audio_dev = miles_audio_device_open(supported_audio_devices[i].id, MILES_AUDIO_FORMAT_PCM, 16000, 2, 1, 640, 1); + if(audio_dev) + break; + } + if(audio_dev == NULL) + exit(-1); + + /* Find first audio device that supports playback */ + for(int i=0; i<n; i++) { + audio_dev_playback = miles_audio_device_open(supported_audio_devices[i].id, MILES_AUDIO_FORMAT_PCM, 16000, 2, 1, 640, 0); + if(audio_dev_playback) { + audio_dev_playback_id = supported_audio_devices[i].id; + break; + } + } + if(audio_dev_playback == NULL) + exit(-1); + + /* Set up outgoing RTP stream for audio */ + out_rtp_audio_stream = miles_rtp_setup_outgoing_stream(audio_session, audio_rtp_out_socket, 0, MILES_RTP_PAYLOAD_TYPE_L16); + + /* Associate RTP stream with codec context */ + out_rtp_audio_stream->codec_ctx = audio_encoder; + out_rtp_video_stream->codec_ctx = video_encoder; + + /* Set up outgoing RTCP streams for audio and video */ + out_rtcp_audio_stream = miles_rtp_setup_outgoing_rtcp_stream(audio_session->rtcp_session, audio_rtcp_out_socket, out_rtp_audio_stream->ssrc); + out_rtcp_video_stream = miles_rtp_setup_outgoing_rtcp_stream(video_session->rtcp_session, video_rtcp_out_socket, out_rtp_video_stream->ssrc); + + _isRunning = true; + +// while(true) { +// rtp_video_receiver(video_session); +// video_transmitter(video_grabber, video_encoder, out_rtp_video_stream, out_rtcp_video_stream); +// rtp_audio_receiver(audio_session); +// audio_transmitter(audio_dev, audio_encoder, out_rtp_audio_stream, out_rtcp_audio_stream); +// } + + _audioThread = new tthread::thread(MilesSessionInvoker::runAudio, this); + _videoThread = new tthread::thread(MilesSessionInvoker::runVideo, this); + } +} + +void MilesSessionInvoker::runAudio(void* instance) { + ((MilesSessionInvoker*)instance)->processAudio(); +} + +void MilesSessionInvoker::runVideo(void* instance) { + ((MilesSessionInvoker*)instance)->processVideo(); +} + +void MilesSessionInvoker::processVideo() { + while(_isRunning) { + rtp_video_receiver(video_session); + video_transmitter(video_grabber, video_encoder, out_rtp_video_stream, out_rtcp_video_stream); + } +} + +void MilesSessionInvoker::processAudio() { + while(_isRunning) { + rtp_audio_receiver(audio_session); + audio_transmitter(audio_dev, audio_encoder, out_rtp_audio_stream, out_rtcp_audio_stream); + } +} + +void MilesSessionInvoker::cancel(const std::string sendId) { +} + +void MilesSessionInvoker::invoke(const InvokeRequest& req) { + video_port = 5566; + audio_port = 5568; +} + +/** + * Do something with an image decoded from an RTP stream (e.g. render to screen) + */ +void MilesSessionInvoker::render_video_image(u_int32_t ssrc, char *img, int width, int height, int img_format) { + + if(img_format != MILES_IMAGE_RGBA) { + miles_image_convert(img, render_img, img_format, MILES_IMAGE_RGBA, width, height); + stbi_write_png("/Users/sradomski/Desktop/image.png", width, height, 3, render_img, width * 3); + } else { + stbi_write_png("/Users/sradomski/Desktop/image.png", width, height, 3, img, width * 3); + } + + /* render image... */ +} + +/** + * Send an audio chunk decoded from an RTP stream to an audio device + */ +void MilesSessionInvoker::playback_audio(u_int32_t ssrc, char *buf, int sample_rate, int bps, int audio_format, int size) { + int n; + + if(size<0) + return; + + /* re-configure audio device, if needed */ + if(audio_dev_playback == NULL || audio_dev_playback->chunk_size != size || audio_dev_playback->sample_rate != sample_rate || + audio_dev_playback->format != audio_format || audio_dev_playback->bytes_per_sample != bps) { + if(audio_dev_playback) + miles_audio_device_close(audio_dev_playback, 0); + audio_dev_playback = miles_audio_device_open(audio_dev_playback_id, audio_format, sample_rate, bps, 1, size, 0); + if(audio_dev_playback == NULL) + return; + } + + /* play audio */ + n = miles_audio_device_write(audio_dev_playback, buf, size); +} + +/** + * Handle incoming video streams + */ + +int MilesSessionInvoker::video_receiver(struct miles_rtp_in_stream *rtp_stream, char *data, int bytes_read) { + int status, n; + struct miles_video_codec_decode_context *codec_ctx; + + codec_ctx = (struct miles_video_codec_decode_context *)rtp_stream->codec_ctx; + + if(codec_ctx == NULL || !miles_video_codec_decoder_supports_rtp_payload_type(codec_ctx, rtp_stream->payload_type)) { + if(codec_ctx) + miles_video_codec_destroy_decoder(codec_ctx); + codec_ctx = miles_video_codec_init_decoder(); + codec_ctx->codec_id = miles_video_codec_get_decoder_for_rtp_payload_type(rtp_stream->payload_type); + if(codec_ctx->codec_id == MILES_VIDEO_CODEC_UNKNOWN) { + /* Cannot decode the video stream */ + return 0; + } + + status = miles_video_codec_setup_decoder(codec_ctx); + if(status == 0) { + /* Cannot decode the video stream */ + return 0; + } + rtp_stream->codec_ctx = (void *)codec_ctx; + return 0; + } + n = miles_video_codec_decode(codec_ctx, data, decoded_in_img, bytes_read); + if(n > 0) { + render_video_image(rtp_stream->ssrc, decoded_in_img, codec_ctx->width, codec_ctx->height, codec_ctx->output_format); + } + return n; +} + +/** + * Handle incoming audio streams + */ + +int MilesSessionInvoker::audio_receiver(struct miles_rtp_in_stream *rtp_stream, char *data, int bytes_read) { + int status, size; + struct miles_audio_codec_decode_context *codec_ctx; + + codec_ctx = (struct miles_audio_codec_decode_context *)rtp_stream->codec_ctx; + + if(codec_ctx == NULL || !miles_audio_codec_decoder_supports_rtp_payload_type(codec_ctx, rtp_stream->payload_type)) { + if(codec_ctx) + miles_audio_codec_destroy_decoder(codec_ctx); + codec_ctx = miles_audio_codec_init_decoder(); + codec_ctx->codec_id = miles_audio_codec_get_decoder_for_rtp_payload_type(rtp_stream->payload_type); + if(codec_ctx->codec_id == MILES_AUDIO_CODEC_UNKNOWN) { + /* Cannot decode the audio stream */ + return 0; + } + status = miles_audio_codec_setup_decoder(codec_ctx); + if(status == 0) { + /* Cannot decode the audio stream */ + return 0; + } + rtp_stream->codec_ctx = (void *)codec_ctx; + } + size = miles_audio_codec_decode(codec_ctx, data, audio_in_buf); + if(size > 0) { + playback_audio(rtp_stream->ssrc, audio_in_buf, codec_ctx->sample_rate, codec_ctx->bytes_per_sample, codec_ctx->output_format, size); + } + return size; +} + +/** + * Read and depacketize incoming RTP streams + */ + +void MilesSessionInvoker::rtp_audio_receiver(struct miles_rtp_session *rtp_session) { + int n, m=0; + struct miles_rtp_in_stream *rtp_stream; + + /* Poll RTP socket, read all available RTP packets */ + while (1) { + n = miles_net_poll_socket(rtp_session->socket); + if(n<=0) return; + + /* Read RTP data */ + n = miles_rtp_recv(rtp_session, &rtp_stream, audio_data); + if(n>0) { + m = audio_receiver(rtp_stream, audio_data, n); + } + + /* Poll RTCP socket */ + n = miles_net_poll_socket(rtp_session->rtcp_session->socket); + if(n>0) { + /* Do RTCP packet processing */ + n = miles_rtp_recv_rtcp(rtp_session->rtcp_session); + } + } +} + +void MilesSessionInvoker::rtp_video_receiver(struct miles_rtp_session *rtp_session) { + int n, m=0; + struct miles_rtp_in_stream *rtp_stream; + + /* Poll RTP socket, read all available RTP packets */ + while (1) { + n = miles_net_poll_socket(rtp_session->socket); + if(n<=0) return; + + /* Read RTP data */ + n = miles_rtp_recv(rtp_session, &rtp_stream, video_data); + if(n>0) { + m = video_receiver(rtp_stream, video_data, n); + } + + /* Poll RTCP socket */ + n = miles_net_poll_socket(rtp_session->rtcp_session->socket); + if(n>0) { + /* Do RTCP packet processing */ + n = miles_rtp_recv_rtcp(rtp_session->rtcp_session); + } + } +} + +/** + * Send RTP video stream + */ +int MilesSessionInvoker::video_transmitter(struct miles_video_grabber_context *grabber, struct miles_video_codec_encode_context *codec_ctx, struct miles_rtp_out_stream *rtp_stream, struct miles_rtcp_out_stream *out_rtcp_stream) { + int n; + + /* Send RTCP packets, if due */ + miles_rtp_send_rtcp(out_rtcp_stream); + + n = miles_video_grabber_grab(grabber, video_out_buf); + if(n <= 0) + return 0; + if(grabber->image_format != codec_ctx->input_format) { + /* image conversion ... */ + } + n = miles_video_codec_encode(codec_ctx, video_out_buf, encoded_out_img); + if(n<=0) + return 0; + return miles_rtp_send(rtp_stream, encoded_out_img, n); +} + +/** + * Send RTP audio stream + */ +int MilesSessionInvoker::audio_transmitter(struct miles_audio_device *dev, struct miles_audio_codec_encode_context *codec_ctx, struct miles_rtp_out_stream *rtp_stream, struct miles_rtcp_out_stream *out_rtcp_audio_stream) { + int n; + + /* Send RTCP packets, if due */ + miles_rtp_send_rtcp(out_rtcp_audio_stream); + + n = miles_audio_device_read(dev, audio_read_buf, codec_ctx->chunk_size); + if(n <= 0) + return 0; + if(dev->format != codec_ctx->input_format) { + /* audio conversion needed ... */ + } + n = miles_audio_codec_encode(codec_ctx, audio_read_buf, encoded_out_audio); + if(n<=0) + return 0; + return miles_rtp_send(rtp_stream, encoded_out_audio, n); +} + + +}
\ No newline at end of file diff --git a/src/uscxml/plugins/invoker/miles/MilesSessionInvoker.h b/src/uscxml/plugins/invoker/miles/MilesSessionInvoker.h new file mode 100644 index 0000000..cb3e9ee --- /dev/null +++ b/src/uscxml/plugins/invoker/miles/MilesSessionInvoker.h @@ -0,0 +1,100 @@ +#ifndef MILESSESIONINVOKER_H_W09J90F0 +#define MILESSESIONINVOKER_H_W09J90F0 + +#include <uscxml/Interpreter.h> + +extern "C" { +#include "miles/miles.h" +#include "miles/network.h" +#include "miles/rtp.h" +#include "miles/audio_codec.h" +#include "miles/audio_device.h" +#include "miles/video_codec.h" +#include "miles/video_grabber.h" +#include "miles/session.h" +#include "miles/image.h" +} +#ifdef BUILD_AS_PLUGINS +#include "uscxml/plugins/Plugins.h" +#endif + +namespace uscxml { + +class MilesSessionInvoker : public InvokerImpl { +public: + MilesSessionInvoker(); + virtual ~MilesSessionInvoker(); + virtual boost::shared_ptr<IOProcessorImpl> create(Interpreter* interpreter); + + virtual std::set<std::string> getNames() { + std::set<std::string> names; + names.insert("miles"); + names.insert("http://uscxml.tk.informatik.tu-darmstadt.de/#miles"); + return names; + } + + virtual Data getDataModelVariables(); + virtual void send(const SendRequest& req); + virtual void cancel(const std::string sendId); + virtual void invoke(const InvokeRequest& req); + +protected: + int video_rtp_in_socket, audio_rtp_in_socket; + int video_rtp_out_socket, audio_rtp_out_socket; + int video_rtcp_in_socket, audio_rtcp_in_socket; + int video_rtcp_out_socket, audio_rtcp_out_socket; + struct miles_rtp_session *video_session, *audio_session; + struct miles_video_codec_encode_context *video_encoder; + struct miles_audio_codec_encode_context *audio_encoder; + int *supported_video_grabbers; + struct miles_video_grabber_context *video_grabber; + struct miles_rtp_out_stream *out_rtp_video_stream, *out_rtp_audio_stream; + struct miles_rtcp_out_stream *out_rtcp_video_stream, *out_rtcp_audio_stream; + struct miles_audio_device *audio_dev; + struct miles_audio_device_description *supported_audio_devices; + int video_port, audio_port; + std::string ip_address; + + char video_out_buf[1000000]; + char encoded_out_img[1000000]; + char decoded_in_img[1000000]; + char audio_in_buf[1000000]; + char render_img[1000000]; + char audio_data[1000000]; + char video_data[1000000]; + + char encoded_out_audio[1000000]; + char audio_read_buf[1000000]; + + struct miles_audio_device *audio_dev_playback; + int audio_dev_playback_id; + + static void runAudio(void* instance); + static void runVideo(void* instance); + void processVideo(); + void processAudio(); + + void render_video_image(u_int32_t ssrc, char *img, int width, int height, int img_format); + void playback_audio(u_int32_t ssrc, char *buf, int sample_rate, int bps, int audio_format, int size); + int video_receiver(struct miles_rtp_in_stream *rtp_stream, char *data, int bytes_read); + int audio_receiver(struct miles_rtp_in_stream *rtp_stream, char *data, int bytes_read); + void rtp_audio_receiver(struct miles_rtp_session *rtp_session); + void rtp_video_receiver(struct miles_rtp_session *rtp_session); + int video_transmitter(struct miles_video_grabber_context *grabber, struct miles_video_codec_encode_context *codec_ctx, struct miles_rtp_out_stream *rtp_stream, struct miles_rtcp_out_stream *out_rtcp_stream); + int audio_transmitter(struct miles_audio_device *dev, struct miles_audio_codec_encode_context *codec_ctx, struct miles_rtp_out_stream *rtp_stream, struct miles_rtcp_out_stream *out_rtcp_audio_stream); + + + bool _isRunning; + tthread::thread* _videoThread; + tthread::thread* _audioThread; + tthread::recursive_mutex _mutex; +}; + +#ifdef BUILD_AS_PLUGINS +PLUMA_INHERIT_PROVIDER(MilesSessionInvoker, Invoker); +#endif + +} + + +#endif /* end of include guard: MILESSESIONINVOKER_H_W09J90F0 */ diff --git a/src/uscxml/server/HTTPServer.cpp b/src/uscxml/server/HTTPServer.cpp index ebc5b91..8be45e6 100644 --- a/src/uscxml/server/HTTPServer.cpp +++ b/src/uscxml/server/HTTPServer.cpp @@ -38,12 +38,25 @@ HTTPServer::HTTPServer(unsigned short port) { _port = port; _base = event_base_new(); _http = evhttp_new(_base); + + evhttp_set_allowed_methods(_http, + EVHTTP_REQ_GET | + EVHTTP_REQ_POST | + EVHTTP_REQ_HEAD | + EVHTTP_REQ_PUT | + EVHTTP_REQ_DELETE | + EVHTTP_REQ_OPTIONS | + EVHTTP_REQ_TRACE | + EVHTTP_REQ_CONNECT | + EVHTTP_REQ_PATCH); // allow all methods + _handle = NULL; while((_handle = evhttp_bind_socket_with_handle(_http, INADDR_ANY, _port)) == NULL) { _port++; } determineAddress(); + evhttp_set_timeout(_http, 5); // generic callback evhttp_set_gencb(_http, HTTPServer::httpRecvReqCallback, NULL); } @@ -56,7 +69,7 @@ tthread::recursive_mutex HTTPServer::_instanceMutex; std::map<std::string, std::string> HTTPServer::mimeTypes; HTTPServer* HTTPServer::getInstance(int port) { - tthread::lock_guard<tthread::recursive_mutex> lock(_instanceMutex); +// tthread::lock_guard<tthread::recursive_mutex> lock(_instanceMutex); if (_instance == NULL) { // this is but a tiny list, supply a content-type <header> yourself @@ -97,9 +110,10 @@ std::string HTTPServer::mimeTypeForExtension(const std::string& ext) { void HTTPServer::httpRecvReqCallback(struct evhttp_request *req, void *callbackData) { // std::cout << (uintptr_t)req << ": Replying" << std::endl; -// evhttp_send_reply(req, 200, NULL, NULL); +// evhttp_send_error(req, 404, NULL); // return; + evhttp_request_own(req); Request request; request.curlReq = req; @@ -153,6 +167,12 @@ void HTTPServer::httpRecvReqCallback(struct evhttp_request *req, void *callbackD request.data.compound["uri"] = Data(HTTPServer::getBaseURL() + req->uri, Data::VERBATIM); request.data.compound["path"] = Data(evhttp_uri_get_path(evhttp_request_get_evhttp_uri(req)), Data::VERBATIM); + // This was used for debugging +// if (boost::ends_with(request.data.compound["path"].atom, ".png")) { +// evhttp_send_error(req, 404, NULL); +// return; +// } + // seperate path into components std::stringstream ss(request.data.compound["path"].atom); std::string item; @@ -175,12 +195,26 @@ void HTTPServer::httpRecvReqCallback(struct evhttp_request *req, void *callbackD // get content buf = evhttp_request_get_input_buffer(req); + if (evbuffer_get_length(buf)) + request.data.compound["content"] = Data("", Data::VERBATIM); while (evbuffer_get_length(buf)) { int n; char cbuf[1024]; n = evbuffer_remove(buf, cbuf, sizeof(buf)-1); if (n > 0) { - request.content.append(cbuf, n); + request.data.compound["content"].atom.append(cbuf, n); + } + } + + // decode content + if (request.data.compound.find("content") != request.data.compound.end() && + request.data.compound["header"].compound.find("Content-Type") != request.data.compound["header"].compound.end()) { + std::string contentType = request.data.compound["header"].compound["Content-Type"].atom; + if (false) { + } else if (boost::iequals(contentType, "application/x-www-form-urlencoded")) { + request.data.compound["content"].atom = evhttp_decode_uri(request.data.compound["content"].atom.c_str()); + } else if (boost::iequals(contentType, "application/json")) { + request.data.compound["content"] = Data::fromJSON(request.data.compound["content"].atom); } } @@ -221,30 +255,44 @@ void HTTPServer::processByMatchingServlet(const Request& request) { } void HTTPServer::reply(const Reply& reply) { - if (reply.content.size() > 0 && reply.headers.find("Content-Type") == reply.headers.end()) { + // we need to reply from the thread calling event_base_dispatch, just add to ist base queue! + Reply* replyCB = new Reply(reply); + HTTPServer* INSTANCE = getInstance(); + event_base_once(INSTANCE->_base, -1, EV_TIMEOUT, HTTPServer::replyCallback, replyCB, NULL); +} + +void HTTPServer::replyCallback(evutil_socket_t fd, short what, void *arg) { + + Reply* reply = (Reply*)arg; + + 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()); + 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); + if (reply->status >= 400) { + evhttp_send_error(reply->curlReq, reply->status, NULL); return; } - struct evbuffer *evb = evbuffer_new(); + struct evbuffer *evb = NULL; - if (!boost::iequals(reply.type, "HEAD")) - evbuffer_add(evb, reply.content.data(), reply.content.size()); + if (!boost::iequals(reply->type, "HEAD") && reply->content.size() > 0) { + evb = evbuffer_new(); + evbuffer_add(evb, reply->content.data(), reply->content.size()); + } - evhttp_send_reply(reply.curlReq, reply.status, NULL, evb); - evbuffer_free(evb); -// evhttp_request_free(reply.curlReq); + evhttp_send_reply(reply->curlReq, reply->status, NULL, evb); + if (evb != NULL) + evbuffer_free(evb); +// evhttp_request_free(reply->curlReq); + delete(reply); } bool HTTPServer::registerServlet(const std::string& path, HTTPServlet* servlet) { diff --git a/src/uscxml/server/HTTPServer.h b/src/uscxml/server/HTTPServer.h index c573422..319b62f 100644 --- a/src/uscxml/server/HTTPServer.h +++ b/src/uscxml/server/HTTPServer.h @@ -55,6 +55,7 @@ private: void determineAddress(); + static void replyCallback(evutil_socket_t fd, short what, void *arg); static void httpRecvReqCallback(struct evhttp_request *req, void *callbackData); void processByMatchingServlet(const Request& request); diff --git a/test/src/test-dirmon.cpp b/test/src/test-dirmon.cpp index f3a83b3..078c50f 100644 --- a/test/src/test-dirmon.cpp +++ b/test/src/test-dirmon.cpp @@ -6,6 +6,18 @@ #include <iostream> #include "uscxml/plugins/invoker/filesystem/dirmon/DirMonInvoker.h" +#include <sys/types.h> +#include <sys/event.h> +#include <sys/time.h> +#include <sys/stat.h> +#include <unistd.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <dirent.h> +#include <string.h> +#include <iostream> + using namespace uscxml; using namespace boost; @@ -17,10 +29,55 @@ class Watcher : public DirectoryWatchMonitor { int main(int argc, char** argv) { - Watcher watcher; - DirectoryWatch* dw = new DirectoryWatch("/Users/sradomski/Desktop/tmp", true); - dw->addMonitor(&watcher); + int mDescriptor = kqueue(); + + struct kevent filters[2]; + struct kevent event; + + struct timespec mTimeOut; + mTimeOut.tv_sec = 20; + mTimeOut.tv_nsec = 20000000; + + int fd1 = open("/Users/sradomski/Desktop/wrls", O_RDONLY); + int fd2 = open("/Users/sradomski/Desktop/tmp", O_RDONLY); + + EV_SET(&filters[0], fd1, EVFILT_VNODE, + EV_ADD | EV_ENABLE | EV_ONESHOT, + NOTE_DELETE | NOTE_WRITE | NOTE_EXTEND | NOTE_ATTRIB | NOTE_LINK | NOTE_RENAME | NOTE_REVOKE, + 0, NULL); + EV_SET(&filters[1], fd2, EVFILT_VNODE, + EV_ADD | EV_ENABLE | EV_ONESHOT, + NOTE_DELETE | NOTE_WRITE | NOTE_EXTEND | NOTE_ATTRIB | NOTE_LINK | NOTE_RENAME | NOTE_REVOKE, + 0, NULL); + + int nev = 0; while(true) { - dw->updateEntries(); + nev = kevent(mDescriptor, filters, 2, &event, 1, &mTimeOut); + if(nev == -1) + perror("kevent"); + else if (nev > 0) { + 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 "); + } + } } + +// Watcher watcher; +// DirectoryWatch* dw = new DirectoryWatch("/Users/sradomski/Desktop/tmp", true); +// dw->addMonitor(&watcher); +// while(true) { +// dw->updateEntries(); +// } }
\ No newline at end of file |