diff --git a/ajanDemoService.js b/ajanDemoService.js new file mode 100644 index 00000000..6b373a89 --- /dev/null +++ b/ajanDemoService.js @@ -0,0 +1,174 @@ +const express = require('express'); +const fs = require('fs'); +const http = require('http'); +const WebSocket = require('ws'); +const cors = require('cors'); +const bodyParser = require('body-parser'); + +const app = express(); +const port = 4203; + +//initialize a simple http server +const server = http.createServer(app); +let running = null; + +let response = [{"@id":"http://localhost:4203/ajan-demo-ns#Received"},{"@id":"http://localhost:4203/post","http://localhost:4203/ajan-demo-ns#message":[{"@id":"http://localhost:4203/ajan-demo-ns#Received"}]}]; +let blocked = [{"@id":"http://localhost:4203/ajan-demo-ns#Blocked"},{"@id":"http://localhost:4203/post","http://localhost:4203/ajan-demo-ns#message":[{"@id":"http://localhost:4203/ajan-demo-ns#Blocked"}]}]; +const wss = new WebSocket.Server({ server }); + +app.use(bodyParser.text({ type: 'text/plain', limit: '50mb' })); +app.use(bodyParser.text({ type: 'text/turtle', limit: '50mb' })); +app.use(bodyParser.text({ type: 'text/xml', limit: '50mb' })); +app.use(bodyParser.text({ type: 'application/json', limit: '50mb' })); +app.use(bodyParser.text({ type: 'application/ld+json', limit: '50mb' })); +app.use(bodyParser.text({ type: 'application/sparql-results+xml', limit: '50mb' })); +app.use(bodyParser.text({ type: 'application/trig', limit: '50mb' })); +app.use(bodyParser()); +app.use(function (err, req, res, next) { + console.error(err.stack); +}); + +app.use(cors()); + +app.get('/', (req, res) => { + res.send('Hello World!'); +}); + +app.get('/initScene', (req, res) => { + wss.clients.forEach(client => { + client.send('{ "init": "true" }'); + }); + res.set('Content-Type', 'application/ld+json'); + res.send([{"@id":"http://www.ajan.de/ajan-ns#Scene","http://www.ajan.de/ajan-ns#init":[{"@value":true}]}]); +}); + +app.post('/pickUp', (req, res) => { + console.log(req.body); + let wssMessage = JSON.parse(req.body); + let action = createAction(wssMessage, "pickUp"); + action.blockX = getActionSubject(wssMessage, "http://www.ajan.de/ajan-ns#Table"); + action.asyncResponse = [{"@id": action.blockX,"http://www.ajan.de/behavior/strips-ns#is":[{"@id":"http://www.ajan.de/ajan-ns#Holding"}]},{"@id":"http://www.ajan.de/ajan-ns#Holding"}]; + action.request = wssMessage; + let actionRequest = JSON.stringify(action); + wss.clients.forEach(client => { + client.send(actionRequest); + }); + res.set('Content-Type', 'application/ld+json'); + res.send(response); +}); + +app.post('/stack', (req, res) => { + console.log(req.body); + let wssMessage = JSON.parse(req.body); + let action = createAction(wssMessage, "stack"); + action.blockX = getActionSubject(wssMessage, "http://www.ajan.de/ajan-ns#Holding"); + action.blockY = getActionSubject(wssMessage, "http://www.ajan.de/ajan-ns#Clear"); + action.asyncResponse = [{"@id":"http://www.ajan.de/ajan-ns#Arm","http://www.ajan.de/behavior/strips-ns#is":[{"@id":"http://www.ajan.de/ajan-ns#Empty"}]},{"@id": action.blockX,"http://www.ajan.de/ajan-ns#on":[{"@id": action.blockY}]},{"@id": action.blockY},{"@id":"http://www.ajan.de/ajan-ns#Clear"},{"@id":"http://www.ajan.de/ajan-ns#Empty"},{"@id": action.blockX,"http://www.ajan.de/behavior/strips-ns#is":[{"@id":"http://www.ajan.de/ajan-ns#Clear"}]}]; + action.request = wssMessage; + let actionRequest = JSON.stringify(action); + wss.clients.forEach(client => { + client.send(actionRequest); + }); + res.set('Content-Type', 'application/ld+json'); + res.send(response); +}); + +app.post('/unStack', (req, res) => { + console.log(req.body); + let wssMessage = JSON.parse(req.body); + let action = createAction(wssMessage, "unStack"); + action.blockX = getActionSubject(wssMessage, "http://www.ajan.de/ajan-ns#Clear"); + action.blockY = getActionObject(wssMessage, "http://www.ajan.de/ajan-ns#on"); + action.asyncResponse = [{"@id": action.blockX,"http://www.ajan.de/behavior/strips-ns#is":[{"@id":"http://www.ajan.de/ajan-ns#Holding"}]},{"@id": action.blockY,"http://www.ajan.de/behavior/strips-ns#is":[{"@id":"http://www.ajan.de/ajan-ns#Clear"}]},{"@id":"http://www.ajan.de/ajan-ns#Clear"},{"@id":"http://www.ajan.de/ajan-ns#Holding"}]; + action.request = wssMessage; + let actionRequest = JSON.stringify(action); + wss.clients.forEach(client => { + client.send(actionRequest); + }); + res.set('Content-Type', 'application/ld+json'); + res.send(response); +}); + +app.post('/putDown', (req, res) => { + console.log(req.body); + let wssMessage = JSON.parse(req.body); + let action = createAction(wssMessage, "putDown"); + action.blockX = getActionSubject(wssMessage, "http://www.ajan.de/ajan-ns#Holding"); + action.asyncResponse = [{"@id":"http://www.ajan.de/ajan-ns#Arm","http://www.ajan.de/behavior/strips-ns#is":[{"@id":"http://www.ajan.de/ajan-ns#Empty"}]},{"@id": action.blockX,"http://www.ajan.de/behavior/strips-ns#is":[{"@id":"http://www.ajan.de/ajan-ns#Table"},{"@id":"http://www.ajan.de/ajan-ns#Clear"}]},{"@id":"http://www.ajan.de/ajan-ns#Clear"},{"@id":"http://www.ajan.de/ajan-ns#Empty"},{"@id":"http://www.ajan.de/ajan-ns#Table"}]; + action.request = wssMessage; + let actionRequest = JSON.stringify(action); + wss.clients.forEach(client => { + client.send(actionRequest); + }); + res.set('Content-Type', 'application/ld+json'); + res.send(response); +}); + +wss.on('connection', function connection(ws) { + ws.on('message', function incoming(message) { + console.log('received: %s', message); + if (running == message) { + running = null; + console.log("done!"); + } + }); + + ws.on("error", (err) => { + console.log("Caught flash policy server socket error: "); + console.log(err.stack); + running = null; + }); + + sendConnectMessage(ws); +}); + +server.listen(port, () => { + console.log(`Server started on port ${server.address().port} :)`); +}); + +function sendConnectMessage(ws) { + let wssMessage = {}; + wssMessage.body = "You are now connected to the AJAN Demo Service (ajanDemoService.js)!"; + ws.send(JSON.stringify(wssMessage)); +} + +function createAction(wssMessage, actionType) { + let action = { "action": actionType }; + wssMessage.forEach((entry) => { + if(entry["http://www.ajan.de/actn#asyncRequestURI"] != null) { + console.log(entry["http://www.ajan.de/actn#asyncRequestURI"][0]["@id"]); + action.requestURI = entry["http://www.ajan.de/actn#asyncRequestURI"][0]["@id"]; + } + }); + return action; +} + +function getActionSubject(wssMessage, parameter) { + let result = null; + wssMessage.forEach((entry) => { + if(entry["http://www.ajan.de/behavior/strips-ns#is"] != null) { + entry["http://www.ajan.de/behavior/strips-ns#is"].forEach((object) => { + if(object["@id"] == parameter) { + console.log(entry["@id"]); + result = entry["@id"]; + } + }); + } + }); + return result; +} + +function getActionObject(wssMessage, parameter) { + let result = null; + wssMessage.forEach((entry) => { + if(entry[parameter] != null) { + entry[parameter].forEach((object) => { + if(object["@id"] != null) { + console.log(object["@id"]); + result = object["@id"]; + } + }); + } + }); + return result; +} diff --git a/app/components/agents/instance/agent-instance.js b/app/components/agents/instance/agent-instance.js index f0693874..a9b096b8 100644 --- a/app/components/agents/instance/agent-instance.js +++ b/app/components/agents/instance/agent-instance.js @@ -39,12 +39,14 @@ export default Ember.Component.extend({ agentMessage: " .", messageError: {}, wssConnection: false, + connectionError: false, wssMessage: "", btView: "", debugReport: null, selectedCapability: null, selectedEndpoint: null, + prefixes: { "http://www.w3.org/1999/02/22-rdf-syntax-ns#": "rdf:", "http://www.w3.org/2000/01/rdf-schema#": "rdfs:", @@ -61,6 +63,7 @@ export default Ember.Component.extend({ this._super(...arguments); that = this; this.get("activeInstance").actions = new Array(); + this.actions.connect(); }, didReceiveAttrs() { @@ -162,14 +165,14 @@ export default Ember.Component.extend({ }, connect() { - console.log("connect"); - console.log("ws://" + document.location.hostname + ":4202"); - var socket = that.get('websockets').socketFor("ws://" + document.location.hostname + ":4202"); - console.log(socket); - socket.on('open', myOpenHandler, that); - socket.on('message', myMessageHandler, that); - socket.on('close', myCloseHandler, that); - that.set('socketRef', socket); + console.log("connect"); + console.log("ws://" + document.location.hostname + ":4202"); + var socket = that.get('websockets').socketFor("ws://" + document.location.hostname + ":4202"); + console.log(socket); + socket.on('open', myOpenHandler, that); + socket.on('message', myMessageHandler, that); + socket.on('close', myCloseHandler, that); + that.set('socketRef', socket); }, disconnect() { @@ -245,6 +248,7 @@ function myOpenHandler(event) { that.set("wssMessage", ""); emptyLogs(); setBTView(that); + that.set("connectionError", false); } function myMessageHandler(event) { @@ -393,9 +397,11 @@ function createEditorMessage($textarea, result, report) { } function myCloseHandler(event) { + console.log(event); console.log(`On close event has been called: ${event}`); closeSocket(); setBTView(that); + that.set("connectionError", true); } function closeSocket() { diff --git a/app/components/services/demo-service.js b/app/components/services/demo-service.js new file mode 100644 index 00000000..a4053582 --- /dev/null +++ b/app/components/services/demo-service.js @@ -0,0 +1,408 @@ +/* + * Created on Tue Nov 10 2020 + * + * The MIT License (MIT) + * Copyright (c) 2020 André Antakli (German Research Center for Artificial Intelligence, DFKI). + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software + * and associated documentation files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + * TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +import Ember from "ember"; + +let that; +let $ = Ember.$; + +const timer = ms => new Promise(res => setTimeout(res, ms)) + +let grid = [[{ "x": 0, "y": 0 }, { "x": 9, "y": 0 }, { "x": 18, "y": 0 }, { "x": 27, "y": 0 }], +[{ "x": 0, "y": -4 }, { "x": 9, "y": -4 }, { "x": 18, "y": -4 }, { "x": 27, "y": -4 }], +[{ "x": 0, "y": -8 }, { "x": 9, "y": -8 }, { "x": 18, "y": -8 }, { "x": 27, "y": -8 }], +[{ "x": 0, "y": -12 }, { "x": 9, "y": -12 }, { "x": 18, "y": -12 }, { "x": 27, "y": -12 }]]; + +export default Ember.Component.extend({ + websockets: Ember.inject.service(), + wssConnection: false, + connectionError: false, + socketRef: null, + response: "", + px2em: 16, + grabbed: "", + requestMsg: "", + responseMsg: "", + actionType: "", + + arm: { "id": "demo-arm", "closed": true, "x": 4, "y": -23 }, + purple: { "id": "demo-block-purple", "name": "purple", "d1": 0, "d2": 0, "color": "#EA5B97" }, + orange: { "id": "demo-block-orange", "name": "orange", "d1": 0, "d2": 1, "color": "#FBBD08" }, + blue: { "id": "demo-block-blue", "name": "blue", "d1": 0, "d2": 2, "color": "#1B3284" }, + green: { "id": "demo-block-green", "name": "green", "d1": 0, "d2": 3, "color": "#8EC8B1" }, + table: { "id": "demo-table", "name": "table", "purple": 0, "orange": 1, "blue": 2, "green": 3 }, + + didInsertElement() { + this._super(...arguments); + that = this; + setTriplestoreField(); + setPX2EM(); + this.actions.connect(); + }, + + willDestroyElement() { + this._super(...arguments); + this.actions.disconnect(); + }, + + actions: { + connect() { + var socket = that.get('websockets').socketFor("ws://" + document.location.hostname + ":4203"); + socket.on('open', myOpenHandler, that); + socket.on('message', myMessageHandler, that); + socket.on('close', myCloseHandler, that); + that.set('socketRef', socket); + }, + + disconnect() { + const socket = that.get('socketRef'); + if (socket != null) { + socket.off('open', myOpenHandler); + socket.off('message', myMessageHandler); + socket.off('close', myCloseHandler); + that.set("wssConnection", false); + that.get('websockets').closeSocketFor("ws://" + document.location.hostname + ":4203"); + that.set('socketRef', null); + } + }, + + clean() { + that.set("wssMessage", ""); + }, + } +}) + +function setPX2EM() { + let $arm = $("#" + that.get("arm.id") + ""); + let armTop = parseInt($arm.css("top"), 10); + let y = that.get("arm.y"); + let value = (armTop) / (y); + that.set("px2em", value); +} + +function getDemoObject(value) { + let object = ""; + switch (value) { + case "http://www.ajan.de/ajan-ns#PurpleBlock": + object = that.purple; + break; + case "http://www.ajan.de/ajan-ns#OrangeBlock": + object = that.orange; + break; + case "http://www.ajan.de/ajan-ns#BlueBlock": + object = that.blue; + break; + case "http://www.ajan.de/ajan-ns#GreenBlock": + object = that.green; + break; + case "http://www.ajan.de/ajan-ns#OrangeBlock": + object = that.table; + break; + default: + object = that.table; + } + return object; +} + +function initScene() { + console.log("init scene"); + initBlock(that.get("purple")); + initBlock(that.get("orange")) + initBlock(that.get("blue")) + initBlock(that.get("green")); +} + +function initBlock(block) { + let $block = $("#" + block.id + ""); + $block.css("left", grid[block.d1][block.d2].x + "em"); + $block.css("top", grid[block.d1][block.d2].y + "em"); +} + +async function moveArm2Init(action) { + let $arm = $("#" + that.get("arm.id") + ""); + let armTop = parseInt($arm.css("top"), 10) / that.get("px2em"); + let armLeft = parseInt($arm.css("left"), 10) / that.get("px2em"); + + for(let i=armTop; i>=that.get("arm.y"); i--) { + $arm.css("top", i + "em"); + await timer(50); + } + $arm.css("top", that.get("arm.y") + "em"); + sendResponse(action); +} + +async function pickUpBlock(action) { + let block = getDemoObject(action.blockX); + let $block = $("#" + block.id + ""); + let $arm = $("#" + that.get("arm.id") + ""); + + if($arm.hasClass("closed")) { + $arm.removeClass("closed"); + $arm.addClass("open"); + } + + let armTop = parseInt($arm.css("top"), 10) / that.get("px2em"); + let armLeft = parseInt($arm.css("left"), 10) / that.get("px2em"); + let cell = grid[block.d1][block.d2]; + let blockTop = cell.y; + let blockLeft = cell.x + 4; + + if(armLeft <= blockLeft) { + for(let i=armLeft; i<=blockLeft; i++) { + $arm.css("left", i + "em"); + await timer(50); + } + } else { + for(let i=armLeft; i>=blockLeft; i--) { + $arm.css("left", i + "em"); + await timer(50); + } + } + $arm.css("left", blockLeft + "em"); + + for(let i=armTop; i<=blockTop; i++) { + $arm.css("top", i + "em"); + await timer(50); + } + $arm.css("top", blockTop + "em"); + + if($arm.hasClass("open")) { + $arm.removeClass("open"); + $arm.addClass("closed"); + } + + armTop = parseInt($arm.css("top"), 10) / that.get("px2em"); + for(let i=armTop; i>=that.get("arm.y"); i--) { + $arm.css("top", i + "em"); + $block.css("top", i + "em"); + await timer(50); + } + $arm.css("top", that.get("arm.y") + "em"); + $block.css("top", that.get("arm.y") + "em"); + + that.set("grabbed", block); + sendResponse(action); +} + +async function stackBlock(action) { + let block = getDemoObject(action.blockX); + let dest = getDemoObject(action.blockY); + let $arm = $("#" + that.get("arm.id") + ""); + let $block = $("#" + block.id + ""); + let $dest = $("#" + dest.id + ""); + + let armTop = parseInt($arm.css("top"), 10) / that.get("px2em"); + let armLeft = parseInt($arm.css("left"), 10) / that.get("px2em"); + let destTop = parseInt($dest.css("top"), 10) / that.get("px2em") - 4; + let destLeft = parseInt($dest.css("left"), 10) / that.get("px2em"); + + if(armLeft <= destLeft) { + for(let i=armLeft; i<=destLeft; i++) { + $arm.css("left", i + 4 + "em"); + $block.css("left", i + "em"); + await timer(50); + } + } else { + for(let i=armLeft; i>=destLeft; i--) { + $arm.css("left", i + 4 + "em"); + $block.css("left", i + "em"); + await timer(50); + } + } + $arm.css("left", destLeft + 4 + "em"); + $block.css("left", destLeft + "em"); + + armTop = parseInt($arm.css("top"), 10) / that.get("px2em"); + for(let i=armTop; i=blockLeft; i--) { + $arm.css("left", i + 4 + "em"); + await timer(50); + } + } + $arm.css("left", blockLeft + 4 + "em"); + + for(let i=armTop; i<=blockTop; i++) { + $arm.css("top", i + "em"); + await timer(50); + } + $arm.css("top", blockTop + "em"); + + if($arm.hasClass("open")) { + $arm.removeClass("open"); + $arm.addClass("closed"); + } + + armTop = parseInt($arm.css("top"), 10) / that.get("px2em"); + for(let i=armTop; i>=that.get("arm.y"); i--) { + $arm.css("top", i + "em"); + $block.css("top", i + "em"); + await timer(50); + } + $arm.css("top", that.get("arm.y") + "em"); + $block.css("top", that.get("arm.y") + "em"); + + that.set("grabbed", block); + + sendResponse(action); +} + +async function putDownBlock(action) { + let block = getDemoObject(action.blockX); + let $arm = $("#" + that.get("arm.id") + ""); + let $block = $("#" + block.id + ""); + + let armTop = parseInt($arm.css("top"), 10) / that.get("px2em"); + let armLeft = parseInt($arm.css("left"), 10) / that.get("px2em"); + + let tableTop = grid[0][that.get("table." + block.name + "")].y; + let tableLeft = grid[0][that.get("table." + block.name + "")].x; + + if(armLeft <= tableLeft) { + for(let i=armLeft; i<=tableLeft; i++) { + $arm.css("left", i + 4 + "em"); + $block.css("left", i + "em"); + await timer(50); + } + } else { + for(let i=armLeft; i>=tableLeft; i--) { + $arm.css("left", i + 4 + "em"); + $block.css("left", i + "em"); + await timer(50); + } + } + $arm.css("left", tableLeft + 4 + "em"); + $block.css("left", tableLeft + "em"); + + armTop = parseInt($arm.css("top"), 10) / that.get("px2em"); + for(let i=armTop; i Connect + {{#if connectionError}} +

