diff --git a/src/blocklyc.js b/src/blocklyc.js index 04c1a823..f82f4f7a 100644 --- a/src/blocklyc.js +++ b/src/blocklyc.js @@ -599,8 +599,8 @@ function loadInto(modal_message, compile_command, load_option, load_action) { if (clientService.type === 'ws') { // Prep for new download messages - launcher_result = ""; - launcher_download = false; + clientService.resultLog = ""; + clientService.loadBinary = false; var prog_to_send = { type: 'load-prop', action: load_action, diff --git a/src/blocklypropclient.js b/src/blocklypropclient.js index 8ef0ff1e..0a00cb77 100644 --- a/src/blocklypropclient.js +++ b/src/blocklypropclient.js @@ -52,42 +52,42 @@ var client_ws_heartbeat_interval = null; var check_com_ports_interval = null; var check_ws_socket_timeout = null; -// BP Launcher result log and download message flag -var launcher_result = ""; -var launcher_download = false; - /** * Client Service Object */ var clientService = { - available: false, // Available for ? - portsAvailable: false, // Are any serial ports enumerated? - path: 'localhost', // Is this always localhost? - port: 6009, // BlocklyProp Client/Launcher port number - type: null, // {string} Seems to be one of "", "ws", "http" + available: false, // {boolean} Has a client (BPC/BPL) successfully connected + portsAvailable: false, // {boolean} Are any serial ports enumerated + path: 'localhost', // {string} usually "localhost", but can be configured to point to a client at any reachable IP/DNS address + port: 6009, // {number} BlocklyProp Client/Launcher port number + type: null, // {string} null, "ws", "http" + + rxBase64: true, // {boolean} BP Lancher full base64 encoding support flag + loadBinary: false, // {boolean} BP Launcher download message flag + resultLog: '', // {boolean} BP Launcher result log /* - rxBase64: true, - portListReceiveCountUp: 0, // This is set to 0 each time the port list is received, and incremented once each 4 second heartbeat - activeConnection: null, + portListReceiveCountUp: 0, // This is set to 0 each time the port list is received, and incremented once each 4 second heartbeat + activeConnection: null, // Used differently by BPL and BPC - pointer to connection object */ url: function (location, protocol) { return (protocol || window.location.protocol.replace(':', '')) + '://' + this.path + ':' + this.port + '/' + (location || ''); }, version: { // Constants - MINIMUM_ALLOWED: '0.7.0', - RECOMMENDED: '0.11.0', - CODED_MINIMUM: '0.7.5', // Minimum client/launcher version supporting coded/verbose responses (remove after MINIMUM_ALLOWED > this) + MINIMUM_ALLOWED: '0.7.0', // {string} Semantic versioning, minimum client (BPL/BPC) allowed + RECOMMENDED: '0.11.0', // {string} Semantic versioning, minimum recommended client/launcher version + CODED_MINIMUM: '0.7.5', // {string} Semantic versioning, Minimum client/launcher version supporting coded/verbose responses (remove after MINIMUM_ALLOWED > this) // Variables - current: '0.0.0', // Current version - currentAsNumber: 0, - isValid: false, // What determines this? - isRecommended: false, // What is this? - isCoded: false, // Also, what is this? + current: '0.0.0', // {string} Semantic versioning, Current version + currentAsNumber: 0, // {number} Version as an integer calulated from string representation + isValid: false, // {boolean} current >= MINIMUM_ALLOWED + isRecommended: false, // {boolean} current >= RECOMMENDED + isCoded: false, // {boolean} current >= CODED_MINIMUM + // Returns integer calulated from passed in string representation of version getNumeric: function (rawVersion) { var tempVersion = rawVersion.toString().split("."); tempVersion.push('0'); @@ -103,6 +103,8 @@ var clientService = { // Equivalent to: (Major * 104856) + (Minor * 1024) + Revision. return (Number(tempVersion[0]) << 20 | Number(tempVersion[1]) << 10 | Number(tempVersion[2])); }, + + // Sets self-knowledge of current client/launcher version. set: function (rawVersion) { this.current = rawVersion; this.currentAsNumber = this.getNumeric(rawVersion); @@ -124,24 +126,24 @@ const NE_DOWNLOAD_FAILED = 102; $(document).ready(function () { - find_client(); + findClient(); }); -var find_client = function () { +var findClient = function () { if (check_ws_socket_timeout) { - //Clear timeout if it exists; without this, back-to-back find_client() calls seem to occur + //Clear timeout if it exists; without this, back-to-back findClient() calls seem to occur clearTimeout(check_ws_socket_timeout); } - establish_socket(); + establishBPLauncherConnection(); if (clientService.type !== 'ws') { // WebSocket'd launcher not found? Try Http'd client - check_client(); + establishBPClientConnection(); } }; -var setPropToolbarButtons = function (ui_btn_state) { - if (ui_btn_state === 'available') { +var setPropToolbarButtons = function () { + if (clientService.available) { if (projectData && projectData.board === 's3') { // Hide the buttons that are not required for the S3 robot $('.no-s3').addClass('hidden'); @@ -201,7 +203,7 @@ function checkClientVersionModal(rawVersion) { /** * This is evaluating the BlocklyProp Client or BlocklyProp Launcher version?? */ -var check_client = function () { +var establishBPClientConnection = function () { // Load data from the server using a HTTP GET request. $.get(clientService.url(), function (data) { if (!clientService.available) { @@ -209,25 +211,26 @@ var check_client = function () { if (!data.server || data.server !== 'BlocklyPropHTTP') { client_version_str = '0.0.0'; } + checkClientVersionModal(client_version_str); clientService.type = 'http'; clientService.available = true; - setPropToolbarButtons('available'); + setPropToolbarButtons(); if (checkForComPorts && typeof (checkForComPorts) === "function") { checkForComPorts(); check_com_ports_interval = setInterval(checkForComPorts, 5000); } } - setTimeout(check_client, 20000); + setTimeout(establishBPClientConnection, 20000); }).fail(function () { clearInterval(check_com_ports_interval); clientService.type = 'none'; clientService.available = false; clientService.portsAvailable = false; - setPropToolbarButtons('unavailable'); - check_ws_socket_timeout = setTimeout(find_client, 3000); + setPropToolbarButtons(); + check_ws_socket_timeout = setTimeout(findClient, 3000); }); }; @@ -249,22 +252,22 @@ var connection_heartbeat = function () { * @deprecated - This function will become obsolete when the * BlocklyProp Client is deprecated and removed from service. */ -var configure_client = function () { +var configureConnectionPaths = function () { // All of this code is building the UI for the Configure // BlocklyProp Client dialog. - let url_input = $("
", { + let pathPortInput = $("", { class: "form-inline" }); // This is hard-coding the HTTP protocol for the BlocklyProp Client $("", { class: "space_right" - }).text("http://").appendTo(url_input); + }).text("http://").appendTo(pathPortInput); // Add the form group to the DOM for the input field defined next - let domain_name_group = $("", { + let domainNameGroup = $("", { class: "form-group" - }).appendTo(url_input); + }).appendTo(pathPortInput); // Default the domain input box $("", { @@ -272,17 +275,17 @@ var configure_client = function () { type: "text", class: "form-control", value: clientService.path - }).appendTo(domain_name_group); + }).appendTo(domainNameGroup); // Hard code the ':' between the domain name and port input fields $("", { class: "space_left space_right" - }).text(":").appendTo(url_input); + }).text(":").appendTo(pathPortInput); // Add the form group to the DOM for the next input field let domain_port_group = $("", { class: "form-group" - }).appendTo(url_input); + }).appendTo(pathPortInput); // Get the port number $("", { @@ -293,7 +296,7 @@ var configure_client = function () { }).appendTo(domain_port_group); // Show the modal dialog - utils.confirm(Blockly.Msg.DIALOG_BLOCKLYPROP_LAUNCHER_CONFIGURE_TITLE, url_input, function (action) { + utils.confirm(Blockly.Msg.DIALOG_BLOCKLYPROP_LAUNCHER_CONFIGURE_TITLE, pathPortInput, function (action) { if (action) { clientService.path = $("#domain_name").val(); clientService.port = $("#port_number").val(); @@ -302,7 +305,7 @@ var configure_client = function () { }; // checks for and, if found, uses a newer WebSockets-only client -function establish_socket() { +function establishBPLauncherConnection() { // TODO: set/clear and load buttons based on status if (!clientService.available) { @@ -318,9 +321,9 @@ function establish_socket() { client_ws_connection.close(); } - var ws_msg = {type: 'hello-browser', baud: baudrate}; + var wsMessage = {type: 'hello-browser', baud: baudrate}; client_ws_connection = connection; - connection.send(JSON.stringify(ws_msg)); + connection.send(JSON.stringify(wsMessage)); }; // Log errors @@ -336,20 +339,20 @@ function establish_socket() { // handle messages from the client connection.onmessage = function (e) { - var ws_msg = JSON.parse(e.data); + var wsMessage = JSON.parse(e.data); // --- hello handshake - establish new connection - if (ws_msg.type === 'hello-client') { + if (wsMessage.type === 'hello-client') { // type: 'hello-client', // version: [String version (semantic versioning)] - checkClientVersionModal(ws_msg.version); + // rxBase64: [boolean, accepts base64-encoded serial streams (all versions transmit base64)] + checkClientVersionModal(wsMessage.version); if (window.getURLParameter('debug')) { - console.log("Websocket client/launcher found - version " + ws_msg.version); + console.log("Websocket client/launcher found - version " + wsMessage.version); } - // TODO: Add version checking here. - + clientService.rxBase64 = wsMessage.rxBase64 || false; clientService.type = 'ws'; clientService.available = true; @@ -361,7 +364,7 @@ function establish_socket() { } // --- com port list/change - else if (ws_msg.type === 'port-list') { + else if (wsMessage.type === 'port-list') { // type: 'port-list', // ports: ['port1', 'port2', 'port3'...] @@ -373,63 +376,73 @@ function establish_socket() { client_ws_heartbeat_interval = setInterval(connection_heartbeat, 4000); } - setPortListUI(ws_msg.ports); + setPortListUI(wsMessage.ports); } // --- serial terminal/graph - else if (ws_msg.type === 'serial-terminal' && - (typeof ws_msg.msg === 'string' || ws_msg.msg instanceof String)) { + else if (wsMessage.type === 'serial-terminal' && + (typeof wsMessage.msg === 'string' || wsMessage.msg instanceof String)) { // sometimes some weird stuff comes through... // type: 'serial-terminal' // msg: [String Base64-encoded message] - var msg_in = atob(ws_msg.msg); + var messageText = ''; + try { + var messageText = atob(wsMessage.msg); + } catch (error) { + // only show the error if it's something other than base-64 encoding + if (error.toString().indexOf("'atob'") < 0) { + console.error(error); + } + messageText = wsMessage.msg; + } + - if (term !== null) { // is the terminal open? - pTerm.display(msg_in); + if (term !== null && messageText !== '' && wsMessage.packetID) { // is the terminal open? + pTerm.display(messageText); pTerm.focus(); - } else if (graph !== null) { // is the graph open? - graph_new_data(msg_in); + } else if (graph !== null && messageText !== '' && wsMessage.packetID) { // is the graph open? + graph_new_data(messageText); } } // --- UI Commands coming from the client - else if (ws_msg.type === 'ui-command') { + else if (wsMessage.type === 'ui-command') { // type: 'ui-command', // action: ['open-terminal','open-graph','close-terminal','close-graph','close-compile','clear-compile','message-compile','alert'] // msg: [String message] - if (ws_msg.action === 'open-terminal') { + if (wsMessage.action === 'open-terminal') { serial_console(); - } else if (ws_msg.action === 'open-graph') { + } else if (wsMessage.action === 'open-graph') { graphing_console(); - } else if (ws_msg.action === 'close-terminal') { + } else if (wsMessage.action === 'close-terminal') { $('#console-dialog').modal('hide'); newTerminal = false; pTerm.display(null); - } else if (ws_msg.action === 'close-graph') { + } else if (wsMessage.action === 'close-graph') { $('#graphing-dialog').modal('hide'); graph_reset(); - } else if (ws_msg.action === 'clear-compile') { + } else if (wsMessage.action === 'clear-compile') { $('#compile-console').val(''); - } else if (ws_msg.action === 'message-compile') { + } else if (wsMessage.action === 'message-compile') { //Messages are coded; check codes, log all and filter out NS_DOWNLOADING duplicates - var msg = ws_msg.msg.split("-"); - if (msg[0] != NS_DOWNLOADING || !launcher_download) { - launcher_result = launcher_result + msg[1] + "\n"; - launcher_download |= (msg[0] == NS_DOWNLOADING); + var msg = wsMessage.msg.split("-"); + if (msg[0] != NS_DOWNLOADING || !clientService.loadBinary) { + clientService.resultLog = clientService.resultLog + msg[1] + "\n"; + clientService.loadBinary |= (msg[0] == NS_DOWNLOADING); } if (msg[0] == NS_DOWNLOAD_SUCCESSFUL) { //Success! Keep it simple $('#compile-console').val($('#compile-console').val() + ' Succeeded.'); } else if (msg[0] == NE_DOWNLOAD_FAILED) { //Failed! Show the details - $('#compile-console').val($('#compile-console').val() + ' Failed!\n\n-------- loader messages --------\n' + launcher_result); + $('#compile-console').val($('#compile-console').val() + ' Failed!\n\n-------- loader messages --------\n' + clientService.resultLog); } else { //Show progress during downloading $('#compile-console').val($('#compile-console').val() + "."); @@ -439,24 +452,24 @@ function establish_socket() { var compileConsoleObj = document.getElementById("compile-console"); compileConsoleObj.scrollTop = compileConsoleObj.scrollHeight; - } else if (ws_msg.action === 'close-compile') { + } else if (wsMessage.action === 'close-compile') { $('#compile-dialog').modal('hide'); $('#compile-console').val(''); - } else if (ws_msg.action === 'console-log') { - console.log(ws_msg.msg); + } else if (wsMessage.action === 'console-log') { + console.log(wsMessage.msg); - } else if (ws_msg.action === 'websocket-close') { + } else if (wsMessage.action === 'websocket-close') { client_ws_connection.close(); - } else if (ws_msg.action === 'alert') { - utils.showMessage(Blockly.Msg.DIALOG_BLOCKLYPROP_LAUNCHER, ws_msg.msg); + } else if (wsMessage.action === 'alert') { + utils.showMessage(Blockly.Msg.DIALOG_BLOCKLYPROP_LAUNCHER, wsMessage.msg); } } // --- older client - disconnect it? else { - console.log('Unknown WS msg: ' + JSON.stringify(ws_msg)); + console.log('Unknown WS msg: ' + JSON.stringify(wsMessage)); } }; @@ -467,7 +480,7 @@ function establish_socket() { } function lostWSConnection() { -// Lost websocket connection, clean up and restart find_client processing +// Lost websocket connection, clean up and restart findClient processing client_ws_connection = null; clientService.type = 'none'; @@ -487,8 +500,8 @@ function lostWSConnection() { client_ws_heartbeat_interval = null; } - //Create new ws socket timeout (find_client) - check_ws_socket_timeout = setTimeout(find_client, 3000); + //Create new ws socket timeout (findClient) + check_ws_socket_timeout = setTimeout(findClient, 3000); }; diff --git a/src/editor.js b/src/editor.js index ff79d4da..d2bdb700 100644 --- a/src/editor.js +++ b/src/editor.js @@ -302,7 +302,7 @@ $(() => { outTo: 'terminal', portPath: getComPort(), baudrate: baudrate.toString(10), - msg: characterToSend, + msg: (clientService.rxBase64 ? btoa(characterToSend) : characterToSend), action: 'msg' }; client_ws_connection.send(JSON.stringify(msg_to_send)); @@ -597,7 +597,7 @@ function initEventHandlers() { // BlocklyProp Client. The BlocklyProp Launcher does not require // a configuration dialog // TODO: Client configuration is deprecated. No needed for Launcher - $('#client-setup').on('click', () => configure_client()); + $('#client-setup').on('click', () => configureConnectionPaths()); // -------------------------------- // End of hamburger menu items