From c6c1f3e2e333705bf7d54fffd4b18939a56f4ed8 Mon Sep 17 00:00:00 2001 From: Stefan Radomski Date: Wed, 20 Mar 2013 09:00:41 +0100 Subject: Started Syteminvoker --- CMakeLists.txt | 21 +- apps/mmi-browser.cpp | 2 + apps/samples/miles-connect.html | 52 --- apps/samples/miles-disconnect.html | 52 --- apps/samples/miles.scxml | 43 --- apps/samples/miles/miles-connect.html | 52 +++ apps/samples/miles/miles-disconnect.html | 52 +++ apps/samples/miles/miles.scxml | 53 +++ apps/samples/server-push/server-push.scxml | 84 +++++ apps/samples/viewer.html | 48 --- apps/samples/viewer.js | 386 ------------------- apps/samples/vrml-server.scxml | 412 --------------------- apps/samples/vrml/README.md | 150 ++++++++ apps/samples/vrml/viewer.html | 48 +++ apps/samples/vrml/viewer.js | 386 +++++++++++++++++++ apps/samples/vrml/vrml-server.scxml | 396 ++++++++++++++++++++ src/uscxml/Factory.cpp | 7 +- src/uscxml/Interpreter.cpp | 19 +- src/uscxml/Interpreter.h | 14 +- src/uscxml/plugins/Plugins.cpp | 1 + src/uscxml/plugins/Plugins.h | 1 + .../plugins/element/postpone/PostponeElement.cpp | 25 +- .../plugins/element/postpone/PostponeElement.h | 7 +- .../plugins/invoker/http/HTTPServletInvoker.cpp | 2 +- .../plugins/invoker/http/HTTPServletInvoker.h | 1 + .../plugins/invoker/system/SystemInvoker.cpp | 44 +++ src/uscxml/plugins/invoker/system/SystemInvoker.h | 40 ++ .../basichttp/libevent/EventIOProcessor.cpp | 8 +- .../basichttp/libevent/EventIOProcessor.h | 2 + src/uscxml/server/HTTPServer.cpp | 31 +- src/uscxml/server/HTTPServer.h | 1 + test/src/test-url.cpp | 48 +++ 32 files changed, 1461 insertions(+), 1027 deletions(-) delete mode 100644 apps/samples/miles-connect.html delete mode 100644 apps/samples/miles-disconnect.html delete mode 100644 apps/samples/miles.scxml create mode 100644 apps/samples/miles/miles-connect.html create mode 100644 apps/samples/miles/miles-disconnect.html create mode 100644 apps/samples/miles/miles.scxml create mode 100644 apps/samples/server-push/server-push.scxml delete mode 100644 apps/samples/viewer.html delete mode 100644 apps/samples/viewer.js delete mode 100644 apps/samples/vrml-server.scxml create mode 100644 apps/samples/vrml/README.md create mode 100644 apps/samples/vrml/viewer.html create mode 100644 apps/samples/vrml/viewer.js create mode 100644 apps/samples/vrml/vrml-server.scxml create mode 100644 src/uscxml/plugins/invoker/system/SystemInvoker.cpp create mode 100644 src/uscxml/plugins/invoker/system/SystemInvoker.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 04261f2..8a10872 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -533,6 +533,25 @@ else() endif() +# System invoker to open a native command + +file(GLOB_RECURSE SYSTEM_INVOKER + src/uscxml/plugins/invoker/system/*.cpp + src/uscxml/plugins/invoker/system/*.h +) +source_group("Invoker\\system" FILES ${SYSTEM_INVOKER}) +include_directories(${PROJECT_SOURCE_DIR}/src/uscxml/plugins/invoker/system/) +if (BUILD_AS_PLUGINS) + add_library( + invoker_system SHARED + ${SYSTEM_INVOKER}) + target_link_libraries(invoker_system uscxml) + set_target_properties(invoker_system PROPERTIES FOLDER "Plugin Invoker") +else() + list (APPEND USCXML_FILES ${SYSTEM_INVOKER}) +endif() + + # SQLite3 SQL Invoker find_package(Sqlite3) @@ -812,7 +831,7 @@ if (MILES_FOUND) # openal is only needed for miles find_package(OpenAL REQUIRED) - find_package(JPEG REQUIRED) +# find_package(JPEG REQUIRED) list (APPEND MILES_LIBRARIES "iconv") list (APPEND MILES_LIBRARIES ${JPEG_LIBRARIES}) include_directories(${OPENAL_INCLUDE_DIR}) diff --git a/apps/mmi-browser.cpp b/apps/mmi-browser.cpp index 8d3a12f..5127d7b 100644 --- a/apps/mmi-browser.cpp +++ b/apps/mmi-browser.cpp @@ -85,6 +85,8 @@ int main(int argc, char** argv) { 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); interpreter->start(); while(interpreter->runOnMainThread(25)); // interpreter->interpret(); diff --git a/apps/samples/miles-connect.html b/apps/samples/miles-connect.html deleted file mode 100644 index ae5f77e..0000000 --- a/apps/samples/miles-connect.html +++ /dev/null @@ -1,52 +0,0 @@ - - - - - - - - - - - - - - - - - - -
-
-
- - diff --git a/apps/samples/miles-disconnect.html b/apps/samples/miles-disconnect.html deleted file mode 100644 index 30f5b39..0000000 --- a/apps/samples/miles-disconnect.html +++ /dev/null @@ -1,52 +0,0 @@ - - - - - - - - - - - - - - - - - - -
-
-
- - diff --git a/apps/samples/miles.scxml b/apps/samples/miles.scxml deleted file mode 100644 index 4eef0eb..0000000 --- a/apps/samples/miles.scxml +++ /dev/null @@ -1,43 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/apps/samples/miles/miles-connect.html b/apps/samples/miles/miles-connect.html new file mode 100644 index 0000000..ae5f77e --- /dev/null +++ b/apps/samples/miles/miles-connect.html @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + +
+
+
+ + diff --git a/apps/samples/miles/miles-disconnect.html b/apps/samples/miles/miles-disconnect.html new file mode 100644 index 0000000..30f5b39 --- /dev/null +++ b/apps/samples/miles/miles-disconnect.html @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + +
+
+
+ + diff --git a/apps/samples/miles/miles.scxml b/apps/samples/miles/miles.scxml new file mode 100644 index 0000000..524729f --- /dev/null +++ b/apps/samples/miles/miles.scxml @@ -0,0 +1,53 @@ + + + + + + + + + + +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/apps/samples/server-push/server-push.scxml b/apps/samples/server-push/server-push.scxml new file mode 100644 index 0000000..2574149 --- /dev/null +++ b/apps/samples/server-push/server-push.scxml @@ -0,0 +1,84 @@ + + + + + +
+
+
+ + + + + + + + + This is awesome! + + + + + + + + + + + + + + + + + + + + +
+ + +]]> +
+
+
+ + + + \ No newline at end of file diff --git a/apps/samples/viewer.html b/apps/samples/viewer.html deleted file mode 100644 index 4cf971b..0000000 --- a/apps/samples/viewer.html +++ /dev/null @@ -1,48 +0,0 @@ - - - - - - - - - - - - - - - - - - -
-
-
- - diff --git a/apps/samples/viewer.js b/apps/samples/viewer.js deleted file mode 100644 index dac3a96..0000000 --- a/apps/samples/viewer.js +++ /dev/null @@ -1,386 +0,0 @@ -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 += '
' + self.serverURL + tree[key].path + '
'; -// self.messageBox.innerHTML += '
' + 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('\ -
\ -
\ -
\ -
\ - \ - \ - \ - \ - \ - \ - \ - \ -
\ -
\ - \ -
\ -
\ -
\ -
\ -
\ -
\ -
\ -
\ -
\ -
\ -
\ -
\ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ -
x:
y:
\ -
\ -
\ -
\ - ')); - - // 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 = '
' + 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 deleted file mode 100644 index c5647b0..0000000 --- a/apps/samples/vrml-server.scxml +++ /dev/null @@ -1,412 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - -
-
-
- - - - - - - -
-
-
- - - - - - - -
-
-
- - - - - - - - -
-
-
- - - - - - - - -
-
-
- - - - - - - \ No newline at end of file diff --git a/apps/samples/vrml/README.md b/apps/samples/vrml/README.md new file mode 100644 index 0000000..2a019da --- /dev/null +++ b/apps/samples/vrml/README.md @@ -0,0 +1,150 @@ +# VRML Server + +The VRML Server allows clients to retrieve sceneshots of 3D models on the filesystem in a variety of formats. + +## General Mode of Operation + +The VRML server will monitor its vrml directory recursively for vrml and wrl +files. Whenever such a file is found, it is converted into a native, binary representation and +saved in the tmp directory. Clients can now access sceneshots of this model by specifying the +desired pose and format. + +## Accessing Sceneshots + +In the simplest case, a sceneshot is retrieved by simply requested its respective URL on the VRML server: + +http://host/vrml/HARD_MP_VAL_000.png + +All paths start with vrml and then reflect the vrml directory structure as it is being monitored. When a directory +foo was added (either by creation or linking) in the vrml directory, its 3D models will be available at: + +http://host/vrml/foo/FANCY_MODEL_000.png + +When you do not pass any parameters, you will get a sceneshot of the model with its largest, axis aligned surface area +facing the camera. That is, the model will be rotated by multiples of 90deg to show the side of the bounding box which +has the largest surface area. The implied assumption is that this side is suited to identify the model and its eventual +problems the quickest. + +If you do not like the standard sceneshot, you can pass a couple of parameters to adapt most aspects of the scene: + + + + + + + + + + + + + +
NameRangeDescription
pitch[0 .. 2π] radRotation along the x-axis
roll[0 .. 2π] radRotation along the z-axis
yaw[0 .. 2π] radRotation along the y-axis
zoom[0 .. ∞] bounding-sphere unitsDistance of camera to model center
x[-∞ .. ∞] OpenGL unitsTranslation on x-axis
y[-∞ .. ∞] OpenGL unitsTranslation on y-axis
z[-∞ .. ∞] OpenGL unitsTranslation on z-axis (consider using zoom instead)
width[0 .. BIG] pixelsThe width of the image (limited by your GPU)
height[0 .. BIG] pixelsThe height of the image
autorotate[on | off]Whether or not to autorotate first
+ +http://host/vrml/HARD_MP_VAL_000.png?pitch=0.3&width=2560&height=1600 + +There are some caveats: +
    +
  • With euler angles such as pitch/roll/yaw, a gimbal lock can occur. +
  • Choosing zoom, x, y or z to big will move the model off the clipping distance. +
  • width and height have no upper limit, this might be a potential DoS. +
  • When observing series of models with autorotating on, not every model is guaranteed to start with the same pose. +
  • The OpenGL units really ought to be expressed in multiples of bounding-sphere units. +