+ + reportService.js on Port 4202 might not be accessible. + +

+ {{/if}} {{/unless}} diff --git a/app/templates/components/services/action-bar.hbs b/app/templates/components/services/action-bar.hbs index 69048c0c..4fd5defa 100644 --- a/app/templates/components/services/action-bar.hbs +++ b/app/templates/components/services/action-bar.hbs @@ -11,6 +11,11 @@ Test Service {{/link-to}} +
+ {{#link-to "editor.services.demo" class="tabbar-link"}} + AJAN Demo + {{/link-to}} +
{{#ui/menu-bar/dropdown label="File" class="action-bar" diff --git a/app/templates/components/services/demo-service.hbs b/app/templates/components/services/demo-service.hbs new file mode 100644 index 00000000..87ebf55d --- /dev/null +++ b/app/templates/components/services/demo-service.hbs @@ -0,0 +1,59 @@ +
+ +
+ {{services/action-bar}} +
+ +
+ +
+
+

+ AJAN Demo + {{#if wssConnection}} + + {{/if}} + {{#unless wssConnection}} + + {{#if connectionError}} +

+ + reportService.js on Port 4202 might not be accessible. + +

+ {{/if}} + {{/unless}} +

+
+ +

Action: {{actionType}} (Block )

+ +
+
+
Request Content-Type: application/ld+json
+ +
+
+
+
+
+

purple

+

orange

+

blue

+

green

+
+
+
+
Response Content-Type: application/ld+json
+ +
+
+
+
+
+
+ diff --git a/app/templates/components/services/test-service.hbs b/app/templates/components/services/test-service.hbs index 5b7d47ab..f100f6a2 100644 --- a/app/templates/components/services/test-service.hbs +++ b/app/templates/components/services/test-service.hbs @@ -19,13 +19,20 @@ + {{#if connectionError}} +

+ + reportService.js on Port 4202 might not be accessible. + +

+ {{/if}} {{/unless}}

- To test the input for ServiceActions (generated by the Payload Query), you can start a TestService. To do this, execute the startTestService.bat in the ajan-editor root folder. Afterwards you can connect to this service via connect. To see the received input of the TestService create a new ServiceAction change the Request-Uri to "http://localhost:4201/post" and execute it with an behavior action. + To test the input for ServiceActions (generated by the Payload Query), you can start a TestService. To do this, execute the startTestService.bat in the ajan-editor root folder. Afterwards you can connect to this service via connect. To see the received input of the TestService, create a new ServiceAction, change the Request-Uri to "http://localhost:4201/post" and execute it with an behavior action.

Received message

Date: {{wssMessage.date}}
diff --git a/app/templates/editor/services/demo.hbs b/app/templates/editor/services/demo.hbs new file mode 100644 index 00000000..5c8d314e --- /dev/null +++ b/app/templates/editor/services/demo.hbs @@ -0,0 +1,4 @@ +{{services/demo-service}} +{{universal-modal class="modal-parent"}} + +{{outlet}} diff --git a/startAJANDemoService.bat b/startAJANDemoService.bat new file mode 100644 index 00000000..2a94356b --- /dev/null +++ b/startAJANDemoService.bat @@ -0,0 +1,3 @@ +node ajanDemoService.js + +pause diff --git a/startAll.bat b/startAll.bat index b4667979..37b44cc4 100644 --- a/startAll.bat +++ b/startAll.bat @@ -9,6 +9,7 @@ if "%NODE_VERSION%" == "node is not recognized as an internal or external comman start cmd.exe /c startEditor.bat start cmd.exe /c startReportService.bat start cmd.exe /c startTestActionService.bat + start cmd.exe /c startAJANDemoService.bat echo Local IP: ipconfig | findstr "IPv4"