From c27ea890c1ad7c8dd0b2e38f5056291fecd12645 Mon Sep 17 00:00:00 2001 From: Chris McGee Date: Wed, 11 May 2022 15:08:29 -0400 Subject: [PATCH 01/20] [UPLOAD-808] adjust padding/word break for long clinic names --- styles/components/ClinicUserSelect.module.less | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/styles/components/ClinicUserSelect.module.less b/styles/components/ClinicUserSelect.module.less index a3bbf5e563..f7fbf463ed 100644 --- a/styles/components/ClinicUserSelect.module.less +++ b/styles/components/ClinicUserSelect.module.less @@ -68,6 +68,10 @@ cursor: pointer; display: flex; align-items: center; + margin-left: 15px; + margin-right: 15px; + max-width: 100%; + word-break: break-all; } .clinicUserDropdown { From 44b186e6096fdd9dd17d6c7dcb3dfa889e9d36c7 Mon Sep 17 00:00:00 2001 From: Chris McGee Date: Mon, 23 May 2022 18:52:23 -0400 Subject: [PATCH 02/20] v2.45.2-padding-adjust --- app/package.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/package.json b/app/package.json index ca6a2af250..47f708d9ba 100644 --- a/app/package.json +++ b/app/package.json @@ -1,7 +1,7 @@ { "name": "tidepool-uploader", "productName": "tidepool-uploader", - "version": "2.45.1", + "version": "2.45.2-padding-adjust", "description": "Tidepool Project Universal Uploader", "main": "./main.prod.js", "author": { diff --git a/package.json b/package.json index c53cb97866..b026299c03 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tidepool-uploader", - "version": "2.45.1", + "version": "2.45.2-padding-adjust", "description": "Tidepool Project Universal Uploader", "private": true, "main": "main.prod.js", From 1f829a9ada9ee04f0c12ae41efd7c7005e88168d Mon Sep 17 00:00:00 2001 From: Chris McGee Date: Tue, 14 Jun 2022 16:18:33 -0400 Subject: [PATCH 03/20] [UPLOAD-808] move styles to parent class, switch word break strategy --- styles/components/ClinicUserSelect.module.less | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/styles/components/ClinicUserSelect.module.less b/styles/components/ClinicUserSelect.module.less index f7fbf463ed..0bd3078d63 100644 --- a/styles/components/ClinicUserSelect.module.less +++ b/styles/components/ClinicUserSelect.module.less @@ -37,6 +37,10 @@ .header { composes: large from '../core/typography.module.less'; + margin-left: 15px; + margin-right: 15px; + max-width: 100%; + overflow-wrap: break-word; } .addLink { @@ -68,10 +72,6 @@ cursor: pointer; display: flex; align-items: center; - margin-left: 15px; - margin-right: 15px; - max-width: 100%; - word-break: break-all; } .clinicUserDropdown { From 921bb1c473af1fdb5ddb8f6b27beee5cb3331f7e Mon Sep 17 00:00:00 2001 From: Chris McGee Date: Tue, 14 Jun 2022 16:20:13 -0400 Subject: [PATCH 04/20] v2.45.2-padding-adjust.1 --- app/package.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/package.json b/app/package.json index 47f708d9ba..848d537e8f 100644 --- a/app/package.json +++ b/app/package.json @@ -1,7 +1,7 @@ { "name": "tidepool-uploader", "productName": "tidepool-uploader", - "version": "2.45.2-padding-adjust", + "version": "2.45.2-padding-adjust.1", "description": "Tidepool Project Universal Uploader", "main": "./main.prod.js", "author": { diff --git a/package.json b/package.json index b026299c03..731bba931a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tidepool-uploader", - "version": "2.45.2-padding-adjust", + "version": "2.45.2-padding-adjust.1", "description": "Tidepool Project Universal Uploader", "private": true, "main": "main.prod.js", From fd16ebd30119e200741d0ba602aee13237adefe8 Mon Sep 17 00:00:00 2001 From: Chris McGee Date: Tue, 14 Jun 2022 17:04:33 -0400 Subject: [PATCH 05/20] [UPLOAD-808] workspace page needs some styles as well --- styles/components/WorkspacePage.module.less | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/styles/components/WorkspacePage.module.less b/styles/components/WorkspacePage.module.less index 2a04b60400..81b1d9cad2 100644 --- a/styles/components/WorkspacePage.module.less +++ b/styles/components/WorkspacePage.module.less @@ -76,11 +76,15 @@ .clinicName { composes: large from '../core/typography.module.less'; margin-left: 24px; + margin-right: 15px; + word-break: break-word; + display: flex; } .clinicSwitchButton { composes: btn btnPrimary from '../core/buttons.module.less'; margin-right: 24px; + height:fit-content; } .addLink { From 629fb1cfdfb7b77068bb55ccd7d2a85aa01ee4f8 Mon Sep 17 00:00:00 2001 From: Chris McGee Date: Tue, 14 Jun 2022 17:05:22 -0400 Subject: [PATCH 06/20] v2.45.2-padding-adjust.2 --- app/package.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/package.json b/app/package.json index 848d537e8f..a1753259c7 100644 --- a/app/package.json +++ b/app/package.json @@ -1,7 +1,7 @@ { "name": "tidepool-uploader", "productName": "tidepool-uploader", - "version": "2.45.2-padding-adjust.1", + "version": "2.45.2-padding-adjust.2", "description": "Tidepool Project Universal Uploader", "main": "./main.prod.js", "author": { diff --git a/package.json b/package.json index 731bba931a..0a209fb0ac 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tidepool-uploader", - "version": "2.45.2-padding-adjust.1", + "version": "2.45.2-padding-adjust.2", "description": "Tidepool Project Universal Uploader", "private": true, "main": "main.prod.js", From 34e707f8752fa7988dbe2f140c964d99f852db4c Mon Sep 17 00:00:00 2001 From: Gerrit Niezen Date: Wed, 22 Jun 2022 12:05:17 +0100 Subject: [PATCH 07/20] make Tandem drivers private --- lib/core/device.js | 11 +- lib/driverManager.js | 8 +- lib/drivers/tandem/tandemDriver.js | 2715 ------------------------- lib/drivers/tandem/tandemSimulator.js | 497 ----- locales/en/translation.missing.json | 29 +- 5 files changed, 19 insertions(+), 3241 deletions(-) delete mode 100644 lib/drivers/tandem/tandemDriver.js delete mode 100644 lib/drivers/tandem/tandemSimulator.js diff --git a/lib/core/device.js b/lib/core/device.js index 17d64c42df..357d2779a6 100644 --- a/lib/core/device.js +++ b/lib/core/device.js @@ -37,7 +37,6 @@ import builder from '../objectBuilder'; import dexcomDriver from '../drivers/dexcom/dexcomDriver'; import oneTouchUltraMini from '../drivers/onetouch/oneTouchUltraMini'; import abbottPrecisionXtra from '../drivers/abbott/abbottPrecisionXtra'; -import tandemDriver from '../drivers/tandem/tandemDriver'; import insuletOmniPod from '../drivers/insulet/insuletDriver'; import oneTouchUltra2 from '../drivers/onetouch/oneTouchUltra2'; import oneTouchVerio from '../drivers/onetouch/oneTouchVerio'; @@ -63,6 +62,13 @@ const device = { log: bows('Device'), }; +let tandemDriver; +try { + tandemDriver = require('../drivers/tandem/tandemDriver'); +} catch (e) { + device.log('Tandem driver is only available to Tidepool developers.'); +} + const hostMap = { darwin: 'mac', win32: 'win', @@ -374,6 +380,9 @@ device.getDriverManifest = (driverId) => { device.detectHelper = (driverId, options, cb) => { const dm = device.createDriverManager(driverId, options); + if (dm == null) { + return cb(new Error('Driver not available.')); + } dm.detect(driverId, cb); }; diff --git a/lib/driverManager.js b/lib/driverManager.js index dc6c20f176..302b1cca6e 100644 --- a/lib/driverManager.js +++ b/lib/driverManager.js @@ -36,7 +36,13 @@ module.exports = function (driverObjects, configs) { var noop = function() {}; for (var d in driverObjects) { - drivers[d] = driverObjects[d](configs[d]); + try { + drivers[d] = driverObjects[d](configs[d]); + } catch (e) { + debug('Driver not available.'); + return null; + } + for (var i=0; i plen) { - return packet; // we don't have enough yet so go back for more - } - packet.packet_len = need_len; - - // we now have enough length for a complete packet, so calc the CRC - packet.crc = struct.extractBEShort(bytes, packet.packet_len - 2); - var checksum = weakChecksum(bytes, 1, packet.packet_len - 3); - if (checksum != packet.crc) { - // if the crc is bad, we should discard the whole packet - // (packet_len is nonzero) - debug('Bad Checksum!'); - debug('checksums:', packet.crc, checksum); - return packet; - } - - if (packet.payload_len) { - packet.payload = new Uint8Array(packet.payload_len); - for (var i = 0; i < packet.payload_len; ++i) { - packet.payload[i] = bytes[i + 3]; - } - var response = getResponseById(packet.descriptor); - if (response && response.fields && response.format) { - packet.payload = struct.unpack(bytes, 3, response.format, response.fields); - if (response.postprocess) { - response.postprocess(packet.payload); - } - } - if(packet.descriptor === RESPONSES.IDP_TE.value) { - // Tandem only returns timestamps for when an event occurred for - // the event history log entries. For other records, e.g. personal profile (IDP_TE) - // or reminder settings, the only available timestamp is the packet timestamp, - // i.e., the device time at time of upload. - packet.timestamp = struct.extractBEInt(bytes,packet.packet_len - 6); - addTimestamp(uploadTime,packet.timestamp); - } - } - packet.valid = true; - return packet; - }; - - var tandemPacketHandler = function (buffer) { - // first, discard bytes that can't start a packet - while ((buffer.len() > 0) && (buffer.get(0) !== SYNC_BYTE)) { - buffer.discard(1); - } - - if (buffer.len() < 9) { // all complete packets must be at least this long - return null; // not enough there yet - } - - // there's enough there to try, anyway - var packet = extractPacket(buffer.bytes()); - if (packet.packet_len !== 0) { - // remove the now-processed packet - buffer.discard(packet.packet_len); - } - if (packet.valid) { - return packet; - } - else { - return null; - } - }; - - var listenForPacket = function (command,args,callback) { - - var abortTimer = setTimeout(function () { - clearInterval(listenTimer); - debug('TIMEOUT'); - callback(new Error('Timeout error'), null); - }, RETRY_TIMEOUT * 2); - - var retryTimer = setTimeout(function() { - console.log('Retrying with ',command, ', ',args); - tandemCommand(command, args, function (err) { - if(err) { - callback(err,null); - } - }); - },RETRY_TIMEOUT); - - var listenTimer = setInterval(function () { - if (cfg.deviceComms.hasAvailablePacket()) { - var pkt = cfg.deviceComms.nextPacket(); - if (pkt.valid && (command.response.value === pkt.descriptor)) { - clearTimeout(retryTimer); - clearTimeout(abortTimer); - clearInterval(listenTimer); - callback(null, pkt); - }else{ - console.log('Packet not valid'); - } - } - }, 1); // spin on this one quickly - }; - - var tandemCommand = function (command, args, callback) { - var format = command.format; - var payload; - var payload_len = 0; - if (format) { - payload_len = struct.structlen(format); - payload = new Uint8Array(payload_len); - switch(args.length) { - case 1 : - struct.pack(payload, 0, format, args); - break; - case 2: - struct.pack(payload, 0, format, args[0], args[1]); - break; - default: - return callback(new Error('Unsupported number of arguments')); - } - } - - var commandPacket = buildPacket(command, payload_len, payload); - cfg.deviceComms.writeSerial(commandPacket, function(err) { - if(err) { - console.log('Write error:',err); - if(err.name === 'pending' || err.name === 'timeout') { - pending = true; - callback(); - setTimeout(function() { - pending = false; - },SEND_WAIT); - } else if(err.name === 'system_error') { - pending = true; - callback(); - cfg.deviceComms.changeBitRate(cfg.deviceInfo.bitrate,function(){ - pending=false; - }); - } - else{ - callback(err); - } - }else{ - callback(); - } - }); - }; - - var tandemLogRequester = function (start, end, progress, callback) { - if (debugMode.isDebug) { - debug('tandemLogRequester', start, end); - var startExec = Date.now(); - } - var sendSeq = start; - var receiveSeq = start; - var recovering = false; - var percentage = 0; - var prevPercentage = 0; - var retryRecoverTimer; - var tries = 0; - - var listenTimer = setInterval(function () { - if(pending) { - debug('pending'); - } - while (cfg.deviceComms.hasAvailablePacket() && !pending) { - var processPacket = function (pkt) { - if (pkt.valid && - pkt.descriptor === RESPONSES.LOG_ENTRY_TE.value && - pkt.payload['header_log_seq_no'] >= receiveSeq) { - if (receiveSeq != pkt.payload['header_log_seq_no']) { - if (!recovering) { - recovering = true; - debug('recovering ', receiveSeq, '(received ',pkt.payload['header_log_seq_no'], ')'); - sendSeq = receiveSeq + 1; - } - - tandemCommand(COMMANDS.LOG_ENTRY_SEQ_REQ, [receiveSeq], function (err) { - - if(err) { - clearInterval(sendTimer); - clearInterval(listenTimer); - callback(err,null); - } - retryRecoverTimer = setTimeout(function() { - if(recovering) { - debug('Retrying for..', receiveSeq); - tandemCommand(COMMANDS.LOG_ENTRY_SEQ_REQ, [receiveSeq], function (err) { - if(err) { - clearInterval(sendTimer); - clearInterval(listenTimer); - callback(err,null); - } - }); - } - },RETRY_TIMEOUT); - }); - } - else { - if (recovering) { - debug('recovered ', receiveSeq, pkt); - clearTimeout(retryRecoverTimer); - } - receiveSeq = pkt.payload['header_log_seq_no'] + 1; - recovering = false; - - if(end-start > 0) { - percentage = ((receiveSeq-start)/(end-start) * 90) + 10; - } - - if(percentage > (prevPercentage+1)) { - // only update progress to UI if there's an increase of at least 1 percent - prevPercentage = percentage; - progress(percentage, cfg.isFirstUpload); - } - - if (receiveSeq % 1000 === 0) { - debug('received ', receiveSeq, ' of ', end); - } - if (receiveSeq > end) { - if (debugMode.isDebug) { - var endExec = Date.now(); - var time = endExec - startExec; - debug('Execution time of tandemLogRequester: ' + time); - } - cfg.deviceComms.flush(); // making sure we flush the buffers - clearInterval(sendTimer); - clearInterval(listenTimer); - } - callback(null, pkt); - } - } - if(pkt.valid && pkt.descriptor === RESPONSES.COMMAND_ACK.value && - pkt.payload['success'] === 0) { - tries += 1; - - if (tries > MAX_TRIES) { - tries = 0; - debug('Failed to recover ', receiveSeq, '. Trying next packet.'); - receiveSeq++; - } - - if (receiveSeq <= end) { - tandemCommand(COMMANDS.LOG_ENTRY_SEQ_REQ, [receiveSeq], function (err) { - if(err) { - clearInterval(sendTimer); - clearInterval(listenTimer); - callback(err,null); - } - }); - } else { - // failed to recover remaining packets, return what we have - debug('Reached the end.'); - clearInterval(sendTimer); - clearInterval(listenTimer); - cfg.deviceComms.flush(); // making sure we flush the buffers - callback(null, { end: true }); - } - } - }; - processPacket(cfg.deviceComms.nextPacket()); - } - }, INTERVAL_FREQ); - - var sendTimer = setInterval(function () { - if (sendSeq % 1000 === 0) { - debug('requesting ', sendSeq); - } - if (!recovering && !pending) { - tandemCommand(COMMANDS.LOG_ENTRY_SEQ_REQ, [sendSeq], function (err) { - if(err) { - clearInterval(listenTimer); - clearInterval(sendTimer); - callback(err,null); - } - }); - if ((sendSeq < end) && !recovering) { - sendSeq++; - } - } - }, INTERVAL_FREQ); // if we spin too quickly on this, packets don't get sent when window doesn't have focus - }; - - var multiLogRequester = function (start, end, progress, callback) { - if (debugMode.isDebug) { - debug('multiLogRequester', start, end); - var startExec = Date.now(); - } - var sendSeq = start; - var receiveSeq = start; - var percentage = 0; - var prevPercentage = 0; - var success = false; - var listenTimer = null; - var abortTimer = null; - var lastPacketRead = null; - var RETRIES = 10; - - var retryTimeout = function() { - clearTimeout(retryTimer); - clearTimeout(abortTimer); - clearInterval(listenTimer); - - debug('Retrying from record ', receiveSeq, ' onwards.', end-receiveSeq, ' record(s) left..'); - - if (_.has(lastPacketRead, 'payload.deviceTime')) { - debug('Last record read successfully has device time', lastPacketRead.payload.deviceTime); - } - - // first we reconnect and flush to clear any connection issues - cfg.deviceComms.clearPacketHandler(); - cfg.deviceComms.disconnect(function () { - cfg.deviceComms.connect(cfg.deviceInfo, tandemPacketHandler, function (err) { - if (err) { - callback(err,null); - } else { - cfg.deviceComms.flush(); - } - - // verify we can get a response from the pump - tandemCommandResponse(COMMANDS.VERSION_REQ, null, function (err, result) { - if (err) { - if (rollbar) { - rollbar.info('Unable to get response from Tandem pump on retry'); - } - debug(err); - callback(err, null); - } - else { - debug('Tandem found: ', result); - - // cancel outstanding multi log request just in case - tandemCommand(COMMANDS.LOG_ENTRY_SEQ_MULTI_STOP_DUMP, null, function (err) { - if(err) { - callback(err,null); - } else { - var err = new Error('Multi-record request failed'); - - if (end-receiveSeq < 128) { - debug('Fewer than 128 records remaining'); - // download remaining records one at a time - err.name = 'multi-fail'; - } else { - err.name = 'multi-retry'; - } - - err.startSeq = receiveSeq; - err.endSeq = end; - return callback(err, null); - } - }); - } - }); - }); - }); - }; - - var retryTimer = setTimeout(retryTimeout, RETRY_TIMEOUT); - - tandemCommand(COMMANDS.LOG_ENTRY_SEQ_MULTI_REQ, [sendSeq, end-sendSeq+1], function (err) { - // second parameter is number of records to retrieve, which is the difference between - // the start and end indexes plus one - if(err) { - callback(err,null); - } - else { - listenTimer = setInterval(function () { - if(pending) { - debug('pending'); - } - while (cfg.deviceComms.hasAvailablePacket() && !pending) { - var processPacket = function (pkt) { - if (pkt.valid && - pkt.descriptor === RESPONSES.LOG_ENTRY_TE.value && - pkt.payload['header_log_seq_no'] >= receiveSeq) { - if (receiveSeq != pkt.payload['header_log_seq_no']) { - debug('Packet out of sequence'); - } - else { - success = true; - lastPacketRead = pkt; - - // reset timeouts - clearTimeout(retryTimer); - clearTimeout(abortTimer); // abortTimer is set by retryTimeout - retryTimer = setTimeout(retryTimeout, RETRY_TIMEOUT); - - receiveSeq = pkt.payload['header_log_seq_no'] + 1; - - if(end-start > 0) { - percentage = ((receiveSeq-start)/(end-start) * 90) + 10; - } - - if(percentage > (prevPercentage+1)) { - // only update progress to UI if there's an increase of at least 1 percent - prevPercentage = percentage; - progress(percentage, cfg.isFirstUpload); - } - - if (receiveSeq % 1000 === 0) { - debug('received ', receiveSeq, ' of ', end); - } - if (receiveSeq > end) { - if (debugMode.isDebug) { - var endExec = Date.now(); - var time = endExec - startExec; - debug('Execution time of multiLogRequester: ' + time); - } - cfg.deviceComms.flush(); // making sure we flush the buffers - clearTimeout(retryTimer); - clearTimeout(abortTimer); - clearInterval(listenTimer); - } - callback(null, pkt); - } - } - else if(success === false) { - clearTimeout(retryTimer); - clearTimeout(abortTimer); - clearInterval(listenTimer); - var err = new Error('Multi-record request failed'); - err.name = 'multi-fail'; - return callback(err,null); - } - }; - processPacket(cfg.deviceComms.nextPacket()); - } - }, INTERVAL_FREQ); - } - }); - }; - - var tandemCommandResponse = function (command, args, callback) { - - tandemCommand(command, args, function (err) { - if(err) { - callback(err,null); - } - listenForPacket(command,args,callback); - }); - }; - - // callback is called when EOF happens with all records retrieved - var tandemDownloadRecords = function (progress, data, callback) { - var retval = []; - var entries; - var end_seq; - var start_seq; - - var isMultiRecordFirmware = function() { - return (data.firmware_version >= COMMANDS.LOG_ENTRY_SEQ_MULTI_REQ.version); - }; - - function iterate(err, result) { - if (err) { - if (err.startSeq && err.endSeq) { - start_seq = err.startSeq; - end_seq = err.endSeq; - } - - if(err.name === 'multi-fail') { - debug('Falling back to regular event history requests'); - tandemLogRequester(start_seq, end_seq, progress, iterate); - } else if (err.name === 'multi-retry') { - multiLogRequester(start_seq, end_seq, progress, iterate); - } else { - debug('error retrieving record ', result); - callback(err, null); - } - } - else { - if (result.payload && !result.payload.tdeps) { - retval.push(result.payload); - } - if (result.payload && result.payload.header_log_seq_no === end_seq) { - debug('fetched all records'); - data.log_records = retval; - - if(isMultiRecordFirmware()) { - tandemCommand(COMMANDS.LOG_ENTRY_SEQ_MULTI_STOP_DUMP, null, function (err) { - if(err) { - callback(err,null); - } - else{ - // After a multi-record dump, we have to wait a second to be sure the pump - // stopped dumping records and is ready for the next request. - debug('Wait a second, phew!'); - var waitTimer = setTimeout(function () { - callback(null,data); - }, 1000); - } - }); - } else { - callback(null, data); - } - } else if (result.end) { - debug(`finished fetching records, missing ${end_seq - retval[retval.length-1].header_log_seq_no} records at end`); - if (rollbar) { - rollbar.info('Unable to retrieve last records on Tandem pump'); - } - data.log_records = retval; - callback(null, data); - } - } - } - - end_seq = data.end_seq; - start_seq = data.start_seq; - debug('Firmware version is #',data.firmware_version); - if(isMultiRecordFirmware()) { - // woohoo, we can use multi-record downloads! - debug('Using multi-record requests'); - multiLogRequester(start_seq, end_seq, progress, iterate); - } - else{ - tandemLogRequester(start_seq, end_seq, progress, iterate); - } - }; - - var tandemFetchEventRange = function (progress, data, callback) { - tandemDownloadRecords(progress, data, function (err, retval) { - if (err) { - debug('fetch failed'); - callback(err, null); - } else { - debug('tandemFetchEventRange:', retval); - tandemFetchSettings(progress, data, function () { - callback(null, data); - }); - } - }); - }; - - var tandemFetchSettings = function (progress, data, callback) { - var profile_ids = []; - var parsed_profiles = []; - - function iterate(err, result) { - if (err) { - debug('error reading settings'); - callback(err, null); - } - else { - if (result.valid && result.descriptor === RESPONSES.IDP_TE.value) { - parsed_profiles.push(result.payload); - var profile_id = profile_ids.shift(); - if (profile_id === undefined) { - data.profiles = parsed_profiles; - debug('parsed profiles: ', parsed_profiles); - callback(null, data); - } - else { - tandemCommandResponse(COMMANDS.IDP_REQ, [profile_id], iterate); - } - } - } - } - - tandemCommandResponse(COMMANDS.GLOBALS_REQ, null, function (err, pkt) { - if (err) { - debug('Error reading globals ', err); - callback(err, null); - } - else { - tandemCommandResponse(COMMANDS.MAX_BOLUS_REQ, null, function (err, pkt) { - if (err) { - debug('Pump does not have a global max bolus setting'); - } - - if (pkt && pkt.valid && pkt.descriptor === RESPONSES.MAX_BOLUS_TE.value) { - data.max_bolus_units = pkt.payload.max_bolus_setting_units; - } - - tandemCommandResponse(COMMANDS.IDP_LIST_REQ, null, function (err, pkt) { - if (err) { - debug('Error reading globals ', err); - callback(err, null); - } - else { - var num_profiles = pkt.payload['num_available']; - for (var i = 1; i <= num_profiles; i++) { - profile_ids.push(pkt.payload['slot' + i]); - } - tandemCommandResponse(COMMANDS.IDP_REQ, [profile_ids.shift()], iterate); - } - }); - }); - } - }); - }; - - - var tandemFetch = function (progress, data, callback) { - if (debugMode.isDebug) { - var startExec = Date.now(); - } - - debug('requesting log size'); - tandemCommandResponse(COMMANDS.LOG_SIZE_REQ, null, function (err, result) { - if (err) { - debug('Error reading log size ', err); - callback(err, null); - } - else { - if (result.valid && (result.descriptor === RESPONSES.LOG_SIZE_TE.value)) { - - api.getMostRecentUploadRecord(cfg.groupId, cfg.deviceInfo.deviceId, function(err, lastUpload) { - if (err) { - return callback(err); - } - - var lastEndPosition = _.get(lastUpload, 'client.private.delta.lastEndPosition', null); - - data.end_seq = result.payload['end_seq']; - - if ((lastEndPosition != null) && - (lastEndPosition <= data.end_seq) && - (uploadDataPeriod.periodGlobal === uploadDataPeriod.PERIODS.DELTA)) { - - if (lastEndPosition === data.end_seq) { - return callback(new Error('No new records since last upload.')); - } - - cfg.isFirstUpload = false; - if (lastEndPosition < result.payload['start_seq']) { - data.start_seq = result.payload['start_seq']; - debug('Last record read is not on pump anymore, so reading from record', data.start_seq); - } else { - debug('Last record read was', lastEndPosition, ', starting from there'); - data.start_seq = lastEndPosition; - } - } else { - cfg.isFirstUpload = true; - debug('Reading from record', result.payload['start_seq']); - data.start_seq = result.payload['start_seq']; - } - debug('Reading up until record', data.end_seq); - - progress(10, cfg.isFirstUpload); - cfg.deviceComms.flush(); // making sure we flush the buffers - tandemFetchEventRange(progress, data, callback); - }); - } - else{ - console.log('Invalid log size:', result); - } - } - }); - }; - - var filterLogEntries = function (types, log_records) { - var neededLogIds = []; - types.forEach(function (element) { neededLogIds.push(element.value); }); - return log_records.filter(function (record) { - return neededLogIds.indexOf(record.header_id) >= 0; - }); - }; - - var buildSettingsRecords = function buildSettingsRecord(data, postrecords) { - var activeName = data.profiles[0].name; // first is always the active profile - var basalSchedules = {}; - var carbSchedules = {}; - var sensitivitySchedules = {}; - var targetSchedules = {}; - var bolusSettings = {}; - var insulinSettings = {}; - data.profiles.forEach(function (profile) { - var scheduleName = profile.name; - var schedule = []; - var carbSchedule = []; - var sensitivitySchedule = []; - var targetSchedule = []; - profile.tdeps.forEach(function (tdep) { - schedule.push({ rate: tdep['basalRate'], start: tdep['startTime'] }); - carbSchedule.push({ amount: tdep['carbRatio'], start: tdep['startTime'] }); - sensitivitySchedule.push({ amount: tdep['ISF'], 'start': tdep['startTime'] }); - targetSchedule.push({ target: tdep['TargetBG'], start: tdep['startTime'] }); - }); - basalSchedules[scheduleName] = schedule; - carbSchedules[scheduleName] = carbSchedule; - sensitivitySchedules[scheduleName] = sensitivitySchedule; - targetSchedules[scheduleName] = targetSchedule; - bolusSettings[scheduleName] = { - amountMaximum : { - value: profile.max_bolus, - units: 'Units' - }, - calculator: { - enabled: profile.carb_entry ? true : false, - insulin: { - duration : profile.insulin_duration, - units: 'minutes' - } - } - }; - - if (data.max_bolus_units) { - // software versions starting with 6.4 and 7.4 have a global max bolus setting, - // per-schedule max bolus setting is invalid and should be replaced with global one - bolusSettings[scheduleName].amountMaximum.value = data.max_bolus_units; - } - }); - - var postsettings = cfg.builder.makeTandemPumpSettings() - .with_activeSchedule(activeName) - .with_units({ carb: 'grams', bg: 'mg/dL' }) - .with_basalSchedules(basalSchedules) - .with_carbRatios(carbSchedules) - .with_insulinSensitivities(sensitivitySchedules) - .with_bgTargets(targetSchedules) - .with_bolus(bolusSettings) - .with_manufacturers(cfg.deviceInfo.manufacturers) - .with_model(cfg.deviceInfo.model) - .with_serialNumber(cfg.deviceInfo.serialNumber) - .with_firmwareVersion(cfg.deviceInfo.firmwareVersion.toString()) - .with_time(sundial.applyTimezone(uploadTime.jsDate, cfg.timezone).toISOString()) - .with_deviceTime(uploadTime.deviceTime) - .with_timezoneOffset(sundial.getOffsetFromZone(uploadTime.jsDate, cfg.timezone)) - .with_conversionOffset(0); - - if (data.controlIQ) { - postsettings.with_automatedDelivery(data.closed_loop_enabled === 1 ? true : false); - } - - postrecords.push(postsettings.done()); - return postrecords; - }; - - var buildTimeChangeRecords = function (data, records) { - var timeChangeLogs = filterLogEntries( - [PUMP_LOG_RECORDS.LID_TIME_CHANGED], - data.log_records - ); - - var dateChangeLogs = filterLogEntries( - [PUMP_LOG_RECORDS.LID_DATE_CHANGED], - data.log_records - ); - - var isValidTimeChange = function(fromDatum, toDatum) { - /* We need to filter out spurious time changes that appear in the data when - the battery is left out for too long. The strategy is to filter out any - time change to a date where the year is less than the current year - minus one. This is the same strategy used in the Medtronic driver. */ - if (fromDatum.getUTCFullYear() < (new Date().getUTCFullYear() - 1) || - toDatum.getUTCFullYear() < (new Date().getUTCFullYear() - 1)) { - debug('Excluding time change from',fromDatum.toISOString().slice(0,-5),'to', toDatum.toISOString().slice(0,-5), 'as spurious.'); - return false; - } - return true; - }; - - - var postrecords = []; - - for (var i = 0; i < timeChangeLogs.length; ++i) { - var tc = timeChangeLogs[i]; - var tc_base = sundial.floor(tc.rawTimestamp, 'day').valueOf(); - - // look for a corresponding date change event - var found = false; - var index = tc.index; - var lastIndex = data.log_records[data.log_records.length - 1].index; - var SEARCH_THRESHOLD = 5; - var start = index < SEARCH_THRESHOLD ? 0 : index - SEARCH_THRESHOLD; // look at x entries before - var end = (index + SEARCH_THRESHOLD) < lastIndex ? - index + SEARCH_THRESHOLD : lastIndex; // and up to x entries after - for (var k = start; k <= end; k++) { - var event = _.find(dateChangeLogs, {index: k}); - if (event) { - found = true; - var rawFromTime = BASE_TIME + (event.date_prior * 864e5) + tc.time_prior; - var rawToTime = BASE_TIME + (event.date_after * 864e5) + tc.time_after; - if (isValidTimeChange(new Date(rawFromTime), new Date(rawToTime))) { - var datetimechange = cfg.builder.makeDeviceEventTimeChange() - .with_change({ - from: sundial.formatDeviceTime(rawFromTime), - to: sundial.formatDeviceTime(rawToTime), - agent: 'manual' - }) - .with_deviceTime(sundial.formatDeviceTime(rawToTime)) - .set('jsDate', new Date(rawToTime)) - .set('index', tc.index); - postrecords.push(datetimechange); - } - - // remove the date change event from dateChangeLogs so that we don't process it twice - dateChangeLogs = _.without(dateChangeLogs, event); - } - } - - if (!found) { // a regular time change event - var timechange = cfg.builder.makeDeviceEventTimeChange() - .with_change({ - from: sundial.formatDeviceTime(tc_base + tc.time_prior), - to: sundial.formatDeviceTime(tc_base + tc.time_after), - agent: 'manual' - }) - .with_deviceTime(tc.deviceTime) - .set('jsDate', tc.jsDate) - .set('index', tc.index); - postrecords.push(timechange); - } - } - - for (var j = 0; j < dateChangeLogs.length; ++j) { - var dc = dateChangeLogs[j]; - var dc_base = BASE_TIME; - - var rawFromTime = BASE_TIME + dc.date_prior * 864e5; - var rawToTime = BASE_TIME + dc.date_after * 864e5; - if (isValidTimeChange(new Date(rawFromTime), new Date(rawToTime))) { - var datechange = cfg.builder.makeDeviceEventTimeChange() - .with_change({ - from: sundial.formatDeviceTime(rawFromTime), - to: sundial.formatDeviceTime(rawToTime), - agent: 'manual' - }) - .with_deviceTime(dc.deviceTime) - .set('jsDate', dc.jsDate) - .set('index', dc.index); - postrecords.push(datechange); - } - } - - var mostRecent = sundial.applyTimezone(data.log_records[data.log_records.length - 1].jsDate, cfg.timezone).toISOString(); - debug('Most recent datum at', mostRecent); - var tzoUtil = new TZOUtil( - cfg.timezone, - mostRecent, - postrecords - ); - - cfg.tzoUtil = tzoUtil; - return records.concat(tzoUtil.records); - }; - - var buildBolusRecords = function (data, records) { - var bolusLogs = filterLogEntries([ - PUMP_LOG_RECORDS.LID_BOLUS_ACTIVATED, - PUMP_LOG_RECORDS.LID_BOLUS_COMPLETED, - PUMP_LOG_RECORDS.LID_BOLEX_ACTIVATED, - PUMP_LOG_RECORDS.LID_BOLEX_COMPLETED, - PUMP_LOG_RECORDS.LID_BOLUS_REQUESTED_MSG1, - PUMP_LOG_RECORDS.LID_BOLUS_REQUESTED_MSG2, - PUMP_LOG_RECORDS.LID_BOLUS_REQUESTED_MSG3 - ], - data.log_records - ); - var boluses = {}; - bolusLogs.forEach(function(event) { - var bolusId = event.bolus_id; - var bolus = _.defaults({ bolus_id: bolusId }, boluses[bolusId], event); - if (event.header_id === PUMP_LOG_RECORDS.LID_BOLUS_ACTIVATED.value) { - bolus.startDeviceTime = event.deviceTime; - } - if (event.header_id === PUMP_LOG_RECORDS.LID_BOLEX_ACTIVATED.value) { - bolus.extendedStartDeviceTime = event.deviceTime; - } - if (event.header_id === PUMP_LOG_RECORDS.LID_BOLEX_COMPLETED.value) { - bolus.endDeviceTime = event.deviceTime; - } - if (event.header_id === PUMP_LOG_RECORDS.LID_BOLUS_REQUESTED_MSG1.value) { - bolus.bc_iob = event.iob; - bolus.wizardDeviceTime = event.deviceTime; - } - boluses[bolusId] = bolus; - }); - for (var key in boluses) { - var bolus = boluses[key]; - var record; - - // bolus records - if (bolus.bolex_size !== undefined || bolus.bolus_option === 'extended') { - if (bolus.bolus_size !== undefined || bolus.insulin_requested !== undefined) { - record = cfg.builder.makeDualBolus(); - } - else { - record = cfg.builder.makeSquareBolus(); - } - } - else if (bolus.bolus_option === 'automated') { - record = cfg.builder.makeAutomatedBolus(); - } - else { - record = cfg.builder.makeNormalBolus(); - } - record = record.with_deviceTime(bolus.deviceTime); - if (bolus.bolex_size !== undefined) { - // extended bolus - // first case: extended bolus was cancelled - if (bolus.bolex_size !== bolus.bolex_insulin_delivered) { - if(bolus.endDeviceTime !== undefined) { - record = record.with_duration( - Date.parse(bolus.endDeviceTime) - Date.parse(bolus.extendedStartDeviceTime) - ); - } - else{ - // no end time, so extended bolus still in progress - debug('Extended bolus in progress: ', bolus); - continue; - } - - // cancelled before any insulin was given on dual bolus - if (bolus.bolex_insulin_delivered === undefined) { - record = record.with_extended(0); - } - else { - record = record.with_extended(bolus.bolex_insulin_delivered); - } - record = record.with_expectedExtended(bolus.bolex_insulin_requested) - .with_expectedDuration(bolus.duration); - } - // other case: extended bolus completed - else { - record = record.with_extended(bolus.bolex_insulin_delivered); - record = record.with_duration(bolus.duration); - } - } - if (bolus.bolus_size !== undefined || bolus.insulin_delivered !== undefined) { - if (bolus.bolus_size !== bolus.insulin_delivered) { - record = record.with_normal(bolus.insulin_delivered); - record = record.with_expectedNormal(bolus.insulin_requested); - } - else { - record = record.with_normal(bolus.insulin_delivered); - } - } - // non-extended bolus cancelled before any insulin was given - if ((bolus.bolus_option === 'standard' || bolus.bolus_option === 'quickbolus') && - bolus.bolus_size === undefined) { - record = record.with_normal(0) - .with_expectedNormal(bolus.insulin_requested); - } - // extended bolus cancelled before any insulin was given - if (bolus.bolus_option === 'extended' && bolus.bolex_size === undefined) { - record = record.with_duration(0) - .with_extended(0) - .with_expectedDuration(bolus.duration) - .with_expectedExtended(bolus.bolex_insulin_requested); - } - record = record.set('index', bolus.index); - - if((record.subType === 'normal' && bolus.insulin_delivered === undefined) || - (record.subType === 'automated' && bolus.insulin_delivered === undefined) || - (record.subType === 'square' && bolus.duration === undefined) || - (record.subType === 'dual/square' && bolus.duration === undefined)) { - debug('Only part of a bolus received, dropping:', bolus); - } - else{ - cfg.tzoUtil.fillInUTCInfo(record, bolus.jsDate); - records.push(record.done()); - } - - // wizard records - if (_.includes(['standard', 'extended'], bolus.bolus_option) && - (bolus.correction_bolus_included || (bolus.food_bolus_size > 0))) { - // a wizard bolus can be a correction bolus or food bolus (or both) - - var netBolus = null; - if (bolus.correction_bolus_included) { - netBolus = bolus.correction_bolus_size + bolus.food_bolus_size; - } - else { - netBolus = bolus.food_bolus_size; - } - - var wizard_record = cfg.builder.makeWizard() - .with_deviceTime(bolus.wizardDeviceTime) - .with_recommended({ - carb: bolus.food_bolus_size, - correction: bolus.correction_bolus_size, - net: netBolus.toFixedNumber(SIGNIFICANT_DIGITS) - }) - .with_bgInput(bolus.bg) - .with_carbInput(bolus.carb_amount) - .with_insulinOnBoard(bolus.bc_iob) - .with_insulinCarbRatio(bolus.carb_ratio) - .with_insulinSensitivity(bolus.isf) - .with_bgTarget({ - target: bolus.target_bg - }) - .with_bolus(record) - .with_units('mg/dL') - .set('index', bolus.index); - cfg.tzoUtil.fillInUTCInfo(wizard_record, bolus.jsDate); - wizard_record = wizard_record.done(); - records.push(wizard_record); - } - } - return records; - }; - - var buildBasalRecords = function (data, records) { - var basalRecords = filterLogEntries( - [PUMP_LOG_RECORDS.LID_BASAL_RATE_CHANGE], - data.log_records - ); - var postbasal = null; - for (var b = 0; b < basalRecords.length; ++b) { - var event = basalRecords[b]; - - var changesTypesArray = event.change_types; - var filteredChangeTypes; - if(event.change_types.length > 1) { - filteredChangeTypes = _.without(event.change_types,'timed_segment','new_profile').join('|'); - } else { - filteredChangeTypes = event.change_types[0]; - } - - switch (filteredChangeTypes) { - case 'timed_segment': - case 'new_profile': - case 'temp_rate_end': - case 'temp_rate_end|pump_resumed': - case 'pump_resumed': - // when the command_basal_rate is not the same as the base_basal_rate - // that means we're in a temp basal that crosses the border between - // scheduled segments, so the temp rate is being recalculated - // as a percentage of the current base_basal_rate (from the schedule) - if (event.command_basal_rate !== event.base_basal_rate) { - postbasal = cfg.builder.makeTempBasal() - .with_deviceTime(event.deviceTime) - .with_rate(event.command_basal_rate) - .with_percent(event.command_basal_rate/event.base_basal_rate) - .set('index', event.index) - .with_payload({change_types: changesTypesArray}); - cfg.tzoUtil.fillInUTCInfo(postbasal, event.jsDate); - break; - } - postbasal = cfg.builder.makeScheduledBasal() - .with_deviceTime(event.deviceTime) - .with_rate(event.command_basal_rate) - .set('index', event.index) - .with_payload({ - personalProfileIndex : event.idp, - change_types: changesTypesArray - }); - cfg.tzoUtil.fillInUTCInfo(postbasal, event.jsDate); - break; - case 'temp_rate_start': - case 'temp_rate_start|pump_resumed': - postbasal = cfg.builder.makeTempBasal() - .with_deviceTime(event.deviceTime) - .with_rate(event.command_basal_rate) - .set('index', event.index) - .with_payload({change_types: changesTypesArray}); - cfg.tzoUtil.fillInUTCInfo(postbasal, event.jsDate); - break; - case 'pump_suspended': - case 'temp_rate_end|pump_suspended': - postbasal = cfg.builder.makeSuspendBasal() - .with_deviceTime(event.deviceTime) - .set('index', event.index) - .with_payload({change_types: changesTypesArray}); - cfg.tzoUtil.fillInUTCInfo(postbasal, event.jsDate); - break; - case 'pump_shut_down': - case 'temp_rate_end|pump_shut_down': - // no basal record gets built in this case - break; - default: - debug('Event with unhandled change type:', event); - throw new Error('Unhandled combination of basal change types: ' + event.change_types.join('|')); - } - if (postbasal != null) { - if(postbasal.deliveryType === 'temp') { - var suppressed = { - type: 'basal', - deliveryType: 'scheduled', - rate: event.base_basal_rate - }; - postbasal.set('suppressed', suppressed); - } - records.push(postbasal); - } - } - return records; - }; - - var buildControlModeRecords = function(data, records) { - var controlModeRecords = filterLogEntries([PUMP_LOG_RECORDS.LID_AA_PCM_CHANGE], data.log_records); - - for(var i = 0; i < controlModeRecords.length; ++i) { - var event = controlModeRecords[i]; - var record = { - type: 'control-mode', - subType: getPumpControlMode(event.current_PCM), - previous: getPumpControlMode(event.previous_PCM), - }; - - record.deviceTime = event.deviceTime; - record.index = event.index; - cfg.tzoUtil.fillInUTCInfo(record, event.jsDate); - records.push(record); - } - return records; - }; - - var buildPumpSettingsOverrideRecords = function(data, records) { - var activityRecords = filterLogEntries([PUMP_LOG_RECORDS.LID_AA_USER_MODE_CHANGE], data.log_records); - - for(var i = 0; i < activityRecords.length; ++i) { - var event = activityRecords[i]; - - var mode = getUserMode(event.current_user_mode); - var prevMode = getUserMode(event.previous_user_mode); - - var modeChange = cfg.builder.makeDeviceEventPumpSettingsOverride() - .with_overrideType(mode) - .with_deviceTime(event.deviceTime) - .set('index', event.index); - - if(prevMode !== USER_MODE.NORMAL.name) { - modeChange.set('previousOverride', prevMode); - } - - cfg.tzoUtil.fillInUTCInfo(modeChange, event.jsDate); - records.push(modeChange); - } - return records; - }; - - var buildTempBasalRecords = function(data, records) { - var tempBasalRecords = filterLogEntries([PUMP_LOG_RECORDS.LID_TEMP_RATE_ACTIVATED,PUMP_LOG_RECORDS.LID_TEMP_RATE_COMPLETED], data.log_records); - - for(var i = 0; i < tempBasalRecords.length; ++i) { - var event = tempBasalRecords[i]; - var record = null; - if(event.header_id === PUMP_LOG_RECORDS.LID_TEMP_RATE_ACTIVATED.value) { - record = { - type: 'temp-basal', - subType: 'start', - percent: event.percent, - duration: event.duration - }; - } - else if(event.header_id === PUMP_LOG_RECORDS.LID_TEMP_RATE_COMPLETED.value) { - record = { - type: 'temp-basal', - subType: 'stop', - time_left: event.time_left - }; - } - record.deviceTime = event.deviceTime; - record.index = event.index; - cfg.tzoUtil.fillInUTCInfo(record, event.jsDate); - records.push(record); - } - return records; - }; - - var buildNewDayRecords = function(data, records) { - var newDayRecords = filterLogEntries([PUMP_LOG_RECORDS.LID_NEW_DAY], data.log_records); - - for (var b = 0; b < newDayRecords.length; ++b) { - var event = newDayRecords[b]; - - var nextEvent = data.log_records[data.log_records.indexOf(event) + 1 ]; - if( nextEvent.header_id === PUMP_LOG_RECORDS.LID_TIME_CHANGED.value || - nextEvent.header_id === PUMP_LOG_RECORDS.LID_DATE_CHANGED.value) { - debug('Dropping new-day event (',event.deviceTime,') followed by date/time change (', nextEvent.deviceTime,')'); - continue; - }; - - var rate = event.commanded_basal_rate; - if ((rate !== null) && (rate > 0)) { - // new day event; breaks up flat-rate basals - var postrecord = cfg.builder.makeScheduledBasal() - .with_deviceTime(event.deviceTime) - .with_rate(rate) - .set('index', event.index); - cfg.tzoUtil.fillInUTCInfo(postrecord, event.jsDate); - postrecord.set('type', 'new-day'); - records.push(postrecord); - } - } - return records; - }; - - var buildSuspendRecords = function (data, records) { - var suspendRecords = filterLogEntries([PUMP_LOG_RECORDS.LID_PUMPING_SUSPENDED], data.log_records); - - suspendRecords.forEach(function (entry) { - var postrecord = cfg.builder.makeDeviceEventSuspend() - .with_deviceTime(entry.deviceTime) - .with_payload( { reason: getSuspendReason(entry.reason) }) - .set('index', entry.index); - - if (entry.reason === SUSPEND_REASON.USER_ABORTED.value) { - postrecord.reason = { suspended: 'manual' }; - } else { - postrecord.reason = { suspended: 'automatic' }; - } - cfg.tzoUtil.fillInUTCInfo(postrecord, entry.jsDate); - records.push(postrecord.done()); - }); - - return records; - }; - - var buildResumeRecords = function (data, records) { - var resumeRecords = filterLogEntries([PUMP_LOG_RECORDS.LID_PUMPING_RESUMED, PUMP_LOG_RECORDS.LID_HYPO_MINIMIZER_RESUME], data.log_records); - - for (var i = 0; i < resumeRecords.length; i++) { - var entry = resumeRecords[i]; - var postrecord = cfg.builder.makeDeviceEventResume() - .with_reason({resumed: 'manual'}) - .with_deviceTime(entry.deviceTime) - .set('index', entry.index); - cfg.tzoUtil.fillInUTCInfo(postrecord, entry.jsDate); - - if (resumeRecords[i+1] != null && resumeRecords[i+1].header_id === PUMP_LOG_RECORDS.LID_HYPO_MINIMIZER_RESUME.value) { - - if (entry.time !== resumeRecords[i+1].time) { - throw new Error('PLGS resume time does not match pump resume time'); - } - // resume is due to PLGS - i += 1; - entry = resumeRecords[i]; - postrecord.reason = {resumed: 'automatic'}; - if (entry.reason > 0) { - postrecord.payload = {reason: getResumeReason(entry.reason)}; - } - } - - records.push(postrecord); - }; - - return records; - }; - - var buildCartridgeChangeRecords = function (data, records) { - var cartridgeChangeRecords = filterLogEntries([PUMP_LOG_RECORDS.LID_TUBING_FILLED,PUMP_LOG_RECORDS.LID_CANNULA_FILLED,PUMP_LOG_RECORDS.LID_CARTRIDGE_FILLED], data.log_records); - - cartridgeChangeRecords.forEach(function (entry) { - var cartridgeChangeRecord; - if (entry.header_id === PUMP_LOG_RECORDS.LID_TUBING_FILLED.value) { - cartridgeChangeRecord = cfg.builder.makeDeviceEventPrime() - .with_primeTarget('tubing') - .with_volume(entry.prime_size); - } else if (entry.header_id === PUMP_LOG_RECORDS.LID_CANNULA_FILLED.value) { - cartridgeChangeRecord = cfg.builder.makeDeviceEventPrime() - .with_primeTarget('cannula') - .with_volume(entry.prime_size); - } else if (entry.header_id === PUMP_LOG_RECORDS.LID_CARTRIDGE_FILLED.value) { - cartridgeChangeRecord = cfg.builder.makeDeviceEventReservoirChange() - .with_payload({event: 'cartridge_filled', - insulin_display: entry.insulin_display, - insulin_actual: entry.insulin_actual}); - } - - cartridgeChangeRecord.with_deviceTime(entry.deviceTime) - .set('index',entry.index); - cfg.tzoUtil.fillInUTCInfo(cartridgeChangeRecord, entry.jsDate); - records.push(cartridgeChangeRecord.done()); - }); - - return records; - }; - var buildCannulaChangeRecords = function (data, records) { - return records; - }; - - var buildBGRecords = function (data, records) { - var bgRecords = filterLogEntries([PUMP_LOG_RECORDS.LID_BG_READING_TAKEN], data.log_records); - bgRecords.forEach(function (bgEntry) { - if (bgEntry.bg_source !== BG_READING_SOURCE.AUTO_POPULATED.value) { - var bgRecord = cfg.builder.makeSMBG() - .with_deviceTime(bgEntry.deviceTime) - .with_subType('manual') - .with_value(bgEntry.bg) - .with_units('mg/dL') - .set('index',bgEntry.index); - cfg.tzoUtil.fillInUTCInfo(bgRecord, bgEntry.jsDate); - bgRecord.done(); - records.push(bgRecord); - } - }); - return records; - }; - - var buildAlarmRecords = function (data,records) { - var alarmRecords = filterLogEntries([PUMP_LOG_RECORDS.LID_ALARM_ACTIVATED,PUMP_LOG_RECORDS.LID_ALERT_ACTIVATED],data.log_records); - alarmRecords.forEach(function (alarmEntry) { - var alarmRecord = cfg.builder.makeDeviceEventAlarm() - .with_deviceTime(alarmEntry.deviceTime) - .set('index', alarmEntry.index); - cfg.tzoUtil.fillInUTCInfo(alarmRecord, alarmEntry.jsDate); - - if(alarmEntry.alarm_id != null) { - var alarmValue = alarmEntry.alarm_id; - var alarmText = getAlarmName(alarmValue,ALARM_TYPES); - var postbasal = null; - - switch (alarmValue) { - case ALARM_TYPES.ALARM_OCCLUSION.value: - alarmRecord = alarmRecord.with_alarmType('occlusion'); - // occlusions do not create suspended basals in basal rate change events, - // so we have to create them manually - postbasal = cfg.builder.makeSuspendBasal() - .with_deviceTime(alarmEntry.deviceTime) - .with_payload({alarm_id: alarmValue}) - .set('index', alarmEntry.index); - annotate.annotateEvent(postbasal, 'tandem/basal/fabricated-from-occlusion-alarm'); - cfg.tzoUtil.fillInUTCInfo(postbasal, alarmEntry.jsDate); - records.push(postbasal); - break; - case ALARM_TYPES.ALARM_SECOND_OCCLUSION.value: - alarmRecord = alarmRecord.with_alarmType('occlusion'); - alarmRecord = alarmRecord.with_payload({alarm_id: alarmValue, alarm_name: alarmText}); - // occlusions do not create suspended basals in basal rate change events, - // so we have to create them manually - postbasal = cfg.builder.makeSuspendBasal() - .with_deviceTime(alarmEntry.deviceTime) - .with_payload({alarm_id: alarmValue, alarm_name: alarmText}) - .set('index', alarmEntry.index); - annotate.annotateEvent(postbasal, 'tandem/basal/fabricated-from-occlusion-alarm'); - cfg.tzoUtil.fillInUTCInfo(postbasal, alarmEntry.jsDate); - records.push(postbasal); - break; - case ALARM_TYPES.ALARM_AUTO_OFF.value: - alarmRecord = alarmRecord.with_alarmType('auto_off'); - break; - case ALARM_TYPES.ALARM_OUT_OF_LIQUID.value: - alarmRecord = alarmRecord.with_alarmType('no_insulin'); - break; - case ALARM_TYPES.ALARM_EXTREMELY_LOW_LIPO.value: - alarmRecord = alarmRecord.with_alarmType('no_power'); - break; - case ALARM_TYPES.ALARM_TEMPERATURE_OUT_OF_RANGE: - case ALARM_TYPES.ALARM_STUCK_WAKE_BUTTON: - case ALARM_TYPES.ALARM_PRESSURE_OUT_OF_RANGE: - case ALARM_TYPES.ALARM_CARTRIDGE_REMOVED: - alarmRecord = alarmRecord.with_alarmType('other'); - alarmRecord = alarmRecord.with_payload({alarm_id: alarmValue, alarm_name: alarmText}); - break; - default: - alarmRecord = alarmRecord.with_alarmType('other'); - alarmRecord = alarmRecord.with_payload({alarm_id: alarmValue}); - } - } - - if(alarmEntry.alert_id != null) { - var alertValue = alarmEntry.alert_id; - var alertText = getAlarmName(alertValue, ALERT_TYPES); - - switch (alertValue) { - case ALERT_TYPES.ALERT_LOW_INSULIN.value: - alarmRecord = alarmRecord.with_alarmType('low_insulin'); - break; - case ALERT_TYPES.ALERT_LOW_LIPO_CHARGE.value: - case ALERT_TYPES.ALERT_VERY_LOW_LIPO_CHARGE.value: - alarmRecord = alarmRecord.with_alarmType('low_power'); - alarmRecord = alarmRecord.with_payload({alert_name: alertText}); - break; - default: - alarmRecord = alarmRecord.with_alarmType('other'); - alarmRecord = alarmRecord.with_payload({alert_id: alertValue}); - } - } - - alarmRecord = alarmRecord.done(); - records.push(alarmRecord); - }); - - return records; - }; - - var buildCGMRecords = function(data, records) { - var cgmRecords = filterLogEntries([ - PUMP_LOG_RECORDS.LID_CGM_DATA, - PUMP_LOG_RECORDS.LID_CGM_CALIBRATION, - PUMP_LOG_RECORDS.LID_CGM_DATA_GX, - PUMP_LOG_RECORDS.LID_CGM_CALIBRATION_GX, - PUMP_LOG_RECORDS.LID_CGM_DATA_GXB - ], data.log_records); - - for (var b = 0; b < cgmRecords.length; ++b) { - var event = cgmRecords[b]; - var value = event.value; - var record = null; - var annotation = null; - - if (event.header_id === PUMP_LOG_RECORDS.LID_CGM_CALIBRATION.value || - event.header_id === PUMP_LOG_RECORDS.LID_CGM_CALIBRATION_GX.value) { - - record = cfg.builder.makeDeviceEventCalibration() - .with_value(value) - .with_deviceTime(event.deviceTime) - .with_units('mg/dL') // hard-coded in mg/dL - .set('index', event.index); - - if (event.header_id === PUMP_LOG_RECORDS.LID_CGM_CALIBRATION.value) { - // for G4 calibrations, we have some extra info to store in payload - record.with_payload({ - timestamp: event.timestamp, - calibration_timestamp: event.cal_timestamp, - current_display_value: event.current_display_value, - current_time: event.current_time - }); - } - - cfg.tzoUtil.fillInUTCInfo(record, event.jsDate); - record = record.done(); - - } else if ((event.header_id === PUMP_LOG_RECORDS.LID_CGM_DATA.value || - (event.header_id === PUMP_LOG_RECORDS.LID_CGM_DATA_GX.value && - event.type === CGM_GX_TYPE.FMR.value) || // not using calibration or backfill values - (event.header_id === PUMP_LOG_RECORDS.LID_CGM_DATA_GXB.value && - (event.type & CGM_GXB_TYPE.FMR.value || - event.type & CGM_GXB_TYPE.BACKFILL.value))) && - ((value >= 40 && value <= 400) || (value === 0)) ) { // HI/LO values are 0 - - record = cfg.builder.makeCBG(); - - if (event.status === CGM_STATUS.CGM_CALIBRATION.value || - event.status === CGM_STATUS.CGM_LO_CALIBRATION.value || - event.status === CGM_STATUS.CGM_HI_CALIBRATION.value || - event.type === CGM_GX_TYPE.CALIBRATION.value) { - // in response to calibration - record.with_payload({ - calibration_response: true - }); - } - - if(event.status === CGM_STATUS.CGM_HI_CALIBRATION.value || - event.status === CGM_STATUS.CGM_HI.value) { - value = 401; // value is larger than 400 - annotation = { - code: 'bg/out-of-range', - value: 'high', - threshold: 400 - }; - } - - if(event.status === CGM_STATUS.CGM_LO_CALIBRATION.value || - event.status === CGM_STATUS.CGM_LO.value) { - value = 39; // value is lower than 40 - annotation = { - code: 'bg/out-of-range', - value: 'low', - threshold: 40 - }; - } - - if (event.header_id === PUMP_LOG_RECORDS.LID_CGM_DATA_GXB.value) { - // use CGM timestamp inside the record as the event log timestamp - // is the time the backfill values were received by the pump - addTimestamp(event, event.timestamp); - } - - record.with_value(value) - .with_deviceTime(event.deviceTime) - .with_units('mg/dL') // hard-coded in mg/dL - .set('index', event.index); - - if (event.header_id === PUMP_LOG_RECORDS.LID_CGM_DATA_GX.value) { - record.with_payload({ - rate: event.rate, - rssi: event.rssi, - timestamp: event.timestamp, - transmitter_timestamp: event.transmitter_timestamp - }); - } - - if (event.header_id === PUMP_LOG_RECORDS.LID_CGM_DATA_GXB.value) { - // type is required for de-duping, and removed in the simulator - record.cgmType = getFlagNames(CGM_GXB_TYPE, event.type); - - record.with_payload({ - rate: event.rate, - rssi: event.rssi, - timestamp: event.timestamp, - type:record.cgmType, - }); - } - - cfg.tzoUtil.fillInUTCInfo(record, event.jsDate); - record = record.done(); - } - - if (record) { - if (annotation) { - annotate.annotateEvent(record, annotation); - } - records.push(record); - } - } - return records; - }; - - var tandemGetConfigInfo = function (cb, data) { - tandemCommandResponse(COMMANDS.VERSION_REQ, null, function (err, result) { - if (err) { - console.log(err); - cb(err, null); - } - else { - console.log('Tandem found: ', result); - if (data == null) { - data = {}; - } - data.deviceModel = result.payload.model_no.toString(); - data.pump_sn = result.payload.pump_sn; - data.firmware_version = parseInt(result.payload.arm_sw_ver); - - if (data.firmware_version >= 51000) { - // according to the specs, only firmware versions higher or equal to - // 51000 can handle CGM records. We also have e-mail confirmation from - // Tandem that this is how to determine if a pump is CGM-capable - cfg.deviceInfo.tags = cfg.deviceInfo.tags.concat('cgm'); - } - - let prefix = 'tandem'; - - tandemCommandResponse(COMMANDS.AA_INFO_REQ, null, function (err, pkt) { - if (err) { - debug('Not a Control-IQ pump'); - data.controlIQ = false; - } else { - debug('This is a Control-IQ pump'); - data.controlIQ = true; - data.closed_loop_enabled = pkt.payload.closed_loop_enabled; - } - - if (data.controlIQ) { - prefix += 'CIQ'; - } - - cfg.deviceInfo.deviceId = prefix + data.deviceModel + data.pump_sn; - cfg.builder.setDefaults({ deviceId: cfg.deviceInfo.deviceId }); - - // this is required for comparing device time against server time - var currentTime = {}; - addTimestamp(currentTime,result.payload.timestamp); - _.assign(cfg.deviceInfo, { - deviceTime: currentTime.deviceTime, - model: data.deviceModel, - serialNumber: data.pump_sn.toString(), - firmwareVersion: data.firmware_version, - }); - common.checkDeviceTime(cfg, function(err) { - return cb(err, data); - }); - }); - } - }); - }; - - return { - detect: function(deviceInfo, cb){ - debug('no detect function needed', arguments); - cb(null, deviceInfo); - }, - - setup: function (deviceInfo, progress, cb) { - debug('in setup!'); - progress(100); - cb(null, { stage: 'setup', deviceInfo: deviceInfo }); - }, - - connect: function (progress, data, cb) { - console.log('connecting'); - cfg.deviceComms.connect(data.deviceInfo, tandemPacketHandler, function (err) { - if(err) { - cb(err,null); - }else{ - cfg.deviceComms.flush(); - progress(100); - data.stage = 'connect'; - cb(null, data); - } - }); - }, - - getConfigInfo: function (progress, data, cb) { - debug('in getConfigInfo'); - data.stage = 'getConfigInfo'; - progress(100); - tandemGetConfigInfo(cb, data); - }, - - fetchData: function (progress, data, cb) { - debug('in fetchData'); - tandemFetch(progress, data, cb); - data.stage = 'fetchData'; - }, - - processData: function (progress, data, cb) { - debug('in processData'); - progress(100); - data.stage = 'processData'; - cb(null, data); - }, - - uploadData: function (progress, data, cb) { - data.stage = 'uploadData'; - progress(0); - - var postrecords = [], settings = null; - /* - Because pump shut-down interferes with BtUTC, anywhere - where a pump shut-down appears in records to be processed - we only attempt to process and upload the data following - the most recent device shut-down. - */ - if (!debugMode.isDebug) { - data.log_records = _.takeRightWhile(data.log_records, function(rec) { - if (rec.change_types && - _.includes(rec.change_types,'pump_shut_down')) { - debug('Most recent pump shut down:', rec); - return false; - } - return true; - }); - debug('Will process', data.log_records.length, 'log records.'); - } else { - debug('Log records:', data.log_records); - } - - if (!_.isEmpty(data.log_records)) { - // ordering below is important for simulator, e.g. - // buildTempBasalRecords needs to be before buildBasalRecords - postrecords = buildSettingsRecords(data, postrecords); - if (!_.isEmpty(postrecords)) { - settings = postrecords[0]; - } - postrecords = buildTimeChangeRecords(data, postrecords); - postrecords = buildBolusRecords(data, postrecords); - postrecords = buildTempBasalRecords(data, postrecords); - postrecords = buildBasalRecords(data, postrecords); - postrecords = buildControlModeRecords(data, postrecords); - postrecords = buildPumpSettingsOverrideRecords(data, postrecords); - postrecords = buildNewDayRecords(data, postrecords); - postrecords = buildBGRecords(data, postrecords); - postrecords = buildCartridgeChangeRecords(data, postrecords); - postrecords = buildSuspendRecords(data, postrecords); - postrecords = buildResumeRecords(data, postrecords); - postrecords = buildAlarmRecords(data, postrecords); - postrecords = buildCGMRecords(data, postrecords); - // sort by time for the simulator - postrecords = _.sortBy(postrecords, function(d) { return d.time; }); - } - else { - return cb(new Error('No records since last upload or most recent pump shut down; nothing to upload.')); - } - - var simulator = tandemSimulatorMaker.make({ - settings, - profiles: data.profiles, - builder: cfg.builder, - tzoUtil: cfg.tzoUtil - }); - - if (data.controlIQ) { - let firstControlMode = _.find(postrecords, o => o.type === 'control-mode'); - if (firstControlMode === undefined) { - // no control mode changes found - return cb(new Error('Not enough Control-IQ data for upload.')); - } else { - simulator.initControlMode(firstControlMode); - } - } - - for (var n = 0; n < postrecords.length; ++n) { - var datum = postrecords[n]; - switch (datum.type) { - case 'basal': - simulator.basal(datum); - break; - case 'control-mode': - simulator.controlMode(datum); - break; - case 'bolus': - simulator.bolus(datum); - break; - case 'deviceEvent': - if (datum.subType === 'timeChange') { - simulator.changeDeviceTime(datum); - } - else if(datum.subType === 'prime' || datum.subType === 'reservoirChange'){ - simulator.cartridgeChange(datum); - } - else if (datum.subType === 'status') { - if(datum.status === 'suspended') { - simulator.suspend(datum); - } - else if (datum.status === 'resumed') { - simulator.resume(datum); - } - } - else if (datum.subType === 'alarm'){ - simulator.alarm(datum); - } - else if (datum.subType === 'calibration'){ - simulator.calibration(datum); - } - else if (datum.subType === 'pumpSettingsOverride') { - simulator.pumpSettingsOverride(datum); - } - else { - debug('Unknown deviceEvent subType:', datum.subType); - } - break; - case 'pumpSettings': - simulator.pumpSettings(datum); - break; - case 'smbg': - simulator.smbg(datum); - break; - case 'wizard': - simulator.wizard(datum); - break; - case 'new-day': - simulator.newDay(datum); - break; - case 'temp-basal': - simulator.tempBasal(datum); - break; - case 'cbg': - simulator.cbg(datum); - break; - default: - debug('[Hand-off to simulator] Unhandled type!', datum.type); - } - } - simulator.finalize(); - - data.post_records = simulator.getEvents(); - - var sessionInfo = { - delta: {lastEndPosition: data.end_seq} , - deviceTags: cfg.deviceInfo.tags, - deviceTime: cfg.deviceInfo.deviceTime, - deviceManufacturers: cfg.deviceInfo.manufacturers, - deviceModel: cfg.deviceInfo.model, - deviceSerialNumber: cfg.deviceInfo.serialNumber, - deviceId: cfg.deviceInfo.deviceId, - start: sundial.utcDateString(), - timeProcessing: cfg.tzoUtil.type, - tzName: cfg.timezone, - version: cfg.version - }; - - cfg.api.upload.toPlatform( - data.post_records, - sessionInfo, - progress, - cfg.groupId, - function (err, result) { - if (err) { - debug('Error: ', err); - debug('Result: ', result); - progress(100); - return cb(err, data); - } else { - progress(100); - return cb(null, data); - } - } - ); - }, - - disconnect: function (progress, data, cb) { - debug('disconnect'); - progress(100); - data.disconnect = true; - cb(null, data); - }, - - cleanup: function (progress, data, cb) { - debug('in cleanup'); - progress(0); - cfg.deviceComms.clearPacketHandler(); - cfg.deviceComms.disconnect(function () { - progress(100); - data.stage = 'cleanup'; - cb(null, data); - }); - } - }; -}; diff --git a/lib/drivers/tandem/tandemSimulator.js b/lib/drivers/tandem/tandemSimulator.js deleted file mode 100644 index c5c9402223..0000000000 --- a/lib/drivers/tandem/tandemSimulator.js +++ /dev/null @@ -1,497 +0,0 @@ -/* - * == BSD2 LICENSE == - * Copyright (c) 2014, Tidepool Project - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the associated License, which is identical to the BSD 2-Clause - * License as published by the Open Source Initiative at opensource.org. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the License for more details. - * - * You should have received a copy of the License along with this program; if - * not, you can obtain one from Tidepool Project at tidepool.org. - * == BSD2 LICENSE == - */ - -var _ = require('lodash'); -var sundial = require('sundial'); -var util = require('util'); -var debug = require('bows')('TandemDriver'); - -var annotate = require('../../eventAnnotations'); -var common = require('../../commonFunctions'); - -/** - * Creates a new "simulator" for Tandem insulin pump data. The simulator has methods for events like - * - * cbg(), smbg(), basal(), bolus(), settingsChange(), etc. - * - * This simulator exists as an abstraction over the Tidepool APIs. It was written to simplify the conversion - * of static, "retrospective" audit logs from devices into events understood by the Tidepool platform. - * - * On the input side, you have events extracted from a Tandem insulin pump. They should be delivered to the simulator - * in time order. - * - * Once all input activities are collected, the simulator will have accumulated events that the Tidepool Platform - * will understand into a local "events" array. You can retrieve the events by calling `getEvents()` - * - * @param config - * @returns {*} - */ -exports.make = function(config){ - if (config == null) { - config = {}; - } - var events = []; - - var currBasal = null; - var currStatus = null; - var currTimestamp = null; - var currTempBasal = null; - var currCBG = null; - var currControlMode = null; - var currPumpSettingsOverride = null; - - function setCurrBasal(basal) { - currBasal = basal; - } - - function setCurrStatus(status) { - currStatus = status; - } - - function setCurrTempBasal(tempBasal) { - currTempBasal = tempBasal; - } - - function setCurrCBG(cbg) { - currCBG = cbg; - } - - function setCurrControlMode(status) { - debug('Control-IQ change: ', status.deviceTime, status.subType); - if (currBasal) { - if ((currBasal.time !== status.time) && - (status.subType === 'Closed loop' || (currControlMode && currControlMode.subType === 'Closed loop'))) { - // Closed loop mode changed in the middle of the basal, - // so we need to split it in two - var basal = _.clone(currBasal); - basal.time = status.time; - basal.deviceTime = status.deviceTime; - basal.timezoneOffset = status.timezoneOffset; - basal.conversionOffset = status.conversionOffset; - currBasal.duration = Date.parse(basal.time) - Date.parse(currBasal.time); - currBasal = currBasal.done(); - basal.previous = _.omit(currBasal, 'previous'); - events.push(currBasal); - setCurrBasal(basal); - } - - if (status.subType === 'Closed loop') { - if (currBasal.deliveryType === 'suspend') { - currBasal.rate = 0; - } - currBasal.deliveryType = 'automated'; - } else if (currBasal.deliveryType === 'automated') { - if (currBasal.rate === 0) { - currBasal.deliveryType = 'suspend'; - } else { - currBasal.deliveryType = 'scheduled'; - } - } - } - currControlMode = status; - } - - function setCurrPumpSettingsOverride(override) { - currPumpSettingsOverride = override; - } - - function ensureTimestamp(e){ - if (currTimestamp > e.time) { - throw new Error( - util.format('Timestamps must be in order. Current timestamp was[%s], but got[%j]', currTimestamp, e) - ); - } - currTimestamp = e.time; - return e; - } - - function simpleSimulate(e) { - ensureTimestamp(e); - events.push(e); - } - - function checkOverrideDuration(override) { - if (override.duration > 604800000) { - // pump was shut down and then suspended for a long time - override.duration = 0; - annotate.annotateEvent(override, 'pumpSettingsOverride/unknown-duration'); - } - } - - return { - alarm: function(event) { - if (currStatus !== null && currStatus.status === 'suspended') { - var type = event.alarmType; - if(type === 'occlusion' || type === 'auto_off' || type === 'no_insulin' || type === 'no_power') { - - var status = currStatus; - - if(status.payload !== undefined) { - status.payload.cause = type; - } - else { - status.payload = {cause: type}; - } - setCurrStatus(status); - } - } - simpleSimulate(event); - }, - initControlMode: function(event) { - /* we need to know what the Control-IQ control mode was - prior to the upload to tag the automated basals correctly */ - setCurrControlMode({ - deviceTime: 'Prior to upload', - subType: event.previous, - }); - }, - controlMode: function(event) { - ensureTimestamp(event); - setCurrControlMode(event); - }, - basal: function(event){ - ensureTimestamp(event); - - if (currControlMode && currControlMode.subType === 'Closed loop') { - if (event.deliveryType === 'suspend') { - event.rate = 0; - } - event.deliveryType = 'automated'; - } - - if (currBasal != null) { - - if(currBasal.deliveryType === 'temp') { - if (currTempBasal != null) { - currBasal.percent = currTempBasal.percent; - if(currBasal.isAssigned('payload')) { - currBasal.payload.duration = currTempBasal.duration; - }else{ - currBasal.payload = {duration : currTempBasal.duration}; - } - setCurrTempBasal(null); - } - } - - // ignore repeated broadcasts of events at the same time - // TODO: do a more thorough (deep equality-ish?) check for same-ness - if (currBasal.time !== event.time) { - if (!currBasal.isAssigned('duration')) { - currBasal.duration = Date.parse(event.time) - Date.parse(currBasal.time); - } - currBasal = currBasal.done(); - event.previous = _.omit(currBasal, 'previous'); - events.push(currBasal); - } - else { - return; - } - - if(currTempBasal && currTempBasal.subType === 'stop') { - // temp basal without a basal rate change (temp_rate_start) event - var tempBasal = _.clone(currTempBasal); - tempBasal.type = 'basal'; - delete tempBasal.subType; - tempBasal.deliveryType = 'temp'; - tempBasal.deviceId = currBasal.deviceId; - annotate.annotateEvent(tempBasal, 'tandem/basal/temp-without-rate-change'); - tempBasal.payload = { - duration : tempBasal.duration, - logIndices: tempBasal.index //to be removed after Jaeb study - }; - delete tempBasal.index; - - var suppressed = { - type: 'basal', - deliveryType: 'scheduled', - rate: currBasal.rate - }; - - tempBasal.previous = _.omit(currBasal, 'previous'); - tempBasal.suppressed = suppressed; - // actual temp basal duration is based on basal rate change event (temp_rate_end) - tempBasal.duration = Date.parse(event.time) - Date.parse(tempBasal.time); - delete tempBasal.time_left; - events.push(tempBasal); - setCurrTempBasal(null); - event.previous = _.omit(tempBasal, 'previous'); - } - } - - setCurrBasal(event); - }, - finalize: function() { - if (currBasal != null) { - if (currBasal.deliveryType !== 'scheduled') { - - if (currBasal.deliveryType === 'temp') { - if(!currBasal.isAssigned('duration')) { - if(currTempBasal != null) { - if (currTempBasal.time_left) { - // temp basal was cancelled - currBasal.duration = currTempBasal.duration - currTempBasal.time_left; - }else{ - currBasal.duration = currTempBasal.duration; - } - } - else { - currBasal.duration = 0; - annotate.annotateEvent(currBasal, 'basal/unknown-duration'); - } - } - currBasal = currBasal.done(); - } - else { - if (!currBasal.isAssigned('duration')) { - currBasal.duration = 0; - annotate.annotateEvent(currBasal, 'basal/unknown-duration'); - currBasal = currBasal.done(); - } - else { - currBasal = currBasal.done(); - } - } - } - else if (config.settings != null) { - currBasal.with_scheduleName(_.find( - config.profiles, - {idp: currBasal.payload.personalProfileIndex} - ).name); - currBasal = common.finalScheduledBasal(currBasal, config.settings, 'tandem'); - } - else { - currBasal.with_duration(0); - annotate.annotateEvent(currBasal, 'basal/unknown-duration'); - currBasal = currBasal.done(); - } - - events.push(currBasal); - } - - if (currPumpSettingsOverride && currPumpSettingsOverride.overrideType !== 'normal') { - if (!currPumpSettingsOverride.isAssigned('duration')) { - // If the pumpSettingsOverride is still active at time of upload, then duration is from start to time of upload. - // The next upload should update the duration of this record to either the actual end or the new time of upload. - currPumpSettingsOverride.duration = Date.parse(config.settings.time) - Date.parse(currPumpSettingsOverride.time); - checkOverrideDuration(currPumpSettingsOverride); - annotate.annotateEvent(currPumpSettingsOverride, 'tandem/pumpSettingsOverride/estimated-duration'); - } - events.push(currPumpSettingsOverride.done()); - } - }, - tempBasal: function(event) { - if(event.subType === 'start') { - setCurrTempBasal(event); - } - else if(event.subType === 'stop') { - // Cancelled temp basal has two 'stop's. We're interested in the first, - // so make sure we're not overwriting the first 'stop' - if(currTempBasal != null && currTempBasal.subType === 'start') { - var tempBasal = _.clone(currTempBasal); - tempBasal.subType = 'stop'; - tempBasal.time_left = event.time_left; - setCurrTempBasal(tempBasal); - } - } - }, - newDay: function(event) { - ensureTimestamp(event); - if (currBasal !== null) { - if (currStatus !== null && currStatus.status === 'suspended') { - return; - // only insert the new day event if the basal rate is not suspended - } - - if (currBasal.time !== event.time) { - if (!currBasal.isAssigned('duration')) { - currBasal.duration = Date.parse(event.time) - Date.parse(currBasal.time); - } - - if(currBasal.deliveryType === 'temp') { - if (currTempBasal != null) { - currBasal.percent = currTempBasal.percent; - if(currBasal.isAssigned('payload')) { - currBasal.payload.duration = currTempBasal.duration; - }else{ - currBasal.payload = {duration : currTempBasal.duration}; - } - } - var suppressed = currBasal.suppressed; - event.deliveryType = 'temp'; - event.percent = currBasal.percent; - event.rate = currBasal.rate; - event.set('suppressed', suppressed); - } - currBasal = currBasal.done(); - - event.set('type', 'basal'); - event.previous = _.omit(currBasal, 'previous'); - event.with_payload(currBasal.payload); - annotate.annotateEvent(event, 'tandem/basal/fabricated-from-new-day'); - - if (currControlMode && currControlMode.subType === 'Closed loop') { - if (event.deliveryType === 'suspend') { - event.rate = 0; - } - event.deliveryType = 'automated'; - } - - events.push(currBasal); - setCurrBasal(event); - } - } - - // split multi-day overrides into smaller segments - if (currPumpSettingsOverride && currPumpSettingsOverride.overrideType !== 'normal') { - currPumpSettingsOverride.duration = Date.parse(event.time) - Date.parse(currPumpSettingsOverride.time); - checkOverrideDuration(currPumpSettingsOverride); - events.push(currPumpSettingsOverride.done()); - - var modeChange = config.builder.makeDeviceEventPumpSettingsOverride() - .with_overrideType(currPumpSettingsOverride.overrideType) - .with_deviceTime(event.deviceTime) - .set('index', event.index); - - config.tzoUtil.fillInUTCInfo(modeChange, sundial.parseFromFormat(event.deviceTime)); - annotate.annotateEvent(modeChange, 'tandem/pumpSettingsOverride/fabricated-from-new-day'); - setCurrPumpSettingsOverride(modeChange); - } - }, - bolus: function(event) { - simpleSimulate(event); - }, - changeDeviceTime: function(event) { - simpleSimulate(event); - }, - cartridgeChange: function(event) { - simpleSimulate(event); - }, - pumpSettingsOverride: function(event) { - if (currPumpSettingsOverride) { - currPumpSettingsOverride.duration = Date.parse(event.time) - Date.parse(currPumpSettingsOverride.time); - checkOverrideDuration(currPumpSettingsOverride); - if (currPumpSettingsOverride.overrideType !== 'normal') { - events.push(currPumpSettingsOverride.done()); - } - } else if (event.previousOverride && events[0]) { - // there was an override active at the beginning, so - // we use the time of the first event for this overrride - - // TODO: when we switch to platform, update duration of the last override - // of the previous upload if it was still active - - var duration = Date.parse(event.time) - Date.parse(events[0].time); - - if (duration < 604800000) { - var modeChange = config.builder.makeDeviceEventPumpSettingsOverride() - .with_overrideType(event.previousOverride) - .with_deviceTime(events[0].deviceTime) - .with_duration(duration) - .set('index', events[0].index); - annotate.annotateEvent(modeChange, 'tandem/pumpSettingsOverride/estimated-duration'); - - config.tzoUtil.fillInUTCInfo(modeChange, sundial.parseFromFormat(events[0].deviceTime)); - events.push(modeChange.done()); - } else { - debug('Override duration is longer than 7-day limit!'); - } - - delete event.previousOverride; - } - setCurrPumpSettingsOverride(event); - }, - pumpSettings: function(event) { - simpleSimulate(event); - }, - smbg: function(event) { - simpleSimulate(event); - }, - cbg: function(event) { - if (currCBG && (event.value === currCBG.value)) { - // backfill values can cause duplicates, so we remove any dupes - // that happen between the Five Minute Reading (FMR) intervals - var duration = Date.parse(event.time) - Date.parse(currCBG.time); - // according to Tandem, backfill timestamps can be around 30 seconds - // off, so we use 2 minutes to add a bit of a buffer - if (duration < (2 * sundial.MIN_TO_MSEC)) { - return; - } - } - delete event.cgmType; - simpleSimulate(event); - setCurrCBG(event); - }, - calibration: function(event) { - simpleSimulate(event); - }, - suspend: function(event) { - if (currStatus != null && currStatus.status === 'suspended') { - return; - } - simpleSimulate(event); - setCurrStatus(event); - }, - resume: function(event) { - ensureTimestamp(event); - if (currStatus !== null) { - event.previous = _.omit(currStatus, 'previous'); - } - events.push(event.done()); - setCurrStatus(event); - }, - wizard: function(event) { - simpleSimulate(event); - }, - getEvents: function() { - function filterOutZeroBoluses() { - return _.filter(events, function(event) { - // we include the index on all objects to be able to sort accurately in - // pump-event order despite date & time settings changes, but it's not - // part of our data model, so we delete before uploading - delete event.index; - if (event.type === 'bolus') { - if (event.normal === 0 && !event.expectedNormal) { - return false; - } - else { - return true; - } - } - else if (event.type === 'wizard') { - var bolus = event.bolus || null; - if (bolus != null) { - if (bolus.normal === 0 && !bolus.expectedNormal) { - return false; - } - else { - return true; - } - } - } - return true; - }); - } - // because we have to wait for the *next* basal to determine the duration of a current - // basal, basal events get added to `events` out of order wrt other events - // (although within their own type all the basals are always in order) - // end result: we have to sort events again before we try to upload them - var orderedEvents = _.sortBy(filterOutZeroBoluses(), function(e) { return e.time; }); - - return orderedEvents; - } - }; -}; diff --git a/locales/en/translation.missing.json b/locales/en/translation.missing.json index 57713aff54..ca14b3034d 100644 --- a/locales/en/translation.missing.json +++ b/locales/en/translation.missing.json @@ -3,31 +3,6 @@ "Plug in meter with cable and set meter to": "Plug in meter with cable and set meter to", "PC Link Mode": "PC Link Mode", "Make sure the meter is switched off and plug in with micro-USB cable": "Make sure the meter is switched off and plug in with micro-USB cable", - "We couldn't log you in. Try again in a few minutes.": "We couldn't log you in. Try again in a few minutes.", - "Your device doesn't appear to be connected. You can try using another USB cable, as some USB cables are designed to carry a signal for power only.": "Your device doesn't appear to be connected. You can try using another USB cable, as some USB cables are designed to carry a signal for power only.", - "Sorry, we don't support the Libre 2 yet": "Sorry, we don't support the Libre 2 yet", - "Please correct the date/time on the linked pump.": "Please correct the date/time on the linked pump.", "Turn meter on and make sure Bluetooth is switched on": "Turn meter on and make sure Bluetooth is switched on", - "Your device doesn't appear to be connected. You can try using another USB cable, as some USB cables are designed to carry a signal for power only.": "Your device doesn't appear to be connected. You can try using another USB cable, as some USB cables are designed to carry a signal for power only." - "Select CSV file downloaded from LibreView": "Select CSV file downloaded from LibreView", - "Preparing file": "Preparing file", - "We couldn't communicate with the meter. You may need to give Uploader": "We couldn't communicate with the meter. You may need to give Uploader", - "Couldn't connect to device.": "Couldn't connect to device.", - "Clinic Workspace": "Clinic Workspace", - "To manage your clinic workspaces and view patient invites": "To manage your clinic workspaces and view patient invites", - "login to your account in Tidepool Web": "login to your account in Tidepool Web", - "Want to use Tidepool for your private data?": "Want to use Tidepool for your private data?", - "Go to Private Workspace": "Go to Private Workspace", - "Go to Workspace": "Go to Workspace", - "clinic": "clinic", - "Can’t find a patient you are looking for?": "Can’t find a patient you are looking for?", - "Change Workspace": "Change Workspace", - "To manage {{workspace}} workspace and view patient invites, go to": "To manage {{workspace}} workspace and view patient invites, go to", - "Tidepool Web": "Tidepool Web", - "Something went wrong while creating patient account.": "Something went wrong while creating patient account.", - "Email address is already associated with another account.": "Email address is already associated with another account.", - "Private Workspace": "Private Workspace" - "Uploading Libre 2 data?": "Uploading Libre 2 data?", - "We couldn't log you in. Try again in a few minutes.": "We couldn't log you in. Try again in a few minutes.", - "Please set your LibreView date format to Year-Month-Day and export the CSV again": "Please set your LibreView date format to Year-Month-Day and export the CSV again" -} + "Plug in PDA with micro-USB": "Plug in PDA with micro-USB" +} \ No newline at end of file From e98a41a6fd5eab299bd78cb3281e6ae19a15515f Mon Sep 17 00:00:00 2001 From: Gerrit Niezen Date: Wed, 22 Jun 2022 12:11:29 +0100 Subject: [PATCH 08/20] add Tandem driver as private submodule --- .gitmodules | 3 +++ lib/drivers/tandem | 1 + 2 files changed, 4 insertions(+) create mode 100644 .gitmodules create mode 160000 lib/drivers/tandem diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000000..b903e9c8d3 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "lib/drivers/tandem"] + path = lib/drivers/tandem + url = git@github.com:tidepool-org/tandem-driver.git diff --git a/lib/drivers/tandem b/lib/drivers/tandem new file mode 160000 index 0000000000..e1dec0c59c --- /dev/null +++ b/lib/drivers/tandem @@ -0,0 +1 @@ +Subproject commit e1dec0c59c9920865b23d02768ae639ea0eb60f3 From aba526631e631c33bda265eec061fd72f67f142b Mon Sep 17 00:00:00 2001 From: Gerrit Niezen Date: Wed, 22 Jun 2022 13:35:45 +0100 Subject: [PATCH 09/20] fix linting --- lib/core/device.js | 6 ++++-- locales/en/translation.missing.json | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/core/device.js b/lib/core/device.js index 357d2779a6..ab8f9e36bd 100644 --- a/lib/core/device.js +++ b/lib/core/device.js @@ -64,6 +64,7 @@ const device = { let tandemDriver; try { + // eslint-disable-next-line global-require, import/no-unresolved tandemDriver = require('../drivers/tandem/tandemDriver'); } catch (e) { device.log('Tandem driver is only available to Tidepool developers.'); @@ -381,9 +382,10 @@ device.getDriverManifest = (driverId) => { device.detectHelper = (driverId, options, cb) => { const dm = device.createDriverManager(driverId, options); if (dm == null) { - return cb(new Error('Driver not available.')); + cb(new Error('Driver not available.')); + } else { + dm.detect(driverId, cb); } - dm.detect(driverId, cb); }; device.createDriverManager = (driverId, options) => { diff --git a/locales/en/translation.missing.json b/locales/en/translation.missing.json index ca14b3034d..f10bd59077 100644 --- a/locales/en/translation.missing.json +++ b/locales/en/translation.missing.json @@ -4,5 +4,5 @@ "PC Link Mode": "PC Link Mode", "Make sure the meter is switched off and plug in with micro-USB cable": "Make sure the meter is switched off and plug in with micro-USB cable", "Turn meter on and make sure Bluetooth is switched on": "Turn meter on and make sure Bluetooth is switched on", - "Plug in PDA with micro-USB": "Plug in PDA with micro-USB" + "Select CSV file downloaded from LibreView": "Select CSV file downloaded from LibreView" } \ No newline at end of file From daa2da525a57c34aee8114b20ae92c02bc197922 Mon Sep 17 00:00:00 2001 From: Gerrit Niezen Date: Wed, 22 Jun 2022 14:49:54 +0100 Subject: [PATCH 10/20] check out submodule in circleci --- .circleci/config.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index 125eb9c92c..c0f6da692b 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -11,6 +11,8 @@ jobs: xcode: '12.5.1' steps: - checkout + - run: git submodule sync + - run: git submodule update --init - run: echo 'export PATH=${PATH}:${HOME}/${CIRCLE_PROJECT_REPONAME}/node_modules/.bin' >> $BASH_ENV - restore_cache: key: dependency-cache-{{ checksum "package.json" }} From d0d86f99b951fb9a1768030d3be7c70343fa768c Mon Sep 17 00:00:00 2001 From: Gerrit Niezen Date: Wed, 22 Jun 2022 14:50:08 +0100 Subject: [PATCH 11/20] v2.45.1-tandem-private.1 --- app/package.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/package.json b/app/package.json index ca6a2af250..69347d25d4 100644 --- a/app/package.json +++ b/app/package.json @@ -1,7 +1,7 @@ { "name": "tidepool-uploader", "productName": "tidepool-uploader", - "version": "2.45.1", + "version": "2.45.1-tandem-private.1", "description": "Tidepool Project Universal Uploader", "main": "./main.prod.js", "author": { diff --git a/package.json b/package.json index c53cb97866..eb7a7e6ffa 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tidepool-uploader", - "version": "2.45.1", + "version": "2.45.1-tandem-private.1", "description": "Tidepool Project Universal Uploader", "private": true, "main": "main.prod.js", From 8225f14d24e3cd7428f0ad4eb70f617fc485ab60 Mon Sep 17 00:00:00 2001 From: Gerrit Niezen Date: Wed, 22 Jun 2022 16:05:54 +0100 Subject: [PATCH 12/20] add windows build to circleci --- .circleci/config.yml | 36 ++++++++++++++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index c0f6da692b..beb68495cc 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,6 +1,6 @@ version: 2 jobs: - build: + build-macos: working_directory: ~/tidepool-org/chrome-uploader parallelism: 1 # CircleCI 2.0 does not support environment variables that refer to each other the same way as 1.0 did. @@ -38,13 +38,45 @@ jobs: - run: yarn test # Package - run: if [ -z "$CIRCLE_PR_NUMBER" ]; then yarn package; else echo "Forked repo; no package built."; fi + build-windows: + machine: + # see https://circleci.com/developer/machine/image/windows-server-2019 for details + image: windows-server-2019-vs2019:stable + resource_class: windows.medium + shell: bash.exe + steps: + - checkout + - run: git submodule sync + - run: git submodule update --init + - restore_cache: + key: dependency-cache-{{ checksum "package.json" }} + # yarn and nvm should already be installed on this image + - run: nvm install v12.13.0 + - run: node -v + - run: yarn config set cache-folder ~/.cache/yarn + - run: yarn --frozen-lockfile + - save_cache: + key: dependency-cache-{{ checksum "package.json" }} + paths: + - ~/.cache/yarn + - ./node_modules + # Test + - run: yarn lint + - run: yarn test + # Package + - run: if [ -z "$CIRCLE_PR_NUMBER" ]; then yarn package; else echo "Forked repo; no package built."; fi + - run: if [ -z "$APPVEYOR_REPO_TAG" ]; then yarn av-whitelist; else echo "Not a tagged release."; fi # runs build for all branches and all tags starting with v. workflows: version: 2 build-release: jobs: - - build: + - build-macos: + filters: + tags: + only: /^v.*/ + - build-windows: filters: tags: only: /^v.*/ From dd09a2c4c5a225a95a7f492ded2f29e38abda9e2 Mon Sep 17 00:00:00 2001 From: Gerrit Niezen Date: Wed, 22 Jun 2022 16:19:51 +0100 Subject: [PATCH 13/20] actually use node v12.13.0 --- .circleci/config.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index beb68495cc..cfb8101f5e 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -52,6 +52,7 @@ jobs: key: dependency-cache-{{ checksum "package.json" }} # yarn and nvm should already be installed on this image - run: nvm install v12.13.0 + - run: nvm use v12.13.0 - run: node -v - run: yarn config set cache-folder ~/.cache/yarn - run: yarn --frozen-lockfile From 87668990060d7adb06f177982b9b7e2b8a060ef1 Mon Sep 17 00:00:00 2001 From: Gerrit Niezen Date: Wed, 22 Jun 2022 16:27:56 +0100 Subject: [PATCH 14/20] install yarn for node v12.13.0 on windows build --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index cfb8101f5e..4368813b46 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -50,10 +50,10 @@ jobs: - run: git submodule update --init - restore_cache: key: dependency-cache-{{ checksum "package.json" }} - # yarn and nvm should already be installed on this image - run: nvm install v12.13.0 - run: nvm use v12.13.0 - run: node -v + - run: npm install --global yarn - run: yarn config set cache-folder ~/.cache/yarn - run: yarn --frozen-lockfile - save_cache: From 27c63a8bd3fa3521463d69f4d539771a0ed46682 Mon Sep 17 00:00:00 2001 From: Gerrit Niezen Date: Wed, 22 Jun 2022 16:50:57 +0100 Subject: [PATCH 15/20] try powershell for yarn lint --- .circleci/config.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 4368813b46..ca1f0d3b9c 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -62,7 +62,9 @@ jobs: - ~/.cache/yarn - ./node_modules # Test - - run: yarn lint + - run: + command: yarn lint + shell: powershell.exe - run: yarn test # Package - run: if [ -z "$CIRCLE_PR_NUMBER" ]; then yarn package; else echo "Forked repo; no package built."; fi From 494854318da973ed51e172e826a9889ac8121b9b Mon Sep 17 00:00:00 2001 From: Gerrit Niezen Date: Thu, 23 Jun 2022 15:02:40 +0100 Subject: [PATCH 16/20] ignore linebreak style, specify node for eslint --- .eslintrc | 1 + lib/core/.eslintrc | 1 + lib/drivers/abbott/.eslintrc | 3 ++- lib/drivers/bayer/.eslintrc | 1 + lib/drivers/dexcom/.eslintrc | 1 + lib/drivers/i-sens/.eslintrc | 1 + lib/drivers/insulet/.eslintrc | 1 + lib/drivers/medtronic600/.eslintrc | 1 + lib/drivers/onetouch/.eslintrc | 1 + lib/drivers/roche/.eslintrc | 1 + lib/drivers/trividia/.eslintrc | 1 + lib/drivers/weitai/.eslintrc | 1 + package.json | 2 +- 13 files changed, 14 insertions(+), 2 deletions(-) diff --git a/.eslintrc b/.eslintrc index 39e02ebea5..95f78dc0ab 100644 --- a/.eslintrc +++ b/.eslintrc @@ -49,6 +49,7 @@ "no-use-before-define": 0, "max-len": ["warn", { "code": 100 }], "prefer-destructuring": "warn", + "linebreak-style": 0, "no-buffer-constructor": "warn" }, "plugins": [ diff --git a/lib/core/.eslintrc b/lib/core/.eslintrc index 8768791de3..35ec57a548 100644 --- a/lib/core/.eslintrc +++ b/lib/core/.eslintrc @@ -18,6 +18,7 @@ "operator-linebreak": "warn", "no-else-return": "warn", "object-curly-newline": "warn", + "linebreak-style": 0, "implicit-arrow-linebreak": "warn" }, "settings": { diff --git a/lib/drivers/abbott/.eslintrc b/lib/drivers/abbott/.eslintrc index 8a61749ede..a49f939fcb 100644 --- a/lib/drivers/abbott/.eslintrc +++ b/lib/drivers/abbott/.eslintrc @@ -20,7 +20,8 @@ "object-curly-newline": "warn", "implicit-arrow-linebreak": "warn", "no-param-reassign": "warn", - "no-restricted-syntax": "warn" + "no-restricted-syntax": "warn", + "linebreak-style": 0 }, "overrides": [ { diff --git a/lib/drivers/bayer/.eslintrc b/lib/drivers/bayer/.eslintrc index 6b72abaff3..2a7c747604 100644 --- a/lib/drivers/bayer/.eslintrc +++ b/lib/drivers/bayer/.eslintrc @@ -18,6 +18,7 @@ "operator-linebreak": "warn", "no-else-return": "warn", "object-curly-newline": "warn", + "linebreak-style": 0, "implicit-arrow-linebreak": "warn" }, "overrides": [ diff --git a/lib/drivers/dexcom/.eslintrc b/lib/drivers/dexcom/.eslintrc index 1e95878a3a..e885a9885a 100644 --- a/lib/drivers/dexcom/.eslintrc +++ b/lib/drivers/dexcom/.eslintrc @@ -11,6 +11,7 @@ "allowForLoopAfterthoughts": true }], "arrow-parens": "warn", + "linebreak-style": 0, "operator-linebreak": "warn" }, "settings": { diff --git a/lib/drivers/i-sens/.eslintrc b/lib/drivers/i-sens/.eslintrc index 1e95878a3a..9da3535361 100644 --- a/lib/drivers/i-sens/.eslintrc +++ b/lib/drivers/i-sens/.eslintrc @@ -7,6 +7,7 @@ "rules": { "max-len": "warn", "consistent-return": "warn", + "linebreak-style": 0, "no-plusplus": ["error", { "allowForLoopAfterthoughts": true }], diff --git a/lib/drivers/insulet/.eslintrc b/lib/drivers/insulet/.eslintrc index ed940cb648..906f2cd43a 100644 --- a/lib/drivers/insulet/.eslintrc +++ b/lib/drivers/insulet/.eslintrc @@ -17,6 +17,7 @@ "lines-between-class-members": "warn", "function-paren-newline": "warn", "comma-style": "warn", + "linebreak-style": 0, "implicit-arrow-linebreak": "warn" }, "settings": { diff --git a/lib/drivers/medtronic600/.eslintrc b/lib/drivers/medtronic600/.eslintrc index a9a779503d..7169c30385 100644 --- a/lib/drivers/medtronic600/.eslintrc +++ b/lib/drivers/medtronic600/.eslintrc @@ -17,6 +17,7 @@ "lines-between-class-members": "warn", "function-paren-newline": "warn", "comma-style": "warn", + "linebreak-style": 0, "implicit-arrow-linebreak": "warn" }, "settings": { diff --git a/lib/drivers/onetouch/.eslintrc b/lib/drivers/onetouch/.eslintrc index b69fa324c5..888f0196dc 100644 --- a/lib/drivers/onetouch/.eslintrc +++ b/lib/drivers/onetouch/.eslintrc @@ -17,6 +17,7 @@ "max-classes-per-file": "warn", "operator-linebreak": "warn", "arrow-parens": "warn", + "linebreak-style": 0, "object-curly-newline": "warn" }, "overrides": [ diff --git a/lib/drivers/roche/.eslintrc b/lib/drivers/roche/.eslintrc index c5e7ff4a75..13e20ed87d 100644 --- a/lib/drivers/roche/.eslintrc +++ b/lib/drivers/roche/.eslintrc @@ -9,6 +9,7 @@ "allowForLoopAfterthoughts": true }], "arrow-parens": "warn", + "linebreak-style": 0, "operator-linebreak": "warn" }, "settings": { diff --git a/lib/drivers/trividia/.eslintrc b/lib/drivers/trividia/.eslintrc index 484a8251e3..d411a4ea00 100644 --- a/lib/drivers/trividia/.eslintrc +++ b/lib/drivers/trividia/.eslintrc @@ -6,6 +6,7 @@ }, "rules": { "max-len": "warn", + "linebreak-style": 0, "no-plusplus": ["error", { "allowForLoopAfterthoughts": true }], diff --git a/lib/drivers/weitai/.eslintrc b/lib/drivers/weitai/.eslintrc index 77187fbae9..96d53aa81e 100644 --- a/lib/drivers/weitai/.eslintrc +++ b/lib/drivers/weitai/.eslintrc @@ -10,6 +10,7 @@ "no-continue": "warn", "prefer-template": "warn", "no-param-reassign": "warn", + "linebreak-style": 0, "no-restricted-syntax": "warn", "no-plusplus": ["error", { "allowForLoopAfterthoughts": true diff --git a/package.json b/package.json index eb7a7e6ffa..158e87b349 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ "serve-docs": "./node_modules/.bin/gitbook serve", "test": "cross-env NODE_ENV=test BABEL_DISABLE_CACHE=1 jest", "test-all": "npm run lint && npm run test && npm run build", - "lint": "./node_modules/eslint/bin/eslint.js --cache --format=node_modules/eslint-formatter-pretty .", + "lint": "node ./node_modules/eslint/bin/eslint.js --cache --format=node_modules/eslint-formatter-pretty .", "lint-fix": "npm run lint -- --fix", "build-main": "yarn build-main-quiet --progress --profile --colors", "build-main-quiet": "cross-env NODE_ENV=production webpack --config webpack.config.main.prod.babel.js", From 1902f630221faf2bb4364eb3504fcbe463e2d655 Mon Sep 17 00:00:00 2001 From: Gerrit Niezen Date: Thu, 23 Jun 2022 15:25:13 +0100 Subject: [PATCH 17/20] also fix eslint files in tests --- test/.eslintrc | 1 + test/lib/medtronic600/.eslintrc | 1 + 2 files changed, 2 insertions(+) diff --git a/test/.eslintrc b/test/.eslintrc index b60e2474e9..fdd846ba69 100755 --- a/test/.eslintrc +++ b/test/.eslintrc @@ -8,6 +8,7 @@ ], "rules": { "no-unused-expressions": 0, + "linebreak-style": 0, "jest/no-disabled-tests": "warn", "jest/no-focused-tests": "error", "jest/no-identical-title": "error" diff --git a/test/lib/medtronic600/.eslintrc b/test/lib/medtronic600/.eslintrc index 5f63599f21..1f6a1cd12b 100644 --- a/test/lib/medtronic600/.eslintrc +++ b/test/lib/medtronic600/.eslintrc @@ -5,6 +5,7 @@ "ecmaVersion": 6 }, "rules": { + "linebreak-style": 0, "no-plusplus": ["error", { "allowForLoopAfterthoughts": true }], From 7fde0121659ee93613c16d9cfce5a117fb8bee57 Mon Sep 17 00:00:00 2001 From: Gerrit Niezen Date: Thu, 23 Jun 2022 15:50:37 +0100 Subject: [PATCH 18/20] use CIRCLE_TAG to check for tagged release --- .circleci/config.yml | 6 ++---- appveyor.yml | 49 -------------------------------------------- 2 files changed, 2 insertions(+), 53 deletions(-) delete mode 100755 appveyor.yml diff --git a/.circleci/config.yml b/.circleci/config.yml index ca1f0d3b9c..ba78afd745 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -62,13 +62,11 @@ jobs: - ~/.cache/yarn - ./node_modules # Test - - run: - command: yarn lint - shell: powershell.exe + - run: yarn lint - run: yarn test # Package - run: if [ -z "$CIRCLE_PR_NUMBER" ]; then yarn package; else echo "Forked repo; no package built."; fi - - run: if [ -z "$APPVEYOR_REPO_TAG" ]; then yarn av-whitelist; else echo "Not a tagged release."; fi + - run: if [ -n "$CIRCLE_TAG" ]; then yarn av-whitelist; else echo "Not a tagged release."; fi # runs build for all branches and all tags starting with v. workflows: diff --git a/appveyor.yml b/appveyor.yml deleted file mode 100755 index c1423c92ac..0000000000 --- a/appveyor.yml +++ /dev/null @@ -1,49 +0,0 @@ -image: Visual Studio 2017 - -environment: - APPVEYOR_CACHE_ENTRY_ZIP_ARGS: -t7z - matrix: - - nodejs_version: 12.13.0 - -cache: - - "%LOCALAPPDATA%\\Yarn" - -platform: - - x64 - -matrix: - fast_finish: true - -build: off - -version: '{build}' - -shallow_clone: true - -clone_depth: 1 - -skip_branch_with_pr: true - -install: - - ps: $env:package_version = (Get-Content -Raw -Path package.json | ConvertFrom-Json).version - - ps: Update-AppveyorBuild -Version "$env:package_version-$env:APPVEYOR_BUILD_NUMBER" - - ps: Install-Product node $env:nodejs_version $env:platform - - set CI=true - - yarn --frozen-lockfile - -test_script: - - node --version - - yarn lint - - yarn test - - cmd: IF /I "%ROLLBAR_POST_TOKEN%"=="" ( ECHO Missing Rollbar POST token; no package built. ) ELSE ( yarn package ) - # submit uploader release to antivirus vendors if tagged - - cmd: IF /I %APPVEYOR_REPO_TAG% == True ( yarn av-whitelist ) ELSE ( echo Not a tagged release. ) - -notifications: - - provider: Slack - incoming_webhook: - # webhook URL is encrypted so can be stored in public repo - secure: PkO2WgPHg816yzPsyT47fxuBlM5bGhsNUn+VaNmLU6zCC31xwttRteKybldC6Jp1kdlWALyRdxt7WYsWDrkrAG3tHYSHSo7RDhea4qLuwjA= - on_build_success: true - on_build_failure: true - on_build_status_changed: false From 3f15f6e818738b2a68486164b4f300dc1b1d2acb Mon Sep 17 00:00:00 2001 From: Gerrit Niezen Date: Thu, 23 Jun 2022 16:09:53 +0100 Subject: [PATCH 19/20] v2.45.1-tandem-private.2 --- app/package.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/package.json b/app/package.json index 69347d25d4..e4993f198d 100644 --- a/app/package.json +++ b/app/package.json @@ -1,7 +1,7 @@ { "name": "tidepool-uploader", "productName": "tidepool-uploader", - "version": "2.45.1-tandem-private.1", + "version": "2.45.1-tandem-private.2", "description": "Tidepool Project Universal Uploader", "main": "./main.prod.js", "author": { diff --git a/package.json b/package.json index 158e87b349..09547383f0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tidepool-uploader", - "version": "2.45.1-tandem-private.1", + "version": "2.45.1-tandem-private.2", "description": "Tidepool Project Universal Uploader", "private": true, "main": "main.prod.js", From d76f87a4efb1580634b6ceddbe7b248aad2ec9b0 Mon Sep 17 00:00:00 2001 From: Gerrit Niezen Date: Mon, 27 Jun 2022 10:26:13 +0100 Subject: [PATCH 20/20] update README --- README.md | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index c2efdcb216..1fcb2fadbc 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,6 @@ [![CircleCI](https://circleci.com/gh/tidepool-org/uploader/tree/master.svg?style=shield)](https://circleci.com/gh/tidepool-org/uploader/tree/master) -[![Build status](https://ci.appveyor.com/api/projects/status/jj71uykxm27s3mla/branch/master?svg=true)](https://ci.appveyor.com/project/krystophv/uploader/branch/master) This is an [Electron App](https://electron.atom.io/) that acts as an uploader client for Tidepool. It is intended to allow you to plug diabetes devices into your computer's USB port, read the data stored on them, and upload a standardized version of the data to the Tidepool cloud. @@ -89,7 +88,7 @@ All debug options are turned *off* by default in `config/local.sh`. ## Tests -To run the tests in this repository as they are run on CircleCI and Appveyor use: +To run the tests in this repository as they are run on CircleCI use: ```bash $ yarn test @@ -103,7 +102,7 @@ $ yarn test We use [ESLint](http://eslint.org/) to lint our JavaScript code. We try to use the same linting options across all our client apps, but there are a few exceptions in this application, noted with comments in the `.eslintrc` configuration file. -To run the linter (which also runs on CircleCI and Appveyor with every push, along with `npm test`), use: +To run the linter (which also runs on CircleCI with every push, along with `npm test`), use: ``` $ npm run lint @@ -124,7 +123,7 @@ See [this guidance on our use of GitBook at Tidepool](http://developer.tidepool. This section is Tidepool-specific. Release management and application updates are handled via the Github provider in the `electron-builder` project. The recommended workflow for a new production release is as follows: 1. When you're working on what might become a new release, increment the version number in `package.json` and `app/package.json` and commit/push (on the branch) -1. The CI server(s) will create a draft release in Github with the title of the version from the `package.json` file and will automatically attach the distribution artifacts to that draft (drafts are not publicly visible) +1. The CI server will create a draft release in Github with the title of the version from the `package.json` file and will automatically attach the distribution artifacts to that draft (drafts are not publicly visible) 1. When your pull request is approved and merged to `master`, go to the draft release and type in the version for the tag name, ensure that you're targeting the `master` branch, fill out the release notes and publish the release. This will create the tag for you. For a non-production release (alpha, dev, etc.) @@ -137,22 +136,24 @@ The Uploader has a self-update mechanism that will look at the latest release an ### CI server environment variables -We use the following environment variables on the CI servers: +We use the following environment variables on the CI server: -| Variable | CI Server | Use | +| Variable | OS Image | Use | |----------|-----------|-------| -| APPLEID | CircleCI | Notarization | -| APPLEIDPASS | CircleCI | Notarization | +| APPLEID | MacOS | Notarization | +| APPLEIDPASS | MacOS | Notarization | | AWS_ACCESS_KEY_ID | Both | S3 builds and AV e-mails | | AWS_SECRET_ACESS_KEY | Both | S3 builds and AV e-mails | | CSC_FOR_PULL_REQUEST | Both | `true`, code signing for PR | -| CSC_KEY_PASSWORD | Both | Certificate password | -| CSC_LINK | Both | Code signing certificate | -| DEBUG | CircleCI | Set to `electron-builder` | +| CSC_KEY_PASSWORD | MacOS | Certificate password | +| CSC_LINK | MacOS | Code signing certificate | +| WIN_CSC_KEY_PASSWORD | Windows | Certificate password | +| WIN_CSC_LINK | Windows | Code signing certificate | +| DEBUG | MacOS | Set to `electron-builder` | | GH_TOKEN | Both | For GitHub builds | | PUBLISH_FOR_PULL_REQUEST | Both | `true`, build artefact for PR | | ROLLBAR_POST_TOKEN | Both | Rollbar logging | -| FTP_AV_PASSWORD_TIDEPOOL | Appveyor | AV submission | +| FTP_AV_PASSWORD_TIDEPOOL | Windows | AV submission | ## Editor Configuration **Atom**