// The local/remote server URL //var ROBOT_SERVER = '192.168.2.1:80'; var ROBOT_SERVER = 'ws.achex.ca:4010'; // The sumorobot object var sumorobot; // Disable / enable coding mode var codingEnabled = false; // Disable / enable live stream var liveStreamVisible = false; window.addEventListener('load', function() { // Activate tooltips $('[data-toggle="tooltip"]').tooltip({ animated: 'fade', placement: 'bottom', html: true }); // Set the robot ID from the localstorage $('#robot-id').val(getLocalStorageItem('sumorobot.robotId')); // Key down event $(document).keydown(function(e) { // When the alt key is not pressed, don't process hotkeys if (e.altKey == false) return; // Prevent typing in textfields e.preventDefault(); // Select the hotkey switch(e.which) { case 32: // space bar if (sumorobot.moving) { $('.btn-stop').addClass('hover'); $('.btn-stop').click(); } else { $('.btn-start').addClass('hover'); $('.btn-start').click(); } break; case 37: // left sumorobot.send('left'); break; case 38: // up sumorobot.send('forward'); break; case 39: // right sumorobot.send('right'); break; case 40: // down sumorobot.send('backward'); break; case 67: // c $('#panel').toggle(); break; case 76: // l // No live stream if cookies are disabled if (!cookiesEnabled) { // TODO: Open the cookie consent popup return; } // Load the Mixer stream when it is not yet loaded if ($('#stream').is(':empty')) { $('#stream').html(''); } $('#stream').toggle(); // Toggle live steam visible liveStreamVisible = !liveStreamVisible; // If not in coding mode if (codingEnabled == false) { $('#readOnlyBlocklyCode').toggle(); } break; case 80: // p $('#blocklyDiv').toggle(); $('#blocklyArea').toggle(); $('#blocklyCode').toggle(); // When live stream is not active if (liveStreamVisible == false) { // Toggle Blockly Python code $('#readOnlyBlocklyCode').toggle(); } // Toggle coding enabled codingEnabled = !codingEnabled; if (codingEnabled) { // Resize the coding editor codingEditor.resize(); // Focus, so the user can start coding codingEditor.focus(); } break; case 82: // r // Implement something break; case 83: // s $('.btn-stop').addClass('hover'); $('.btn-stop').click(); break; case 84: // t sumorobot.send('calibrate_line'); break; case 87: // w $('.btn-start').addClass('hover'); $('.btn-start').click(); break; } }); // Key up event $(document).keyup(function(e) { // When the alt key is not pressed, don't process hotkeys if (e.altKey == false) return; // Remove hover from buttons $('.btn').removeClass('hover'); // If arrow keys were pressed if (e.which == 37 || e.which == 38 || e.which == 39 || e.which == 40) { sumorobot.send('stop'); } }); var range_id; var lines = []; var foundTrue = false; // Function to process code and highlight blocks and lines function processCode(index) { // When all lines are already processed if (lines.length == 0) return; // Split into code and block ID var temp = lines[index].split(';;'); var code = temp[0]; // Default timeout for processing the next line var timeout = 100; var isCondition = /(if|elif|else)/.test(code); // When it is a condition line if (isCondition) { if (foundTrue) { // Start processing the code from the beginning again index = -1; foundTrue = false; } else if (/opponent/.test(code)) { foundTrue = (sumorobot.sensors['opponent'] < 40.0); } else if (/LEFT/.test(code)) { foundTrue = (sumorobot.sensors['line_left'] > 2000); } else if (/RIGHT/.test(code)) { foundTrue = (sumorobot.sensors['line_right'] > 2000); } else { foundTrue = true; } } // Some lines don't correspond to any block if (temp[1] && index != -1 && ((!isCondition && foundTrue) || isCondition || !/if/.test(lines[0]))) { // When sleep function, we get the timeout value from the function if (/\d/.test(code)) { timeout = parseInt(code.replace(/[a-z\.()]/g, '')); } if (range_id) { readOnlyCodingEditor.session.removeMarker(range_id); } var range = new Range(index, 0, index, 1); range_id = readOnlyCodingEditor.session.addMarker(range, "highlight", "fullLine"); // Block ID should always be 20 symbols long var blockId = temp[1].substring(0, 20); // Highlight the block workspace.highlightBlock(blockId); } // Set an timeout for processing the next line index = (index + 1) % lines.length setTimeout(function() { processCode(index) }, timeout); } // Start button listener $('.btn-start').click(function() { // When we are in Python coding mode if (codingEnabled) { // Get the Python code parsedCode = codingEditor.getValue(); // Otherwise when we are in Blockly mode } else { // Get the code from the blocks parsedCode = Blockly.Python.workspaceToCode(workspace).replace(/;;.{20}/g, ''); } // Escape the qoutes, replace new lines and send the code sumorobot.send('code', parsedCode.replace(/"/g, '\\"').replace(/\n/g, ';;')); // Split into lines of code and filter empty lines lines = Blockly.Python.workspaceToCode(workspace).split('\n').filter(Boolean); // Process the code starting from the first line processCode(0); }); // Stop button listener $('.btn-stop').click(function() { sumorobot.send('stop'); // Stop highlighting blocks and lines lines = []; workspace.highlightBlock(''); if (range_id) { readOnlyCodingEditor.session.removeMarker(range_id); } }); // Enter (return) keypress listener on robot ID field $('#robot-id').keypress(function(e) { if (e.which == 13) { // Simulate robot GO button click $('.btn-robot-go').click(); } }); // Robot GO button listener $('.btn-robot-go').click(function() { // Get and validate the selected robot ID var robotId = $('#robot-id').val().trim().toLowerCase(); if (robotId === '' || /^([a-f0-9]{6})$/.test(robotId) == false) { $('#robot-id, #robot-label').addClass('has-error'); return; } else { $('#robot-id, #robot-label').removeClass('has-error'); } // Update robot ID in local storage setLocalStorageItem('sumorobot.robotId', robotId); // When a connection was already opened if (sumorobot) { // Close the connection sumorobot.close(); } // Connect to the selected robots WebSocket sumorobot = new Sumorobot(`ws://${ROBOT_SERVER}`, robotId); // Hide the configuration panel $('#panel').hide(); }); });