/* the server URL */ //var robotServer = "10.42.0.1"; var robotServer = "iot.koodur.com"; /* enable disable remote control */ var remoteControl = false; /* the sumorobot state */ var sumostart = false; /* the sumorobot object */ var sumorobot = null; /* Blockly workspace */ var workspace = null; /* last hightlighted block */ var highlighted = ""; /* disable enable hotkeys */ var hotkeys = true; /* the sumorobot code */ var sumocode = ""; var block_highlight = null; var Sumorobot = function(wsUri) { /* assign the WebSocket URI */ this.wsUri = wsUri; /* start connecting to the WebSocket */ this.connect(); /* to ping the robot */ this.watchdogTimer = null; }; 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 */ self.watchdogTimer = setInterval(function() { /* send a ping to the robot */ //console.log("ping the robot") self.send("ping"); }, 2000); }; /* when the WebSocket closes */ this.websocket.onclose = function(evt) { console.log("INFO websocket disconnected"); /* clear the pinging */ clearInterval(self.watchdogTimer); /* 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 */ if (evt.data.length == 20) { workspace.highlightBlock(evt.data); highlighted = evt.data; } else { console.log(evt.data); } }; /* 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() { /* 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(); /* 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) { var code = 'sumorobot.sleep(' + parseFloat(block.getFieldValue('DELAY')) + ', "' + block.id + '")\n'; return code; }; Blockly.Sumorobot['sumorobot_move'] = function(block) { var code = 'sumorobot.move(' + block.getFieldValue('MOVE') + ', "' + block.id + '")\n'; return code; }; Blockly.Sumorobot['sumorobot_enemy'] = function(block) { var code = 'sumorobot.is_enemy("' + block.id + '")'; return [code, Blockly.Sumorobot.ORDER_ATOMIC]; }; Blockly.Sumorobot['sumorobot_line'] = function(block) { var code = 'sumorobot.is_line(' + block.getFieldValue('LINE') + ', "' + block.id + '")'; return [code, Blockly.Sumorobot.ORDER_ATOMIC]; }; /* change the if block to be more cheerful */ Blockly.Blocks.logic.HUE = '#44CC00'; var blocklyArea = document.getElementById('blocklyArea'); var blocklyDiv = document.getElementById('blocklyDiv'); workspace = Blockly.inject(blocklyDiv, { 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) { /* only process change and move commands */ if (event.type != Blockly.Events.CHANGE && event.type != Blockly.Events.MOVE) return; /* generate code from the used blocks */ sumocode = Blockly.Sumorobot.workspaceToCode(workspace); /* show the code to the user, filter out block IDs */ document.getElementById("blocklyCode").value = sumocode.replace(/[,]?[ ]?"(.*?)"/g, ""); /* save the code to the local storage */ var xml = Blockly.Xml.workspaceToDom(workspace); localStorage.setItem("sumorobot.currentProgram", Blockly.Xml.domToText(xml)); /* if the if condition block is used */ if (sumocode.indexOf("if") != -1) { /* disable the if condition block */ $("block[type=controls_if]").replaceWith(""); workspace.updateToolbox(document.getElementById("toolbox")); } else { /* enable the if condition block */ $("block[type=controls_if]").replaceWith(""); workspace.updateToolbox(document.getElementById("toolbox")); } } /* add a change listener to Blockly */ workspace.addChangeListener(onCodeChanged); /* key down event */ $(document).keydown(function(e) { /* if the hotkeys are disabled or the focused element is a textarea or text input, don't use hotkeys */ if (hotkeys == false || $(e.target).is("textarea") || $(e.target).is("[type=text]") || $(e.target).is("[class=blocklyHtmlInput]")) return; /* 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 updateControlPanel(); $("#panel").toggle(); break; case 76: // l $("#stream").toggle(); case 82: // r $("#remote-control").click(); break; case 83: // s $(".btn-stop").addClass("hover"); $(".btn-stop").click(); break; case 87: // w $(".btn-start").addClass("hover"); $(".btn-start").click(); break; } }); /* key up event */ $(document).keyup(function(e) { /* if the hotkeys are disabled or the focused element is a textarea or text input, don't use hotkeys */ if (hotkeys == false || $(e.target).is("textarea") || $(e.target).is("[type=text]") || $(e.target).is("[class=blocklyHtmlInput]")) return; /* 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-close").click(function() { $("#stream").hide(); }); /* start button listener */ $(".btn-start").click(function() { sumostart = true; sumorobot.send("start:" + sumocode.replace(/sumorobot./g, "")); }); /* stop button listener */ $(".btn-stop").click(function() { sumostart = false; sumorobot.send("stop"); setTimeout(function() { workspace.highlightBlock(highlighted, false); }, 500); }); /* remote control checkbox listener */ $("#remote-control").click(function() { remoteControl = $("#remote-control").is(":checked"); }); /* 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); /* connect to the selected robot channel */ sumorobot = new Sumorobot("ws://" + robotServer + ":80/p2p/browser/" + robotID + "/"); block_highlight = new WebSocket("ws://" + robotServer + ":80/p2p/browser/" + robotID + "-highlight/"); /* when there is a message from the WebSocket */ block_highlight.onmessage = function(evt) { /* when scope is received */ if (evt.data.length == 20) { workspace.highlightBlock(evt.data); highlighted = evt.data; } }; /* hide the configuration panel */ $("#panel").hide(); }); /* try to close if block bubble canvas */ /*$(document).click(function(e) { var target = e.target; if (!$(target).is('.blocklyBubbleCanvas') && !$(target).parents().is('.blocklyBubbleCanvas')) { if (!$(target).is('.blocklyIconGroup') && !$(target).parents().is('.blocklyIconGroup')) $('.blocklyBubbleCanvas').empty(); } });*/ }