sumorobot-web/sumorobot.js

514 lines
19 KiB
JavaScript
Raw Normal View History

/* disable enable hotkeys */
var hotkeys = true;
/* enable disable remote control */
var remoteControl = false;
2018-01-04 23:09:08 +00:00
/* the local/remote server URL */
2017-09-25 09:37:41 +00:00
//var robotServer = "10.42.0.1";
var robotServer = "iot.koodur.com";
/* the sumorobot code */
var sumocode = "";
2017-09-25 09:37:41 +00:00
/* the sumorobot object */
var sumorobot = null;
2017-10-16 21:11:01 +00:00
/* Blockly workspace */
var workspace = null;
/* the sumorobot state */
var sumostart = false;
/* disable / enable Python code */
var pythonEnabled = false;
2018-01-15 00:25:07 +00:00
/* control_if block id */
var controlBlockId = "";
2017-10-31 16:59:15 +00:00
/* last hightlighted block id */
2018-01-15 00:25:07 +00:00
var lastHighlighted = "";
2017-10-31 16:59:15 +00:00
/* block highlight WebSocket */
2018-01-15 00:25:07 +00:00
var blockHighlight = null;
2017-09-25 09:37:41 +00:00
var Sumorobot = function(wsUri) {
/* assign the WebSocket URI */
this.wsUri = wsUri;
/* start connecting to the WebSocket */
this.connect();
/* to ping the robot */
2017-10-02 19:41:10 +00:00
this.watchdogTimer = null;
2017-09-25 09:37:41 +00:00
};
Sumorobot.prototype.connect = function() {
/* to have access to this object */
var self = this;
this.websocket = new WebSocket(this.wsUri);
/* when the WebSocket gets connected */
this.websocket.onopen = function(evt) {
console.log("INFO websocket connected");
/* setup a timer to ping the robot */
2017-10-02 19:41:10 +00:00
self.watchdogTimer = setInterval(function() {
2017-09-25 09:37:41 +00:00
/* send a ping to the robot */
2017-10-16 21:11:01 +00:00
//console.log("ping the robot")
2017-09-25 09:37:41 +00:00
self.send("ping");
}, 2000);
};
/* when the WebSocket closes */
this.websocket.onclose = function(evt) {
console.log("INFO websocket disconnected");
/* clear the pinging */
2017-10-02 19:41:10 +00:00
clearInterval(self.watchdogTimer);
2017-09-25 09:37:41 +00:00
/* Try to recnnect to the sumorobot */
self.connect();
};
/* when there is a message from the WebSocket */
this.websocket.onmessage = function(evt) {
/* when scope is received */
2017-10-31 16:59:15 +00:00
var data = evt.data.replace(/'/g, '"').toLowerCase();
console.log(data);
var battery = JSON.parse(data)["battery_voltage"];
2018-01-04 23:09:08 +00:00
$("#battery").html(battery + "V");
$("#battery").addClass("connected");
2017-09-25 09:37:41 +00:00
};
/* when there is an WebSocket error */
this.websocket.onerror = function(err) {
console.log("ERROR websocket error: " + err);
};
};
Sumorobot.prototype.send = function(msg) {
/* Ready state constants: CONNECTING 0, OPEN 1, CLOSING 2, CLOSED 3 */
/* https://developer.mozilla.org/en-US/docs/Web/API/WebSocket */
if (this.websocket.readyState == 1) {
this.websocket.send(msg);
}
};
window.onload = function() {
2017-10-02 19:41:10 +00:00
/* function to update the control panel */
function updateControlPanel() {
/* hide all buttons and text fields */
$(".robot-id, .robot-nr, .btn-robot-nr").hide();
/* show the first button and text field */
$(".robot-id:eq(0), .robot-nr:eq(0), .btn-robot-nr:eq(0)").show();
/* adjust the buttons and text fields to be in the middle */
$(".input-group, .btn-group-robot").css("width", "20%");
$(".input-group, .btn-group-robot").css("margin-left", "40%");
/* hide the robot add button */
$(".btn-robot-add").hide();
/* populate robots IDs and buttons */
for (var i = 0; i < 5; i++) {
var id = getLocalStorageItem("sumorobot.robotID" + i);
if (id) {
$(".robot-id:eq(" + i + ")").val(id);
$(".robot-id:eq(" + i + "), .robot-nr:eq(" + i + "), .btn-robot-nr:eq(" + i + ")").show();
$(".input-group, .btn-group-robot").css("width", 20 + (i * 20) + "%");
$(".input-group, .btn-group-robot").css("margin-left", 40 - (i * 10) + "%");
} else {
/* when no robots yet added */
if (i != 0) {
/* show the robot add button */
$(".btn-robot-add").show();
$(".btn-group-robot").css("width", 20 + (i * 20) + "%");
$(".input-group, .btn-group-robot").css("margin-left", 40 - (i * 10) + "%");
/* add click listener to the robot add button */
$(".btn-robot-add").click(function() {
$(".robot-id:eq(" + i + "), .robot-nr:eq(" + i + "), .btn-robot-nr:eq(" + i + ")").show();
$(".input-group, .btn-group-robot").css("width", 20 + (i * 20) + "%");
$(".input-group, .btn-group-robot").css("margin-left", 40 - (i * 10) + "%");
$(this).hide();
});
}
break;
}
}
}
/* load the control panel */
updateControlPanel();
2017-09-25 09:37:41 +00:00
/* remove previous and next statement from if block */
Blockly.Blocks.controls_if.init = function() {
this.setHelpUrl(Blockly.Msg.CONTROLS_IF_HELPURL);
this.setColour(Blockly.Blocks.logic.HUE);
this.appendValueInput("IF0").setCheck("Boolean").appendField(Blockly.Msg.CONTROLS_IF_MSG_IF);
this.appendStatementInput("DO0").appendField(Blockly.Msg.CONTROLS_IF_MSG_THEN);
this.setPreviousStatement(0);
this.setNextStatement(0);
this.setMutator(new Blockly.Mutator(["controls_if_elseif","controls_if_else"]));
var a = this;
this.setTooltip(function() {
if (a.elseifCount_ || a.elseCount_) {
if(!a.elseifCount_ && a.elseCount_) return Blockly.Msg.CONTROLS_IF_TOOLTIP_2;
if(a.elseifCount_ && !a.elseCount_) return Blockly.Msg.CONTROLS_IF_TOOLTIP_3;
if(a.elseifCount_ && a.elseCount_) return Blockly.Msg.CONTROLS_IF_TOOLTIP_4
} else return Blockly.Msg.CONTROLS_IF_TOOLTIP_1;
return "";
});
this.elseCount_=this.elseifCount_ = 0;
};
/* magnify the tool icon on the if block */
Blockly.Icon.prototype.renderIcon = function(cursorX) {
if (this.collapseHidden && this.block_.isCollapsed()) {
this.iconGroup_.setAttribute('display', 'none');
return cursorX;
}
this.iconGroup_.setAttribute('display', 'block');
var SIZE = 1.7;
var TOP_MARGIN = 2;
var LEFT_MARGIN = 5;
var width = this.SIZE;
if (this.block_.RTL) {
cursorX -= width;
}
this.iconGroup_.setAttribute('transform',
'translate(' + LEFT_MARGIN + ',' + TOP_MARGIN + ') scale(' + SIZE + ')');
this.computeIconLocation();
if (this.block_.RTL) {
cursorX -= Blockly.BlockSvg.SEP_SPACE_X;
} else {
cursorX += width + Blockly.BlockSvg.SEP_SPACE_X;
}
return cursorX;
};
/* change to python code */
Blockly.Sumorobot['controls_if'] = function(block) {
// If/elseif/else condition.
var n = 0;
var argument = Blockly.Sumorobot.valueToCode(block, 'IF' + n,
Blockly.Sumorobot.ORDER_NONE) || 'False';
var branch = Blockly.Sumorobot.statementToCode(block, 'DO' + n);
var code = 'if ' + argument + ':\n' + branch;
if (branch === '') {
code += ' pass\n';
}
for (n = 1; n <= block.elseifCount_; n++) {
argument = Blockly.Sumorobot.valueToCode(block, 'IF' + n,
Blockly.Sumorobot.ORDER_NONE) || 'False';
branch = Blockly.Sumorobot.statementToCode(block, 'DO' + n);
code += 'elif ' + argument + ':\n' + branch;
if (branch === '') {
code += ' pass\n';
}
}
if (block.elseCount_) {
branch = Blockly.Sumorobot.statementToCode(block, 'ELSE');
code += 'else:\n' + branch;
if (branch === '') {
code += ' pass';
}
}
return code + '\n';
};
Blockly.Sumorobot['sumorobot_delay'] = function(block) {
2017-10-16 21:11:01 +00:00
var code = 'sumorobot.sleep(' + parseFloat(block.getFieldValue('DELAY')) + ', "' + block.id + '")\n';
2017-09-25 09:37:41 +00:00
return code;
};
Blockly.Sumorobot['sumorobot_move'] = function(block) {
2017-10-16 21:11:01 +00:00
var code = 'sumorobot.move(' + block.getFieldValue('MOVE') + ', "' + block.id + '")\n';
2017-09-25 09:37:41 +00:00
return code;
};
Blockly.Sumorobot['sumorobot_enemy'] = function(block) {
2017-10-16 21:11:01 +00:00
var code = 'sumorobot.is_enemy("' + block.id + '")';
2017-09-25 09:37:41 +00:00
return [code, Blockly.Sumorobot.ORDER_ATOMIC];
};
Blockly.Sumorobot['sumorobot_line'] = function(block) {
2017-10-16 21:11:01 +00:00
var code = 'sumorobot.is_line(' + block.getFieldValue('LINE') + ', "' + block.id + '")';
2017-09-25 09:37:41 +00:00
return [code, Blockly.Sumorobot.ORDER_ATOMIC];
};
/* change the if block to be more cheerful */
Blockly.Blocks.logic.HUE = '#44CC00';
/* inject blockly */
2017-09-25 09:37:41 +00:00
var blocklyArea = document.getElementById('blocklyArea');
var blocklyDiv = document.getElementById('blocklyDiv');
2017-10-16 21:11:01 +00:00
workspace = Blockly.inject(blocklyDiv, {
2017-09-25 09:37:41 +00:00
scrollbars: false,
media: 'media/',
trashcan: true,
sounds: true,
zoom: {
wheel: true,
controls: true,
startScale: 1.2
},
toolbox: document.getElementById('toolbox')
});
/* on blockly resize */
var onresize = function(e) {
// Compute the absolute coordinates and dimensions of blocklyArea.
var element = blocklyArea;
var x = 0;
var y = 0;
do {
x += element.offsetLeft;
y += element.offsetTop;
element = element.offsetParent;
} while (element);
/* position blocklyDiv over blocklyArea */
blocklyDiv.style.left = x + 'px';
blocklyDiv.style.top = y + 'px';
blocklyDiv.style.width = blocklyArea.offsetWidth + 'px';
blocklyDiv.style.height = blocklyArea.offsetHeight + 'px';
};
window.addEventListener('resize', onresize, false);
onresize();
Blockly.svgResize(workspace);
/* function to set local storage */
function getLocalStorageItem(item) {
/* when the local storage doesn't exist, return empty string */
if (typeof(Storage) === "undefined") return "";
/* otherwise return item from the local storage*/
return localStorage.getItem(item);
}
/* function to set local storage */
function setLocalStorageItem(item, value) {
/* when local storage doesn't exist, return */
if (typeof(Storage) === "undefined") return;
/* otherwise set the item to the local storage */
localStorage.setItem(item, value)
}
/* retrieve the blocks */
var xml = Blockly.Xml.textToDom(getLocalStorageItem("sumorobot.currentProgram"));
/* resume the blocks */
Blockly.Xml.domToWorkspace(xml, workspace);
/* on blockly code change */
function onCodeChanged(event) {
/* if the if condition block was created */
2018-01-15 00:25:07 +00:00
if (event.type == Blockly.Events.CREATE && event.xml.getAttributeNode("type").nodeValue == "controls_if") {
2018-01-15 09:54:08 +00:00
2018-01-15 00:25:07 +00:00
/* remember the control_if block id */
controlBlockId = event.blockId;
/* automatically add the else statement input */
2018-01-15 00:25:07 +00:00
var block = workspace.getBlockById(event.blockId);
block.elseCount_ = 1;
block.updateShape_();
/* if the if condition block was removed */
} else if (event.type == Blockly.Events.DELETE && event.oldXml.getAttributeNode("type").nodeValue == "controls_if") {
2018-01-15 00:25:07 +00:00
/* remove the control_if block id */
controlBlockId = "";
2018-01-15 14:09:54 +00:00
/* enable the if condition block */
workspace.updateToolbox(document.getElementById("toolbox"));
}
2017-10-13 12:16:28 +00:00
/* only process change and move commands */
if (event.type != Blockly.Events.CHANGE && event.type != Blockly.Events.MOVE) return;
2017-10-02 19:41:10 +00:00
/* generate code from the used blocks */
2017-10-16 21:11:01 +00:00
sumocode = Blockly.Sumorobot.workspaceToCode(workspace);
2017-10-02 19:41:10 +00:00
2017-10-16 21:11:01 +00:00
/* show the code to the user, filter out block IDs */
document.getElementById("blocklyCode").value = sumocode.replace(/[,]?[ ]?"(.*?)"/g, "");
2017-09-25 09:37:41 +00:00
/* save the code to the local storage */
var xml = Blockly.Xml.workspaceToDom(workspace);
localStorage.setItem("sumorobot.currentProgram", Blockly.Xml.domToText(xml));
2018-01-15 09:54:08 +00:00
2018-01-15 14:09:54 +00:00
/* if control_if block is used */
2018-01-15 09:54:08 +00:00
if (controlBlockId != "") {
/* disable the if condition block */
workspace.updateToolbox(document.getElementById("toolbox_no_if"));
}
2017-09-25 09:37:41 +00:00
}
/* add a change listener to Blockly */
workspace.addChangeListener(onCodeChanged);
/* key down event */
$(document).keydown(function(e) {
/* if the hotkeys are disabled or the alt key is not pressed, don't use hotkeys */
if (hotkeys == false || e.altKey == false) return;
/* prevent typing in textfields */
e.preventDefault();
2017-09-25 09:37:41 +00:00
/* select the hotkey */
switch(e.which) {
case 32: // space bar
sumostart = !sumostart;
if (sumostart) {
$(".btn-start").addClass("hover");
$(".btn-start").click();
} else {
$(".btn-stop").addClass("hover");
$(".btn-stop").click();
}
break;
case 37: // left
if (remoteControl) sumorobot.send("left");
break;
case 38: // up
if (remoteControl) sumorobot.send("forward");
break;
case 39: // right
if (remoteControl) sumorobot.send("right");
break;
case 40: // down
if (remoteControl) sumorobot.send("backward");
break;
case 49: // 1
$(".btn-robot-nr:eq(0)").click();
break;
case 50: // 2
$(".btn-robot-nr:eq(1)").click();
break;
case 51: // 3
$(".btn-robot-nr:eq(2)").click();
break;
case 52: // 4
$(".btn-robot-nr:eq(3)").click();
break;
case 53: // 5
$(".btn-robot-nr:eq(4)").click();
break;
case 67: // c
2017-10-02 19:41:10 +00:00
updateControlPanel();
2017-09-25 09:37:41 +00:00
$("#panel").toggle();
break;
2017-10-15 22:41:00 +00:00
case 76: // l
$("#stream").toggle();
$("#blocklyCode").toggle();
break;
case 80: // p
$("#blocklyCode").blur();
$("#blocklyDiv").toggle();
$("#blocklyArea").toggle();
$("#blocklyCode").toggleClass("non-clickable");
var event = {type: Blockly.Events.CHANGE};
workspace.fireChangeListener(event);
/* toggle python enabled */
pythonEnabled = !pythonEnabled;
break;
2017-09-25 09:37:41 +00:00
case 82: // r
2017-12-27 12:12:45 +00:00
if (remoteControl)
$("#remote-disabled").click();
else
$("#remote-enabled").click();
2017-09-25 09:37:41 +00:00
break;
case 83: // s
$(".btn-stop").addClass("hover");
$(".btn-stop").click();
break;
2018-01-13 15:23:09 +00:00
case 84: // t
sumorobot.send("calibrate_line");
break;
2017-09-25 09:37:41 +00:00
case 87: // w
$(".btn-start").addClass("hover");
$(".btn-start").click();
break;
}
});
/* key up event */
$(document).keyup(function(e) {
2017-10-02 19:41:10 +00:00
/* if the hotkeys are disabled or the focused element is a textarea or text input, don't use hotkeys */
if (hotkeys == false || e.altKey == false) return;
2017-09-25 09:37:41 +00:00
/* remove hover from buttons */
$('.btn').removeClass('hover');
/* if arrow keys */
if (e.which == 37 || e.which == 38 || e.which == 39 || e.which == 40) {
if (remoteControl) sumorobot.send("stop");
}
});
2017-10-16 09:45:30 +00:00
/* start button listener */
$(".btn-close").click(function() {
$("#stream").hide();
});
2017-09-25 09:37:41 +00:00
/* start button listener */
$(".btn-start").click(function() {
2017-10-02 19:41:10 +00:00
sumostart = true;
2018-01-06 10:55:13 +00:00
/* if we are in Python mode */
if (pythonEnabled) {
2018-01-11 18:34:04 +00:00
/* send the code from the textarea to the SumoRobot */
sumorobot.send("start:" + $("#blocklyCode").val());
2018-01-06 10:55:13 +00:00
/* otherwise when we are in blockly mode */
} else {
2018-01-11 18:34:04 +00:00
/* send the code from the blocks to the SumoRobot */
sumorobot.send("start:" + sumocode);
}
2017-09-25 09:37:41 +00:00
});
/* stop button listener */
$(".btn-stop").click(function() {
2017-10-02 19:41:10 +00:00
sumostart = false;
2017-09-25 09:37:41 +00:00
sumorobot.send("stop");
2018-01-15 00:25:07 +00:00
workspace.highlightBlock(lastHighlighted, false);
2017-09-25 09:37:41 +00:00
});
2017-12-27 12:12:45 +00:00
/* remote control enable listener */
$("#remote-enabled").click(function() {
remoteControl = true;
});
/* remote control disable listener */
$("#remote-disabled").click(function() {
remoteControl = false;
2017-09-25 09:37:41 +00:00
});
/* robot number button listener */
$(".btn-robot-nr").click(function() {
/* extract and validate the selected robot ID */
var index = $(".btn-robot-nr").index($(this));
var robotID = $(".robot-id:eq(" + index + ")").val();
if (robotID.trim() === "") {
$(".robot-nr:eq(" + index + "), .robot-id:eq(" + index + ")").addClass("has-error");
return;
} else {
$(".robot-nr:eq(" + index + "), .robot-id:eq(" + index + ")").removeClass("has-error");
}
/* highlight the selected robot button */
$(".btn-robot-nr").removeClass("btn-selected");
$(this).addClass("btn-selected");
/* update robot IDs in local storage */
setLocalStorageItem("sumorobot.robotID" + index, robotID);
2018-01-15 00:25:07 +00:00
/* in case there is a open connection */
if (sumorobot && blockHighlight) {
/* close the connections */
sumorobot.close();
blockHighlight.close();
}
2017-10-31 16:59:15 +00:00
/* connect to the selected robots WebSocket */
2017-09-25 09:37:41 +00:00
sumorobot = new Sumorobot("ws://" + robotServer + ":80/p2p/browser/" + robotID + "/");
2017-10-31 16:59:15 +00:00
/* connect to the other block highlight WebSocket */
2018-01-15 00:25:07 +00:00
blockHighlight = new WebSocket("ws://" + robotServer + ":80/p2p/browser/" + robotID + "-highlight/");
/* when there is a message from the WebSocket */
2018-01-15 00:25:07 +00:00
blockHighlight.onmessage = function(evt) {
2017-10-31 16:59:15 +00:00
/* when scope is received */
2017-10-31 16:43:43 +00:00
if (evt.data.length == 20 && sumostart) {
workspace.highlightBlock(evt.data);
2018-01-15 00:25:07 +00:00
lastHighlighted = evt.data;
}
};
2017-09-25 09:37:41 +00:00
/* hide the configuration panel */
$("#panel").hide();
});
2018-01-13 15:23:09 +00:00
/* load the Mixer stream */
$("#stream").html('<iframe width="100%" height="100%" allowfullscreen="true" src="https://mixer.com/embed/player/14551694"></iframe>');
2018-01-15 00:25:07 +00:00
/* set a click listener on the document */
$(document).click(function(e) {
var target = e.target;
/* when control_if block is in use */
if (controlBlockId) {
/* when the user clicks anywhere outside the mutator and not on the mutator icon */
if (!$(target).is('.blocklyBubbleCanvas') && !$(target).parents().is('.blocklyBubbleCanvas')) {
if (!$(target).is('.blocklyIconGroup') && !$(target).parents().is('.blocklyIconGroup')) {
/* hide the mutator */
workspace.getBlockById(controlBlockId).mutator.setVisible(false);
}
}
}
});
2017-09-25 09:37:41 +00:00
}