2017-12-20 00:35:50 +00:00
|
|
|
|
/* 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";
|
2017-12-20 00:35:50 +00:00
|
|
|
|
|
2018-01-17 11:16:26 +00:00
|
|
|
|
/* ace editor object */
|
2018-01-19 11:35:17 +00:00
|
|
|
|
var codingEditor = null;
|
2018-01-20 21:41:08 +00:00
|
|
|
|
/* read only ace editor object */
|
|
|
|
|
var readOnlyCodingEditor = null;
|
2018-01-17 11:16:26 +00:00
|
|
|
|
|
2017-12-20 00:35:50 +00:00
|
|
|
|
/* 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;
|
2017-12-20 00:35:50 +00:00
|
|
|
|
/* the sumorobot state */
|
|
|
|
|
var sumostart = false;
|
2018-01-19 11:35:17 +00:00
|
|
|
|
/* disable / enable coding mode */
|
|
|
|
|
var codingEnabled = false;
|
2018-01-20 21:41:08 +00:00
|
|
|
|
/* disable / enable live stream */
|
|
|
|
|
var liveStreamVisible = false;
|
2017-12-20 00:35:50 +00:00
|
|
|
|
|
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-10-22 10:03:33 +00:00
|
|
|
|
|
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) {
|
2018-01-16 21:13:23 +00:00
|
|
|
|
/* ready state constants: CONNECTING 0, OPEN 1, CLOSING 2, CLOSED 3 */
|
2017-09-25 09:37:41 +00:00
|
|
|
|
/* https://developer.mozilla.org/en-US/docs/Web/API/WebSocket */
|
|
|
|
|
if (this.websocket.readyState == 1) {
|
|
|
|
|
this.websocket.send(msg);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2018-01-16 21:13:23 +00:00
|
|
|
|
Sumorobot.prototype.close = function() {
|
|
|
|
|
/* close the WebSocket connection */
|
|
|
|
|
this.websocket.close();
|
|
|
|
|
};
|
|
|
|
|
|
2018-01-17 19:41:55 +00:00
|
|
|
|
/* 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)
|
|
|
|
|
}
|
2017-10-02 19:41:10 +00:00
|
|
|
|
|
2018-01-17 19:41:55 +00:00
|
|
|
|
window.onload = function() {
|
|
|
|
|
$("#robot-id").val(getLocalStorageItem("sumorobot.robotId"));
|
2018-01-20 21:41:08 +00:00
|
|
|
|
/* load read only ace editor */
|
|
|
|
|
readOnlyCodingEditor = ace.edit("readOnlyBlocklyCode");
|
|
|
|
|
/* set the style */
|
|
|
|
|
readOnlyCodingEditor.setTheme("ace/theme/textmate");
|
|
|
|
|
readOnlyCodingEditor.session.setMode("ace/mode/python");
|
|
|
|
|
readOnlyCodingEditor.session.setTabSize(2);
|
|
|
|
|
/* make as read only */
|
|
|
|
|
readOnlyCodingEditor.setReadOnly(true);
|
|
|
|
|
/* disable scrolling warning */
|
|
|
|
|
readOnlyCodingEditor.$blockScrolling = Infinity;
|
|
|
|
|
|
2018-01-17 11:16:26 +00:00
|
|
|
|
/* load ace editor */
|
2018-01-19 11:35:17 +00:00
|
|
|
|
codingEditor = ace.edit("blocklyCode");
|
2018-01-17 11:16:26 +00:00
|
|
|
|
/* set the style */
|
2018-01-19 11:35:17 +00:00
|
|
|
|
codingEditor.setTheme("ace/theme/textmate");
|
|
|
|
|
codingEditor.session.setMode("ace/mode/python");
|
|
|
|
|
codingEditor.session.setTabSize(2);
|
2018-01-17 11:16:26 +00:00
|
|
|
|
/* disable scrolling warning */
|
2018-01-19 11:35:17 +00:00
|
|
|
|
codingEditor.$blockScrolling = Infinity;
|
2018-01-17 11:16:26 +00:00
|
|
|
|
/* enable autocomplete */
|
|
|
|
|
ace.require("ace/ext/language_tools");
|
2018-01-19 11:35:17 +00:00
|
|
|
|
codingEditor.setOptions({
|
2018-01-17 11:16:26 +00:00
|
|
|
|
enableSnippets: true,
|
|
|
|
|
enableLiveAutocompletion: true,
|
|
|
|
|
enableBasicAutocompletion: true
|
|
|
|
|
});
|
|
|
|
|
/* add autocomplete keywords */
|
2018-01-19 11:35:17 +00:00
|
|
|
|
codingEditor.completers.push({
|
2018-01-17 11:16:26 +00:00
|
|
|
|
getCompletions: function(editor, session, pos, prefix, callback) {
|
|
|
|
|
callback(null, [
|
|
|
|
|
{value: "STOP", score: 1000, meta: "sumorobot"},
|
|
|
|
|
{value: "LEFT", score: 1000, meta: "sumorobot"},
|
|
|
|
|
{value: "RIGHT", score: 1000, meta: "sumorobot"},
|
|
|
|
|
{value: "FORWARD", score: 1000, meta: "sumorobot"},
|
|
|
|
|
{value: "BACKWARD", score: 1000, meta: "sumorobot"},
|
|
|
|
|
{value: "STATUS", score: 1000, meta: "sumorobot"},
|
|
|
|
|
{value: "LEFT_LINE", score: 1000, meta: "sumorobot"},
|
|
|
|
|
{value: "RIGHT_LINE", score: 1000, meta: "sumorobot"},
|
|
|
|
|
{value: "sumorobot", score: 1000, meta: "sumorobot"},
|
|
|
|
|
{value: "move", score: 1000, meta: "sumorobot"},
|
2018-01-17 11:46:03 +00:00
|
|
|
|
{value: "sleep", score: 1000, meta: "sumorobot"},
|
2018-01-17 11:16:26 +00:00
|
|
|
|
{value: "set_led", score: 1000, meta: "sumorobot"},
|
|
|
|
|
{value: "is_line", score: 1000, meta: "sumorobot"},
|
2018-01-17 11:46:03 +00:00
|
|
|
|
{value: "get_line", score: 1000, meta: "sumorobot"},
|
2018-01-17 11:16:26 +00:00
|
|
|
|
{value: "set_servo", score: 1000, meta: "sumorobot"},
|
2018-01-17 11:46:03 +00:00
|
|
|
|
{value: "is_opponent", score: 1000, meta: "sumorobot"},
|
2018-01-17 11:48:37 +00:00
|
|
|
|
{value: "calibrate_line", score: 1000, meta: "sumorobot"},
|
2018-01-17 11:46:03 +00:00
|
|
|
|
{value: "get_battery_voltage", score: 1000, meta: "sumorobot"},
|
|
|
|
|
{value: "get_opponent_distance", score: 1000, meta: "sumorobot"}
|
2018-01-17 11:16:26 +00:00
|
|
|
|
]);
|
|
|
|
|
}
|
|
|
|
|
});
|
2018-01-20 21:41:08 +00:00
|
|
|
|
/* set the code to the saved code from local storage or empty */
|
|
|
|
|
codingEditor.setValue(getLocalStorageItem("sumorobot.code") || "");
|
|
|
|
|
/* clear the selection after setting the value */
|
|
|
|
|
codingEditor.clearSelection();
|
|
|
|
|
/* add an change listener for the code editor */
|
|
|
|
|
codingEditor.on("change", function() {
|
|
|
|
|
/* when change occurs, save the new code to the localstorage */
|
|
|
|
|
setLocalStorageItem("sumorobot.code", codingEditor.getValue())
|
|
|
|
|
});
|
2018-01-17 11:16:26 +00:00
|
|
|
|
|
2018-01-16 21:13:23 +00:00
|
|
|
|
/* change the if block to be more cheerful */
|
|
|
|
|
Blockly.Msg.LOGIC_HUE = '#44CC00';
|
|
|
|
|
|
|
|
|
|
/* remote previous and next statement from control_if block */
|
|
|
|
|
Blockly.defineBlocksWithJsonArray([
|
|
|
|
|
{
|
|
|
|
|
"type": "controls_if",
|
|
|
|
|
"message0": "%{BKY_CONTROLS_IF_MSG_IF} %1",
|
|
|
|
|
"args0": [
|
|
|
|
|
{
|
|
|
|
|
"type": "input_value",
|
|
|
|
|
"name": "IF0",
|
|
|
|
|
"check": "Boolean"
|
|
|
|
|
}
|
|
|
|
|
],
|
|
|
|
|
"message1": "%{BKY_CONTROLS_IF_MSG_THEN} %1",
|
|
|
|
|
"args1": [
|
|
|
|
|
{
|
|
|
|
|
"type": "input_statement",
|
|
|
|
|
"name": "DO0"
|
|
|
|
|
}
|
|
|
|
|
],
|
|
|
|
|
"colour": "%{BKY_LOGIC_HUE}",
|
|
|
|
|
"helpUrl": "%{BKY_CONTROLS_IF_HELPURL}",
|
|
|
|
|
"mutator": "controls_if_mutator",
|
|
|
|
|
"extensions": ["controls_if_tooltip"]
|
|
|
|
|
}
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
/* make control_if mutator icon bigger */
|
2017-09-25 09:37:41 +00:00
|
|
|
|
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;
|
|
|
|
|
};
|
|
|
|
|
|
2018-01-16 21:13:23 +00:00
|
|
|
|
/* when mouse click occures on Blockly workspace */
|
|
|
|
|
Blockly.utils.isRightButton = 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
|
|
|
|
}
|
|
|
|
|
}
|
2018-01-16 21:13:23 +00:00
|
|
|
|
/* disable right click on Blockly workspace */
|
|
|
|
|
return false;
|
|
|
|
|
};
|
|
|
|
|
|
2018-01-20 21:41:08 +00:00
|
|
|
|
Blockly.Blocks['sumorobot_sleep'] = {
|
2018-01-16 21:13:23 +00:00
|
|
|
|
init: function() {
|
|
|
|
|
this.setColour("#E64C00");
|
|
|
|
|
this.appendDummyInput()
|
2018-01-20 21:41:08 +00:00
|
|
|
|
.appendField("sleep")
|
2018-01-16 21:13:23 +00:00
|
|
|
|
.appendField(new Blockly.FieldTextInput('1000',
|
2018-01-20 21:41:08 +00:00
|
|
|
|
Blockly.FieldNumber.numberValidator), 'SLEEP');
|
2018-01-16 21:13:23 +00:00
|
|
|
|
this.setPreviousStatement(true);
|
|
|
|
|
this.setNextStatement(true);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
Blockly.Blocks['sumorobot_move'] = {
|
|
|
|
|
init: function() {
|
|
|
|
|
var OPERATORS = [
|
|
|
|
|
['move stop', 'STOP'],
|
|
|
|
|
['move left', 'LEFT'],
|
|
|
|
|
['move right', 'RIGHT'],
|
|
|
|
|
['move forward', 'FORWARD'],
|
|
|
|
|
['move backward', 'BACKWARD']
|
|
|
|
|
];
|
|
|
|
|
this.setColour("#E60000");
|
|
|
|
|
var dropdown = new Blockly.FieldDropdown(OPERATORS);
|
|
|
|
|
this.appendDummyInput().appendField(dropdown, 'MOVE');
|
|
|
|
|
this.setPreviousStatement(true);
|
|
|
|
|
this.setNextStatement(true);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
Blockly.Blocks['sumorobot_opponent'] = {
|
|
|
|
|
init: function() {
|
|
|
|
|
this.setColour("#0099E6");
|
|
|
|
|
this.appendDummyInput().appendField('opponent');
|
|
|
|
|
this.setOutput(true, 'Boolean');
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
Blockly.Blocks['sumorobot_line'] = {
|
|
|
|
|
init: function() {
|
|
|
|
|
var OPERATORS = [
|
|
|
|
|
['line left', 'LEFT'],
|
|
|
|
|
['line right', 'RIGHT']
|
|
|
|
|
];
|
|
|
|
|
this.setColour("#E6BF00");
|
|
|
|
|
var dropdown = new Blockly.FieldDropdown(OPERATORS);
|
|
|
|
|
this.appendDummyInput().appendField(dropdown, 'LINE');
|
|
|
|
|
this.setOutput(true, 'Boolean');
|
2017-09-25 09:37:41 +00:00
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2018-01-20 21:41:08 +00:00
|
|
|
|
Blockly.Python['sumorobot_sleep'] = function(block) {
|
|
|
|
|
var code = 'sumorobot.sleep(' + parseFloat(block.getFieldValue('SLEEP')) + ', "' + block.id + '")\n';
|
2017-09-25 09:37:41 +00:00
|
|
|
|
return code;
|
|
|
|
|
};
|
|
|
|
|
|
2018-01-16 21:13:23 +00:00
|
|
|
|
Blockly.Python['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;
|
|
|
|
|
};
|
|
|
|
|
|
2018-01-16 21:13:23 +00:00
|
|
|
|
Blockly.Python['sumorobot_opponent'] = function(block) {
|
|
|
|
|
var code = 'sumorobot.is_opponent("' + block.id + '")';
|
|
|
|
|
return [code, Blockly.Python.ORDER_ATOMIC];
|
2017-09-25 09:37:41 +00:00
|
|
|
|
};
|
|
|
|
|
|
2018-01-16 21:13:23 +00:00
|
|
|
|
Blockly.Python['sumorobot_line'] = function(block) {
|
2017-10-16 21:11:01 +00:00
|
|
|
|
var code = 'sumorobot.is_line(' + block.getFieldValue('LINE') + ', "' + block.id + '")';
|
2018-01-16 21:13:23 +00:00
|
|
|
|
return [code, Blockly.Python.ORDER_ATOMIC];
|
2017-09-25 09:37:41 +00:00
|
|
|
|
};
|
|
|
|
|
|
2018-01-15 19:18:37 +00:00
|
|
|
|
/* 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')
|
|
|
|
|
});
|
|
|
|
|
|
2018-01-15 19:18:37 +00:00
|
|
|
|
/* on Blockly resize */
|
2017-09-25 09:37:41 +00:00
|
|
|
|
var onresize = function(e) {
|
2018-01-16 21:13:23 +00:00
|
|
|
|
// compute the absolute coordinates and dimensions of blocklyArea.
|
2017-09-25 09:37:41 +00:00
|
|
|
|
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);
|
|
|
|
|
|
|
|
|
|
/* retrieve the blocks */
|
2018-01-17 11:16:26 +00:00
|
|
|
|
var xml = Blockly.Xml.textToDom(getLocalStorageItem("sumorobot.blockly"));
|
2017-09-25 09:37:41 +00:00
|
|
|
|
/* resume the blocks */
|
|
|
|
|
Blockly.Xml.domToWorkspace(xml, workspace);
|
|
|
|
|
|
2018-01-15 19:18:37 +00:00
|
|
|
|
/* on Blockly code change */
|
2017-09-25 09:37:41 +00:00
|
|
|
|
function onCodeChanged(event) {
|
2018-01-14 23:15:07 +00:00
|
|
|
|
/* 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") {
|
|
|
|
|
/* remember the control_if block id */
|
|
|
|
|
controlBlockId = event.blockId;
|
2018-01-16 21:13:23 +00:00
|
|
|
|
/* get the control_if block object */
|
2018-01-15 00:25:07 +00:00
|
|
|
|
var block = workspace.getBlockById(event.blockId);
|
2018-01-16 21:13:23 +00:00
|
|
|
|
/* if the control_if block doesn't already have an else */
|
|
|
|
|
if (block.elseCount_ == 0) {
|
|
|
|
|
/* automatically add the else statement input */
|
|
|
|
|
block.elseCount_ = 1;
|
|
|
|
|
block.updateShape_();
|
|
|
|
|
}
|
2018-01-14 23:15:07 +00:00
|
|
|
|
/* 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"));
|
2018-01-14 23:15:07 +00:00
|
|
|
|
}
|
|
|
|
|
|
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 */
|
2018-01-16 21:13:23 +00:00
|
|
|
|
sumocode = Blockly.Python.workspaceToCode(workspace);
|
2017-10-02 19:41:10 +00:00
|
|
|
|
|
2018-01-17 11:16:26 +00:00
|
|
|
|
/* show the code in the ace editor, filter out block IDs */
|
2018-01-20 21:41:08 +00:00
|
|
|
|
readOnlyCodingEditor.setValue("\n" + sumocode.replace(/[,]?[ ]?"(.*?)"/g, ""));
|
|
|
|
|
readOnlyCodingEditor.clearSelection();
|
2017-09-25 09:37:41 +00:00
|
|
|
|
|
|
|
|
|
/* save the code to the local storage */
|
|
|
|
|
var xml = Blockly.Xml.workspaceToDom(workspace);
|
2018-01-17 11:16:26 +00:00
|
|
|
|
localStorage.setItem("sumorobot.blockly", 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) {
|
2017-12-20 00:35:50 +00:00
|
|
|
|
/* 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 67: // c
|
|
|
|
|
$("#panel").toggle();
|
|
|
|
|
break;
|
2017-10-15 22:41:00 +00:00
|
|
|
|
case 76: // l
|
|
|
|
|
$("#stream").toggle();
|
2018-01-20 21:41:08 +00:00
|
|
|
|
/* toggle live steam visible */
|
|
|
|
|
liveStreamVisible = !liveStreamVisible;
|
|
|
|
|
/* if not in coding mode */
|
|
|
|
|
if (codingEnabled == false) {
|
|
|
|
|
$("#readOnlyBlocklyCode").toggle();
|
|
|
|
|
}
|
2017-12-20 00:35:50 +00:00
|
|
|
|
break;
|
|
|
|
|
case 80: // p
|
|
|
|
|
$("#blocklyDiv").toggle();
|
|
|
|
|
$("#blocklyArea").toggle();
|
2018-01-20 21:41:08 +00:00
|
|
|
|
$("#blocklyCode").toggle();
|
|
|
|
|
if (liveStreamVisible == false) {
|
|
|
|
|
$("#readOnlyBlocklyCode").toggle();
|
|
|
|
|
}
|
2018-01-19 11:35:17 +00:00
|
|
|
|
/* toggle coding enabled */
|
|
|
|
|
codingEnabled = !codingEnabled;
|
|
|
|
|
if (codingEnabled) {
|
2018-01-20 21:41:08 +00:00
|
|
|
|
/* resize the coding editor */
|
|
|
|
|
codingEditor.resize();
|
|
|
|
|
/* focus, so the user can start coding */
|
2018-01-19 11:35:17 +00:00
|
|
|
|
codingEditor.focus();
|
2018-01-17 11:16:26 +00:00
|
|
|
|
}
|
2017-12-20 00:35:50 +00:00
|
|
|
|
break;
|
2017-09-25 09:37:41 +00:00
|
|
|
|
case 82: // r
|
2018-01-17 19:41:55 +00:00
|
|
|
|
/* toggle remote control */
|
|
|
|
|
remoteControl = !remoteControl;
|
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 */
|
2017-12-20 00:35:50 +00:00
|
|
|
|
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");
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
/* start button listener */
|
|
|
|
|
$(".btn-start").click(function() {
|
2017-10-02 19:41:10 +00:00
|
|
|
|
sumostart = true;
|
2018-01-19 11:35:17 +00:00
|
|
|
|
/* if we are in coding mode */
|
|
|
|
|
if (codingEnabled) {
|
2018-01-11 18:34:04 +00:00
|
|
|
|
/* send the code from the textarea to the SumoRobot */
|
2018-01-19 11:35:17 +00:00
|
|
|
|
sumorobot.send("start:" + codingEditor.getValue());
|
2018-01-15 19:18:37 +00:00
|
|
|
|
/* otherwise when we are in Blockly mode */
|
2017-12-20 00:35:50 +00:00
|
|
|
|
} else {
|
2018-01-11 18:34:04 +00:00
|
|
|
|
/* send the code from the blocks to the SumoRobot */
|
|
|
|
|
sumorobot.send("start:" + sumocode);
|
2017-12-20 00:35:50 +00:00
|
|
|
|
}
|
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
|
|
|
|
});
|
|
|
|
|
|
2018-01-27 12:03:21 +00:00
|
|
|
|
/* enter listener on robot ID field */
|
|
|
|
|
$("#robot-id").keypress(function(e) {
|
|
|
|
|
if (e.which == 13) {
|
|
|
|
|
/* simulate robot GO button click */
|
|
|
|
|
$(".btn-robot-go").click();
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
2017-09-25 09:37:41 +00:00
|
|
|
|
/* robot number button listener */
|
2018-01-17 19:41:55 +00:00
|
|
|
|
$(".btn-robot-go").click(function() {
|
2017-09-25 09:37:41 +00:00
|
|
|
|
/* extract and validate the selected robot ID */
|
2018-01-17 19:41:55 +00:00
|
|
|
|
var robotId = $("#robot-id").val().trim();
|
|
|
|
|
if (robotId === "" || /^(sumo-[a-f0-9]{6})$/.test(robotId) == false) {
|
|
|
|
|
$("#robot-id, #robot-label").addClass("has-error");
|
2017-09-25 09:37:41 +00:00
|
|
|
|
return;
|
|
|
|
|
} else {
|
2018-01-17 19:41:55 +00:00
|
|
|
|
$("#robot-id, #robot-label").removeClass("has-error");
|
2017-09-25 09:37:41 +00:00
|
|
|
|
}
|
|
|
|
|
/* update robot IDs in local storage */
|
2018-01-17 19:41:55 +00:00
|
|
|
|
setLocalStorageItem("sumorobot.robotId", 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 */
|
2018-01-17 19:41:55 +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-17 19:41:55 +00:00
|
|
|
|
blockHighlight = new WebSocket("ws://" + robotServer + ":80/p2p/browser/" + robotId + "-highlight/");
|
2017-10-22 10:03:33 +00:00
|
|
|
|
/* 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) {
|
2017-10-22 10:03:33 +00:00
|
|
|
|
workspace.highlightBlock(evt.data);
|
2018-01-15 00:25:07 +00:00
|
|
|
|
lastHighlighted = evt.data;
|
2017-10-22 10:03:33 +00:00
|
|
|
|
}
|
|
|
|
|
};
|
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 */
|
2017-12-20 00:35:50 +00:00
|
|
|
|
$("#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 */
|
2018-01-15 20:07:23 +00:00
|
|
|
|
if (controlBlockId != "") {
|
2018-01-15 00:25:07 +00:00
|
|
|
|
/* 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
|
|
|
|
}
|