Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 44 additions & 45 deletions lib/drivers/glucorx/glucoRxDriver.js
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ const MODELS = {
};

const READ_TIMEOUT = 2000; // in milliseconds
const MAX_IDLE_READS = 2; // consecutive read timeouts before giving up on a packet
const KETONE_VALUE_FACTOR = 10;
const KETONE_HI = 8.0;

Expand Down Expand Up @@ -189,58 +190,48 @@ class GlucoRx {
return response;
}

async commandResponse(cmd, payload) {
let message = '';

await this.hidDevice.sendPromisified(this.buildPacket(cmd, payload));
debug('Sent command.');

/**
* The CP2110 hands us UART bytes in HID reports of varying size, so a single
* packet can arrive in one report or spread over several (macOS 26 delivers
* them one byte at a time). Reassemble the stream until we have a full packet,
* discarding anything that precedes the start byte.
*/
async readPacket() {
let raw = [];
let result;
let foundStart = false;
let foundEnd = false;
let packetSize = 64;
do {
result = [];
let idleReads = 0;

while (true) {
// requests to devices are sequential
// eslint-disable-next-line no-await-in-loop
result = await this.hidDevice.receiveTimeout(READ_TIMEOUT);
debug('Incoming bytes:', common.bytes2hex(result));

if (result.length > 0) {
let bytes = null;
if (result.length > 1) {
if (result.slice(1).every(item => item === 0)) {
// only first byte in array is valid
bytes = [result[0]];
} else {
const [ length ] = result;
bytes = result.slice(1, length + 1);
}
} else {
bytes = result;
}
const chunk = await this.hidDevice.receiveTimeout(READ_TIMEOUT);

if (!foundStart) {
if (bytes.includes(CONTROL.START)) {
foundStart = true;
}
if (chunk.length === 0) {
idleReads += 1;
if (idleReads >= MAX_IDLE_READS) {
throw new Error('Timed out waiting for a complete packet.');
}
} else {
idleReads = 0;
debug('Incoming bytes:', common.bytes2hex(chunk));
raw = raw.concat(chunk);

if (foundStart) {
raw = raw.concat(bytes);
const start = raw.indexOf(CONTROL.START);
if (start > 0) {
raw = raw.slice(start);
}

if (raw[raw.length-2] === CONTROL.MD_STOP && raw.length >= PACKET_SIZE) {
foundEnd = true;
}
if (start >= 0 && raw.length >= PACKET_SIZE) {
return raw.slice(0, PACKET_SIZE);
}
}
} while (!foundEnd);

// Only process if we get data
if (raw.length > 0) {
message = this.extractPacketIntoMessages(raw);
}
}

async commandResponse(cmd, payload) {
await this.hidDevice.sendPromisified(this.buildPacket(cmd, payload));
debug('Sent command.');

const message = this.extractPacketIntoMessages(await this.readPacket());

if (message === COMMAND.COMM_MODE) {
// try again
Expand Down Expand Up @@ -322,10 +313,17 @@ class GlucoRx {
async ping() {
await this.hidDevice.sendPromisified(this.buildPacket(COMMAND.READ_MODEL));
debug('Sent ping.');
const result = await this.hidDevice.receiveTimeout(READ_TIMEOUT);
debug('Received:', common.bytes2hex(result));

if (result.length === 0) {
let result = null;
try {
// consume the whole reply, so that we don't leave part of it behind for
// the next command to trip over
result = await this.readPacket();
} catch (err) {
debug('No response to ping:', err.message);
}

if (result == null) {
if (this.retries <= 3) {
debug('Retrying..');
this.retries += 1;
Expand All @@ -334,6 +332,7 @@ class GlucoRx {
throw new Error('Device not responding.');
}
} else {
debug('Received:', common.bytes2hex(result));
this.retries = 0;
}
}
Expand Down
4 changes: 3 additions & 1 deletion lib/hidDevice.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@ module.exports = (config) => {
const packets = [];

function readListener(event) {
packets.push(new Uint8Array(event.data.buffer));
// event.data is a view of the report payload, without the report ID, and is
// not necessarily positioned at the start of its underlying buffer.
packets.push(new Uint8Array(event.data.buffer, event.data.byteOffset, event.data.byteLength));
webHid.dispatchEvent(new Event('data'));
}

Expand Down
5 changes: 4 additions & 1 deletion locales/en/translation.missing.json
Original file line number Diff line number Diff line change
Expand Up @@ -101,5 +101,8 @@
"Return to Login": "Return to Login",
"Turn meter on and check the Bluetooth icon is flashing": "Turn meter on and check the Bluetooth icon is flashing",
"Please correct the {{device}}'s time or change your Tidepool time zone to match your {{device}} if appropriate and try again.": "Please correct the {{device}}'s time or change your Tidepool time zone to match your {{device}} if appropriate and try again.",
"Plug in meter with mini-USB cable": "Plug in meter with mini-USB cable"
"Plug in meter with mini-USB cable": "Plug in meter with mini-USB cable",
"Plug in meter with": "Plug in meter with",
"EZSync002B cable": "EZSync002B cable",
"Plug in meter with mini-USB cable. V6 meter not compatible with MacOS.": "Plug in meter with mini-USB cable. V6 meter not compatible with MacOS."
}