+ +## REST API + +The main purpose of the REST API is to provide clients with a list of available model files. + + + + + + + + + + + + + + + + + + + + + + + + + +
PathTypeReturn ValueExample
/vrmlGET + A JSON structure identifying all known models in the hierarchy as found on the filesystem.
+ + The entries are organized in a tree, reflecting the original locations relative to the vrml + directory. The suffix of png is just one example, supported extensions are ultimately + defined by the available OSG writer plugins, but limited for + now to gif, jpg, png, tif and bmp. +
+
+{
+  "models": {
+    "HARD_MP_VAL_000": {
+      "path": "/HARD_MP_VAL_000.png", 
+      "url": "http://host/vrml/HARD_MP_VAL_000.png"
+    }, 
+    "HARD_MP_VAL_001": {
+      "path": "/HARD_MP_VAL_001.png", 
+      "url": "http://host/vrml/HARD_MP_VAL_001.png"
+    }, 
+    "data": {
+      "SOFT_MP_VAL_000": {
+        "path": "/data/SOFT_MP_VAL_000.png", 
+        "url": "http://host/vrml/data/SOFT_MP_VAL_000.png"
+      }, 
+  ...
+
+
/vrml/models, /vrml/wrlsGET + A JSON structure with information about the available binary model files in the tmp directory or the wrl + files in the vrml directory respectively.
+ + The entries correspond to the tree at /vrml but all paths are flattened using the path delimiter + (':' per default). +
+
+{
+  "HARD_MP_VAL_000": {
+    "atime":        1363002503, 
+    "ctime":        1362521747, 
+    "dir":          "/tmp", 
+    "extension":    "osgb", 
+    "group":        "/", 
+    "mtime":        "1362521747", 
+    "name":         "HARD_MP_VAL_000.osgb", 
+    "path":         "/tmp/HARD_MP_VAL_000.osgb", 
+    "relDir":       "/", 
+    "relPath":      "/HARD_MP_VAL_000.osgb", 
+    "size":         "580201", 
+    "strippedName": "HARD_MP_VAL_000"
+  }, 
+...
+
+
/vrml/processedGET + A JSON structure with information about the sceneshots that were requested recently and are still on disc.
+ + The individual entries within a model key encode the request parameters seperated by underscores, that is:
+ The euler angles pitch, roll, yaw, zoom, translation in x, translation + in y, translation in z, width, height, and whether or not to autorotate. +
+
+{
+  "HARD_MP_VAL_000": {
+    "0.94_0_0_1_0_0_0_640_480_on.png": {
+      "atime":        1363002687, 
+      "ctime":        1363002687, 
+      "dir":          "/tmp", 
+...
+	
+
diff --git a/apps/samples/vrml/viewer.html b/apps/samples/vrml/viewer.html new file mode 100644 index 0000000..4cf971b --- /dev/null +++ b/apps/samples/vrml/viewer.html @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + +
+
+
+ + diff --git a/apps/samples/vrml/viewer.js b/apps/samples/vrml/viewer.js new file mode 100644 index 0000000..dac3a96 --- /dev/null +++ b/apps/samples/vrml/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 += '
' + self.serverURL + tree[key].path + '
'; +// self.messageBox.innerHTML += '
' + 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('\ +
\ +
\ +
\ +
\ + \ + \ + \ + \ + \ + \ + \ + \ +
\ +
\ + \ +
\ +
\ +
\ +
\ +
\ +
\ +
\ +
\ +
\ +
\ +
\ +
\ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ +
x:
y:
\ +
\ +
\ +
\ + ')); + + // 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 = '
' + 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/vrml-server.scxml b/apps/samples/vrml/vrml-server.scxml new file mode 100644 index 0000000..0d6517c --- /dev/null +++ b/apps/samples/vrml/vrml-server.scxml @@ -0,0 +1,396 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + + +
+
+
+ + + + + + + + +
+
+
+ + + + + + + \ No newline at end of file diff --git a/src/uscxml/Factory.cpp b/src/uscxml/Factory.cpp index 4011446..623b551 100644 --- a/src/uscxml/Factory.cpp +++ b/src/uscxml/Factory.cpp @@ -15,6 +15,7 @@ # include "uscxml/plugins/invoker/http/HTTPServletInvoker.h" # include "uscxml/plugins/invoker/heartbeat/HeartbeatInvoker.h" # include "uscxml/plugins/invoker/filesystem/dirmon/DirMonInvoker.h" +# include "uscxml/plugins/invoker/system/SystemInvoker.h" # ifdef UMUNDO_FOUND # include "uscxml/plugins/invoker/umundo/UmundoInvoker.h" @@ -64,6 +65,7 @@ Factory::Factory() { pluma.acceptProviderType(); pluma.acceptProviderType(); pluma.acceptProviderType(); + pluma.acceptProviderType(); pluma.loadFromFolder(pluginPath); std::vector invokerProviders; @@ -159,10 +161,13 @@ Factory::Factory() { registerInvoker(invoker); } { + SystemInvoker* invoker = new SystemInvoker(); + registerInvoker(invoker); + } + { EventIOProcessor* ioProcessor = new EventIOProcessor(); registerIOProcessor(ioProcessor); } - { FetchElement* element = new FetchElement(); registerExecutableContent(element); diff --git a/src/uscxml/Interpreter.cpp b/src/uscxml/Interpreter.cpp index 95f4658..4ae4689 100644 --- a/src/uscxml/Interpreter.cpp +++ b/src/uscxml/Interpreter.cpp @@ -39,7 +39,9 @@ Interpreter::Interpreter() : Arabica::SAX2DOM::Parser() { _parentQueue = NULL; _running = false; _done = false; + _isInitialized = false; _httpServlet = NULL; + _capabilities = CAN_BASIC_HTTP | CAN_GENERIC_HTTP; #ifdef _WIN32 WSADATA wsaData; @@ -170,7 +172,7 @@ Interpreter* Interpreter::fromInputSource(Arabica::SAX::InputSource } else { interpreter->_document = interpreter->Arabica::SAX2DOM::Parser::getDocument(); } - interpreter->init(); +// interpreter->init(); return interpreter; } @@ -191,14 +193,17 @@ void Interpreter::init() { normalize(_document); + if (_capabilities & CAN_GENERIC_HTTP) + _httpServlet = new HTTPServletInvoker(this); + _sendQueue = new DelayedEventQueue(); - _httpServlet = new HTTPServletInvoker(this); _sendQueue->start(); } else { LOG(ERROR) << "Cannot find SCXML element" << std::endl; } } + _isInitialized = true; } Interpreter::~Interpreter() { @@ -259,10 +264,13 @@ bool Interpreter::runOnMainThread(int fps, bool blocking) { // see: http://www.w3.org/TR/scxml/#AlgorithmforSCXMLInterpretation void Interpreter::interpret() { + if (!_isInitialized) + init(); + if (!_scxml) return; // dump(); - + _sessionId = getUUID(); std::string datamodelName; @@ -2520,6 +2528,11 @@ void Interpreter::setupIOProcessors() { tthread::lock_guard lock(_mutex); std::map::iterator ioProcIter = Factory::getInstance()->_ioProcessors.begin(); while(ioProcIter != Factory::getInstance()->_ioProcessors.end()) { + if (boost::iequals(ioProcIter->first, "basichttp") && !(_capabilities & CAN_BASIC_HTTP)) { + ioProcIter++; + continue; + } + _ioProcessors[ioProcIter->first] = Factory::createIOProcessor(ioProcIter->first, this); _ioProcessors[ioProcIter->first].setType(ioProcIter->first); _ioProcessors[ioProcIter->first].setInterpreter(this); diff --git a/src/uscxml/Interpreter.h b/src/uscxml/Interpreter.h index 1a44f80..90c2b3b 100644 --- a/src/uscxml/Interpreter.h +++ b/src/uscxml/Interpreter.h @@ -76,6 +76,12 @@ public: LATE = 1 }; + enum Capabilities { + CAN_NOTHING = 0, + CAN_BASIC_HTTP = 1, + CAN_GENERIC_HTTP = 2, + }; + virtual ~Interpreter(); static Interpreter* fromDOM(const Arabica::DOM::Node& node); @@ -162,6 +168,10 @@ public: return _document; } + void setCapabilities(unsigned int capabilities) { + _capabilities = capabilities; + } + void setName(const std::string& name); const std::string& getName() { return _name; @@ -222,6 +232,7 @@ protected: bool _running; bool _done; + bool _isInitialized; Binding _binding; Arabica::XPath::NodeSet _configuration; Arabica::XPath::NodeSet _statesToInvoke; @@ -306,7 +317,8 @@ protected: long _lastRunOnMainThread; std::string _name; std::string _sessionId; - + unsigned int _capabilities; + Data _cmdLineOptions; IOProcessor getIOProcessor(const std::string& type); diff --git a/src/uscxml/plugins/Plugins.cpp b/src/uscxml/plugins/Plugins.cpp index ba27728..65740e1 100644 --- a/src/uscxml/plugins/Plugins.cpp +++ b/src/uscxml/plugins/Plugins.cpp @@ -6,6 +6,7 @@ namespace uscxml { PLUMA_PROVIDER_SOURCE(DataModelImpl, 1, 1); PLUMA_PROVIDER_SOURCE(IOProcessorImpl, 1, 1); PLUMA_PROVIDER_SOURCE(InvokerImpl, 1, 1); +PLUMA_PROVIDER_SOURCE(ElementImpl, 1, 1); #endif } \ No newline at end of file diff --git a/src/uscxml/plugins/Plugins.h b/src/uscxml/plugins/Plugins.h index 1e6ac0b..338dcaf 100644 --- a/src/uscxml/plugins/Plugins.h +++ b/src/uscxml/plugins/Plugins.h @@ -9,6 +9,7 @@ namespace uscxml { #ifdef BUILD_AS_PLUGINS PLUMA_PROVIDER_HEADER(IOProcessorImpl); PLUMA_PROVIDER_HEADER(InvokerImpl); +PLUMA_PROVIDER_HEADER(ElementImpl); PLUMA_PROVIDER_HEADER(DataModelImpl); #endif diff --git a/src/uscxml/plugins/element/postpone/PostponeElement.cpp b/src/uscxml/plugins/element/postpone/PostponeElement.cpp index 96cda6f..53782e1 100644 --- a/src/uscxml/plugins/element/postpone/PostponeElement.cpp +++ b/src/uscxml/plugins/element/postpone/PostponeElement.cpp @@ -40,6 +40,12 @@ void PostponeElement::enterElement(const Arabica::DOM::Node& node) } } + // chaining causes the event to fire if the condition was true since postponing + bool chained = false; + if (HAS_ATTR(node, "chaining")) { + chained = boost::iequals(ATTR(node, "chaining"), "true"); + } + // when will we refire the event? std::string until; try { @@ -85,36 +91,29 @@ void PostponeElement::enterElement(const Arabica::DOM::Node& node) } #endif Event currEvent = _interpreter->getCurrentEvent(); - Resubmitter::postpone(currEvent, until, 0, _interpreter); + Resubmitter::postpone(currEvent, until, 0, chained, _interpreter); } void PostponeElement::exitElement(const Arabica::DOM::Node& node) { } -void PostponeElement::Resubmitter::postpone(const Event& event, std::string until, uint64_t timeout, Interpreter* interpreter) { +void PostponeElement::Resubmitter::postpone(const Event& event, std::string until, uint64_t timeout, bool chained, Interpreter* interpreter) { Resubmitter* resubmitter = getInstance(interpreter); - resubmitter->_postponedEvents.push_back(Postponed(event, until, timeout)); + resubmitter->_postponedEvents.push_back(Postponed(event, until, timeout, chained)); } void PostponeElement::Resubmitter::onStableConfiguration(Interpreter* interpreter) { std::list::iterator eventIter = _postponedEvents.begin(); + bool dispatched = false; while(eventIter != _postponedEvents.end()) { try { // LOG(INFO) << "Reevaluating: >> " << eventIter->first << " <<"; - if (eventIter->timeout > 0 && tthread::chrono::system_clock::now() < eventIter->timeout) { - // TODO: We should use an event queue -// LOG(INFO) << " -> Timeout"; - eventIter->event.name += ".timeout"; - interpreter->receive(eventIter->event, true); - _postponedEvents.erase(eventIter); - break; - } - if (interpreter->getDataModel().evalAsBool(eventIter->until)) { + if ((!dispatched || eventIter->chaining) && interpreter->getDataModel().evalAsBool(eventIter->until)) { // LOG(INFO) << " -> is TRUE"; eventIter->event.name += ".postponed"; interpreter->receive(eventIter->event, true); _postponedEvents.erase(eventIter); - break; + dispatched = true; } // LOG(INFO) << " -> is FALSE"; } catch (Event e) { diff --git a/src/uscxml/plugins/element/postpone/PostponeElement.h b/src/uscxml/plugins/element/postpone/PostponeElement.h index eb7a738..268493f 100644 --- a/src/uscxml/plugins/element/postpone/PostponeElement.h +++ b/src/uscxml/plugins/element/postpone/PostponeElement.h @@ -13,11 +13,12 @@ namespace uscxml { class PostponeElement : public ExecutableContentImpl { public: struct Postponed { - Postponed(const Event& event, const std::string& until, long timeout) : - event(event), until(until), timeout(timeout) {} + Postponed(const Event& event, const std::string& until, long timeout, bool chaining = false) : + event(event), until(until), timeout(timeout), chaining(chaining) {} Event event; std::string until; uint64_t timeout; + bool chaining; }; PostponeElement() {} @@ -48,7 +49,7 @@ protected: } static Resubmitter* getInstance(Interpreter* interpreter); - static void postpone(const Event& event, std::string until, uint64_t timeout, Interpreter* interpreter); + static void postpone(const Event& event, std::string until, uint64_t timeout, bool chained, Interpreter* interpreter); // InterpreterMonitor void onStableConfiguration(Interpreter* interpreter); diff --git a/src/uscxml/plugins/invoker/http/HTTPServletInvoker.cpp b/src/uscxml/plugins/invoker/http/HTTPServletInvoker.cpp index a3556c2..ad7bb15 100644 --- a/src/uscxml/plugins/invoker/http/HTTPServletInvoker.cpp +++ b/src/uscxml/plugins/invoker/http/HTTPServletInvoker.cpp @@ -30,7 +30,7 @@ HTTPServletInvoker::HTTPServletInvoker(Interpreter* interpreter) { while(!HTTPServer::registerServlet(path.str(), this)) { path.clear(); path.str(); - path << _interpreter->getName() << toStr(i++); + path << _interpreter->getName() << i++; } } diff --git a/src/uscxml/plugins/invoker/http/HTTPServletInvoker.h b/src/uscxml/plugins/invoker/http/HTTPServletInvoker.h index 5d2d4b9..024616d 100644 --- a/src/uscxml/plugins/invoker/http/HTTPServletInvoker.h +++ b/src/uscxml/plugins/invoker/http/HTTPServletInvoker.h @@ -35,6 +35,7 @@ public: virtual void setURL(const std::string& url) { _url = url; } + bool canAdaptPath() { return false; } tthread::recursive_mutex& getMutex() { return _mutex; diff --git a/src/uscxml/plugins/invoker/system/SystemInvoker.cpp b/src/uscxml/plugins/invoker/system/SystemInvoker.cpp new file mode 100644 index 0000000..492d6d3 --- /dev/null +++ b/src/uscxml/plugins/invoker/system/SystemInvoker.cpp @@ -0,0 +1,44 @@ +#include "SystemInvoker.h" +#include + +#ifdef BUILD_AS_PLUGINS +#include +#endif + +namespace uscxml { + +#ifdef BUILD_AS_PLUGINS +PLUMA_CONNECTOR +bool connect(pluma::Host& host) { + host.add(new SystemInvokerProvider()); + return true; +} +#endif + +SystemInvoker::SystemInvoker() { +} + +SystemInvoker::~SystemInvoker() { +}; + +boost::shared_ptr SystemInvoker::create(Interpreter* interpreter) { + boost::shared_ptr invoker = boost::shared_ptr(new SystemInvoker()); + invoker->_interpreter = interpreter; + return invoker; +} + +Data SystemInvoker::getDataModelVariables() { + Data data; + return data; +} + +void SystemInvoker::send(const SendRequest& req) { +} + +void SystemInvoker::cancel(const std::string sendId) { +} + +void SystemInvoker::invoke(const InvokeRequest& req) { +} + +} \ No newline at end of file diff --git a/src/uscxml/plugins/invoker/system/SystemInvoker.h b/src/uscxml/plugins/invoker/system/SystemInvoker.h new file mode 100644 index 0000000..1440e79 --- /dev/null +++ b/src/uscxml/plugins/invoker/system/SystemInvoker.h @@ -0,0 +1,40 @@ +#ifndef SYSTEMINVOKER_H_W09J90F0 +#define SYSTEMINVOKER_H_W09J90F0 + +#include + +#ifdef BUILD_AS_PLUGINS +#include "uscxml/plugins/Plugins.h" +#endif + +namespace uscxml { + +class SystemInvoker : public InvokerImpl { +public: + SystemInvoker(); + virtual ~SystemInvoker(); + virtual boost::shared_ptr create(Interpreter* interpreter); + + virtual std::set getNames() { + std::set names; + names.insert("system"); + names.insert("http://uscxml.tk.informatik.tu-darmstadt.de/#system"); + 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: +}; + +#ifdef BUILD_AS_PLUGINS +PLUMA_INHERIT_PROVIDER(SystemInvoker, Invoker); +#endif + +} + + +#endif /* end of include guard: SYSTEMINVOKER_H_W09J90F0 */ diff --git a/src/uscxml/plugins/ioprocessor/basichttp/libevent/EventIOProcessor.cpp b/src/uscxml/plugins/ioprocessor/basichttp/libevent/EventIOProcessor.cpp index 6c7a8fc..ad47896 100644 --- a/src/uscxml/plugins/ioprocessor/basichttp/libevent/EventIOProcessor.cpp +++ b/src/uscxml/plugins/ioprocessor/basichttp/libevent/EventIOProcessor.cpp @@ -51,9 +51,11 @@ boost::shared_ptr EventIOProcessor::create(Interpreter* interpr // register at http server std::string path = interpreter->getName(); - path += "/basichttp"; - if (!HTTPServer::registerServlet(path, io.get())) { - LOG(ERROR) << "Cannot register basichttp ioprocessor at " << path << ": " << " already taken"; + int i = 2; + while (!HTTPServer::registerServlet(path + "/basichttp", io.get())) { + std::stringstream ss; + ss << interpreter->getName() << i++; + path = ss.str(); } return io; diff --git a/src/uscxml/plugins/ioprocessor/basichttp/libevent/EventIOProcessor.h b/src/uscxml/plugins/ioprocessor/basichttp/libevent/EventIOProcessor.h index 70c6bea..9bb717b 100644 --- a/src/uscxml/plugins/ioprocessor/basichttp/libevent/EventIOProcessor.h +++ b/src/uscxml/plugins/ioprocessor/basichttp/libevent/EventIOProcessor.h @@ -43,6 +43,8 @@ public: _url = url; } + bool canAdaptPath() { return false; } + // URLMonitor void downloadStarted(const URL& url); void downloadCompleted(const URL& url); diff --git a/src/uscxml/server/HTTPServer.cpp b/src/uscxml/server/HTTPServer.cpp index 8be45e6..dd06ab7 100644 --- a/src/uscxml/server/HTTPServer.cpp +++ b/src/uscxml/server/HTTPServer.cpp @@ -56,7 +56,8 @@ HTTPServer::HTTPServer(unsigned short port) { } determineAddress(); - evhttp_set_timeout(_http, 5); +// evhttp_set_timeout(_http, 5); + // generic callback evhttp_set_gencb(_http, HTTPServer::httpRecvReqCallback, NULL); } @@ -217,7 +218,7 @@ void HTTPServer::httpRecvReqCallback(struct evhttp_request *req, void *callbackD request.data.compound["content"] = Data::fromJSON(request.data.compound["content"].atom); } } - + if (callbackData == NULL) { HTTPServer::getInstance()->processByMatchingServlet(request); } else { @@ -236,7 +237,7 @@ void HTTPServer::processByMatchingServlet(const Request& request) { // is the servlet path a prefix of the actual path? std::string servletPath = "/" + servletIter->first; if (boost::iequals(actualPath.substr(0, servletPath.length()), servletPath) && // actual path is a prefix - boost::iequals(actualPath.substr(servletPath.length(), 1), "/")) { // and next character is a '/' + boost::iequals(actualPath.substr(servletPath.length(), 1), "/")) { // and next character is a '/' if (bestPath.length() < servletPath.length()) { // this servlet is a better match bestPath = servletPath; @@ -299,20 +300,34 @@ bool HTTPServer::registerServlet(const std::string& path, HTTPServlet* servlet) HTTPServer* INSTANCE = getInstance(); tthread::lock_guard lock(INSTANCE->_mutex); - if(INSTANCE->_servlets.find(path) != INSTANCE->_servlets.end()) { - return false; + // remove trailing and leading slash + std::string actualPath = path; + if (boost::ends_with(actualPath, "/")) + actualPath = actualPath.substr(0, actualPath.size() - 1); + if (boost::starts_with(actualPath, "/")) + actualPath = actualPath.substr(1); + std::string suffixedPath = actualPath; + + // if this servlet allows to adapt the path, do so + int i = 2; + while(INSTANCE->_servlets.find(suffixedPath) != INSTANCE->_servlets.end()) { + if (!servlet->canAdaptPath()) + return false; + std::stringstream ss; + ss << actualPath << i++; + suffixedPath = ss.str(); } std::stringstream servletURL; - servletURL << "http://" << INSTANCE->_address << ":" << INSTANCE->_port << "/" << path; + servletURL << "http://" << INSTANCE->_address << ":" << INSTANCE->_port << "/" << suffixedPath; servlet->setURL(servletURL.str()); - INSTANCE->_servlets[path] = servlet; + INSTANCE->_servlets[suffixedPath] = servlet; LOG(INFO) << "HTTP Servlet listening at: " << servletURL.str() << std::endl; // register callback - evhttp_set_cb(INSTANCE->_http, ("/" + path).c_str(), HTTPServer::httpRecvReqCallback, servlet); + evhttp_set_cb(INSTANCE->_http, ("/" + suffixedPath).c_str(), HTTPServer::httpRecvReqCallback, servlet); return true; } diff --git a/src/uscxml/server/HTTPServer.h b/src/uscxml/server/HTTPServer.h index 319b62f..990d0a7 100644 --- a/src/uscxml/server/HTTPServer.h +++ b/src/uscxml/server/HTTPServer.h @@ -83,6 +83,7 @@ private: class HTTPServlet { public: virtual void httpRecvRequest(const HTTPServer::Request& request) = 0; + virtual bool canAdaptPath() { return true; } virtual void setURL(const std::string& url) = 0; /// Called by the server with the actual URL }; diff --git a/test/src/test-url.cpp b/test/src/test-url.cpp index 344df9f..5d497e3 100644 --- a/test/src/test-url.cpp +++ b/test/src/test-url.cpp @@ -1,5 +1,7 @@ #include "uscxml/URL.h" #include "uscxml/Message.h" +#include "uscxml/server/HTTPServer.h" + #include #include #include @@ -7,9 +9,54 @@ using namespace uscxml; using namespace boost; +class TestServlet : public HTTPServlet { +public: + TestServlet(bool adaptPath) : _canAdaptPath(adaptPath) {} + + void httpRecvRequest(const HTTPServer::Request& request) {}; + bool canAdaptPath() { return _canAdaptPath; } + void setURL(const std::string& url) { _actualUrl = url; } + + std::string _actualUrl; + bool _canAdaptPath; +}; + int main(int argc, char** argv) { { + TestServlet* testServlet1 = new TestServlet(false); + TestServlet* testServlet2 = new TestServlet(false); + + assert(HTTPServer::registerServlet("/foo", testServlet1)); + assert(!HTTPServer::registerServlet("/foo", testServlet2)); + HTTPServer::unregisterServlet(testServlet1); + assert(HTTPServer::registerServlet("/foo", testServlet2)); + HTTPServer::unregisterServlet(testServlet1); + + assert(HTTPServer::registerServlet("/foo/bar/", testServlet1)); + assert(!HTTPServer::registerServlet("/foo/bar/", testServlet2)); + HTTPServer::unregisterServlet(testServlet1); + HTTPServer::unregisterServlet(testServlet2); + } + + { + TestServlet* testServlet1 = new TestServlet(true); + TestServlet* testServlet2 = new TestServlet(true); + TestServlet* testServlet3 = new TestServlet(true); + + assert(HTTPServer::registerServlet("/foo", testServlet1)); + assert(HTTPServer::registerServlet("/foo", testServlet2)); + assert(HTTPServer::registerServlet("/foo", testServlet3)); + assert(boost::ends_with(testServlet1->_actualUrl, "foo")); + assert(boost::ends_with(testServlet2->_actualUrl, "foo2")); + assert(boost::ends_with(testServlet3->_actualUrl, "foo3")); + + HTTPServer::unregisterServlet(testServlet1); + HTTPServer::unregisterServlet(testServlet2); + HTTPServer::unregisterServlet(testServlet3); + } + + { Data data = Data::fromJSON("asdf"); std::cout << data << std::endl; } @@ -72,4 +119,5 @@ int main(int argc, char** argv) { assert(iequals(url.pathComponents()[2], "Some Spaces")); assert(iequals(url.pathComponents()[3], "index.txt")); } + } \ No newline at end of file -- cgit v0.12