Skip to content

Commit aea870e

Browse files
committed
Trial of moving the packet parsing straight into the input data handler
1 parent 80188d7 commit aea870e

File tree

2 files changed

+102
-127
lines changed

2 files changed

+102
-127
lines changed

core/serial.js

Lines changed: 90 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,12 @@ To add a new serial device, you must add an object to
5151
var flowControlXOFF = false;
5252
/// Set up when flow control received - if no response is received we start sending anyway
5353
var flowControlTimeout;
54+
/// used by rxDataHandler - last received character
55+
let rxDataHandlerLastCh = 0;
56+
/// used by rxDataHandler - used for parsing
57+
let rxDataHandlerPacket = undefined;
58+
/// timeout for unfinished packet
59+
let rxDataHandlerTimeout = undefined;
5460

5561

5662
function init() {
@@ -159,6 +165,88 @@ To add a new serial device, you must add an object to
159165
return openSerialInternal(serialPort, connectCallback, disconnectCallback, 5);
160166
}
161167

168+
var rxDataHandler = function(data) {
169+
if (!(data instanceof ArrayBuffer)) console.warn("Serial port implementation is not returning ArrayBuffers")
170+
171+
// Filter incoming data to handle and remove control characters
172+
const filteredData = new Uint8Array(data).filter((ch) => {
173+
let lastCh = rxDataHandlerLastCh;
174+
rxDataHandlerLastCh = ch;
175+
176+
if (rxDataHandlerPacket!==undefined) {
177+
rxDataHandlerPacket.push(ch);
178+
let flags = (rxDataHandlerPacket[0]<<8) | rxDataHandlerPacket[1];
179+
let len = flags & 0x1FFF;
180+
let rxLen = rxDataHandlerPacket.length;
181+
if (rxLen>=2 && rxLen>=(len+2)) {
182+
console.log("Got packet end");
183+
if (rxDataHandlerTimeout) {
184+
clearTimeout(rxDataHandlerTimeout);
185+
rxDataHandlerTimeout = undefined;
186+
}
187+
emit("packet", flags&0xE000, new Uint8Array(rxDataHandlerPacket.slice(2)));
188+
rxDataHandlerPacket = undefined; // stop packet reception
189+
}
190+
return false; // don't pass data in packet through
191+
} else switch (ch) {
192+
case 16: // DLE - potential start of packet (ignore)
193+
return false;
194+
195+
case 1: // SOH - start of packet if after DLE
196+
if (lastCh == 16/*DLE*/) {
197+
console.log("Packet start received");
198+
rxDataHandlerPacket = [];
199+
rxDataHandlerTimeout = setTimeout(()=>{
200+
rxDataHandlerTimeout = undefined;
201+
console.log(`Packet timeout (2s - contents ${JSON.stringify(rxDataHandlerPacket)})`);
202+
// TODO: emit packet error?
203+
rxDataHandlerPacket = undefined;
204+
}, 2000);
205+
return false;
206+
} else
207+
return true; // else pass through
208+
209+
case 17: // XON
210+
if (Espruino.Config.SERIAL_FLOW_CONTROL && rxDataHandlerPacket===undefined) {
211+
console.log("XON received => resume upload")
212+
flowControlXOFF = false
213+
if (flowControlTimeout) {
214+
clearTimeout(flowControlTimeout)
215+
flowControlTimeout = undefined
216+
}
217+
}
218+
return false;
219+
220+
case 19: // XOFF
221+
if (Espruino.Config.SERIAL_FLOW_CONTROL && rxDataHandlerPacket===undefined) {
222+
console.log("XOFF received => pause upload")
223+
flowControlXOFF = true
224+
if (flowControlTimeout) clearTimeout(flowControlTimeout)
225+
flowControlTimeout = setTimeout(function () {
226+
console.log(
227+
`XOFF timeout (${FLOW_CONTROL_RESUME_TIMEOUT}s) => resume upload anyway`
228+
)
229+
flowControlXOFF = false
230+
flowControlTimeout = undefined
231+
}, FLOW_CONTROL_RESUME_TIMEOUT)
232+
}
233+
return false;
234+
235+
case 6: // ACK
236+
emit("ack")
237+
return false;
238+
239+
case 21: // NACK
240+
emit("nack")
241+
return false;
242+
}
243+
244+
return true;
245+
})
246+
247+
if (readListener) readListener(filteredData.buffer)
248+
};
249+
162250
var openSerialInternal=function(serialPort, connectCallback, disconnectCallback, attempts) {
163251
/* If openSerial is called, we need to have called getPorts first
164252
in order to figure out which one of the serial_ implementations
@@ -214,52 +302,8 @@ To add a new serial device, you must add an object to
214302
connectCallback = undefined;
215303
});
216304
}
217-
}, function(data) { // RECEIEVE DATA
218-
if (!(data instanceof ArrayBuffer)) console.warn("Serial port implementation is not returning ArrayBuffers")
219-
220-
// Filter incoming data to handle and remove control characters
221-
const filteredData = new Uint8Array(data).filter((v) => {
222-
switch (v) {
223-
case 17: // XON
224-
if (Espruino.Config.SERIAL_FLOW_CONTROL) {
225-
console.log("XON received => resume upload")
226-
flowControlXOFF = false
227-
if (flowControlTimeout) {
228-
clearTimeout(flowControlTimeout)
229-
flowControlTimeout = undefined
230-
}
231-
}
232-
return false
233-
234-
case 19: // XOFF
235-
if (Espruino.Config.SERIAL_FLOW_CONTROL) {
236-
console.log("XOFF received => pause upload")
237-
flowControlXOFF = true
238-
if (flowControlTimeout) clearTimeout(flowControlTimeout)
239-
flowControlTimeout = setTimeout(function () {
240-
console.log(
241-
`XOFF timeout (${FLOW_CONTROL_RESUME_TIMEOUT}s) => resume upload anyway`
242-
)
243-
flowControlXOFF = false
244-
flowControlTimeout = undefined
245-
}, FLOW_CONTROL_RESUME_TIMEOUT)
246-
}
247-
return false
248-
249-
case 6: // ACK
250-
emit("ack")
251-
return false
252-
253-
case 21: // NACK
254-
emit("nack")
255-
return false
256-
}
257-
258-
return true
259-
})
260-
261-
if (readListener) readListener(filteredData.buffer)
262-
}, function(error) { // DISCONNECT
305+
}, rxDataHandler, // RECEIEVE DATA
306+
function(error) { // DISCONNECT
263307
currentDevice = undefined;
264308
if (writeTimeout!==undefined)
265309
clearTimeout(writeTimeout);

core/utils.js

Lines changed: 12 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -512,57 +512,6 @@
512512
) + data; // Data blob
513513
}
514514

515-
/**
516-
* Take an input buffer and look for the initial control characters and then attempt to parse a
517-
* complete data packet from the buffer. Any complete packet is sent via `emit("packet")` and then
518-
* stripped from `buffer` modifiying it.
519-
* @param {Uint8Array} buffer
520-
* @returns {Uint8Array}
521-
*/
522-
function parsePacketsFromBuffer(buffer) {
523-
524-
// Find DLE
525-
const dle = buffer.findIndex(v => v === 0x10)
526-
if (dle < 0) return buffer
527-
528-
// Check for SOH
529-
if (buffer.at(dle + 1) !== 0x1) {
530-
// console.warn("DLE not followed by SOH")
531-
// TODO: Not stripping out this invalid control will cause a loop
532-
buffer.set([undefined], dle) // Remove this DLE
533-
return buffer
534-
}
535-
536-
// Check there's still space for headers
537-
if (buffer.at(dle + 2) === undefined || buffer.at(dle + 3) === undefined) {
538-
console.warn("NO SPACE FOR HEADERS")
539-
return buffer
540-
}
541-
const upper = buffer.at(dle + 2)
542-
const lower = buffer.at(dle + 3)
543-
544-
// Parse heading from 2 bytes after control headers
545-
const heading = new Number(upper << 8) | new Number(lower)
546-
const pkLen = heading & 0x1FFF
547-
const pkTyp = heading & 0xE000
548-
549-
// Ignoring heading bytes, check if there's enough bytes in the buffer to satisfy pkLen
550-
if (buffer.length < dle + 4 + pkLen) {
551-
return buffer
552-
}
553-
554-
// Pick out a packet from the buffer and emit it via the event handler
555-
const packet = buffer.subarray(dle, dle + 4 + pkLen)
556-
console.log("Packet recieved... type:", pkTyp, "length:", pkLen)
557-
Espruino.Core.Serial.emit('packet', pkTyp, packet.subarray(4, packet.length))
558-
559-
// Fill the buffer region of the packet that was sent with undefined
560-
buffer.fill(undefined, 0, dle + packet.length)
561-
562-
// Return the input buffer but with the stripped packet filtered out
563-
return buffer.filter(v => v !== undefined)
564-
}
565-
566515
/**
567516
* Send a packet
568517
* @param {number} pkType
@@ -584,23 +533,23 @@
584533

585534
let allData
586535
function onPacket(rxPkType, data) {
587-
tidy()
588-
const packetData = String.fromCharCode(...data)
589-
536+
const packetData = String.fromCharCode(...data);
590537
// TODO: Depending on the rx type and tx type match up packet types, wait for x number of data
591538
if (pkTypes[pkType] === pkTypes.EVAL && rxPkType === pkTypes.RESPONSE) {
539+
tidy();
592540
callback(packetData)
593-
594-
// If the packet type is data, we need to wait for the 0 length `DATA` packet and then send all of the data joined together
541+
// If the packet type is data, we need to wait for the 0 length `DATA` packet and then send all of the data joined together
595542
} else if (pkTypes[pkType] === pkTypes.FILE_RECV && rxPkType === pkTypes.DATA) {
596543
if (data.length === 0) {
597-
callback(allData)
598-
console.log("zero packet")
544+
tidy();
545+
console.log("zero packet");
546+
callback(allData);
599547
} else {
600548
console.log("appending data", String.fromCharCode(...data))
601549
allData += String.fromCharCode(...data)
602550
}
603551
}else {
552+
tidy();
604553
callback("nodata")
605554
}
606555
}
@@ -612,31 +561,13 @@
612561
Espruino.Core.Serial.removeListener("packet", onPacket)
613562
}
614563

615-
// Attach event handlers for this packet event
616-
Espruino.Core.Serial.on("ack", onAck)
617-
Espruino.Core.Serial.on("nack", onNack)
618-
Espruino.Core.Serial.on("packet", onPacket)
619-
620564
// Write packet to serial port
621565
Espruino.Core.Serial.write(createPacket(pkType, data), undefined, function () {
622-
// TODO: Add 1 sec timeout
623-
624-
let dataBuffer = new Uint8Array()
625-
626-
// Each time data comes in, expand the buffer and add the new data to it
627-
// TODO: This seems problematic if there are subsequent/concurrent calls
628-
Espruino.Core.Serial.startListening((data) => {
629-
const newBuffer = new Uint8Array(data)
630-
631-
const tempBuffer = new Uint8Array(dataBuffer.length + newBuffer.length)
632-
tempBuffer.set(dataBuffer, 0)
633-
tempBuffer.set(newBuffer, dataBuffer.length)
634-
635-
dataBuffer = tempBuffer
636-
637-
// Now we've added more data to the buffer, try to parse out any packets
638-
dataBuffer = parsePacketsFromBuffer(dataBuffer)
639-
})
566+
// Attach event handlers for this packet event
567+
Espruino.Core.Serial.on("ack", onAck);
568+
Espruino.Core.Serial.on("nack", onNack);
569+
Espruino.Core.Serial.on("packet", onPacket);
570+
// TODO: Timeout handling?
640571
})
641572
}
642573

0 commit comments

Comments
 (0)