From 70418e6851590ab1794b6ee24bb878babe6d89b4 Mon Sep 17 00:00:00 2001 From: Jeff Martin Date: Tue, 11 Sep 2018 15:48:53 -0700 Subject: [PATCH 01/24] Refactored to remove the need for the transmissionId variable. --- loader.js | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/loader.js b/loader.js index ce6183f..ecfbeca 100644 --- a/loader.js +++ b/loader.js @@ -258,7 +258,7 @@ function talkToProp(sock, port, binImage, toEEPROM) { setTimeout(function() { //Prep for expected packetID:transmissionId response (Micro-Boot-Loader's "Ready" signal) propComm.mblEPacketId[0] = packetId; - propComm.mblETransId[0] = transmissionId; + propComm.mblETransId[0] = 0; //MBL transmission's Id is always 0 //Send Micro Boot Loader package and get response; if wired port, unpause (may be auto-paused by incoming data error); wireless ports, carry on immediately log("Transmitting Micro Boot Loader package", mDeep); send(port, txData, true) @@ -320,11 +320,10 @@ function talkToProp(sock, port, binImage, toEEPROM) { Math.min(Math.trunc(maxDataSize / 4) - 2, Math.trunc(binImage.byteLength / 4) - pIdx); txData = new ArrayBuffer(txPacketLength * 4); //Set packet length (in longs)} txView = new Uint8Array(txData); - transmissionId = Math.floor(Math.random()*4294967296); //Create next random Transmission ID - propComm.mblEPacketId[0] = packetId-1; - propComm.mblETransId[0] = transmissionId; + propComm.mblEPacketId[0] = packetId-1; //Set next expected packetId + propComm.mblETransId[0] = Math.floor(Math.random()*4294967296); //Set next random Transmission ID (new DataView(txData, 0, 4)).setUint32(0, packetId, true); //Store Packet ID - (new DataView(txData, 4, 4)).setUint32(0, transmissionId, true); //Store random Transmission ID + (new DataView(txData, 4, 4)).setUint32(0, propComm.mblETransId[0], true); //Store random Transmission ID txView.set((new Uint8Array(binImage)).slice(pIdx * 4, pIdx * 4 + (txPacketLength - 2) * 4), 8); //Store section of binary image send(port, txData, false) //Transmit packet .then(function() {pIdx += txPacketLength - 2; packetId--; resolve();}); //Increment image index, decrement Packet ID (to next packet), resolve @@ -359,16 +358,16 @@ function talkToProp(sock, port, binImage, toEEPROM) { log(next.value.sendLog, mAll, sock); generateLoaderPacket(next.value.type, packetId); //Generate next executable packet - transmissionId = Math.floor(Math.random()*4294967296); //Create next random Transmission ID - (new DataView(txData, 4, 4)).setUint32(0, transmissionId, true); //Store random Transmission ID if (next.value.type !== ltLaunchNow) { //Response expected from MBL? prepForMBLResponse(next.value.recvTime, notice(neCommunicationLost)); // Prepare to receive next MBL response packetId = next.value.nextId; // Ready next Packet ID propComm.mblEPacketId[0] = packetId; // Note expected response - propComm.mblETransId[0] = transmissionId; + propComm.mblETransId[0] = Math.floor(Math.random()*4294967296); } + (new DataView(txData, 4, 4)).setUint32(0, propComm.mblETransId[0], true); //Store random Transmission ID (or 0) + send(port, txData, false) //Transmit packet .then(function() { if (next.value.type !== ltLaunchNow) { // If not last instruction packet... @@ -396,7 +395,6 @@ function talkToProp(sock, port, binImage, toEEPROM) { //Determine number of required packets for target application image; value becomes first Packet ID var totalPackets = Math.ceil(binImage.byteLength / (maxDataSize-4*2)); //binary image size (in bytes) / (max packet size - packet header) var packetId = totalPackets; - var transmissionId = 0; //Initial Transmission ID var pIdx = 0; //Packet index (points to next data in binary image to send //Calculate target application's full checksum (used for RAM Checksum confirmation)} binView = new Uint8Array(binImage); //Create view of the Propeller Application Image From 8110d513e040a61a55686f1c5945a46ff762a16e Mon Sep 17 00:00:00 2001 From: Jeff Martin Date: Tue, 11 Sep 2018 16:14:33 -0700 Subject: [PATCH 02/24] Disabled Nagle timer. --- serial.js | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/serial.js b/serial.js index a809546..70f76a0 100644 --- a/serial.js +++ b/serial.js @@ -243,11 +243,14 @@ function send(port, data, command) { updatePort(port, {[p.socket]: info.socketId}); chrome.sockets.tcp.connect(port[p.socket], port.ip, p.portNum, function () { //TODO Handle connect result - log("in sockets.tcp.connect()", mDbug); //!!!! - chrome.sockets.tcp.send(port[p.socket], data, function () { - //TODO handle send result - log("in sockets.tcp.connect > send()", mDbug); //!!!! - resolve(); + chrome.sockets.tcp.setNoDelay(info.socketId, true, function(result) { + if (result < 0) {log("Warning: unable to disable Nagle timer", mDbug)} + log("in sockets.tcp.connect()", mDbug); //!!!! + chrome.sockets.tcp.send(port[p.socket], data, function () { + //TODO handle send result + log("in sockets.tcp.connect > send()", mDbug); //!!!! + resolve(); + }); }); }); }); From 4576f449ddbce65c878820eb7b1077f839f223c9 Mon Sep 17 00:00:00 2001 From: Jeff Martin Date: Wed, 12 Sep 2018 13:28:34 -0700 Subject: [PATCH 03/24] Removed conversion from string or buffer to ArrayBuffer inside of send() and required data to be an ArrayBuffer. This will speed up response time. --- index.js | 6 ++++++ serial.js | 9 +-------- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/index.js b/index.js index 7ef9c0e..1b8116d 100644 --- a/index.js +++ b/index.js @@ -462,6 +462,12 @@ function serialTerminal(sock, action, portPath, baudrate, msg) { // Find port from portPath let port = findPort(byPath, portPath); if (port) { + // Convert msg from string or buffer to an ArrayBuffer + if (typeof msg === 'string') { + msg = str2ab(msg); + } else { + if (msg instanceof ArrayBuffer === false) {msg = buf2ab(msg);} + } if(port.isWired) { if (action === "open") { // Open port for terminal use diff --git a/serial.js b/serial.js index 70f76a0..3a66256 100644 --- a/serial.js +++ b/serial.js @@ -228,7 +228,7 @@ function ageWiredPorts() { function send(port, data, command) { /* Return a promise that transmits data on port port is the port's object - data is an ArrayBuffer (preferrably), string, or array + data is an ArrayBuffer command [ignored unless wireless] must be true to send to Wi-Fi Module's HTTP-based command service and false to send to Propeller via Telnet service*/ return new Promise(function(resolve, reject) { @@ -264,13 +264,6 @@ function send(port, data, command) { } } - // Convert data from string or buffer to an ArrayBuffer - if (typeof data === 'string') { - data = str2ab(data); - } else { - if (data instanceof ArrayBuffer === false) {data = buf2ab(data);} - } - if (port.isWired) { // Wired port chrome.serial.send(port.connId, data, function (sendResult) { resolve(); From 921431fc99cacee9bd358f686db83d108b3f2ca5 Mon Sep 17 00:00:00 2001 From: Jeff Martin Date: Thu, 13 Sep 2018 09:41:01 -0700 Subject: [PATCH 04/24] Increased max packet delay for debugging and fixed max packet size to 1492 (minimum typical TCP/IP packet size). --- port.js | 4 ++-- serial.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/port.js b/port.js index 15f2b4d..216b6d9 100644 --- a/port.js +++ b/port.js @@ -28,8 +28,8 @@ const wlLife = 3; var ports = []; // Serial packet handling (for transmissions to browser's terminal) -const serPacketFillTime = 10; // Max wait time to fill packet (ms) -const serPacketMax = Math.trunc(serPacketFillTime/1000/(1/finalBaudrate*10)); // Size of buffer to hold max bytes receivable in FillTime at max baudrate +const serPacketMaxTxTime = 100; // Max wait time before transmitting packet (ms) +const serPacketMax = 1492; // Size of buffer to transmit serial data to browser const serPacket = { id : 0, bufView : new Uint8Array(new ArrayBuffer(serPacketMax)), diff --git a/serial.js b/serial.js index 3a66256..d366193 100644 --- a/serial.js +++ b/serial.js @@ -289,7 +289,7 @@ chrome.serial.onReceive.addListener(function(info) { if (port.packet.len === serPacketMax) { sendDebugPacket(port); } else if (port.packet.timer === null) { - port.packet.timer = setTimeout(sendDebugPacket, serPacketFillTime, port) + port.packet.timer = setTimeout(sendDebugPacket, serPacketMaxTxTime, port) } } while (offset < info.data.byteLength); } From 6361b52f0117263a8f162357aca1a58f59434808 Mon Sep 17 00:00:00 2001 From: Jeff Martin Date: Thu, 13 Sep 2018 14:43:55 -0700 Subject: [PATCH 05/24] Added byPHID and byPTID port identifier types and enhanced serial onReceive and onReceiveError functions to be usable for both wired and wireless connections. --- port.js | 20 ++++++++++++++------ serial.js | 36 +++++++++++++++++++++++------------- wx.js | 3 +++ 3 files changed, 40 insertions(+), 19 deletions(-) diff --git a/port.js b/port.js index 216b6d9..1a38260 100644 --- a/port.js +++ b/port.js @@ -16,9 +16,11 @@ // Find Port identifier types -const byID = "connId"; -const byMAC = "mac"; -const byPath = "path"; +const byCID = "connId"; //Represents numeric Connection ID (cid) type +const byPHID = "phSocket"; //Represents numeric Propeller HTTP Socket ID type +const byPTID = "ptSocket"; //Represents numeric Propeller Telnet Socket ID type +const byMAC = "mac"; //Represents alphanumeric MAC address type +const byPath = "path"; //Represents alphanumeric path (wired/wireless port identifier) type // Port's max lifetime const wLife = 2; @@ -149,7 +151,9 @@ function exists(attr, src) { function findPortIdx(type, clue) { /* Return index of wired or wireless port associated with clue type / clue pairs must be: - byID / numeric Connection ID (cid) + byCID / numeric Connection ID (cid) + byPHID / numeric Propeller HTTP Socket ID + byPTID / numeric Propeller Telnet Socket ID byMAC / alphanumeric MAC address byPath / alphanumeric path (wired/wireless port identifier) Returns -1 if not found*/ @@ -159,7 +163,9 @@ function findPortIdx(type, clue) { function findPort(type, clue) { /* Return port record associated with clue. This allows caller to later directly retrieve any member of the record (provided caller safely checks for null) type / clue pairs must be: - byID / numeric Connection ID (cid) + byCID / numeric Connection ID (cid) + byPHID / numeric Propeller HTTP Socket ID + byPTID / numeric Propeller Telnet Socket ID byMAC / alphanumeric MAC address byPath / alphanumeric path (wired/wireless port identifier) Returns null if not found*/ @@ -176,7 +182,9 @@ function findPort(type, clue) { function deletePort(type, clue) { /* Delete wired or wireless port associated with clue type / clue pairs must be: - byID / numeric Connection ID (cid) + byCID / numeric Connection ID (cid) + byPHID / numeric Propeller HTTP Socket ID + byPTID / numeric Propeller Telnet Socket ID byMAC / alphanumeric MAC address byPath / alphanumeric path (wired/wireless port identifier)*/ let idx = findPortIdx(type, clue); diff --git a/serial.js b/serial.js index d366193..e41ccdd 100644 --- a/serial.js +++ b/serial.js @@ -274,11 +274,13 @@ function send(port, data, command) { }); } -chrome.serial.onReceive.addListener(function(info) { -// Permanent serial receive listener- routes debug data from Propeller to connected browser when necessary - let port = findPort(byID, info.connectionId); +//TODO !!! This is no longer a pure-wired-serial function; decide what to do long-term +function debugReceiver(info) { +// Wired and wireless receive listener- routes debug data from Propeller to connected browser when necessary + let wired = (info.hasOwnProperty(connectionId)); + let port = wired ? findPort(byCID, info.connectionId) : findPort(byPTID, info.socketId); if (port) { - if (port.mode === 'debug' && port.bSocket !== null) { + if (port.mode === 'debug' && port.bSocket) { // send to terminal in browser tab let offset = 0; do { @@ -303,14 +305,22 @@ chrome.serial.onReceive.addListener(function(info) { port.bSocket.send(JSON.stringify({type: 'serial-terminal', packetID: port.packet.id++, msg: btoa(ab2str(port.packet.bufView.slice(0, port.packet.len)))})); port.packet.len = 0; } -}); +}; -chrome.serial.onReceiveError.addListener(function(info) { -// Permanent serial receive error listener. - switch (info.error) { - case "disconnected": - case "device_lost" : - case "system_error": deletePort(byID, info.connectionId); +//TODO !!! This is no longer a pure-wired-serial function; decide what to do long-term +function debugErrorReceiver(info) { +// Wired and wireless receive error listener. + if (info.hasOwnProperty(connectionId)) { + switch (info.error) { + case "disconnected": + case "device_lost" : + case "system_error": deletePort(byCID, info.connectionId); + } +// log("Error: PortID "+info.connectionId+" "+info.error, mDeep); + } else { + log("Error: SocketID "+info.socketId+" Code "+info.resultCode, mDeep); } -// log("Error: PortID "+info.connectionId+" "+info.error, mDeep); -}); \ No newline at end of file +}; + +chrome.serial.onReceive.addListener(debugReceiver); +chrome.serial.onReceiveError.addListener(debugErrorReceiver); \ No newline at end of file diff --git a/wx.js b/wx.js index 2734e22..42cef40 100644 --- a/wx.js +++ b/wx.js @@ -180,6 +180,9 @@ function isValidWiFiVersion(response) { return valid; } +chrome.sockets.tcp.onReceive.addListener(debugReceiver); +chrome.sockets.tcp.onReceiveError.addListener(debugErrorReceiver); + /* function loadPropellerWX(portPath, action, payload, debug) { From 7f7cfb63822cb464a43acc832f21bbb1075bd96b Mon Sep 17 00:00:00 2001 From: Jeff Martin Date: Thu, 13 Sep 2018 15:56:31 -0700 Subject: [PATCH 06/24] Fixed bugs and added better error message. --- serial.js | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/serial.js b/serial.js index e41ccdd..8e31d79 100644 --- a/serial.js +++ b/serial.js @@ -277,7 +277,7 @@ function send(port, data, command) { //TODO !!! This is no longer a pure-wired-serial function; decide what to do long-term function debugReceiver(info) { // Wired and wireless receive listener- routes debug data from Propeller to connected browser when necessary - let wired = (info.hasOwnProperty(connectionId)); + let wired = (info.hasOwnProperty("connectionId")); let port = wired ? findPort(byCID, info.connectionId) : findPort(byPTID, info.socketId); if (port) { if (port.mode === 'debug' && port.bSocket) { @@ -310,7 +310,7 @@ function debugReceiver(info) { //TODO !!! This is no longer a pure-wired-serial function; decide what to do long-term function debugErrorReceiver(info) { // Wired and wireless receive error listener. - if (info.hasOwnProperty(connectionId)) { + if (info.hasOwnProperty("connectionId")) { switch (info.error) { case "disconnected": case "device_lost" : @@ -318,7 +318,14 @@ function debugErrorReceiver(info) { } // log("Error: PortID "+info.connectionId+" "+info.error, mDeep); } else { - log("Error: SocketID "+info.socketId+" Code "+info.resultCode, mDeep); + switch (info.resultCode) { + case -100: let port = findPort(byPTID, info.socketId); + if (!port) {port = findPort(byPHID, info.socketId)} + log("Error: SocketID "+info.socketId+" connection closed" + ((port) ? " for port ." : "."), mDeep); + break; + default: log("Error: SocketID "+info.socketId+" Code "+info.resultCode, mDeep); + } + } }; From 0f21f8a9c9f44170ba779e18f94db8bdb55a6d6d Mon Sep 17 00:00:00 2001 From: Jeff Martin Date: Fri, 14 Sep 2018 15:32:53 -0700 Subject: [PATCH 07/24] Patched loose ends in loadPropeller() so that debug messages transport to terminal/graph. Updated loader POST message to include final baud rate. --- loader.js | 7 +++++-- serial.js | 3 ++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/loader.js b/loader.js index ecfbeca..ea9683f 100644 --- a/loader.js +++ b/loader.js @@ -123,7 +123,7 @@ function loadPropeller(sock, portPath, action, payload, debug) { if (port.connId) { // Connection exists, prep to reuse it originalBaudrate = port.baud; - port.mode = "programming"; + updatePort(port, {mode: "programming", bSocket: sock}); connect = function() {return changeBaudrate(port, initialBaudrate)} } else { // No connection yet, prep to create one @@ -135,6 +135,7 @@ function loadPropeller(sock, portPath, action, payload, debug) { postResetDelay = 1; //TODO Retrieve actual current baudrate originalBaudrate = initialBaudrate; + updatePort(port, {mode: "programming", bSocket: sock}); connect = function() {return Promise.resolve()}; } // Use connection to download application to the Propeller @@ -151,6 +152,7 @@ function loadPropeller(sock, portPath, action, payload, debug) { sock.send(JSON.stringify({type:"ui-command", action:(debug === "term") ? "open-terminal" : "open-graph"})); sock.send(JSON.stringify({type:"ui-command", action:"close-compile"})); } else { //Else + updatePort(port, {mode: ""}); // Clear port mode if (port.isWireless) closePort(port, false).catch(function(e) {log(e.message, mAll, sock);}) // Close Telnet port (if wireless) } }) //Error? Disable listener and display error @@ -158,6 +160,7 @@ function loadPropeller(sock, portPath, action, payload, debug) { listen(port, false); log(e.message, mAll, sock); log(notice(neDownloadFailed), mAll, sock); + updatePort(port, {mode: ""}); if ((port.isWired && port.connId) || port.isWireless) {changeBaudrate(port, originalBaudrate)} if (port.isWireless) {closePort(port, false)} }); @@ -856,7 +859,7 @@ function generateLoaderPacket(loaderType, packetId, clockSpeed, clockMode) { txView.set(timingPulses, txLength + encodedLoader.byteLength); } else /*loaderType === ltCore*/ { //[ltUnEncCore] Prepare unencoded loader packet (for wireless downloads) - let postStr = str2ab("POST /propeller/load?baud-rate="+initialBaudrate+"&reset-pin=12&response-size=8&response-timeout=1000 HTTP/1.1\r\nContent-Length: "+patchedLoader.byteLength+"\r\n\r\n"); + let postStr = str2ab("POST /propeller/load?baud-rate="+initialBaudrate+"&final-baud-rate="+finalBaudrate+"&reset-pin=12&response-size=8&response-timeout=1000 HTTP/1.1\r\nContent-Length: "+patchedLoader.byteLength+"\r\n\r\n"); txData = new ArrayBuffer(postStr.byteLength+patchedLoader.byteLength); txView = new Uint8Array(txData); diff --git a/serial.js b/serial.js index 8e31d79..5f68f22 100644 --- a/serial.js +++ b/serial.js @@ -241,6 +241,7 @@ function send(port, data, command) { chrome.sockets.tcp.create(function (info) { log("in sockets.tcp.create()", mDbug); //!!!! updatePort(port, {[p.socket]: info.socketId}); + log(p.socket + " SocketID "+port[p.socket]+" transmitting" + ((port) ? " for port " + port.path + "." : "."), mDeep); chrome.sockets.tcp.connect(port[p.socket], port.ip, p.portNum, function () { //TODO Handle connect result chrome.sockets.tcp.setNoDelay(info.socketId, true, function(result) { @@ -321,7 +322,7 @@ function debugErrorReceiver(info) { switch (info.resultCode) { case -100: let port = findPort(byPTID, info.socketId); if (!port) {port = findPort(byPHID, info.socketId)} - log("Error: SocketID "+info.socketId+" connection closed" + ((port) ? " for port ." : "."), mDeep); + log("SocketID "+info.socketId+" connection closed" + ((port) ? " for port " + port.path + "." : "."), mDeep); break; default: log("Error: SocketID "+info.socketId+" Code "+info.resultCode, mDeep); } From b2f50d2e539f6b9e7ba6d1c032282839efd106ff Mon Sep 17 00:00:00 2001 From: Jeff Martin Date: Mon, 17 Sep 2018 12:56:12 -0700 Subject: [PATCH 08/24] Added protection to debugReceiver()'s sendDebug() to prevent sending if bSocket is null. --- serial.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/serial.js b/serial.js index 5f68f22..59e5c0a 100644 --- a/serial.js +++ b/serial.js @@ -303,7 +303,9 @@ function debugReceiver(info) { clearTimeout(port.packet.timer); port.packet.timer = null; } - port.bSocket.send(JSON.stringify({type: 'serial-terminal', packetID: port.packet.id++, msg: btoa(ab2str(port.packet.bufView.slice(0, port.packet.len)))})); + if (port.mode === 'debug' && port.bSocket) { + port.bSocket.send(JSON.stringify({type: 'serial-terminal', packetID: port.packet.id++, msg: btoa(ab2str(port.packet.bufView.slice(0, port.packet.len)))})); + } port.packet.len = 0; } }; From 5fb281ae4b9442fbde4cb88e735f3b3310e71361 Mon Sep 17 00:00:00 2001 From: Jeff Martin Date: Wed, 19 Sep 2018 15:32:22 -0700 Subject: [PATCH 09/24] Fixed bug causing downloads to fail due to previous execution's debug stream. Commented out many debugging logs. Refactored for speed. --- loader.js | 45 +++++++++++++++++++++++++-------------------- serial.js | 27 ++++++++++++--------------- 2 files changed, 37 insertions(+), 35 deletions(-) diff --git a/loader.js b/loader.js index ea9683f..c434941 100644 --- a/loader.js +++ b/loader.js @@ -23,19 +23,20 @@ let txData; //Data to transmit to the Pr const defaultClockSpeed = 80000000; const defaultClockMode = 0x6F; const maxDataSize = 1392; //Max data packet size (for packets sent to running Micro Boot Loader) +const mblRespSize = 8; //Size of Micro Boot Loader Response (and Expected) array buffers // propComm stage values const sgIdle = -1; const sgHandshake = 0; const sgVersion = 1; const sgRAMChecksum = 2; -const sgMBLResponse = 3; +const sgMBLResponse = 3; //NOTE: hearFromProp() requires all further to be wireless stages const sgWXResponse = 4; // Propeller Communication (propComm) status; categorizes Propeller responses let propComm = {}; //Holds current status -let mblRespAB = new ArrayBuffer(8); //Buffer for Micro Boot Loader actual responses -let mblExpdAB = new ArrayBuffer(8); //Buffer for Micro Boot Loader expected responses +let mblRespAB = new ArrayBuffer(mblRespSize); //Buffer for Micro Boot Loader actual responses +let mblExpdAB = new ArrayBuffer(mblRespSize); //Buffer for Micro Boot Loader expected responses const propCommStart = { //propCommStart is used to initialize propComm stage : sgIdle, //Propeller Protocol Stage @@ -441,8 +442,11 @@ function hearFromProp(info) { log("Received " + info.data.byteLength + " bytes", mDeep); // Parse HTTP-command responses into proper object, or treat wired and Telnet-wireless streams as an unformatted array let stream = (propComm.port.phSocket) ? parseHTTP(info.data) : new Uint8Array(info.data) - // Exit if we're idling or if socket-based data is not in response to our Propeller communication socket - if ((propComm.stage === sgIdle) || (info.hasOwnProperty("socketId") && info.socketId !== propComm.port.phSocket && info.socketId !== propComm.port.ptSocket)) { + // Exit if we're idling or if socket-based data is not in response to our communication + if (propComm.stage === sgIdle || + !(info.hasOwnProperty("socketId") && + ((propComm.stage === sgMBLResponse && (info.socketId === propComm.port.phSocket || info.socketId === propComm.port.ptSocket)) || + (propComm.stage === sgWXResponse && info.socketId === propComm.port.phSocket)))) { log("...ignoring", mDeep); return; } @@ -544,39 +548,40 @@ function hearFromProp(info) { propComm.rxCount = 0; } - // Receive Micro Boot Loader's response. The first is its "Ready" signal; the rest are packet responses. + // Receive Micro Boot Loader's response. The first serves as its "Ready" signal, the rest are packet responses; all are formatted as 4-byte expected Packet ID followed by 4-byte Transmission ID. + // NOTE: For wireless, the first is contained in the body of an HTTP response and the rest are delivered as Telnet responses. if (propComm.stage === sgMBLResponse) { if (propComm.port.isWireless) { // Wireless response console.log(stream); - if (propComm.port.phSocket) { //HTTP-command response + if (propComm.port.phSocket) { //HTTP-command response; we'll assume right size, errors will be caught by timeout if (stream.ResponseCode === 200) { - propComm.mblRespBuf.set(new Uint8Array(stream.Body.slice(0, propComm.mblRespBuf.byteLength))); - propComm.rxCount = propComm.mblRespBuf.byteLength; + propComm.mblRespBuf.set(new Uint8Array(stream.Body.slice(-mblRespSize))); + propComm.rxCount = mblRespSize; } if (stream.hasOwnProperty("Connection") && stream.Connection === "close") {updatePort(propComm.port, {phSocket: null})} // Forget socket id (closed by host)} - } else { //Telnet response - propComm.mblRespBuf.set(new Uint8Array(stream.slice(0, propComm.mblRespBuf.byteLength))); - propComm.rxCount = propComm.mblRespBuf.byteLength; + } else { //Telnet response; we'll assume right size (perhaps tacked onto end of previous debug stream), errors will be caught by timeout + propComm.mblRespBuf.set(new Uint8Array(stream.slice(-mblRespSize))); + propComm.rxCount = mblRespSize; } } else { // Wired response - while (sIdx < stream.length && propComm.rxCount < propComm.mblRespBuf.byteLength) { + while (sIdx < stream.length && propComm.rxCount < mblRespSize) { propComm.mblRespBuf[propComm.rxCount++] = stream[sIdx++]; } } //Finish stage when expected response size received - if (propComm.rxCount === propComm.mblRespBuf.byteLength) { - clearPropCommTimer(); - propComm.stage = sgIdle; + if (propComm.rxCount === mblRespSize) { // log("Response PacketId: "+ propComm.mblRPacketId+ " TransId: "+ propComm.mblRTransId, mDeep); // log("Expected PacketId: "+ propComm.mblEPacketId+ " TransId: "+ propComm.mblETransId, mDeep); if ((propComm.mblRPacketId[0] === propComm.mblEPacketId[0]) && (propComm.mblRTransId[0] === propComm.mblETransId[0])) { //MBL Response is perfect; Note resolved + clearPropCommTimer(); + propComm.stage = sgIdle; propComm.response.resolve(); } else { - //MBL Response invalid; Note rejected; Ignore the rest - propComm.response.reject(Error(notice(neLoaderFailed))); + //MBL Response invalid; may be leftover debug data; ignore + propComm.rxCount = 0; } return; } @@ -588,8 +593,8 @@ function hearFromProp(info) { propComm.stage = sgIdle; console.log(stream); if (stream.ResponseCode === 200) { -// propComm.mblRespBuf.set(new Uint8Array(stream.Body.slice(0, propComm.mblRespBuf.byteLength))); -// propComm.rxCount = propComm.mblRespBuf.byteLength; +// propComm.mblRespBuf.set(new Uint8Array(stream.Body.slice(0, mblRespSize))); +// propComm.rxCount = mblRespSize; propComm.response.resolve(); } else { //TODO Designate a proper error here (probably best to pass on error from response) diff --git a/serial.js b/serial.js index 59e5c0a..4513617 100644 --- a/serial.js +++ b/serial.js @@ -157,9 +157,6 @@ function changeBaudrate(port, baudrate) { chrome.sockets.tcp.create(function (info) { //Update port record with socket to Propeller's HTTP service updatePort(port, {phSocket: info.socketId}); - if (!port.phSocket) { - log("NULL SOCKET!!!", mDbug); //!!!! - } let postStr = "POST /wx/setting?name=baud-rate&value=" + baudrate + " HTTP/1.1\r\n\r\n"; chrome.sockets.tcp.connect(port.phSocket, port.ip, 80, function() { chrome.sockets.tcp.send(port.phSocket, str2ab(postStr), function () { @@ -233,33 +230,33 @@ function send(port, data, command) { return new Promise(function(resolve, reject) { - log("in send()", mDbug); //!!!! +// log("in send()", mDbug); //!!!! function socketSend(p) { - log("in socketSend()", mDbug); //!!!! +// log("in socketSend()", mDbug); //!!!! if (!port[p.socket]) { // No ph or pt socket yet; create one and connect to it chrome.sockets.tcp.create(function (info) { - log("in sockets.tcp.create()", mDbug); //!!!! +// log("in sockets.tcp.create()", mDbug); //!!!! updatePort(port, {[p.socket]: info.socketId}); - log(p.socket + " SocketID "+port[p.socket]+" transmitting" + ((port) ? " for port " + port.path + "." : "."), mDeep); +// log(p.socket + " SocketID "+port[p.socket]+" transmitting" + ((port) ? " for port " + port.path + "." : "."), mDeep); chrome.sockets.tcp.connect(port[p.socket], port.ip, p.portNum, function () { //TODO Handle connect result chrome.sockets.tcp.setNoDelay(info.socketId, true, function(result) { if (result < 0) {log("Warning: unable to disable Nagle timer", mDbug)} - log("in sockets.tcp.connect()", mDbug); //!!!! +// log("in sockets.tcp.connect()", mDbug); //!!!! chrome.sockets.tcp.send(port[p.socket], data, function () { //TODO handle send result - log("in sockets.tcp.connect > send()", mDbug); //!!!! +// log("in sockets.tcp.connect > send()", mDbug); //!!!! resolve(); }); }); }); }); } else { // Socket exists; use it - log("socket exists", mDbug); //!!!! +// log("socket exists", mDbug); //!!!! chrome.sockets.tcp.send(port[p.socket], data, function () { //TODO handle send result - log("in sockets.tcp.send()", mDbug); //!!!! +// log("in sockets.tcp.send()", mDbug); //!!!! resolve(); }); } @@ -322,13 +319,13 @@ function debugErrorReceiver(info) { // log("Error: PortID "+info.connectionId+" "+info.error, mDeep); } else { switch (info.resultCode) { - case -100: let port = findPort(byPTID, info.socketId); - if (!port) {port = findPort(byPHID, info.socketId)} - log("SocketID "+info.socketId+" connection closed" + ((port) ? " for port " + port.path + "." : "."), mDeep); + case -100: //port closed + // let port = findPort(byPTID, info.socketId); + // if (!port) {port = findPort(byPHID, info.socketId)} + // log("SocketID "+info.socketId+" connection closed" + ((port) ? " for port " + port.path + "." : "."), mDeep); break; default: log("Error: SocketID "+info.socketId+" Code "+info.resultCode, mDeep); } - } }; From 64405370666e6b3667639a65dc0be054e949824e Mon Sep 17 00:00:00 2001 From: Jeff Martin Date: Thu, 20 Sep 2018 19:39:48 -0700 Subject: [PATCH 10/24] Compressed dataSource into ternary logic and restructured to do zero processing on data if it's from an unexpected source. Also moved constant definitions out of hearFromProp() and up to top of loader.js to clean up appearance. --- loader.js | 64 +++++++++++++++++++++++++++++++++---------------------- 1 file changed, 39 insertions(+), 25 deletions(-) diff --git a/loader.js b/loader.js index c434941..3cb5f85 100644 --- a/loader.js +++ b/loader.js @@ -33,6 +33,12 @@ const sgRAMChecksum = 2; const sgMBLResponse = 3; //NOTE: hearFromProp() requires all further to be wireless stages const sgWXResponse = 4; +// hearFromProp data source valuse +const dsDontCare = -1; +const dsWired = 0; +const dsHTTP = 1; +const dsTelnet = 2; + // Propeller Communication (propComm) status; categorizes Propeller responses let propComm = {}; //Holds current status let mblRespAB = new ArrayBuffer(mblRespSize); //Buffer for Micro Boot Loader actual responses @@ -61,6 +67,18 @@ const ltProgramEEPROM = 1; //Generate Program EEPROM e const ltReadyToLaunch = 2; //Generate Ready To Launch executable packet const ltLaunchNow = 3; //Generate Launch Now executable packet +//Receiver Handshake pattern +const rxHandshake = [ + 0xEE,0xCE,0xCE,0xCF,0xEF,0xCF,0xEE,0xEF,0xCF,0xCF,0xEF,0xEF,0xCF,0xCE,0xEF,0xCF, //The rxHandshake array consists of 125 bytes encoded to represent + 0xEE,0xEE,0xCE,0xEE,0xEF,0xCF,0xCE,0xEE,0xCE,0xCF,0xEE,0xEE,0xEF,0xCF,0xEE,0xCE, //the expected 250-bit (125-byte @ 2 bits/byte) response of + 0xEE,0xCE,0xEE,0xCF,0xEF,0xEE,0xEF,0xCE,0xEE,0xEE,0xCF,0xEE,0xCF,0xEE,0xEE,0xCF, //continuing-LFSR stream bits from the Propeller, prompted by the + 0xEF,0xCE,0xCF,0xEE,0xEF,0xEE,0xEE,0xEE,0xEE,0xEF,0xEE,0xCF,0xCF,0xEF,0xEE,0xCE, //timing templates following the txHandshake stream. + 0xEF,0xEF,0xEF,0xEF,0xCE,0xEF,0xEE,0xEF,0xCF,0xEF,0xCF,0xCF,0xCE,0xCE,0xCE,0xCF, + 0xCF,0xEF,0xCE,0xEE,0xCF,0xEE,0xEF,0xCE,0xCE,0xCE,0xEF,0xEF,0xCF,0xCF,0xEE,0xEE, + 0xEE,0xCE,0xCF,0xCE,0xCE,0xCF,0xCE,0xEE,0xEF,0xEE,0xEF,0xEF,0xCF,0xEF,0xCE,0xCE, + 0xEF,0xCE,0xEE,0xCE,0xEF,0xCE,0xCE,0xEE,0xCF,0xCF,0xCE,0xCF,0xCF +]; + /*********************************************************** * Support Functions * ***********************************************************/ @@ -437,30 +455,27 @@ function talkToProp(sock, port, binImage, toEEPROM) { function hearFromProp(info) { /* Receive Propeller's responses during programming. Parse responses for expected stages. - This function is called asynchronously whenever data arrives*/ - - log("Received " + info.data.byteLength + " bytes", mDeep); - // Parse HTTP-command responses into proper object, or treat wired and Telnet-wireless streams as an unformatted array - let stream = (propComm.port.phSocket) ? parseHTTP(info.data) : new Uint8Array(info.data) - // Exit if we're idling or if socket-based data is not in response to our communication - if (propComm.stage === sgIdle || - !(info.hasOwnProperty("socketId") && - ((propComm.stage === sgMBLResponse && (info.socketId === propComm.port.phSocket || info.socketId === propComm.port.ptSocket)) || - (propComm.stage === sgWXResponse && info.socketId === propComm.port.phSocket)))) { - log("...ignoring", mDeep); + This function is called asynchronously whenever data arrives and may receive data not related to programming (such as a debug stream or unrelated IP traffic) + so it filters and ignores what it doesn't need.*/ + + let dataSource = (info.hasOwnProperty("socketId")) ? + /*Is Expected Wireless HTTP?*/ (info.socketId === propComm.port.phSocket) ? dsHTTP : + /*Is Expected Wireless Telnet?*/ (!propComm.port.phSocket && propComm.stage === sgMBLResponse && info.socketId === propComm.port.ptSocket) ? dsTelnet : + /*Unexpected WL Protocol for State*/ dsDontCare : + /*Is Expected Wired stream?*/ (propComm.stage !== sgIdle) ? dsWired : dsDontCare; + + // Exit if this isn't the data we're looking for + if (dataSource === dsDontCare) { + log("Ignoring " + info.data.byteLength + " unexpected bytes", mDeep); + console.log(info.data); //!!!! return; } - const rxHandshake = [ - 0xEE,0xCE,0xCE,0xCF,0xEF,0xCF,0xEE,0xEF,0xCF,0xCF,0xEF,0xEF,0xCF,0xCE,0xEF,0xCF, //The rxHandshake array consists of 125 bytes encoded to represent - 0xEE,0xEE,0xCE,0xEE,0xEF,0xCF,0xCE,0xEE,0xCE,0xCF,0xEE,0xEE,0xEF,0xCF,0xEE,0xCE, //the expected 250-bit (125-byte @ 2 bits/byte) response of - 0xEE,0xCE,0xEE,0xCF,0xEF,0xEE,0xEF,0xCE,0xEE,0xEE,0xCF,0xEE,0xCF,0xEE,0xEE,0xCF, //continuing-LFSR stream bits from the Propeller, prompted by the - 0xEF,0xCE,0xCF,0xEE,0xEF,0xEE,0xEE,0xEE,0xEE,0xEF,0xEE,0xCF,0xCF,0xEF,0xEE,0xCE, //timing templates following the txHandshake stream. - 0xEF,0xEF,0xEF,0xEF,0xCE,0xEF,0xEE,0xEF,0xCF,0xEF,0xCF,0xCF,0xCE,0xCE,0xCE,0xCF, - 0xCF,0xEF,0xCE,0xEE,0xCF,0xEE,0xEF,0xCE,0xCE,0xCE,0xEF,0xEF,0xCF,0xCF,0xEE,0xEE, - 0xEE,0xCE,0xCF,0xCE,0xCE,0xCF,0xCE,0xEE,0xEF,0xEE,0xEF,0xEF,0xCF,0xEF,0xCE,0xCE, - 0xEF,0xCE,0xEE,0xCE,0xEF,0xCE,0xCE,0xEE,0xCF,0xCF,0xCE,0xCF,0xCF - ]; + // Parse HTTP-command responses into proper object, or treat wired and Telnet-wireless streams as an unformatted array + let stream = (dataSource === dsHTTP) ? parseHTTP(info.data) : new Uint8Array(info.data) + log("Received " + info.data.byteLength + " bytes", mDeep); + console.log(stream); //!!!! + var sIdx = 0; /* Validate rxHandshake @@ -551,10 +566,9 @@ function hearFromProp(info) { // Receive Micro Boot Loader's response. The first serves as its "Ready" signal, the rest are packet responses; all are formatted as 4-byte expected Packet ID followed by 4-byte Transmission ID. // NOTE: For wireless, the first is contained in the body of an HTTP response and the rest are delivered as Telnet responses. if (propComm.stage === sgMBLResponse) { - if (propComm.port.isWireless) { + if (dataSource !== dsWired) { // Wireless response - console.log(stream); - if (propComm.port.phSocket) { //HTTP-command response; we'll assume right size, errors will be caught by timeout + if (dataSource === dsHTTP) { //HTTP-command response; we'll assume right size, errors will be caught by timeout if (stream.ResponseCode === 200) { propComm.mblRespBuf.set(new Uint8Array(stream.Body.slice(-mblRespSize))); propComm.rxCount = mblRespSize; @@ -566,6 +580,7 @@ function hearFromProp(info) { } } else { // Wired response + //TODO consider set function here while (sIdx < stream.length && propComm.rxCount < mblRespSize) { propComm.mblRespBuf[propComm.rxCount++] = stream[sIdx++]; } @@ -591,7 +606,6 @@ function hearFromProp(info) { if (propComm.stage === sgWXResponse) { clearPropCommTimer(); propComm.stage = sgIdle; - console.log(stream); if (stream.ResponseCode === 200) { // propComm.mblRespBuf.set(new Uint8Array(stream.Body.slice(0, mblRespSize))); // propComm.rxCount = mblRespSize; From a20728992e89ad3894ef9c7ebcb5b396de29b07e Mon Sep 17 00:00:00 2001 From: Jeff Martin Date: Fri, 21 Sep 2018 12:04:00 -0700 Subject: [PATCH 11/24] Set default port mode to 'none' throughout code and updated serialTerminal() to set mode to 'none' upon closing wireless ports and also to send messages to wireless ports. --- index.js | 6 ++++-- loader.js | 4 ++-- port.js | 4 ++-- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/index.js b/index.js index 1b8116d..2d0222e 100644 --- a/index.js +++ b/index.js @@ -494,9 +494,11 @@ function serialTerminal(sock, action, portPath, baudrate, msg) { if (action === 'open') { } else if (action === 'close') { - + port.mode = 'none'; } else if (action === 'msg') { - + if (port.ptSocket) { + send(port, msg, false); + } } } } else { diff --git a/loader.js b/loader.js index 3cb5f85..f0565c2 100644 --- a/loader.js +++ b/loader.js @@ -171,7 +171,7 @@ function loadPropeller(sock, portPath, action, payload, debug) { sock.send(JSON.stringify({type:"ui-command", action:(debug === "term") ? "open-terminal" : "open-graph"})); sock.send(JSON.stringify({type:"ui-command", action:"close-compile"})); } else { //Else - updatePort(port, {mode: ""}); // Clear port mode + updatePort(port, {mode: "none"}); // Clear port mode if (port.isWireless) closePort(port, false).catch(function(e) {log(e.message, mAll, sock);}) // Close Telnet port (if wireless) } }) //Error? Disable listener and display error @@ -179,7 +179,7 @@ function loadPropeller(sock, portPath, action, payload, debug) { listen(port, false); log(e.message, mAll, sock); log(notice(neDownloadFailed), mAll, sock); - updatePort(port, {mode: ""}); + updatePort(port, {mode: "none"}); if ((port.isWired && port.connId) || port.isWireless) {changeBaudrate(port, originalBaudrate)} if (port.isWireless) {closePort(port, false)} }); diff --git a/port.js b/port.js index 1a38260..8b5e499 100644 --- a/port.js +++ b/port.js @@ -80,7 +80,7 @@ function addPort(alist) { bSocketIdx : -1, /*[>=-1] Index of browser socket in sockets list*/ phSocket : null, /*[null+] Socket to Propeller's HTTP service (not persistent)*/ ptSocket : null, /*[null+] Socket to Propeller's Telnet service (persistent)*/ - mode : "", /*[""+] Intention of the connection; "", "debug", or "programming"*/ + mode : "none", /*["none"+] Intention of the connection; "none", "debug", or "programming"*/ baud : 0, /*[>=0] Wired port's data rate*/ packet : {}, /*[...] Packet buffer for socket*/ isWired : !Boolean(get("ip", alist, "")), /*[true/false] indicates if port is wired or not*/ @@ -101,7 +101,7 @@ function updatePort(port, alist) { bSocket: active socket to browser to associate with port; may be null phSocket: active socket to Propeller's HTTP service; may be null ptSocket: active socket to Propeller's Telnet service; may be null - mode: the current point of the connection; "", "debug", "programming" + mode: the current point of the connection; "none", "debug", "programming" baud: wired serial speed*/ return new Promise(function(resolve, reject) { From 701f1aff184139fe7270b4b296166945fed2dea5 Mon Sep 17 00:00:00 2001 From: Jeff Martin Date: Fri, 21 Sep 2018 16:26:28 -0700 Subject: [PATCH 12/24] Removed received-data console output. --- loader.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/loader.js b/loader.js index f0565c2..e358aa1 100644 --- a/loader.js +++ b/loader.js @@ -467,14 +467,14 @@ function hearFromProp(info) { // Exit if this isn't the data we're looking for if (dataSource === dsDontCare) { log("Ignoring " + info.data.byteLength + " unexpected bytes", mDeep); - console.log(info.data); //!!!! +// console.log(info.data); //!!!! return; } // Parse HTTP-command responses into proper object, or treat wired and Telnet-wireless streams as an unformatted array let stream = (dataSource === dsHTTP) ? parseHTTP(info.data) : new Uint8Array(info.data) log("Received " + info.data.byteLength + " bytes", mDeep); - console.log(stream); //!!!! +// console.log(stream); //!!!! var sIdx = 0; From 2ddfef9fe4aba86024000221a37d2656246d6e85 Mon Sep 17 00:00:00 2001 From: Jeff Martin Date: Thu, 27 Sep 2018 08:16:53 -0700 Subject: [PATCH 13/24] Added openSocket() and updated related code to open HTTP or Telnet port if needed at the moment it's needed. This consolidates some code and prepares for allowing browser to open wireless port for immediate debugging. --- serial.js | 145 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 74 insertions(+), 71 deletions(-) diff --git a/serial.js b/serial.js index 4513617..e1d1932 100644 --- a/serial.js +++ b/serial.js @@ -27,9 +27,10 @@ //TODO Consider returning error object //TODO Consider enhancing error to indicate if the port is already open (this would only be for developer mistakes though) function openPort(sock, portPath, baudrate, connMode) { -/* Return a promise to open serial port at portPath with baudrate and connect to sock. - sock can be null to open serial port without an associated socket - portPath is the string path to the wired serial port +/* Return a promise to open wired or wireless port at portPath with baudrate and connect to browser sock. If wireless, the port is opened + as a Telnet-based debug service. + sock can be null to open port without an associated browser socket + portPath is the string path to the wired or wireless port baudrate is optional; defaults to initialBaudrate connMode is the current point of the connection; 'debug', 'programming' Resolves (with nothing); rejects with Error*/ @@ -37,32 +38,33 @@ function openPort(sock, portPath, baudrate, connMode) { baudrate = baudrate ? parseInt(baudrate) : initialBaudrate; var port = findPort(byPath, portPath); if (port) { - if (port.connId) { - //Already open; ensure correct baudrate, socket, and connMode, then resolve. - updatePort(port, {bSocket: sock, mode: connMode, baud: baudrate}) - .then(function() {resolve()}) - .catch(function (e) {reject(e)}); - } else { - //Not already open; attempt to open it - chrome.serial.connect(portPath, { - 'bitrate': baudrate, - 'dataBits': 'eight', - 'parityBit': 'no', - 'stopBits': 'one', - 'ctsFlowControl': false - }, - function (openInfo) { - if (!chrome.runtime.lastError) { - // No error; update serial port object - updatePort(port, {connId: openInfo.connectionId, bSocket: sock, mode: connMode, baud: baudrate}); - log("Port " + portPath + " open with ID " + openInfo.connectionId, mStat); - resolve(); - } else { - // Error - reject(Error(notice(neCanNotOpenPort, [portPath]))); + if (port.isWired) { /*Wired port*/ + if (port.connId) { + //Already open; ensure correct baudrate, socket, and connMode, then resolve. + updatePort(port, {bSocket: sock, mode: connMode, baud: baudrate}) + .then(function() {resolve()}) + .catch(function(e) {reject(e)}); + } else { + //Not already open; attempt to open it + chrome.serial.connect(portPath, {bitrate: baudrate, dataBits: 'eight', parityBit: 'no', stopBits: 'one', ctsFlowControl: false}, + function (openInfo) { + if (!chrome.runtime.lastError) { + // No error; update serial port object + updatePort(port, {connId: openInfo.connectionId, bSocket: sock, mode: connMode, baud: baudrate}); + log("Port " + portPath + " open with ID " + openInfo.connectionId, mStat); + resolve(); + } else { + // Error + reject(Error(notice(neCanNotOpenPort, [portPath]))); + } } - } - ); + ); + } + } else { /*Wireless port*/ + openSocket(port, false) + .then(updatePort(port, {bSocket: sock, mode: connMode, baud: baudrate}) + .then(function() {resolve()}) + .catch(function (e) {reject(e)})); } } else { // Error; port record not found @@ -71,6 +73,33 @@ function openPort(sock, portPath, baudrate, connMode) { }); } +function openSocket(port, command) { +/* Open Propeller command (HTTP) or debug (Telnet) socket on port + port is the port's object + command is true to open HTTP-based command service and false to open Telnet-based Debug service + Resolves with object describing socket type*/ + return new Promise(function(resolve, reject) { + let p = (command) ? {socket: "phSocket", portNum: 80} : {socket: "ptSocket", portNum: 23}; + if (port[p.socket]) { // Already open; resolve + resolve(); + } else { // No ph or pt socket yet; create one and connect to it + chrome.sockets.tcp.create(function (info) { +// log("in sockets.tcp.create()", mDbug); //!!!! + updatePort(port, {[p.socket]: info.socketId}); +// log(p.socket + " SocketID "+port[p.socket]+" transmitting" + ((port) ? " for port " + port.path + "." : "."), mDeep); + chrome.sockets.tcp.connect(port[p.socket], port.ip, p.portNum, function () { + //TODO Handle connect result + chrome.sockets.tcp.setNoDelay(info.socketId, true, function(result) { + if (result < 0) {log("Warning: unable to disable Nagle timer", mDbug)} +// log("in sockets.tcp.connect()", mDbug); //!!!! + resolve(p); + }); + }); + }); + } + }); +} + //TODO !!! This is no longer a pure-wired-serial function; decide what to do long-term function closePort(port, command) { /* Close the port. @@ -154,18 +183,16 @@ function changeBaudrate(port, baudrate) { } else { //TODO Need to check for errors. resetPropComm(port, 1500, sgWXResponse, notice(neCanNotSetBaudrate, [port.path, baudrate]), true); - chrome.sockets.tcp.create(function (info) { - //Update port record with socket to Propeller's HTTP service - updatePort(port, {phSocket: info.socketId}); - let postStr = "POST /wx/setting?name=baud-rate&value=" + baudrate + " HTTP/1.1\r\n\r\n"; - chrome.sockets.tcp.connect(port.phSocket, port.ip, 80, function() { + openSocket(port, true) + .then(function(p) { + let postStr = "POST /wx/setting?name=baud-rate&value=" + baudrate + " HTTP/1.1\r\n\r\n"; chrome.sockets.tcp.send(port.phSocket, str2ab(postStr), function () { propComm.response .then(function() {port.baud = baudrate; return resolve();}) //Update baud; does not use updatePort() because of circular reference //!!! .catch(function(e) {return reject(e);}) }); - }); - }); + }) + .catch(function(e) {return reject(e)}); } } else { // Port is already set to baudrate @@ -223,51 +250,27 @@ function ageWiredPorts() { //TODO Check send callback //TODO Reject with error objects as needed function send(port, data, command) { -/* Return a promise that transmits data on port +/* Return a promise that transmits data on port. Port must already be open if wired, may be open or not if wireless. port is the port's object data is an ArrayBuffer - command [ignored unless wireless] must be true to send to Wi-Fi Module's HTTP-based command service and false to send to Propeller via Telnet service*/ - + command [ignored unless wireless] is true to send to Wi-Fi Module's HTTP-based command service and false to send to Propeller via Telnet service*/ return new Promise(function(resolve, reject) { - // log("in send()", mDbug); //!!!! - - function socketSend(p) { -// log("in socketSend()", mDbug); //!!!! - if (!port[p.socket]) { // No ph or pt socket yet; create one and connect to it - chrome.sockets.tcp.create(function (info) { -// log("in sockets.tcp.create()", mDbug); //!!!! - updatePort(port, {[p.socket]: info.socketId}); -// log(p.socket + " SocketID "+port[p.socket]+" transmitting" + ((port) ? " for port " + port.path + "." : "."), mDeep); - chrome.sockets.tcp.connect(port[p.socket], port.ip, p.portNum, function () { - //TODO Handle connect result - chrome.sockets.tcp.setNoDelay(info.socketId, true, function(result) { - if (result < 0) {log("Warning: unable to disable Nagle timer", mDbug)} -// log("in sockets.tcp.connect()", mDbug); //!!!! - chrome.sockets.tcp.send(port[p.socket], data, function () { - //TODO handle send result -// log("in sockets.tcp.connect > send()", mDbug); //!!!! - resolve(); - }); - }); - }); - }); - } else { // Socket exists; use it -// log("socket exists", mDbug); //!!!! - chrome.sockets.tcp.send(port[p.socket], data, function () { - //TODO handle send result -// log("in sockets.tcp.send()", mDbug); //!!!! - resolve(); - }); - } - } - if (port.isWired) { // Wired port chrome.serial.send(port.connId, data, function (sendResult) { resolve(); }); } else { // Wireless port - socketSend((command) ? {socket: "phSocket", portNum: 80} : {socket: "ptSocket", portNum: 23}); + openSocket(port, command) + .then(function (p) { +// log("socket exists", mDbug); //!!!! + chrome.sockets.tcp.send(port[p.socket], data, function () { + //TODO handle send result +// log("in sockets.tcp.send()", mDbug); //!!!! + resolve(); + }); + } + .catch(function (e) {reject(e)}) } }); } From 918297b5d88db8e2dec59c18c13c2c2ec9e281f1 Mon Sep 17 00:00:00 2001 From: Jeff Martin Date: Fri, 28 Sep 2018 11:16:41 -0700 Subject: [PATCH 14/24] Combined wired and wireless portions of serialTerminal(). --- index.js | 51 +++++++++++++++++++-------------------------------- 1 file changed, 19 insertions(+), 32 deletions(-) diff --git a/index.js b/index.js index 2d0222e..e24993c 100644 --- a/index.js +++ b/index.js @@ -468,41 +468,28 @@ function serialTerminal(sock, action, portPath, baudrate, msg) { } else { if (msg instanceof ArrayBuffer === false) {msg = buf2ab(msg);} } - if(port.isWired) { - if (action === "open") { - // Open port for terminal use - openPort(sock, portPath, baudrate, 'debug') - .then(function() {log('Connected terminal to ' + portPath + ' at ' + baudrate + ' baud.');}) - .catch(function() { - log('Unable to connect terminal to ' + portPath); - var msg_to_send = {type:'serial-terminal', msg:'Failed to connect.\rPlease close this terminal and select a connected serial port.'}; - sock.send(JSON.stringify(msg_to_send)); - }); - } else if (action === "close") { - /* Terminal closed. Keep port open because chrome.serial always toggles DTR upon closing (resetting the Propeller) which causes - lots of unnecessary confusion (especially if an older version of the user's app is in the Propeller's EEPROM). - Instead, update the connection mode so that serial debug data halts.*/ - port.mode = 'none'; - } else if (action === "msg") { - // Message to send to the Propeller - if (port.connId) { - send(port, msg, false); - } - } - } else { - // TODO add WX module debug passthrough functions - if (action === 'open') { - - } else if (action === 'close') { - port.mode = 'none'; - } else if (action === 'msg') { - if (port.ptSocket) { - send(port, msg, false); - } + if (action === "open") { + // Open port for terminal use + openPort(sock, portPath, baudrate, 'debug') + .then(function() {log('Connected terminal to ' + portPath + ' at ' + baudrate + ' baud.');}) + .catch(function() { + log('Unable to connect terminal to ' + portPath); + var msg_to_send = {type:'serial-terminal', msg:'Failed to connect.\rPlease close this terminal and select a connected port.'}; + sock.send(JSON.stringify(msg_to_send)); + }); + } else if (action === "close") { + /* Terminal closed. Keep wired port open because chrome.serial always toggles DTR upon closing (resetting the Propeller) which causes + lots of unnecessary confusion (especially if an older version of the user's app is in the Propeller's EEPROM). + Instead, update the connection mode so that serial debug data halts.*/ + port.mode = 'none'; + } else if (action === "msg") { + // Message to send to the Propeller + if ((port.isWired && port.connId) || (port.isWireless && port.ptSocket)) { //Send only if port is open + send(port, msg, false); } } } else { - var msg_to_send = {type:'serial-terminal', msg:'Failed to connect.\rPlease close this terminal and select a valid serial port.'}; + var msg_to_send = {type:'serial-terminal', msg:'Port ' + portPath + ' not found.\rPlease close this terminal and select an existing port.'}; sock.send(JSON.stringify(msg_to_send)); } } From 8d81c1a2a15d18b50b229a9427e2bcd9919954c9 Mon Sep 17 00:00:00 2001 From: Jeff Martin Date: Sun, 30 Sep 2018 16:36:33 -0700 Subject: [PATCH 15/24] Fixed bug in http.js from failure to check chrome.runtime.lastError. Uncommented deleteSocket() dubug messages. Fixed omission in openSocket that didn't return socket type object. Enabled closed socket debug. --- http.js | 12 ++++++++---- index.js | 4 ++-- serial.js | 12 +++++++----- 3 files changed, 17 insertions(+), 11 deletions(-) diff --git a/http.js b/http.js index dbe9a4f..70a7043 100644 --- a/http.js +++ b/http.js @@ -80,10 +80,14 @@ PSocket.prototype.write = function(data) { var that = this; return new Promise(function(resolve, reject) { chrome.sockets.tcp.send(that.socketId, data, function(info) { - if (info && info.resultCode >= 0) - resolve(info.bytesSent); - else - reject(new Error('chrome sockets.tcp error ' + (info && info.resultCode))); + if (!chrome.runtime.lastError) { + if (info.resultCode === 0) + resolve(info.bytesSent); + else + reject(new Error('Socket TCP error ' + (info.resultCode))); + } else { + reject(new Error('Socket ID ' + that.socketId + ' error: ' + (chrome.runtime.lastError.message))); + } }); }); }; diff --git a/index.js b/index.js index e24993c..7ff3df0 100644 --- a/index.js +++ b/index.js @@ -272,11 +272,11 @@ function deleteSocket(socketOrIdx) { /* Delete socket from lists (sockets and ports) socketOrIdx is socket object or index of socket record to delete*/ let idx = (typeof socketOrIdx === "number") ? socketOrIdx : findSocketIdx(socketOrIdx); -// log("Deleting socket at index " + idx, mDbug); + log("Deleting socket at index " + idx, mDbug); if (idx > -1 && idx < sockets.length) { // Clear port's knowledge of socket connection record if (sockets[idx].portIdx > -1) { -// log(" Clearing port index " + sockets[idx].portIdx + " reference to this socket", mDbug); + log(" Clearing port index " + sockets[idx].portIdx + " reference to this socket", mDbug); ports[sockets[idx].portIdx].bSocket = null; ports[sockets[idx].portIdx].bSocketIdx = -1; } diff --git a/serial.js b/serial.js index e1d1932..f1e4614 100644 --- a/serial.js +++ b/serial.js @@ -81,7 +81,7 @@ function openSocket(port, command) { return new Promise(function(resolve, reject) { let p = (command) ? {socket: "phSocket", portNum: 80} : {socket: "ptSocket", portNum: 23}; if (port[p.socket]) { // Already open; resolve - resolve(); + resolve(p); } else { // No ph or pt socket yet; create one and connect to it chrome.sockets.tcp.create(function (info) { // log("in sockets.tcp.create()", mDbug); //!!!! @@ -269,7 +269,7 @@ function send(port, data, command) { // log("in sockets.tcp.send()", mDbug); //!!!! resolve(); }); - } + }) .catch(function (e) {reject(e)}) } }); @@ -323,9 +323,11 @@ function debugErrorReceiver(info) { } else { switch (info.resultCode) { case -100: //port closed - // let port = findPort(byPTID, info.socketId); - // if (!port) {port = findPort(byPHID, info.socketId)} - // log("SocketID "+info.socketId+" connection closed" + ((port) ? " for port " + port.path + "." : "."), mDeep); + let port = findPort(byPTID, info.socketId); + if (!port) {port = findPort(byPHID, info.socketId)} + if (port) { + log("SocketID "+info.socketId+" connection closed" + ((port) ? " for port " + port.path + "." : "."), mDeep); + } break; default: log("Error: SocketID "+info.socketId+" Code "+info.resultCode, mDeep); } From 3c4cba3db3f015216b3592086efa084b675d86e8 Mon Sep 17 00:00:00 2001 From: Jeff Martin Date: Sun, 30 Sep 2018 19:32:15 -0700 Subject: [PATCH 16/24] Removed sockets[] list and related management code. This required solving the only feature of index.js that required a log of the browser socket(s) it's connected to (calls to sendPortList()), and the solution makes port (and socket) management much simpler and fixes a multip-port use bug that is know to break functionality for the entire session. --- index.js | 69 ++++++++++++++++++++----------------------------------- port.js | 24 +++---------------- serial.js | 2 +- 3 files changed, 29 insertions(+), 66 deletions(-) diff --git a/index.js b/index.js index 7ff3df0..397087c 100644 --- a/index.js +++ b/index.js @@ -92,9 +92,6 @@ var platform = pfUnk; Unknown ChromeOS Linux macOS Windows */ portPattern = ["", "/dev/ttyUSB", "dev/tty", "/dev/cu.usbserial", "COM"]; -// A list of connected websockets. -var sockets = []; - // Http and ws servers var server = new http.Server(); var wsServer = new http.WebSocketServer(server); @@ -254,36 +251,21 @@ function closeServer() { isServer = false; } -function findSocketIdx(socket) { -/* Return index of socket in sockets list - Returns -1 if not found*/ - return sockets.findIndex(function(s) {return s.socket === socket}); -} - +//!!!! function closeSockets() { -// Close all sockets and remove them from the list - while (sockets.length) { - sockets[0].socket.close(); - deleteSocket(0); - } +// Close all sockets and remove them from the ports list + ports.forEach(function(p) { + if (p.bSocket) { + p.bSocket.close(); + p.bSocket = null; + }}) } -function deleteSocket(socketOrIdx) { -/* Delete socket from lists (sockets and ports) - socketOrIdx is socket object or index of socket record to delete*/ - let idx = (typeof socketOrIdx === "number") ? socketOrIdx : findSocketIdx(socketOrIdx); - log("Deleting socket at index " + idx, mDbug); - if (idx > -1 && idx < sockets.length) { - // Clear port's knowledge of socket connection record - if (sockets[idx].portIdx > -1) { - log(" Clearing port index " + sockets[idx].portIdx + " reference to this socket", mDbug); - ports[sockets[idx].portIdx].bSocket = null; - ports[sockets[idx].portIdx].bSocketIdx = -1; - } - // Delete socket connection record and adjust ports' later references down, if any - sockets.splice(idx, 1); - ports.forEach(function(v) {if (v.bSocketIdx > idx) {v.bSocketIdx--}}); - } +function deleteSocket(socket) { +/* Delete socket from ports list + socket is object to delete*/ + log("Deleting socket " + socket, mDbug); + ports.forEach(function(p) {if (p.bSocket === socket) {p.bSocket = null}}); } function connect_ws(ws_port, url_path) { @@ -308,12 +290,11 @@ function connect_ws(ws_port, url_path) { wsServer.addEventListener('request', function(req) { var socket = req.accept(); -// log("Adding socket at index " + sockets.length, mDbug); - sockets.push({socket:socket, portIdx:-1}); - + + //!!!! TODO Figure out why there are two separate sendPortList calls //Listen for ports if(portListener === null) { - portListener = setInterval(function() {sendPortList();}, 5000); + portListener = setInterval(function() {sendPortList(socket)}, 5000); } socket.addEventListener('message', function(e) { @@ -329,7 +310,7 @@ function connect_ws(ws_port, url_path) { serialTerminal(socket, ws_msg.action, ws_msg.portPath, ws_msg.baudrate, ws_msg.msg); // action is "open", "close" or "msg" // send an updated port list } else if (ws_msg.type === "port-list-request") { - sendPortList(); + sendPortList(socket); // Handle unknown messages } else if (ws_msg.type === "hello-browser") { helloClient(socket, ws_msg.baudrate || 115200); @@ -347,10 +328,12 @@ function connect_ws(ws_port, url_path) { }); - // When a socket is closed, remove it from the list of connected sockets. + // When a socket is closed, remove it from the list of ports. socket.addEventListener('close', function() { deleteSocket(socket); - if (sockets.length === 0) { + let count = 0; + ports.forEach(function(p) {if (p.bSocket) count++}); + if (count) { updateStatus(false); clearInterval(portListener); portListener = null; @@ -419,8 +402,8 @@ function disableWX() { } } -function sendPortList() { -// find and send list of communication ports (filtered according to platform and type) +function sendPortList(socket) { +// Find and send list of communication ports (filtered according to platform and type) to browser via socket chrome.serial.getDevices( function(portlist) { let wn = []; @@ -440,12 +423,10 @@ function sendPortList() { // report back to editor var msg_to_send = {type:'port-list',ports:wn.concat(wln)}; - for (var i = 0; i < sockets.length; i++) { - sockets[i].socket.send(JSON.stringify(msg_to_send)); - if (chrome.runtime.lastError) { - console.log(chrome.runtime.lastError); + socket.send(JSON.stringify(msg_to_send)); + if (chrome.runtime.lastError) { + console.log(chrome.runtime.lastError); } - } } ); } diff --git a/port.js b/port.js index 8b5e499..25e8795 100644 --- a/port.js +++ b/port.js @@ -77,7 +77,6 @@ function addPort(alist) { ip : get("ip", alist, ""), /*[""+] Wireless port's IP address; */ life : (!get("ip", alist, "")) ? wLife : wlLife, /*[>=0] Initial life value; wired and wireless*/ bSocket : null, /*[null+] Socket to browser (persistent)*/ - bSocketIdx : -1, /*[>=-1] Index of browser socket in sockets list*/ phSocket : null, /*[null+] Socket to Propeller's HTTP service (not persistent)*/ ptSocket : null, /*[null+] Socket to Propeller's Telnet service (persistent)*/ mode : "none", /*["none"+] Intention of the connection; "none", "debug", or "programming"*/ @@ -92,7 +91,7 @@ function addPort(alist) { } function updatePort(port, alist) { -/* Update port attributes if necessary. Automatically handles special cases like baudrate changes and sockets<->ports links. +/* Update port attributes if necessary. Automatically handles special case of baudrate changes. port: [required] port object to update alist: [required] one or more attributes of port to update. Unchanging attributes can be omitted. Possible attributes are: path: the string path to the wired serial port, or custom name of wireless port. Can be empty ("") and a wireless name will be fabricated from the MAC address @@ -120,18 +119,7 @@ function updatePort(port, alist) { set("connId"); set("ip"); port.life = (port.isWired) ? wLife : wlLife; - // Update sockets<->ports links as necessary - if (exists("bSocket", alist)) { - let sIdx = findSocketIdx(alist.bSocket); - if (port.bSocketIdx !== sIdx) { - // new browser socket is different; adjust existing browser socket's record (if any), then apply new browser socket details to port -// log(" Linking to browser socket index " + sIdx, mDbug); - if (port.bSocketIdx !== -1) {sockets[port.bSocketIdx].portIdx = -1} - port.bSocket = alist.bSocket; - port.bSocketIdx = sIdx; - if (sIdx > -1) {sockets[sIdx].portIdx = findPortIdx(byPath, port.path)} - } - } + set("bSocket"); set("phSocket"); set("ptSocket"); set("mode"); @@ -190,12 +178,6 @@ function deletePort(type, clue) { let idx = findPortIdx(type, clue); if (idx > -1) { log("Deleting port: " + ports[idx].path, mDbug); - if (ports[idx].bSocketIdx > -1) { - // Clear socket's knowledge of wired or wireless port record - sockets[ports[idx].bSocketIdx].portIdx = -1; - } - // Delete port record and adjust socket's later references down, if any - ports.splice(idx, 1) - sockets.forEach(function(v) {if (v.portIdx > idx) {v.portIdx--}}); + ports.splice(idx, 1); } } \ No newline at end of file diff --git a/serial.js b/serial.js index f1e4614..dc863c2 100644 --- a/serial.js +++ b/serial.js @@ -110,7 +110,7 @@ function closePort(port, command) { return new Promise(function(resolve, reject) { function socketClose(socket) { - // Nullify port's socket reference + // Nullify port's HTTP or Telnet socket reference let sID = port[socket]; updatePort(port, {[socket]: null}); if (sID) { From 4add10a30844df78c4c93baf5fccf40f974cb70f Mon Sep 17 00:00:00 2001 From: Jeff Martin Date: Sun, 30 Sep 2018 19:55:50 -0700 Subject: [PATCH 17/24] Added more socket-related debugging statements. --- index.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/index.js b/index.js index 397087c..117f353 100644 --- a/index.js +++ b/index.js @@ -264,7 +264,7 @@ function closeSockets() { function deleteSocket(socket) { /* Delete socket from ports list socket is object to delete*/ - log("Deleting socket " + socket, mDbug); + log("Deleting socket " + socket.pSocket_.socketId, mDbug); ports.forEach(function(p) {if (p.bSocket === socket) {p.bSocket = null}}); } @@ -294,6 +294,7 @@ function connect_ws(ws_port, url_path) { //!!!! TODO Figure out why there are two separate sendPortList calls //Listen for ports if(portListener === null) { + log("Setting up portListener (sendPortList()) for socket " + socket.pSocket_.socketId, mDbug); portListener = setInterval(function() {sendPortList(socket)}, 5000); } @@ -310,6 +311,7 @@ function connect_ws(ws_port, url_path) { serialTerminal(socket, ws_msg.action, ws_msg.portPath, ws_msg.baudrate, ws_msg.msg); // action is "open", "close" or "msg" // send an updated port list } else if (ws_msg.type === "port-list-request") { + log("Calling sendPortList() from port-list-request of socket " + socket.pSocket_.socketId, mDbug); sendPortList(socket); // Handle unknown messages } else if (ws_msg.type === "hello-browser") { @@ -330,6 +332,7 @@ function connect_ws(ws_port, url_path) { // When a socket is closed, remove it from the list of ports. socket.addEventListener('close', function() { + log("Socket closing " + socket.pSocket_.socketId, mDbug); deleteSocket(socket); let count = 0; ports.forEach(function(p) {if (p.bSocket) count++}); @@ -408,6 +411,7 @@ function sendPortList(socket) { function(portlist) { let wn = []; let wln = []; + log("sendPortList() for socket " + socket.pSocket_.socketId, mDbug); // update wired ports portlist.forEach(function(port) { if ((port.path.indexOf(portPattern[platform]) === 0) && (port.displayName.indexOf(' bt ') === -1 && port.displayName.indexOf('bluetooth') === -1)) { From 3077ebf9a4e496fc33dfaa8033df4635911da913 Mon Sep 17 00:00:00 2001 From: Jeff Martin Date: Mon, 1 Oct 2018 09:17:11 -0700 Subject: [PATCH 18/24] Resolved two separate sendPortList() calls into one intent. Added socket error log (promise error is not being exposed for some reason. Fixed browser socket close code to clean up properly. --- http.js | 4 +++- index.js | 34 +++++++++++++++------------------- 2 files changed, 18 insertions(+), 20 deletions(-) diff --git a/http.js b/http.js index 70a7043..3d7dd40 100644 --- a/http.js +++ b/http.js @@ -86,7 +86,9 @@ PSocket.prototype.write = function(data) { else reject(new Error('Socket TCP error ' + (info.resultCode))); } else { - reject(new Error('Socket ID ' + that.socketId + ' error: ' + (chrome.runtime.lastError.message))); + let emsg = 'Socket ID ' + that.socketId + ' error: ' + chrome.runtime.lastError.message; + console.log(emsg); + reject(new Error(emsg)); } }); }); diff --git a/index.js b/index.js index 117f353..2f89f22 100644 --- a/index.js +++ b/index.js @@ -97,8 +97,8 @@ var server = new http.Server(); var wsServer = new http.WebSocketServer(server); var isServer = false; -// Keep track of the interval that sends the port list so it can be turned off -var portListener = null; +// Timer(s) to scan and send the port list +var portScanner = []; // Is verbose loggin turned on? var verboseLogging = false; @@ -291,13 +291,6 @@ function connect_ws(ws_port, url_path) { wsServer.addEventListener('request', function(req) { var socket = req.accept(); - //!!!! TODO Figure out why there are two separate sendPortList calls - //Listen for ports - if(portListener === null) { - log("Setting up portListener (sendPortList()) for socket " + socket.pSocket_.socketId, mDbug); - portListener = setInterval(function() {sendPortList(socket)}, 5000); - } - socket.addEventListener('message', function(e) { if (isJson(e.data)) { var ws_msg = JSON.parse(e.data); @@ -311,8 +304,10 @@ function connect_ws(ws_port, url_path) { serialTerminal(socket, ws_msg.action, ws_msg.portPath, ws_msg.baudrate, ws_msg.msg); // action is "open", "close" or "msg" // send an updated port list } else if (ws_msg.type === "port-list-request") { - log("Calling sendPortList() from port-list-request of socket " + socket.pSocket_.socketId, mDbug); + // Send port list now and set up scanner to send port list on regular interval + log("Browser requested port-list for socket " + socket.pSocket_.socketId, mDbug); sendPortList(socket); + portScanner.push({socket: socket, scanner: setInterval(function() {sendPortList(socket)}, 5000)}); // Handle unknown messages } else if (ws_msg.type === "hello-browser") { helloClient(socket, ws_msg.baudrate || 115200); @@ -330,17 +325,18 @@ function connect_ws(ws_port, url_path) { }); - // When a socket is closed, remove it from the list of ports. + // Browser socket closed; terminate its port scans and remove it from list of ports. socket.addEventListener('close', function() { - log("Socket closing " + socket.pSocket_.socketId, mDbug); + log("Browser socket closing: " + socket.pSocket_.socketId, mDbug); + let Idx = portScanner.findIndex(function(s) {return s.socket === socket}); + if (Idx > -1) { + clearInterval(portScanner.scanner); + portScanner.slice(Idx, 1); + } deleteSocket(socket); - let count = 0; - ports.forEach(function(p) {if (p.bSocket) count++}); - if (count) { - updateStatus(false); - clearInterval(portListener); - portListener = null; - chrome.app.window.current().drawAttention(); + if (!portScanner.length) { + updateStatus(false); + chrome.app.window.current().drawAttention(); } }); From 1a0a9cd3803f97a36df91a8a7a790d75eed6387d Mon Sep 17 00:00:00 2001 From: Jeff Martin Date: Mon, 1 Oct 2018 10:26:21 -0700 Subject: [PATCH 19/24] Fixed socket handling bugs. --- index.js | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/index.js b/index.js index 2f89f22..27d2ad3 100644 --- a/index.js +++ b/index.js @@ -251,21 +251,17 @@ function closeServer() { isServer = false; } -//!!!! function closeSockets() { -// Close all sockets and remove them from the ports list +// Close all sockets and remove them from the ports and portScanner lists ports.forEach(function(p) { if (p.bSocket) { p.bSocket.close(); p.bSocket = null; - }}) -} - -function deleteSocket(socket) { -/* Delete socket from ports list - socket is object to delete*/ - log("Deleting socket " + socket.pSocket_.socketId, mDbug); - ports.forEach(function(p) {if (p.bSocket === socket) {p.bSocket = null}}); + }}); + while (portScanner.length) { + clearInterval(portScanner[0].scanner); + portScanner.splice(0, 1); + } } function connect_ws(ws_port, url_path) { @@ -307,7 +303,8 @@ function connect_ws(ws_port, url_path) { // Send port list now and set up scanner to send port list on regular interval log("Browser requested port-list for socket " + socket.pSocket_.socketId, mDbug); sendPortList(socket); - portScanner.push({socket: socket, scanner: setInterval(function() {sendPortList(socket)}, 5000)}); + let s = setInterval(function() {sendPortList(socket)}, 5000); + portScanner.push({socket: socket, scanner: s}); // Handle unknown messages } else if (ws_msg.type === "hello-browser") { helloClient(socket, ws_msg.baudrate || 115200); @@ -330,10 +327,10 @@ function connect_ws(ws_port, url_path) { log("Browser socket closing: " + socket.pSocket_.socketId, mDbug); let Idx = portScanner.findIndex(function(s) {return s.socket === socket}); if (Idx > -1) { - clearInterval(portScanner.scanner); - portScanner.slice(Idx, 1); + clearInterval(portScanner[Idx].scanner); + portScanner.splice(Idx, 1); } - deleteSocket(socket); + ports.forEach(function(p) {if (p.bSocket === socket) {p.bSocket = null}}); if (!portScanner.length) { updateStatus(false); chrome.app.window.current().drawAttention(); From d9ffad9bea61977b42ff00eac2fc59f81d7116b7 Mon Sep 17 00:00:00 2001 From: Jeff Martin Date: Mon, 1 Oct 2018 12:53:02 -0700 Subject: [PATCH 20/24] Removed some old debug statements in openSocket() and send(). --- loader.js | 4 ++-- serial.js | 7 ------- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/loader.js b/loader.js index e358aa1..9303219 100644 --- a/loader.js +++ b/loader.js @@ -467,14 +467,14 @@ function hearFromProp(info) { // Exit if this isn't the data we're looking for if (dataSource === dsDontCare) { log("Ignoring " + info.data.byteLength + " unexpected bytes", mDeep); -// console.log(info.data); //!!!! +// console.log(info.data); return; } // Parse HTTP-command responses into proper object, or treat wired and Telnet-wireless streams as an unformatted array let stream = (dataSource === dsHTTP) ? parseHTTP(info.data) : new Uint8Array(info.data) log("Received " + info.data.byteLength + " bytes", mDeep); -// console.log(stream); //!!!! +// console.log(stream); var sIdx = 0; diff --git a/serial.js b/serial.js index dc863c2..ae75fd4 100644 --- a/serial.js +++ b/serial.js @@ -24,7 +24,6 @@ * Serial Support Functions * ***********************************************************/ -//TODO Consider returning error object //TODO Consider enhancing error to indicate if the port is already open (this would only be for developer mistakes though) function openPort(sock, portPath, baudrate, connMode) { /* Return a promise to open wired or wireless port at portPath with baudrate and connect to browser sock. If wireless, the port is opened @@ -84,14 +83,11 @@ function openSocket(port, command) { resolve(p); } else { // No ph or pt socket yet; create one and connect to it chrome.sockets.tcp.create(function (info) { -// log("in sockets.tcp.create()", mDbug); //!!!! updatePort(port, {[p.socket]: info.socketId}); -// log(p.socket + " SocketID "+port[p.socket]+" transmitting" + ((port) ? " for port " + port.path + "." : "."), mDeep); chrome.sockets.tcp.connect(port[p.socket], port.ip, p.portNum, function () { //TODO Handle connect result chrome.sockets.tcp.setNoDelay(info.socketId, true, function(result) { if (result < 0) {log("Warning: unable to disable Nagle timer", mDbug)} -// log("in sockets.tcp.connect()", mDbug); //!!!! resolve(p); }); }); @@ -255,7 +251,6 @@ function send(port, data, command) { data is an ArrayBuffer command [ignored unless wireless] is true to send to Wi-Fi Module's HTTP-based command service and false to send to Propeller via Telnet service*/ return new Promise(function(resolve, reject) { -// log("in send()", mDbug); //!!!! if (port.isWired) { // Wired port chrome.serial.send(port.connId, data, function (sendResult) { resolve(); @@ -263,10 +258,8 @@ function send(port, data, command) { } else { // Wireless port openSocket(port, command) .then(function (p) { -// log("socket exists", mDbug); //!!!! chrome.sockets.tcp.send(port[p.socket], data, function () { //TODO handle send result -// log("in sockets.tcp.send()", mDbug); //!!!! resolve(); }); }) From 1483585ed17ad1319178bd13bf97c224fe734b5f Mon Sep 17 00:00:00 2001 From: Jeff Martin Date: Mon, 1 Oct 2018 13:55:15 -0700 Subject: [PATCH 21/24] Separated the wired port scanning process from the port list sending process. --- index.js | 81 +++++++++++++++++++++++++++++++------------------------- 1 file changed, 45 insertions(+), 36 deletions(-) diff --git a/index.js b/index.js index 27d2ad3..d434163 100644 --- a/index.js +++ b/index.js @@ -98,7 +98,8 @@ var wsServer = new http.WebSocketServer(server); var isServer = false; // Timer(s) to scan and send the port list -var portScanner = []; +var portScanner = null; +var portLister = []; // Is verbose loggin turned on? var verboseLogging = false; @@ -223,10 +224,13 @@ document.addEventListener('DOMContentLoaded', function() { function connect() { connect_ws($('bpc-port').value, $('bpc-url').value); + portScanner = setInterval(scanWPorts, 6010); // 6010: Scan at different intervals than send processes } function disconnect() { closeSockets(); + clearInterval(portScanner); + portScanner = null; } function updateStatus(connected) { @@ -252,15 +256,15 @@ function closeServer() { } function closeSockets() { -// Close all sockets and remove them from the ports and portScanner lists +// Close all sockets and remove them from the ports and portLister lists ports.forEach(function(p) { if (p.bSocket) { p.bSocket.close(); p.bSocket = null; }}); - while (portScanner.length) { - clearInterval(portScanner[0].scanner); - portScanner.splice(0, 1); + while (portLister.length) { + clearInterval(portLister[0].scanner); + portLister.splice(0, 1); } } @@ -304,7 +308,7 @@ function connect_ws(ws_port, url_path) { log("Browser requested port-list for socket " + socket.pSocket_.socketId, mDbug); sendPortList(socket); let s = setInterval(function() {sendPortList(socket)}, 5000); - portScanner.push({socket: socket, scanner: s}); + portLister.push({socket: socket, scanner: s}); // Handle unknown messages } else if (ws_msg.type === "hello-browser") { helloClient(socket, ws_msg.baudrate || 115200); @@ -325,13 +329,13 @@ function connect_ws(ws_port, url_path) { // Browser socket closed; terminate its port scans and remove it from list of ports. socket.addEventListener('close', function() { log("Browser socket closing: " + socket.pSocket_.socketId, mDbug); - let Idx = portScanner.findIndex(function(s) {return s.socket === socket}); + let Idx = portLister.findIndex(function(s) {return s.socket === socket}); if (Idx > -1) { - clearInterval(portScanner[Idx].scanner); - portScanner.splice(Idx, 1); + clearInterval(portLister[Idx].scanner); + portLister.splice(Idx, 1); } ports.forEach(function(p) {if (p.bSocket === socket) {p.bSocket = null}}); - if (!portScanner.length) { + if (!portLister.length) { updateStatus(false); chrome.app.window.current().drawAttention(); } @@ -398,34 +402,39 @@ function disableWX() { } } +function scanWPorts() { +// Generate list of current communication ports (filtered according to platform and type) + chrome.serial.getDevices( + function(portlist) { + let wn = []; + let wln = []; + // update wired ports + portlist.forEach(function(port) { + if ((port.path.indexOf(portPattern[platform]) === 0) && (port.displayName.indexOf(' bt ') === -1 && port.displayName.indexOf('bluetooth') === -1)) { + addPort({path: port.path}); + } + }); + ageWiredPorts(); //Note, wired ports age here (just scanned) and wireless ports age elsewhere (where they are scanned) + } + ); +} + function sendPortList(socket) { // Find and send list of communication ports (filtered according to platform and type) to browser via socket - chrome.serial.getDevices( - function(portlist) { - let wn = []; - let wln = []; - log("sendPortList() for socket " + socket.pSocket_.socketId, mDbug); - // update wired ports - portlist.forEach(function(port) { - if ((port.path.indexOf(portPattern[platform]) === 0) && (port.displayName.indexOf(' bt ') === -1 && port.displayName.indexOf('bluetooth') === -1)) { - addPort({path: port.path}); - } - }); - ageWiredPorts(); //Note, wired ports age here (just scanned) and wireless ports age elsewhere (where they are scanned) - - // gather separated and sorted port lists (wired names and wireless names) - ports.forEach(function(p) {if (p.isWired) {wn.push(p.path)} else {wln.push(p.path)}}); - wn.sort(); - wln.sort(); - - // report back to editor - var msg_to_send = {type:'port-list',ports:wn.concat(wln)}; - socket.send(JSON.stringify(msg_to_send)); - if (chrome.runtime.lastError) { - console.log(chrome.runtime.lastError); - } - } - ); + let wn = []; + let wln = []; + log("sendPortList() for socket " + socket.pSocket_.socketId, mDbug); + // gather separated and sorted port lists (wired names and wireless names) + ports.forEach(function(p) {if (p.isWired) {wn.push(p.path)} else {wln.push(p.path)}}); + wn.sort(); + wln.sort(); + + // report back to editor + var msg_to_send = {type:'port-list',ports:wn.concat(wln)}; + socket.send(JSON.stringify(msg_to_send)); + if (chrome.runtime.lastError) { + console.log(chrome.runtime.lastError); + } } From 964f68b7b9366cae8fc51fd01a30b97fc285c7df Mon Sep 17 00:00:00 2001 From: Jeff Martin Date: Mon, 1 Oct 2018 14:12:27 -0700 Subject: [PATCH 22/24] Made wired and wireless interval names consistent. --- index.js | 29 +++++++++++++++++------------ wx.js | 2 +- 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/index.js b/index.js index d434163..0b71036 100644 --- a/index.js +++ b/index.js @@ -98,7 +98,7 @@ var wsServer = new http.WebSocketServer(server); var isServer = false; // Timer(s) to scan and send the port list -var portScanner = null; +var wScannerInterval = null; var portLister = []; // Is verbose loggin turned on? @@ -224,13 +224,14 @@ document.addEventListener('DOMContentLoaded', function() { function connect() { connect_ws($('bpc-port').value, $('bpc-url').value); - portScanner = setInterval(scanWPorts, 6010); // 6010: Scan at different intervals than send processes + scanWPorts(); + wScannerInterval = setInterval(scanWPorts, 6010); // 6010: Scan at different intervals than send processes } function disconnect() { closeSockets(); - clearInterval(portScanner); - portScanner = null; + clearInterval(wScannerInterval); + wScannerInterval = null; } function updateStatus(connected) { @@ -388,22 +389,19 @@ function connect_ws(ws_port, url_path) { } function enableWX() { - wx_scanner_interval = setInterval(function() { - discoverWirelessPorts(); - ageWirelessPorts(); - displayWirelessPorts(); - }, 3500); + scanWXPorts(); + wScannerInterval = setInterval(scanWXPorts, 3500); } function disableWX() { - if(wx_scanner_interval) { - clearInterval(wx_scanner_interval); + if(wScannerInterval) { + clearInterval(wScannerInterval); $('wx-list').innerHTML = ''; } } function scanWPorts() { -// Generate list of current communication ports (filtered according to platform and type) +// Generate list of current wired ports (filtered according to platform and type) chrome.serial.getDevices( function(portlist) { let wn = []; @@ -419,6 +417,13 @@ function scanWPorts() { ); } +function scanWXPorts() { +// Generate list of current wireless ports + discoverWirelessPorts(); + ageWirelessPorts(); + displayWirelessPorts(); +} + function sendPortList(socket) { // Find and send list of communication ports (filtered according to platform and type) to browser via socket let wn = []; diff --git a/wx.js b/wx.js index 42cef40..ba94c3e 100644 --- a/wx.js +++ b/wx.js @@ -29,7 +29,7 @@ var udp_sock; var disc_packet = '\0\0\0\0'; // Holder for the interval for discovering modules -var wx_scanner_interval = null; +var wxScannerInterval = null; function calcBroadcastAddr(mip) { // Calculate a broadcast IP from a given address and subnet mask From 9a98ff9b1e76cb1e09a4b197c8d9411f365837d8 Mon Sep 17 00:00:00 2001 From: Jeff Martin Date: Tue, 2 Oct 2018 09:17:21 -0700 Subject: [PATCH 23/24] Fixed bug in port.js causing colliding debug streams when there are multiple sources. Object.assign() only makes shallow copies, and JSON-based deep copies lose integer array types; solution is to set buffer to new integer array after port assignment. --- port.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/port.js b/port.js index 25e8795..b2d8704 100644 --- a/port.js +++ b/port.js @@ -34,7 +34,7 @@ const serPacketMaxTxTime = 100; const serPacketMax = 1492; // Size of buffer to transmit serial data to browser const serPacket = { id : 0, - bufView : new Uint8Array(new ArrayBuffer(serPacketMax)), + bufView : null, /*set later to new Uint8Array(new ArrayBuffer(serPacketMax)) since Object.assign() only shallow-copies*/ len : 0, timer : null, }; @@ -85,8 +85,9 @@ function addPort(alist) { isWired : !Boolean(get("ip", alist, "")), /*[true/false] indicates if port is wired or not*/ isWireless : Boolean(get("ip", alist, "")) /*[true/false] indicates if port is wireless or not*/ }); - // Give it its own packet buffer + // Give it its own packet object and buffer Object.assign(ports[ports.length-1].packet, serPacket); + ports[ports.length-1].packet.bufView = new Uint8Array(new ArrayBuffer(serPacketMax)); } } From 2b37689960bd6fbf578cf5dcbffe3528b66ca25e Mon Sep 17 00:00:00 2001 From: Jeff Martin Date: Tue, 2 Oct 2018 09:38:54 -0700 Subject: [PATCH 24/24] Updated version to v0.9.1. Removed two socket debug messages. --- index.js | 4 ++-- manifest.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/index.js b/index.js index 0b71036..91cb965 100644 --- a/index.js +++ b/index.js @@ -306,7 +306,7 @@ function connect_ws(ws_port, url_path) { // send an updated port list } else if (ws_msg.type === "port-list-request") { // Send port list now and set up scanner to send port list on regular interval - log("Browser requested port-list for socket " + socket.pSocket_.socketId, mDbug); +// log("Browser requested port-list for socket " + socket.pSocket_.socketId, mDbug); sendPortList(socket); let s = setInterval(function() {sendPortList(socket)}, 5000); portLister.push({socket: socket, scanner: s}); @@ -428,7 +428,7 @@ function sendPortList(socket) { // Find and send list of communication ports (filtered according to platform and type) to browser via socket let wn = []; let wln = []; - log("sendPortList() for socket " + socket.pSocket_.socketId, mDbug); +// log("sendPortList() for socket " + socket.pSocket_.socketId, mDbug); // gather separated and sorted port lists (wired names and wireless names) ports.forEach(function(p) {if (p.isWired) {wn.push(p.path)} else {wln.push(p.path)}}); wn.sort(); diff --git a/manifest.json b/manifest.json index 78eb318..00e2678 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "name": "BlocklyProp Launcher", "description": "A Chrome application that connects your Propeller-Powered Hardware to the BlocklyProp website.", - "version": "0.9.0", + "version": "0.9.1", "manifest_version": 2, "minimum_chrome_version": "45",