diff options
author | Stefan Radomski <radomski@tk.informatik.tu-darmstadt.de> | 2013-06-10 22:47:14 (GMT) |
---|---|---|
committer | Stefan Radomski <radomski@tk.informatik.tu-darmstadt.de> | 2013-06-10 22:47:14 (GMT) |
commit | 6f56474450b7c54f2c95b5dea6a7a42623141649 (patch) | |
tree | 420c52085d8cf778360c09baf9722b21d01259da /apps | |
parent | a154682fc1b25581742d38dd5fe9aa06ede167b7 (diff) | |
download | uscxml-6f56474450b7c54f2c95b5dea6a7a42623141649.zip uscxml-6f56474450b7c54f2c95b5dea6a7a42623141649.tar.gz uscxml-6f56474450b7c54f2c95b5dea6a7a42623141649.tar.bz2 |
W3C MMI Architecture framework
Diffstat (limited to 'apps')
23 files changed, 1513 insertions, 278 deletions
diff --git a/apps/samples/map/map.html b/apps/samples/map/map.html new file mode 100644 index 0000000..01dc92b --- /dev/null +++ b/apps/samples/map/map.html @@ -0,0 +1,30 @@ +<!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="map.css" --> + <script type="text/javascript" src="http://openlayers.org/api/OpenLayers.js"></script> + <style type="text/css"> + </style> + + <script type="text/javascript"> + </script> + + <script src="http://ajax.googleapis.com/ajax/libs/dojo/1.8.3/dojo/dojo.js"></script> + <script type="text/javascript" src="map.js"></script> + + <script type="text/javascript"> + require(["dojo/domReady!", "dojo"], function(dom, dojo) { + var map = new Map("map"); + }); + </script> + </head> + <body class="tundra"> + <table> + <tr><td> + <div id="map" /> + </td></tr> + </table> + </body> +</html> diff --git a/apps/samples/map/map.js b/apps/samples/map/map.js new file mode 100644 index 0000000..6794875 --- /dev/null +++ b/apps/samples/map/map.js @@ -0,0 +1,158 @@ +function Map(element) { + + // private attributes + var self = this; + + // private instanceId + if (!Map.instances) + Map.instances = 0; + var instanceId = Map.instances++; + + // public attributes + this.coords = {}; + + + require([ + "dojo/ready", + "dojo/dom-construct", + "dojo/_base/window", + "dojo/dom", + "dojox/geo/openlayers/Map", + "dojox/geo/openlayers/GfxLayer", + "dojox/geo/openlayers/Layer", + "dojox/geo/openlayers/GeometryFeature", + "dojox/geo/openlayers/Point", + "dojox/geo/openlayers/LineString", + "dojox/geo/openlayers/WidgetFeature", + "dojox/charting/widget/Chart", + "dojox/charting/widget/Chart2D", + "dojox/charting/plot2d/Pie", + "dojox/charting/themes/PlotKit/blue" + ], function( + ready, + domConstruct, + win, + dom, + Map, + GfxLayer, + Layer, + GeometryFeature, + Point, + LineString, + WidgetFeature, + Chart, + Chart2D, + Pie, + blue + ) { + ready(function(){ + + if (typeof(element) === 'string') { + element = dom.byId(element); + } + + element.style.width = "450px"; + element.style.height = "350px"; + + var options = { + baseLayerName: "WorldMap", + baseLayerType : dojox.geo.openlayers.BaseLayerType.OSM, + //baseLayerUrl: "http://localhost/mapserver/mapserv.cgi?map=./world.map", + baseLayerOptions: { + layers: ['contry','state','city','town','highway'] + }, + touchHandler: false, + accessible: true + }; + + // Available base layers: http://dojotoolkit.org/reference-guide/1.7/dojox/geo/openlayers.html#id5 + self.map = new Map(element, options); + + // This is New York location + var ny = { + latitude : 49.877648, + longitude : 8.654762 + }; + + var people = [ { + name : 'Dirk', + y : 49.877848, + x : 8.653762 + }, { + name : 'Stefan', + y : 49.877348, + x : 8.655462 + } ]; + + var div = domConstruct.create("div", {}, win.body()); + //var div = domConstruct.create("div", {}); + var chart = new Chart({ + margins : { + l : 0, + r : 0, + t : 0, + b : 0 + } + }, div); + + var c = chart.chart; + c.addPlot("default", { + type : "Pie", + radius : 50, + labelOffset : 100, + fontColor : "black", + fontSize : 20 + }); + + var ser = [ 2, 8, 12, 3 ]; + c.addSeries("Series", ser); + c.setTheme(blue); + c.render(); + c.theme.plotarea.fill = undefined; + + var descr = { + longitude : ny.longitude, + latitude : ny.latitude, + widget : chart, + width : 120, + height : 120 + }; + feat3 = new WidgetFeature(descr); + + // create a GfxLayer + var layer = new GfxLayer(); + + var point = new Point({ + x:ny.longitude, + y:ny.latitude + }); + // create a GeometryFeature + var feat = new GeometryFeature(point); + // set the shape properties, fill and stroke + feat.setFill([ 0, 128, 128 ]); + feat.setStroke([ 0, 0, 0 ]); + feat.setShapeProperties({ + r : 8 + }); + + var pts = new LineString(people); + // create a GeometryFeature + var feat2 = new GeometryFeature(pts); + // set the shape stroke property + feat2.setStroke([ 0, 0, 0 ]); + + // add the feature to the layer + layer.addFeature(feat); + layer.addFeature(feat2); + layer.addFeature(feat3); + // add layer to the map + self.map.addLayer(layer); + + // fit to New York with 0.1 degrees extent + self.map.fitTo({ + position : [ ny.longitude, ny.latitude ], + extent : 0.001 + }); + }); + }); +} diff --git a/apps/samples/vrml.zip b/apps/samples/vrml.zip Binary files differnew file mode 100644 index 0000000..730236b --- /dev/null +++ b/apps/samples/vrml.zip diff --git a/apps/samples/vrml/img/Tutorial.png b/apps/samples/vrml/img/Tutorial.png Binary files differnew file mode 100644 index 0000000..7f5a041 --- /dev/null +++ b/apps/samples/vrml/img/Tutorial.png diff --git a/apps/samples/vrml/img/Tutorial.pxm b/apps/samples/vrml/img/Tutorial.pxm Binary files differnew file mode 100644 index 0000000..682a00c --- /dev/null +++ b/apps/samples/vrml/img/Tutorial.pxm diff --git a/apps/samples/vrml/img/drag.png b/apps/samples/vrml/img/drag.png Binary files differnew file mode 100644 index 0000000..b398a3f --- /dev/null +++ b/apps/samples/vrml/img/drag.png diff --git a/apps/samples/vrml/img/drag2.png b/apps/samples/vrml/img/drag2.png Binary files differnew file mode 100644 index 0000000..4dfb651 --- /dev/null +++ b/apps/samples/vrml/img/drag2.png diff --git a/apps/samples/vrml/img/drag3.png b/apps/samples/vrml/img/drag3.png Binary files differnew file mode 100644 index 0000000..467ba6e --- /dev/null +++ b/apps/samples/vrml/img/drag3.png diff --git a/apps/samples/vrml/img/pitchRoll.png b/apps/samples/vrml/img/pitchRoll.png Binary files differnew file mode 100644 index 0000000..4f73745 --- /dev/null +++ b/apps/samples/vrml/img/pitchRoll.png diff --git a/apps/samples/vrml/img/pitchRoll3.png b/apps/samples/vrml/img/pitchRoll3.png Binary files differnew file mode 100644 index 0000000..ede9247 --- /dev/null +++ b/apps/samples/vrml/img/pitchRoll3.png diff --git a/apps/samples/vrml/img/xy.png b/apps/samples/vrml/img/xy.png Binary files differnew file mode 100644 index 0000000..fe081ee --- /dev/null +++ b/apps/samples/vrml/img/xy.png diff --git a/apps/samples/vrml/img/xy2.png b/apps/samples/vrml/img/xy2.png Binary files differnew file mode 100644 index 0000000..2cc3f15 --- /dev/null +++ b/apps/samples/vrml/img/xy2.png diff --git a/apps/samples/vrml/img/yawZoom.png b/apps/samples/vrml/img/yawZoom.png Binary files differnew file mode 100644 index 0000000..a256b3b --- /dev/null +++ b/apps/samples/vrml/img/yawZoom.png diff --git a/apps/samples/vrml/viewer.html b/apps/samples/vrml/viewer.html index 1e1043b..9838c33 100644 --- a/apps/samples/vrml/viewer.html +++ b/apps/samples/vrml/viewer.html @@ -38,16 +38,37 @@ <script type="text/javascript"> require(["dojo/domReady!", "dojo"], function(dom, dojo) { - var viewer = new VRMLViewer("scene1"); - var annotations = new Annotations("annotations1", { 'viewer': viewer }); + var viewer = new VRMLViewer("scene1", { + "pose": { + "pitch" : 0, + "roll" : 0, + "yaw" : 0, + "zoom" : 1, + "x" : 0, + "y" : 0, + "z" : 0, + "width" : 400, + "height" : 400, + "autorotate" : false + }, + "serverURL": "http://smartvortex.tbm.tudelft.nl:8090/vrml", + "imageURL": "http://smartvortex.tbm.tudelft.nl:8090/vrml/hook/HARD_MP_VAL_005.png" + }); + var viewer2 = new VRMLViewer("scene2"); +// var annotations = new Annotations("annotations1", { 'viewer': viewer }); }); </script> </head> <body class="tundra"> - <table> - <tr><td> - <div id="scene1" /> - </td></tr> + <table align="center"> + <tr> + <td><br /><br /><br /><br /><br /><br /> + <div id="scene1" /> + </td> + <td><br /><br /><br /><br /><br /><br /> + <div id="scene2" /> + </td> + </tr> <tr><td> <div id="annotations1"></div> </td></tr> diff --git a/apps/samples/vrml/viewer.js b/apps/samples/vrml/viewer.js index f6f2e42..660a66a 100644 --- a/apps/samples/vrml/viewer.js +++ b/apps/samples/vrml/viewer.js @@ -6,10 +6,13 @@ function VRMLViewer(element, params) { // private instanceId if (!VRMLViewer.instances) VRMLViewer.instances = 0; - var instanceId = VRMLViewer.instances++; + this.instanceId = VRMLViewer.instances++; var batchChanges = false; // public attributes + this.width = 450; + this.height = 350; + this.pose = {}; this.pose.pitch = 0; this.pose.roll = 0; @@ -18,49 +21,142 @@ function VRMLViewer(element, params) { this.pose.x = 0; this.pose.y = 0; this.pose.z = 0; - this.pose.width = 450; - this.pose.height = 350; this.pose.autorotate = false; - - this.serverURL = "http://88.69.49.213:8080/vrml"; + this.serverURL; this.imageURL; + + this.pose.width = this.width; + this.pose.height = this.height; + + this.params = params; + + // privileged public methods + this.updateScene = function() { + if (self.imageURL && !self.batchChanges) { + self.imgElem.src = self.imageURL + urlSuffixForPose(self.pose); + } + } + + var urlSuffixForPose = function(pose) { + var url = + '?width=' + pose.width + + '&height=' + pose.height + + '&pitch=' + pose.pitch + + '&roll=' + pose.roll + + '&yaw=' + pose.yaw + + '&x=' + pose.x + + '&y=' + pose.y + + '&z=' + pose.z + + '&zoom=' + pose.zoom + + '&autorotate=' + (pose.autorotate ? '1' : '0'); + return url; + } + + var moverRelativeTo = function(mover, container) { + var containerPos = absolutePosition(container); + return { + x: mover.x - containerPos.x, + y: mover.y - containerPos.y + }; + } + + // see http://stackoverflow.com/questions/288699/get-the-position-of-a-div-span-tag + var absolutePosition = function(el) { + for (var lx=0, ly=0; el != null; lx += el.offsetLeft, ly += el.offsetTop, el = el.offsetParent); + return {x: lx,y: ly}; + } + + this.refreshServer = function(server) { + self.serverURL = server; + self.localStorage.put("vrmlServer", self.serverURL, null); + self.progressElem.appendChild(self.progress.domNode); + self.progress.start(); + self.xhr.get({ + // The URL to request + url: server, + handleAs:"json", + headers:{"X-Requested-With":null}, + load: function(result) { + self.progress.stop(); + for (id in self.fileStore.query) { + self.fileStore.remove(id); + } + (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}); + } else { + self.fileStore.add({id:parentId+key, name:key, parent:parentId}); + fillstore(tree[key], parentId+key); + } + } + } (result.models, "root", "")); + } + }); + } + + this.setPose = function(imageURL, pose, serverURL) { + if (serverURL && serverURL != self.serverURL) { + self.refreshServer(serverURL); + } + self.imageURL = imageURL; + self.pose = pose; + + var pitch = (pose.pitch % (2 * 3.14159) + 0.5) * 100; + var roll = (pose.roll % (2 * 3.14159) + 0.5) * 100; + var yaw = (pose.yaw % (2 * 3.14159) + 0.5) * 100; + + var x = ((pose.x / 100) + 0.5) * 100; + var y = ((pose.y / 100) + 0.5) * 100; + + var zoom = (((pose.zoom - 1) / 3) + 0.5) * 100; + + self.pitchRollHandlerElem.parentNode.style.right = pitch + "%"; + self.pitchRollHandlerElem.parentNode.style.top = roll + "%"; + + self.yawZoomHandlerElem.parentNode.style.right = yaw + "%"; + self.yawZoomHandlerElem.parentNode.style.top = zoom + "%"; + + self.xyHandlerElem.parentNode.style.right = x + "%"; + self.xyHandlerElem.parentNode.style.top = y + "%"; + + self.updateScene(); + } require(["dojo/dom-construct", "dojo/_base/xhr", "dojo/dom", + "dojo/on", "dojox/storage", - "dojox/layout/FloatingPane", "dojo/store/Memory", "dojo/store/Observable", "dijit/tree/ObjectStoreModel", "dijit/Tree", "dijit/form/TextBox", "dijit/form/Button", - "dijit/form/NumberSpinner", - "dijit/form/VerticalSlider", - "dijit/form/VerticalRuleLabels", - "dijit/form/VerticalRule", - "dijit/form/HorizontalSlider", "dojox/mobile/ProgressIndicator", - "dojo/ready"], + "dijit/form/DropDownButton", + "dijit/TooltipDialog", + "dojo/dnd/Moveable", + "dojo/ready", + "dojo/dnd/Source"], function(domConst, xhr, dom, + on, storage, - FloatingPane, Memory, Observable, ObjectStoreModel, Tree, TextBox, Button, - NumberSpinner, - VerticalSlider, - VerticalRuleLabels, - VerticalRule, - HorizontalSlider, ProgressIndicator, - ready) { + DropDownButton, + TooltipDialog, + Moveable, + ready, + Source) { ready(function() { @@ -78,41 +174,54 @@ function VRMLViewer(element, params) { // establish our dom element.appendChild(domConst.toDom('\ - <div class="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.pose.width + 'px; min-height: ' + self.pose.height + 'px"></img>\ + <img class="model" src="img/Tutorial.png" style="z-index: -1; width: ' + self.pose.width + 'px; height: ' + self.pose.height + 'px"></img>\ <div style="z-index: 1; position: absolute; right: 45%; top: 45%">\ <div class="progress"></div>\ </div>\ - <div style="position: absolute; left: 10px; top: 7%; height: 100%">\ - <div class="pitchSlide"></div>\ + <div style="position: absolute; left: 10px; top: 10px">\ + <table></tr>\ + <td class="browseDropDown" style="vertical-align: middle"></td>\ + <td align="right"><button type="button" class="resetButton"></button></td>\ + <td class="dragHandler" style="vertical-align: middle; padding-top: 4px;"></td>\ + </tr></table>\ </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 style="position: absolute; right: 55%; top: 48%">\ + <div class="pitchRollHandler" style="font-size: 0.5em; background-color: rgba(255,255,255,0.5); border-radius: 5px; moz-border-radius: 5px;">\ + <table>\ + <tr>\ + <td><img class="pitchRollHandlerImg" src="img/pitchRoll.png" width="20px" style="padding: 2px 0px 0px 4px;" /></td>\ + <td><div class="pitchLabel"></div><div class="rollLabel"></div></td>\ + </tr>\ + </table>\ + </div>\ </div>\ - <div style="position: absolute; left: 7%; bottom: 15px;">\ - <div class="yawSlide"></div>\ + <div style="position: absolute; right: 45%; top: 48%">\ + <div class="yawZoomHandler" style="font-size: 0.5em; background-color: rgba(255,255,255,0.5); border-radius: 5px; moz-border-radius: 5px;">\ + <table>\ + <tr>\ + <td><img class="yawZoomHandlerImg" src="img/yawZoom.png" width="20px" style="padding: 2px 0px 0px 4px;" /></td>\ + <td><div class="yawLabel"></div><div class="zoomLabel"></div></td>\ + </tr>\ + </table>\ + </div>\ + </div>\ + <div style="position: absolute; right: 50%; top: 58%">\ + <div class="xyHandler" style="font-size: 0.5em; background-color: rgba(255,255,255,0.5); border-radius: 5px; moz-border-radius: 5px;">\ + <table>\ + <tr>\ + <td><img class="xyHandlerImg" src="img/xy.png" width="20px" style="padding: 2px 0px 0px 4px;" /></td>\ + <td><div class="xLabel"></div><div class="yLabel"></div></td>\ + </tr>\ + </table>\ + </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%">\ @@ -127,28 +236,127 @@ function VRMLViewer(element, params) { // fetch special dom nodes for content self.messageBox = dojo.query("div.messages", element)[0]; self.imgElem = dojo.query("img.model", element)[0]; - self.serverBoxElem = dojo.query("div.server", element)[0]; - self.browseButtonElem = dojo.query("button.browseButton", element)[0]; - self.fileListElem = dojo.query("div.fileList", element)[0]; + self.browseDropDownElem = dojo.query("td.browseDropDown", element)[0]; self.resetButtonElem = dojo.query("button.resetButton", element)[0]; - self.xSpinnerElem = dojo.query("input.xSpinner", element)[0]; - self.ySpinnerElem = dojo.query("input.ySpinner", element)[0]; - self.pitchSlideElem = dojo.query("div.pitchSlide", element)[0]; - self.rollSlideElem = dojo.query("div.rollSlide", element)[0]; - self.yawSlideElem = dojo.query("div.yawSlide", element)[0]; - self.zoomSlideElem = dojo.query("div.zoomSlide", element)[0]; self.progressElem = dojo.query("div.progress", element)[0]; - self.floatPaneElem = dojo.query("div.floatPane", element)[0]; + self.pitchRollHandlerElem = dojo.query(".pitchRollHandler", element)[0]; + self.yawZoomHandlerElem = dojo.query(".yawZoomHandler", element)[0]; + self.xyHandlerElem = dojo.query(".xyHandler", element)[0]; - 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", - }, self.floatPaneElem); - self.floatPane.startup(); + self.pitchRollHandler = new Moveable(self.pitchRollHandlerElem); + self.pitchRollHandler.onMoveStop = function(mover) { + var handlerImg = dojo.query("img.pitchRollHandlerImg", mover.node)[0]; + var pitchLabel = dojo.query("div.pitchLabel", mover.node)[0]; + var rollLabel = dojo.query("div.rollLabel", mover.node)[0]; + + pitchLabel.innerHTML = ''; + rollLabel.innerHTML = ''; + + self.updateScene(); + } + self.pitchRollHandler.onMoving = function(mover) { + // mover.node.style.backgroundColor = "rgba(255,255,255,0.5)"; + // mover.node.style.borderRadius = "5px"; + // mover.node.style.mozBorderRadius = "5px"; + // mover.node.style.webkitBorderRadius = "5px"; + var handlerImg = dojo.query(".pitchRollHandlerImg", mover.node)[0]; + var pitchLabel = dojo.query(".pitchLabel", mover.node)[0]; + var rollLabel = dojo.query(".rollLabel", mover.node)[0]; + var offset = moverRelativeTo(handlerImg, self.element); + + // self.pose.pitch = self.pose.pitch % (2 * 3.14159); + // self.pose.roll = self.pose.roll % (2 * 3.14159); + self.pose.roll = offset.x / self.pose.width - 0.5; + self.pose.pitch = offset.y / self.pose.height - 0.5; + self.pose.roll *= 2 * 3.14159; + self.pose.pitch *= 2 * 3.14159; + self.pose.roll = Math.ceil((self.pose.roll) * 10) / 10; + self.pose.pitch = Math.ceil((self.pose.pitch) * 10) / 10; + pitchLabel.innerHTML = 'Pitch:' + self.pose.pitch; + rollLabel.innerHTML = 'Roll:' + self.pose.roll; + } + + self.yawZoomHandler = new Moveable(self.yawZoomHandlerElem); + self.yawZoomHandler.onMoveStop = function(mover) { + var handlerImg = dojo.query("img.yawZoomHandlerImg", mover.node)[0]; + var yawLabel = dojo.query("div.yawLabel", mover.node)[0]; + var zoomLabel = dojo.query("div.zoomLabel", mover.node)[0]; + + yawLabel.innerHTML = ''; + zoomLabel.innerHTML = ''; + + self.updateScene(); + } + self.yawZoomHandler.onMoving = function(mover) { + var handlerImg = dojo.query("img.yawZoomHandlerImg", mover.node)[0]; + var yawLabel = dojo.query("div.yawLabel", mover.node)[0]; + var zoomLabel = dojo.query("div.zoomLabel", mover.node)[0]; + var offset = moverRelativeTo(handlerImg, self.element); + + // self.pose.pitch = self.pose.pitch % (2 * 3.14159); + // self.pose.roll = self.pose.roll % (2 * 3.14159); + self.pose.yaw = (self.pose.width - offset.x) / self.pose.width - 0.5; + self.pose.zoom = offset.y / self.pose.height - 0.5; + self.pose.yaw *= 2 * 3.14159; + self.pose.zoom = self.pose.zoom * 3 + 1; + self.pose.zoom = Math.ceil((self.pose.zoom) * 10) / 10; + self.pose.yaw = Math.ceil((self.pose.yaw) * 10) / 10; + yawLabel.innerHTML = 'Yaw:' + self.pose.yaw; + zoomLabel.innerHTML = 'Zoom:' + self.pose.zoom; + } + self.xyHandler = new Moveable(self.xyHandlerElem); + self.xyHandler.onMoveStop = function(mover) { + var handlerImg = dojo.query("img.xyHandlerImg", mover.node)[0]; + var xLabel = dojo.query("div.xLabel", mover.node)[0]; + var yLabel = dojo.query("div.yLabel", mover.node)[0]; + + xLabel.innerHTML = ''; + yLabel.innerHTML = ''; + + self.updateScene(); + } + self.xyHandler.onMoving = function(mover) { + var handlerImg = dojo.query("img.xyHandlerImg", mover.node)[0]; + var xLabel = dojo.query("div.xLabel", mover.node)[0]; + var yLabel = dojo.query("div.yLabel", mover.node)[0]; + var offset = moverRelativeTo(handlerImg, self.element); + + self.pose.x = offset.x / self.pose.width - 0.5; + self.pose.y = offset.y / self.pose.height - 0.5; + self.pose.x *= 100; + self.pose.y *= 100; + self.pose.y = Math.ceil((self.pose.y) * 10) / 10; + self.pose.x = Math.ceil((self.pose.x) * 10) / 10; + xLabel.innerHTML = 'X:' + self.pose.x; + yLabel.innerHTML = 'Y:' + self.pose.y; + } + + self.createAvatar = function(item, mode) { + if (mode == 'avatar') { + // create your avatar if you want + var avatar = dojo.create( 'div', { innerHTML: item.data }); + var avatarPose = dojo.clone(self.pose); + avatarPose.width=60; + avatarPose.height=60; + var avatarImgUrl = urlSuffixForPose(avatarPose); + avatar.innerHTML = '<img src=' + self.imageURL + avatarImgUrl + ' /> '; + item.srcEcc = "VRMLViewer"; + item.iconPoseUrl = self.imageURL + avatarImgUrl; + item.imageURL = self.imageURL; + item.serverURL = self.serverURL; + item.pose = avatarPose; + return {node: avatar, data: item, type: item.type}; + } + console.log(item, mode); + var handler = dojo.create( 'div', { innerHTML: '<img src="img/drag.png" width="20px" />' }); + return {node: handler, data: item, type: item.type}; + }; + + self.dragHandler = new Source(dojo.query("td.dragHandler", element)[0], {copyOnly: true, creator: self.createAvatar}); + self.dragHandler.insertNodes(false, [ { } ]); + // setup fileStore for tree list self.fileStore = new Observable(new Memory({ data: [ { id: 'root', name:'3D Models'} ], @@ -163,12 +371,12 @@ function VRMLViewer(element, params) { // setup actual tree dijit self.fileList = new dijit.Tree({ - id: "fileList", + id: "fileList" + self.instanceId, model: self.fileTreeModel, persist: false, showRoot: false, + style: "height: 200px;", 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(); @@ -184,14 +392,8 @@ function VRMLViewer(element, params) { } //return {backgroundImage: "url('" + item.url + "?width=16&height=16')"}; - }).placeAt(self.fileListElem); - //tree.dndController.singular = true; - - var savedServerURL = self.localStorage.get("vrmlServer"); - if (savedServerURL) { - self.serverURL = savedServerURL; - } - + }); + self.serverBox = new TextBox({ name: "Server", value: self.serverURL, @@ -205,221 +407,57 @@ function VRMLViewer(element, params) { return false; } }, - }, self.serverBoxElem); - - - 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.domNode.style.top = "10px"; - self.floatPane.domNode.style.left = "10px"; -// self.floatPane.startup(); - self.floatPane.show(); - } - }, self.resetButtonElem); - - self.xSpinner = new NumberSpinner({ - value: 0, - smallDelta: 1, - constraints: { places:0 }, - style: "width:60px", - onChange: function(value){ - self.pose.x = value; - self.updateScene(); - } - }, self.xSpinnerElem ); - - self.ySpinner = new NumberSpinner({ - value: 0, - smallDelta: 1, - constraints: { places:0 }, - style: "width:60px", - onChange: function(value){ - self.pose.y = value; - self.updateScene(); - } - }, self.ySpinnerElem ); - - self.zSpinner = new NumberSpinner({ - value: 0, - smallDelta: 1, - constraints: { places:0 }, - style: "width:60px", - onChange: function(value){ - self.pose.z = value; - self.updateScene(); - } - }, self.zSpinnerElem ); + }); self.browseButton = new Button({ label: "Browse", onClick: function(){ self.refreshServer(self.serverBox.get("value")); } - }, self.browseButtonElem); - - // add zoom slider - self.zoomSlide = new VerticalSlider({ - minimum: 0.001, - showButtons: false, - maximum: 1, - value: 1, - intermediateChanges: false, - style: "height: 90%", - onChange: function(value){ - self.pose.zoom = Math.ceil(value * 1000) / 1000; - self.updateScene(); - } - }, self.zoomSlideElem); - - // add pitch slider - // 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.browseDropDownContent = domConst.toDom('<div />'); + self.browseDropDownContent.appendChild(self.serverBox.domNode); + self.browseDropDownContent.appendChild(self.browseButton.domNode); + self.browseDropDownContent.appendChild(self.fileList.domNode); - self.pitchSlide = new VerticalSlider({ - minimum: 0, - showButtons: false, - maximum: 2 * 3.14159, - value: 0, - intermediateChanges: false, - style: "height: 90%", - onChange: function(value){ - self.pose.pitch = Math.ceil(value * 100) / 100; - self.updateScene(); - } - }, self.pitchSlideElem); + self.browseToolTip = new TooltipDialog({ content:self.browseDropDownContent, style:"max-height:200px"}); + self.browseDropDown = new DropDownButton({ label: "Files", dropDown: self.browseToolTip }); + self.browseDropDownElem.appendChild(self.browseDropDown.domNode); - // add roll slider - self.rollSlide = new HorizontalSlider({ - minimum: 0, - showButtons: false, - maximum: 2 * 3.14159, - intermediateChanges: false, - style: "width: 90%", - onChange: function(value){ - self.pose.roll = Math.ceil(value * 100) / 100; - self.updateScene(); - } - }, self.rollSlideElem); + self.resetButton = new Button({ + label: "Reset", + onClick: function(){ + self.pose.x = 0; + self.pose.y = 0; + self.pose.pitch = 0; + self.pose.roll = 0; + self.pose.yaw = 0; + self.pose.zoom = 1; + + self.xyHandler.node.style.left = 0; + self.xyHandler.node.style.top = 0; + self.pitchRollHandler.node.style.left = 0; + self.pitchRollHandler.node.style.top = 0; + self.yawZoomHandler.node.style.left = 0; + self.yawZoomHandler.node.style.top = 0; - // add yaw slider - self.yawSlide = new HorizontalSlider({ - minimum: 0, - showButtons: false, - maximum: 2 * 3.14159, - intermediateChanges: false, - style: "width: 90%", - onChange: function(value){ - self.pose.yaw = Math.ceil(value * 100) / 100; self.updateScene(); } - }, self.yawSlideElem); - - }) - }); + }, self.resetButtonElem); - // privileged public methods - this.updateScene = function() { - if (self.imageURL && !self.batchChanges) { - self.imgElem.src = self.imageURL + - '?width=' + self.pose.width + - '&height=' + self.pose.height + - '&pitch=' + self.pose.pitch + - '&roll=' + self.pose.roll + - '&yaw=' + self.pose.yaw + - '&x=' + self.pose.x + - '&y=' + self.pose.y + - '&z=' + self.pose.z + - '&zoom=' + self.pose.zoom + - '&autorotate=' + (self.pose.autorotate ? '1' : '0'); - } - } + // do we have parameters for the initial pose? + if(self.params) + self.setPose(self.params.imageURL, self.params.pose, self.params.serverURL); - this.refreshServer = function(server) { - self.serverURL = server; - self.localStorage.put("vrmlServer", self.serverURL, null); - self.progressElem.appendChild(self.progress.domNode); - self.progress.start(); - self.xhr.get({ - // The URL to request - url: server, - handleAs:"json", - headers:{"X-Requested-With":null}, - load: function(result) { - self.progress.stop(); - for (id in self.fileStore.query) { - self.fileStore.remove(id); + var savedServerURL = self.localStorage.get("vrmlServer"); + if (savedServerURL && !self.serverURL) { + self.serverURL = savedServerURL; + self.refreshServer(savedServerURL); } - (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", "")); - } - }); - } - this.setPose = function(imageURL, pose, serverURL) { - if (serverURL && serverURL != self.serverURL) { - refreshServer(serverURL); - } - self.imageURL = imageURL; - self.pose = pose; - - self.batchChanges = true; -// self.fileList.set('item', imageURL); - self.xSpinner.set('value',pose.x); - self.ySpinner.set('value',pose.y); - self.zSpinner.set('value',pose.z); - self.pitchSlide.attr('value',pose.pitch); - self.rollSlide.attr('value',pose.roll); - self.yawSlide.attr('value',pose.yaw); - self.zoomSlide.attr('value',pose.zoom); - self.batchChanges = false; - updateScene(); - } + }) + }); -/* - 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; - // } -*/ } diff --git a/apps/mmi-browser.cpp b/apps/uscxml-browser.cpp index a6db6ec..afe4e7f 100644 --- a/apps/mmi-browser.cpp +++ b/apps/uscxml-browser.cpp @@ -105,16 +105,17 @@ void customTerminate() { } void printUsageAndExit() { - printf("mmi-browser version " USCXML_VERSION " (" CMAKE_BUILD_TYPE " build - " CMAKE_COMPILER_STRING ")\n"); + printf("uscxml-browser version " USCXML_VERSION " (" CMAKE_BUILD_TYPE " build - " CMAKE_COMPILER_STRING ")\n"); printf("Usage\n"); - printf("\tmmi-browser"); + printf("\tuscxml-browser"); #ifdef BUILD_AS_PLUGINS - printf(" [-p pluginPath]"); + printf(" [-d pluginPath]"); #endif printf(" URL\n"); printf("\n"); printf("Options\n"); printf("\t-v : be verbose\n"); + printf("\t-pN : port for HTTP server\n"); printf("\n"); exit(1); } @@ -133,6 +134,7 @@ int main(int argc, char** argv) { } bool verbose = false; + size_t port = 8080; google::InitGoogleLogging(argv[0]); google::LogToStderr(); @@ -140,14 +142,17 @@ int main(int argc, char** argv) { opterr = 0; #endif int option; - while ((option = getopt(argc, argv, "vl:p:")) != -1) { + while ((option = getopt(argc, argv, "vl:d:p:")) != -1) { switch(option) { case 'l': google::InitGoogleLogging(optarg); break; - case 'p': + case 'd': uscxml::Factory::pluginPath = optarg; break; + case 'p': + port = strTo<size_t>(optarg); + break; case 'v': verbose = true; break; @@ -163,6 +168,9 @@ int main(int argc, char** argv) { // std::cout << argv[i] << std::endl; // std::cout << optind << std::endl; + // intialize http server on given port + HTTPServer::getInstance(port); + LOG(INFO) << "Processing " << argv[optind]; Interpreter interpreter = Interpreter::fromURI(argv[optind]); if (interpreter) { diff --git a/apps/mmi-browser.vbs b/apps/uscxml-browser.vbs index fc8ea9a..fc8ea9a 100644 --- a/apps/mmi-browser.vbs +++ b/apps/uscxml-browser.vbs diff --git a/apps/w3c-mmi/MMIEventServlet.cpp b/apps/w3c-mmi/MMIEventServlet.cpp new file mode 100644 index 0000000..5ee1807 --- /dev/null +++ b/apps/w3c-mmi/MMIEventServlet.cpp @@ -0,0 +1,210 @@ +#include "MMIEventServlet.h" +#include <uscxml/NameSpacingParser.h> + +#ifdef _WIN32 +#include <winsock2.h> +#include <windows.h> +#endif + +#include "uscxml/Message.h" +#include <iostream> +#include <event2/dns.h> +#include <event2/buffer.h> +#include <event2/keyvalq_struct.h> + +#include <string.h> + +#include <io/uri.hpp> +#include <glog/logging.h> + +#ifndef _WIN32 +#include <netdb.h> +#include <arpa/inet.h> +#endif + +#define MMI_HTTP_EVENT_CASE(type) \ +else if (boost::iequals(mmiEvent.getLocalName(), #type)) { \ + monIter = _monitors.begin(); \ + while(monIter != _monitors.end()) { \ + (*monIter)->received(type::fromXML(mmiDoc)); \ + monIter++; \ + } \ +} + +namespace uscxml { + + using namespace Arabica::DOM; + + MMIEventServlet::MMIEventServlet(const std::string& path) : _path(path) { + // register at http server + bool success = HTTPServer::registerServlet(_path, this); + assert(success); + } + + MMIEventServlet::~MMIEventServlet() { + HTTPServer* httpServer = HTTPServer::getInstance(); + httpServer->unregisterServlet(this); + } + + void MMIEventServlet::send(const MMIEvent& mmiEvent) { + URL url(mmiEvent.target); + url.addMonitor(this); + + std::stringstream content; + content << mmiEvent.toXML(); + url.setOutContent(content.str()); + url.download(); + + } + + void MMIEventServlet::httpRecvRequest(const HTTPServer::Request& req) { + + // is this a request from a HTML browser? + + + NameSpacingParser* parser = NameSpacingParser::fromXML(req.data.compound.at("content").atom); + if (!parser) { + evhttp_send_error(req.curlReq, 402, NULL); + return; + } + + Document<std::string> mmiDoc = parser->getDocument(); + std::cout << mmiDoc.getNamespaceURI() << std::endl; + Node<std::string> mmiEvent = mmiDoc.getFirstChild(); + // get the first element + while (mmiEvent && mmiEvent.getNodeType() != Node_base::ELEMENT_NODE) { + mmiEvent = mmiEvent.getNextSibling(); + } + // get the contained message + if (boost::iequals(mmiEvent.getLocalName(), "mmi")) { + mmiEvent = mmiEvent.getFirstChild(); + while (mmiEvent && mmiEvent.getNodeType() != Node_base::ELEMENT_NODE) { + mmiEvent = mmiEvent.getNextSibling(); + } + } + std::cout << mmiEvent << std::endl; + + if (!mmiEvent) { + evhttp_send_error(req.curlReq, 402, NULL); + return; + } + + std::set<MMIEventReceiver*>::iterator monIter; + if (false) {} + MMI_HTTP_EVENT_CASE(NewContextRequest) + MMI_HTTP_EVENT_CASE(NewContextResponse) + MMI_HTTP_EVENT_CASE(PrepareRequest) + MMI_HTTP_EVENT_CASE(PrepareResponse) + MMI_HTTP_EVENT_CASE(StartRequest) + MMI_HTTP_EVENT_CASE(StartResponse) + MMI_HTTP_EVENT_CASE(DoneNotification) + MMI_HTTP_EVENT_CASE(CancelRequest) + MMI_HTTP_EVENT_CASE(CancelResponse) + MMI_HTTP_EVENT_CASE(PauseRequest) + MMI_HTTP_EVENT_CASE(PauseResponse) + MMI_HTTP_EVENT_CASE(ResumeRequest) + MMI_HTTP_EVENT_CASE(ResumeResponse) + MMI_HTTP_EVENT_CASE(ExtensionNotification) + MMI_HTTP_EVENT_CASE(ClearContextRequest) + MMI_HTTP_EVENT_CASE(ClearContextResponse) + MMI_HTTP_EVENT_CASE(StatusRequest) + MMI_HTTP_EVENT_CASE(StatusResponse) + else { + LOG(ERROR) << "Unknown MMI Event"; + evhttp_send_error(req.curlReq, 402, NULL); + return; + } + + evhttp_send_reply(req.curlReq, 204, NULL, NULL); + +#if 0 + Event reqEvent = req; + reqEvent.type = Event::EXTERNAL; + bool scxmlStructFound = false; + + if (reqEvent.data.compound["header"].compound.find("Content-Type") != reqEvent.data.compound["header"].compound.end() && + boost::iequals(reqEvent.data.compound["header"].compound["Content-Type"].atom, "application/x-www-form-urlencoded")) { + std::stringstream ss(reqEvent.data.compound["content"].atom); + std::string term; + while(std::getline(ss, term, '&')) { + size_t split = term.find_first_of("="); + if (split != std::string::npos) { + std::string key = evhttp_decode_uri(term.substr(0, split).c_str()); + std::string value = evhttp_decode_uri(term.substr(split + 1).c_str()); + if (boost::iequals(key, "_scxmleventname")) { + reqEvent.name = value; + } else if (boost::iequals(key, "content")) { + reqEvent.initContent(value); + } else { + reqEvent.data.compound[key] = value; + reqEvent.params.insert(std::make_pair(key, value)); + } + } else { + // this is most likely wrong + reqEvent.content = evhttp_decode_uri(term.c_str()); + } + } + } else { + if (reqEvent.data.compound["header"].compound.find("_scxmleventstruct") != reqEvent.data.compound["header"].compound.end()) { + // TODO: this looses all other information + reqEvent = Event::fromXML(evhttp_decode_uri(reqEvent.data.compound["header"].compound["_scxmleventstruct"].atom.c_str())); + scxmlStructFound = true; + } + if (reqEvent.data.compound["header"].compound.find("_scxmleventname") != reqEvent.data.compound["header"].compound.end()) { + reqEvent.name = evhttp_decode_uri(reqEvent.data.compound["header"].compound["_scxmleventname"].atom.c_str()); + } + } + std::map<std::string, Data>::iterator headerIter = reqEvent.data.compound["header"].compound.begin(); + while(headerIter != reqEvent.data.compound["header"].compound.end()) { + reqEvent.data.compound[headerIter->first] = Data(evhttp_decode_uri(headerIter->second.atom.c_str()), Data::VERBATIM); + headerIter++; + } + + + /// test532 + if (reqEvent.name.length() == 0) + reqEvent.name = "http." + req.data.compound.at("type").atom; + + if (!scxmlStructFound) { + // get content into event + reqEvent.data.compound["content"] = Data(req.content, Data::VERBATIM); + } + + evhttp_send_reply(req.curlReq, 200, "OK", NULL); +#endif + } + + void MMIEventServlet::downloadStarted(const URL& url) {} + + void MMIEventServlet::downloadCompleted(const URL& url) { + std::map<std::string, std::pair<URL, SendRequest> >::iterator reqIter = _sendRequests.begin(); + while(reqIter != _sendRequests.end()) { + if (reqIter->second.first == url) { + _sendRequests.erase(reqIter); + return; + } + reqIter++; + } + assert(false); + } + + void MMIEventServlet::downloadFailed(const URL& url, int errorCode) { + + std::map<std::string, std::pair<URL, SendRequest> >::iterator reqIter = _sendRequests.begin(); + while(reqIter != _sendRequests.end()) { + if (reqIter->second.first == url) { + Event failEvent; + failEvent.name = "error.communication"; +// returnEvent(failEvent); + + _sendRequests.erase(reqIter); + return; + } + reqIter++; + } + assert(false); + + } + + +}
\ No newline at end of file diff --git a/apps/w3c-mmi/MMIEventServlet.h b/apps/w3c-mmi/MMIEventServlet.h new file mode 100644 index 0000000..10adbc3 --- /dev/null +++ b/apps/w3c-mmi/MMIEventServlet.h @@ -0,0 +1,59 @@ +#ifndef MMIEVENTSERVLET_H_92WSR1SU +#define MMIEVENTSERVLET_H_92WSR1SU + + +#include "uscxml/concurrency/eventqueue/DelayedEventQueue.h" +#include "uscxml/server/HTTPServer.h" +#include "uscxml/Interpreter.h" +#include "uscxml/Factory.h" +#ifndef _WIN32 +#include <sys/time.h> +#endif + +#include <event2/http.h> +#include <event2/http_struct.h> + +#include <uscxml/plugins/ioprocessor/modality/MMIMessages.h> + +namespace uscxml { + + class MMIEventServlet : public HTTPServlet, public URLMonitor, public MMIEventSender { + public: + MMIEventServlet(const std::string& path); + virtual ~MMIEventServlet(); + + void addMonitor(MMIEventReceiver* mmiMonitor) { + _monitors.insert(mmiMonitor); + } + void removeMonitor(MMIEventReceiver* mmiMonitor) { + _monitors.erase(mmiMonitor); + } + + /// HTTPServlet + void httpRecvRequest(const HTTPServer::Request& req); + void setURL(const std::string& url) { + _url = url; + } + std::string getURL() { return _url; } + + bool canAdaptPath() { + return false; + } + + // URLMonitor + void downloadStarted(const URL& url); + void downloadCompleted(const URL& url); + void downloadFailed(const URL& url, int errorCode); + + // MMIEventSender + virtual void send(const MMIEvent& mmiEvent); + + protected: + std::string _url; + std::string _path; + std::map<std::string, std::pair<URL, SendRequest> > _sendRequests; + std::set<MMIEventReceiver*> _monitors; + }; +} + +#endif /* end of include guard: MMIEVENTSERVLET_H_92WSR1SU */ diff --git a/apps/w3c-mmi/im/MMISessionManager.cpp b/apps/w3c-mmi/im/MMISessionManager.cpp new file mode 100644 index 0000000..768a8a1 --- /dev/null +++ b/apps/w3c-mmi/im/MMISessionManager.cpp @@ -0,0 +1,257 @@ +#include "MMISessionManager.h" +#include <uscxml/NameSpacingParser.h> +#include <uscxml/concurrency/tinythread.h> + +#include <io/uri.hpp> +#include <glog/logging.h> + +namespace uscxml { + + using namespace Arabica::DOM; + + MMISessionManager::MMISessionManager(Interpreter interpreter) : _protoInterpreter(interpreter) { + bool success = HTTPServer::registerServlet(interpreter.getName(), this); + assert(success); + _factory = new Factory(Factory::getInstance()); + _factory->registerIOProcessor(new MMIIOProcessor(this)); + } + + MMISessionManager::~MMISessionManager() { + HTTPServer* httpServer = HTTPServer::getInstance(); + httpServer->unregisterServlet(this); + } + + void MMISessionManager::setupHTMLClient(const HTTPServer::Request& req) { + // open template file + HTTPServer::Reply reply(req); + URL templateURL(_protoInterpreter.getBaseURI().asString() + "/templates/mc-html.html"); + templateURL.download(true); + std::string templateContent = templateURL.getInContent(); + boost::replace_all(templateContent, "${im.url}", _url); + reply.content = templateContent; + HTTPServer::reply(reply); + } + + void MMISessionManager::httpRecvRequest(const HTTPServer::Request& req) { + // is this an initial request from an HTML MC? + if (!req.data["query"]["token"] && // no token in query + boost::iequals(req.data["type"].atom, "get") && // request type is GET + boost::icontains(req.data["header"]["Accept"].atom, "text/html") && // accepts html + req.content.length() == 0) { // no content + setupHTMLClient(req); + return; + } + + // is this a comet longpolling request? + if (req.data["query"]["token"] && + boost::iequals(req.data["type"].atom, "get")) { + std::string token = req.data["query"]["token"].atom; + if (_tokens.find(token) != _tokens.end()) { + MMISessionManager::CometMMISession* comet = static_cast<MMISessionManager::CometMMISession*>(_tokens[token]); + comet->longPoll(req); + return; + } + } + + // assume that there is an mmi event inside + NameSpacingParser* parser = NameSpacingParser::fromXML(req.data.compound.at("content").atom); + if (!parser) { + evhttp_send_error(req.curlReq, 204, NULL); + return; + } + + Node<std::string> mmiEvent = MMIEvent::getEventNode(parser->getDocument()); + if (!mmiEvent) { + evhttp_send_error(req.curlReq, 204, NULL); + return; + } + + switch(MMIEvent::getType(mmiEvent)) { + case MMIEvent::NEWCONTEXTREQUEST: { + received(NewContextRequest::fromXML(mmiEvent), req.data["query"]["token"].atom); + evhttp_send_error(req.curlReq, 204, NULL); + break; + } + case MMIEvent::EXTENSIONNOTIFICATION: { + received(ExtensionNotification::fromXML(mmiEvent)); + evhttp_send_error(req.curlReq, 204, NULL); + break; + } + default: { + LOG(ERROR) << "Unknown MMI Event"; + evhttp_send_error(req.curlReq, 204, NULL); + break; + } + } + } + + void MMISessionManager::received(const ExtensionNotification& mmiEvent) { + assert(_sessions.find(mmiEvent.context) != _sessions.end()); + _sessions[mmiEvent.context]->_interpreter.receive(mmiEvent); + } + + void MMISessionManager::received(const NewContextRequest& mmiEvent, const std::string& token) { + + // copy DOM from prototype instance + Arabica::DOM::DOMImplementation<std::string> domFactory = Arabica::SimpleDOM::DOMImplementation<std::string>::getDOMImplementation(); + Arabica::DOM::Document<std::string> newDOM = domFactory.createDocument("", "", 0); + newDOM.appendChild(newDOM.importNode(_protoInterpreter.getDocument().getDocumentElement(), true)); + + // instantiate new interpreter and name it after the context + std::string contextId = Interpreter::getUUID(); + Interpreter interpreter = Interpreter::fromDOM(newDOM); + interpreter.setFactory(_factory); + interpreter.setName(contextId); + interpreter.setNameSpaceInfo(_protoInterpreter.getNameSpaceInfo()); + interpreter.setBaseURI(_protoInterpreter.getBaseURI().asString()); + + MMISession* session; + + if (token.length() > 0) { + session = new MMISessionManager::CometMMISession(); + static_cast<MMISessionManager::CometMMISession*>(session)->_token = token; + _tokens[token] = session; + } else { + // todo handle other cases + session = new MMISessionManager::CometMMISession(); + } + session->_interpreter = interpreter; + + // save interpreter + _sessions[contextId] = session; + + interpreter.start(); + interpreter.receive(mmiEvent); + + } + + void MMISessionManager::received(const NewContextResponse& mmiEvent) { + } + + void MMISessionManager::send(const std::string& name, const SendRequest& req) { + assert(_sessions.find(name) != _sessions.end()); + _sessions[name]->send(req); + } + + void MMISessionManager::CometMMISession::send(const SendRequest& req) { + tthread::lock_guard<tthread::recursive_mutex> lock(_mutex); + if (!_longPollingReq) { + _outQueue.push_back(req); + return; + } + + if (req.dom) { + std::stringstream ss; + Node<std::string> mmiEvent = MMIEvent::getEventNode(req.dom); + HTTPServer::Reply reply(_longPollingReq); + + switch (MMIEvent::getType(mmiEvent)) { + case MMIEvent::NEWCONTEXTRESPONSE: { + NewContextResponse response = NewContextResponse::fromXML(mmiEvent); + ss << response.toXML(); + reply.content = ss.str(); + break; + } + case MMIEvent::STARTREQUEST: { + StartRequest request = StartRequest::fromXML(mmiEvent); + std::cout << mmiEvent << std::endl; + ss << request.toXML(); + reply.content = ss.str(); + break; + } + default: + break; + } + reply.headers["Content-Type"] = "application/xml"; + HTTPServer::reply(reply); + _longPollingReq = HTTPServer::Request(); + } + } + + void MMISessionManager::CometMMISession::receive(const Arabica::DOM::Node<std::string>& msg) { + + } + + void MMISessionManager::CometMMISession::longPoll(const HTTPServer::Request& req) { + tthread::lock_guard<tthread::recursive_mutex> lock(_mutex); + if (_longPollingReq) + evhttp_send_error(_longPollingReq.curlReq, 204, NULL); + _longPollingReq = req; + if (!_outQueue.empty()) { + send(_outQueue.front()); + _outQueue.pop_front(); + } + } + + boost::shared_ptr<IOProcessorImpl> MMISessionManager::MMIIOProcessor::create(InterpreterImpl* interpreter) { + boost::shared_ptr<IOProcessorImpl> ioProc = boost::shared_ptr<IOProcessorImpl>(new MMISessionManager::MMIIOProcessor(_sessionManager)); + return ioProc; + } + + Data MMISessionManager::MMIIOProcessor::getDataModelVariables() { + Data data; + return data; + } + + void MMISessionManager::MMIIOProcessor::send(const SendRequest& req) { + SendRequest reqCopy(req); + + if (req.dom) { + Arabica::DOM::Node<std::string> mmiEvent = MMIEvent::getEventNode(req.dom); + if (!mmiEvent || !mmiEvent.getNodeType() == Node_base::ELEMENT_NODE) + return; + + Arabica::DOM::Element<std::string> mmiElem = Arabica::DOM::Element<std::string>(mmiEvent); + switch (MMIEvent::getType(mmiEvent)) { + case MMIEvent::STARTRESPONSE: + case MMIEvent::PREPARERESPONSE: + case MMIEvent::PAUSERESPONSE: + case MMIEvent::RESUMERESPONSE: + case MMIEvent::CANCELRESPONSE: + case MMIEvent::DONENOTIFICATION: + case MMIEvent::NEWCONTEXTRESPONSE: + case MMIEvent::CLEARCONTEXTRESPONSE: + case MMIEvent::STATUSRESPONSE: { + // all of the above have a status + if (!mmiElem.hasAttributeNS(MMIEvent::nameSpace, "Status")) { + mmiElem.setAttributeNS(MMIEvent::nameSpace, "Status", "Success"); + } + } + case MMIEvent::PAUSEREQUEST: + case MMIEvent::RESUMEREQUEST: + case MMIEvent::CANCELREQUEST: + case MMIEvent::CLEARCONTEXTREQUEST: + case MMIEvent::STATUSREQUEST: { + // all of the above have a context + if (!mmiElem.hasAttributeNS(MMIEvent::nameSpace, "Context")) { + mmiElem.setAttributeNS(MMIEvent::nameSpace, "Context", _interpreter->getName()); + } + } + default: { + if (!mmiElem.hasAttributeNS(MMIEvent::nameSpace, "Source")) { + mmiElem.setAttributeNS(MMIEvent::nameSpace, "Source", _sessionManager->getURL()); + } + if (!mmiElem.hasAttributeNS(MMIEvent::nameSpace, "Target")) { + if (boost::starts_with(_interpreter->getCurrentEvent().name, "mmi.")) { + mmiElem.setAttributeNS(MMIEvent::nameSpace, "Target", _interpreter->getCurrentEvent().origin); + } + } + if (!mmiElem.hasAttributeNS(MMIEvent::nameSpace, "RequestID")) { + if (boost::starts_with(_interpreter->getCurrentEvent().name, "mmi.")) { + mmiElem.setAttributeNS(MMIEvent::nameSpace, "RequestID", _interpreter->getCurrentEvent().sendid); + } + } + } + } + + if (MMIEvent::getType(mmiEvent) == MMIEvent::EXTENSIONNOTIFICATION && !mmiElem.hasAttribute("Name") && req.name.length() > 0) { + mmiElem.setAttribute("Name", req.name); + } + // use session mgr to dispatch to session + + _sessionManager->send(_interpreter->getName(), reqCopy); + } + + } + +}
\ No newline at end of file diff --git a/apps/w3c-mmi/im/MMISessionManager.h b/apps/w3c-mmi/im/MMISessionManager.h new file mode 100644 index 0000000..2ca3d0c --- /dev/null +++ b/apps/w3c-mmi/im/MMISessionManager.h @@ -0,0 +1,81 @@ +#ifndef MMISESSIONMANAGER_H_IHDWUAKD +#define MMISESSIONMANAGER_H_IHDWUAKD + +#include <uscxml/Interpreter.h> +#include <deque> +#include "../MMIEventServlet.h" + +namespace uscxml { + class MMISessionManager : public HTTPServlet { + public: + MMISessionManager(Interpreter interpreter); + ~MMISessionManager(); + + class MMISession { + public: + Interpreter _interpreter; + tthread::recursive_mutex _mutex; + virtual void send(const SendRequest& req) = 0; + virtual void receive(const Arabica::DOM::Node<std::string>& msg) = 0; + }; + + class CometMMISession : public MMISession { + public: + HTTPServer::Request _request; + std::deque<SendRequest> _outQueue; + HTTPServer::Request _longPollingReq; + std::string _token; + + void send(const SendRequest& req); + void receive(const Arabica::DOM::Node<std::string>& msg); + void longPoll(const HTTPServer::Request& req); + }; + + class MMIIOProcessor : public IOProcessorImpl { + public: + MMIIOProcessor(MMISessionManager* sessionManager) : _sessionManager(sessionManager) {} + + // IOProcessorImpl + virtual boost::shared_ptr<IOProcessorImpl> create(InterpreterImpl* interpreter); + virtual std::set<std::string> getNames() { + std::set<std::string> names; + names.insert("mmi.event"); + return names; + } + virtual Data getDataModelVariables(); + virtual void send(const SendRequest& req); + protected: + MMISessionManager* _sessionManager; + }; + + void send(const std::string& name, const SendRequest& req); + + /// HTTPServlet + void httpRecvRequest(const HTTPServer::Request& req); + void setURL(const std::string& url) { + _url = url; + } + std::string getURL() { return _url; } + + bool canAdaptPath() { + return false; + } + + protected: + void received(const NewContextRequest& mmiEvent, const std::string& token = ""); + void received(const NewContextResponse& mmiEvent); + void received(const ExtensionNotification& mmiEvent); + + void setupHTMLClient(const HTTPServer::Request& req); + + Interpreter _protoInterpreter; + Factory* _factory; + DelayedEventQueue _eventQueue; + std::map<std::string, MMISession*> _sessions; + std::map<std::string, MMISession*> _tokens; + std::string _url; + }; +} + + +#endif /* end of include guard: MMISESSIONMANAGER_H_IHDWUAKD */ diff --git a/apps/w3c-mmi/im/uscxml-interaction-manager.cpp b/apps/w3c-mmi/im/uscxml-interaction-manager.cpp new file mode 100644 index 0000000..a7162cc --- /dev/null +++ b/apps/w3c-mmi/im/uscxml-interaction-manager.cpp @@ -0,0 +1,182 @@ +#include "uscxml/config.h" +#include "uscxml/Interpreter.h" +#include <glog/logging.h> +#include "uscxml/concurrency/tinythread.h" + +#include "MMISessionManager.h" + +#ifdef HAS_SIGNAL_H +#include <signal.h> +#endif + +#ifdef HAS_EXECINFO_H +#include <execinfo.h> +#endif + +#ifdef HAS_DLFCN_H +#include <dlfcn.h> +#endif + +#ifdef _WIN32 +#include "XGetopt.h" +#endif + +class VerboseMonitor : public uscxml::InterpreterMonitor { + void onStableConfiguration(uscxml::Interpreter interpreter) { + printConfig(interpreter.getConfiguration()); + } + + void beforeCompletion(uscxml::Interpreter interpreter) { + printConfig(interpreter.getConfiguration()); + } + + void printConfig(const Arabica::XPath::NodeSet<std::string>& config) { + std::string seperator; + std::cout << "Config: {"; + for (int i = 0; i < config.size(); i++) { + std::cout << seperator << ATTR(config[i], "id"); + seperator = ", "; + } + std::cout << "}" << std::endl; + } +}; + + +#ifdef HAS_EXECINFO_H +void printBacktrace(void** array, int size) { + char** messages = backtrace_symbols(array, size); + for (int i = 0; i < size && messages != NULL; ++i) { + std::cerr << "\t" << messages[i] << std::endl; + } + std::cerr << std::endl; + free(messages); +} + +#ifdef HAS_DLFCN_H +// see https://gist.github.com/nkuln/2020860 +typedef void (*cxa_throw_type)(void *, void *, void (*) (void *)); +cxa_throw_type orig_cxa_throw = 0; + +void load_orig_throw_code() { + orig_cxa_throw = (cxa_throw_type) dlsym(RTLD_NEXT, "__cxa_throw"); +} + +extern "C" +void __cxa_throw (void *thrown_exception, void *pvtinfo, void (*dest)(void *)) { + std::cerr << __FUNCTION__ << " will throw exception from " << std::endl; + if (orig_cxa_throw == 0) + load_orig_throw_code(); + + void *array[50]; + size_t size = backtrace(array, 50); + printBacktrace(array, size); + orig_cxa_throw(thrown_exception, pvtinfo, dest); +} +#endif +#endif + + +// see http://stackoverflow.com/questions/2443135/how-do-i-find-where-an-exception-was-thrown-in-c +void customTerminate() { + static bool tried_throw = false; + try { + // try once to re-throw currently active exception + if (!tried_throw) { + throw; + tried_throw = true; + } else { + tried_throw = false; + }; + } catch (const std::exception &e) { + std::cerr << __FUNCTION__ << " caught unhandled exception. what(): " + << e.what() << std::endl; + } catch (const uscxml::Event &e) { + std::cerr << __FUNCTION__ << " caught unhandled exception. Event: " + << e << std::endl; + } catch (...) { + std::cerr << __FUNCTION__ << " caught unknown/unhandled exception." + << std::endl; + } + +#ifdef HAS_EXECINFO_H + void * array[50]; + int size = backtrace(array, 50); + + printBacktrace(array, size); +#endif + abort(); +} + +void printUsageAndExit() { + printf("uscxml-browser version " USCXML_VERSION " (" CMAKE_BUILD_TYPE " build - " CMAKE_COMPILER_STRING ")\n"); + printf("Usage\n"); + printf("\tuscxml-browser"); +#ifdef BUILD_AS_PLUGINS + printf(" [-d pluginPath]"); +#endif + printf(" URL\n"); + printf("\n"); + printf("Options\n"); + printf("\t-v : be verbose\n"); + printf("\t-pN : port for HTTP server\n"); + printf("\n"); + exit(1); +} + +int main(int argc, char** argv) { + using namespace uscxml; + + std::set_terminate(customTerminate); + +#if defined(HAS_SIGNAL_H) && !defined(WIN32) + signal(SIGPIPE, SIG_IGN); +#endif + + if (argc < 2) { + printUsageAndExit(); + } + + bool verbose = false; + size_t port = 8080; + google::InitGoogleLogging(argv[0]); + google::LogToStderr(); + +#ifndef _WIN32 + opterr = 0; +#endif + int option; + while ((option = getopt(argc, argv, "vl:d:p:")) != -1) { + switch(option) { + case 'l': + google::InitGoogleLogging(optarg); + break; + case 'd': + uscxml::Factory::pluginPath = optarg; + break; + case 'p': + port = strTo<size_t>(optarg); + break; + case 'v': + verbose = true; + break; + case '?': + break; + default: + printUsageAndExit(); + break; + } + } + + // intialize http server on given port + HTTPServer::getInstance(port); + + LOG(INFO) << "Processing " << argv[optind]; + Interpreter protoInterpreter = Interpreter::fromURI(argv[optind]); + if (protoInterpreter) { + MMISessionManager mmiSessionManager(protoInterpreter); + while(true) + tthread::this_thread::sleep_for(tthread::chrono::milliseconds(1000)); + } + + return EXIT_SUCCESS; +}
\ No newline at end of file diff --git a/apps/w3c-mmi/mc/uscxml-modality-component.cpp b/apps/w3c-mmi/mc/uscxml-modality-component.cpp new file mode 100644 index 0000000..afe4e7f --- /dev/null +++ b/apps/w3c-mmi/mc/uscxml-modality-component.cpp @@ -0,0 +1,191 @@ +#include "uscxml/config.h" +#include "uscxml/Interpreter.h" +#include <glog/logging.h> + +#ifdef HAS_SIGNAL_H +#include <signal.h> +#endif + +#ifdef HAS_EXECINFO_H +#include <execinfo.h> +#endif + +#ifdef HAS_DLFCN_H +#include <dlfcn.h> +#endif + +#ifdef _WIN32 +#include "XGetopt.h" +#endif + +class VerboseMonitor : public uscxml::InterpreterMonitor { + void onStableConfiguration(uscxml::Interpreter interpreter) { + printConfig(interpreter.getConfiguration()); + } + + void beforeCompletion(uscxml::Interpreter interpreter) { + printConfig(interpreter.getConfiguration()); + } + + void printConfig(const Arabica::XPath::NodeSet<std::string>& config) { + std::string seperator; + std::cout << "Config: {"; + for (int i = 0; i < config.size(); i++) { + std::cout << seperator << ATTR(config[i], "id"); + seperator = ", "; + } + std::cout << "}" << std::endl; + } +}; + + +#ifdef HAS_EXECINFO_H +void printBacktrace(void** array, int size) { + char** messages = backtrace_symbols(array, size); + for (int i = 0; i < size && messages != NULL; ++i) { + std::cerr << "\t" << messages[i] << std::endl; + } + std::cerr << std::endl; + free(messages); +} + +#ifdef HAS_DLFCN_H +// see https://gist.github.com/nkuln/2020860 +typedef void (*cxa_throw_type)(void *, void *, void (*) (void *)); +cxa_throw_type orig_cxa_throw = 0; + +void load_orig_throw_code() { + orig_cxa_throw = (cxa_throw_type) dlsym(RTLD_NEXT, "__cxa_throw"); +} + +extern "C" +void __cxa_throw (void *thrown_exception, void *pvtinfo, void (*dest)(void *)) { + std::cerr << __FUNCTION__ << " will throw exception from " << std::endl; + if (orig_cxa_throw == 0) + load_orig_throw_code(); + + void *array[50]; + size_t size = backtrace(array, 50); + printBacktrace(array, size); + orig_cxa_throw(thrown_exception, pvtinfo, dest); +} +#endif +#endif + + +// see http://stackoverflow.com/questions/2443135/how-do-i-find-where-an-exception-was-thrown-in-c +void customTerminate() { + static bool tried_throw = false; + try { + // try once to re-throw currently active exception + if (!tried_throw) { + throw; + tried_throw = true; + } else { + tried_throw = false; + }; + } catch (const std::exception &e) { + std::cerr << __FUNCTION__ << " caught unhandled exception. what(): " + << e.what() << std::endl; + } catch (const uscxml::Event &e) { + std::cerr << __FUNCTION__ << " caught unhandled exception. Event: " + << e << std::endl; + } catch (...) { + std::cerr << __FUNCTION__ << " caught unknown/unhandled exception." + << std::endl; + } + +#ifdef HAS_EXECINFO_H + void * array[50]; + int size = backtrace(array, 50); + + printBacktrace(array, size); +#endif + abort(); +} + +void printUsageAndExit() { + printf("uscxml-browser version " USCXML_VERSION " (" CMAKE_BUILD_TYPE " build - " CMAKE_COMPILER_STRING ")\n"); + printf("Usage\n"); + printf("\tuscxml-browser"); +#ifdef BUILD_AS_PLUGINS + printf(" [-d pluginPath]"); +#endif + printf(" URL\n"); + printf("\n"); + printf("Options\n"); + printf("\t-v : be verbose\n"); + printf("\t-pN : port for HTTP server\n"); + printf("\n"); + exit(1); +} + +int main(int argc, char** argv) { + using namespace uscxml; + + std::set_terminate(customTerminate); + +#if defined(HAS_SIGNAL_H) && !defined(WIN32) + signal(SIGPIPE, SIG_IGN); +#endif + + if (argc < 2) { + printUsageAndExit(); + } + + bool verbose = false; + size_t port = 8080; + google::InitGoogleLogging(argv[0]); + google::LogToStderr(); + +#ifndef _WIN32 + opterr = 0; +#endif + int option; + while ((option = getopt(argc, argv, "vl:d:p:")) != -1) { + switch(option) { + case 'l': + google::InitGoogleLogging(optarg); + break; + case 'd': + uscxml::Factory::pluginPath = optarg; + break; + case 'p': + port = strTo<size_t>(optarg); + break; + case 'v': + verbose = true; + break; + case '?': + break; + default: + printUsageAndExit(); + break; + } + } + +// for (int i = 0; i < argc; i++) +// std::cout << argv[i] << std::endl; +// std::cout << optind << std::endl; + + // intialize http server on given port + HTTPServer::getInstance(port); + + LOG(INFO) << "Processing " << argv[optind]; + Interpreter interpreter = Interpreter::fromURI(argv[optind]); + if (interpreter) { + interpreter.setCmdLineOptions(argc, argv); +// interpreter->setCapabilities(Interpreter::CAN_NOTHING); +// interpreter->setCapabilities(Interpreter::CAN_BASIC_HTTP | Interpreter::CAN_GENERIC_HTTP); + + if (verbose) { + VerboseMonitor* vm = new VerboseMonitor(); + interpreter.addMonitor(vm); + } + + interpreter.start(); + while(interpreter.runOnMainThread(25)); + } + + return EXIT_SUCCESS; +}
\ No newline at end of file |