From 407523dbf03be962b7c49e3a02e30ed194687fc2 Mon Sep 17 00:00:00 2001 From: Sulka Haro Date: Mon, 21 Oct 2019 19:31:31 +0300 Subject: [PATCH 1/3] Further fixes to profile data fetches, change the client to not create excessive amount of date objects --- lib/client/index.js | 16 +++++++++++----- lib/plugins/cob.js | 1 - lib/profilefunctions.js | 31 ++++++++++++++++++++++--------- 3 files changed, 33 insertions(+), 15 deletions(-) diff --git a/lib/client/index.js b/lib/client/index.js index dd7fba1a063..ea0aa302bc1 100644 --- a/lib/client/index.js +++ b/lib/client/index.js @@ -124,7 +124,11 @@ client.load = function load (serverSettings, callback) { , urgentAlarmSound = 'alarm2.mp3' , previousNotifyTimestamp; - client.entryToDate = function entryToDate (entry) { return new Date(entry.mills); }; + client.entryToDate = function entryToDate (entry) { + if (entry.date) return entry.date; + entry.date = new Date(entry.mills); + return entry.date; + }; client.now = Date.now(); client.ddata = require('../data/ddata')(); @@ -261,10 +265,12 @@ client.load = function load (serverSettings, callback) { //client.ctx.bus.uptime( ); client.dataExtent = function dataExtent () { - return client.entries.length > 0 ? - d3.extent(client.entries, client.entryToDate) : - d3.extent([new Date(client.now - times.hours(history).msecs), new Date(client.now)]); - }; + if (client.entries.length > 0) { + return d3.extent(client.entries, client.entryToDate); + } else { + return [new Date(client.now - times.hours(history).msecs), new Date(client.now)]; + } + }; client.bottomOfPills = function bottomOfPills () { //the offset's might not exist for some tests diff --git a/lib/plugins/cob.js b/lib/plugins/cob.js index f3bb1902ad6..5020f2e6458 100644 --- a/lib/plugins/cob.js +++ b/lib/plugins/cob.js @@ -47,7 +47,6 @@ function init (ctx) { if (_.isEmpty(result) || _.isNil(result.cob) || (Date.now() - result.mills) > TEN_MINUTES) { - console.log('Calculating COB'); var treatmentCOB = (treatments !== undefined && treatments.length) ? cob.fromTreatments(treatments, devicestatus, profile, time, spec_profile) : {}; result = treatmentCOB; diff --git a/lib/profilefunctions.js b/lib/profilefunctions.js index ec9e29fded5..a079d0b419e 100644 --- a/lib/profilefunctions.js +++ b/lib/profilefunctions.js @@ -6,14 +6,13 @@ var c = require('memory-cache'); var times = require('./times'); var crypto = require('crypto'); -var cacheTTL = 600; - +var cacheTTL = 1000; var prevBasalTreatment = null; +var cache = new c.Cache(); function init (profileData) { var profile = {}; - var cache = new c.Cache(); profile.loadData = function loadData (profileData) { if (profileData && profileData.length) { @@ -139,10 +138,22 @@ function init (profileData) { }; profile.getCurrentProfile = function getCurrentProfile (time, spec_profile) { - time = time || new Date().getTime(); + + time = time || Date.now(); + var minuteTime = Math.round(time / 60000) * 60000; + var cacheKey = ("profile" + minuteTime + spec_profile); + var returnValue = cache.get(cacheKey); + + if (returnValue) { + return returnValue; + } + var data = profile.hasData() ? profile.data[0] : null; var timeprofile = spec_profile || profile.activeProfileToTime(time); - return data && data.store[timeprofile] ? data.store[timeprofile] : {}; + returnValue = data && data.store[timeprofile] ? data.store[timeprofile] : {}; + + cache.put(cacheKey, returnValue, cacheTTL); + return returnValue; }; profile.getUnits = function getUnits (spec_profile) { @@ -223,9 +234,10 @@ function init (profileData) { }; profile.activeProfileTreatmentToTime = function activeProfileTreatmentToTime (time) { - var cacheKey = 'profile' + time + profile.profiletreatments_hash; - //var returnValue = profile.timeValueCache[cacheKey]; - var returnValue; + + var minuteTime = Math.round(time / 60000) * 60000; + var cacheKey = 'profile' + minuteTime + profile.profiletreatments_hash; + var returnValue = cache.get(cacheKey); if (returnValue) { return returnValue; @@ -312,7 +324,8 @@ function init (profileData) { profile.getTempBasal = function getTempBasal (time, spec_profile) { - var cacheKey = 'basal' + time + profile.tempbasaltreatments_hash + profile.combobolustreatments_hash + profile.profiletreatments_hash + spec_profile; + var minuteTime = Math.round(time / 60000) * 60000; + var cacheKey = 'basal' + minuteTime + profile.tempbasaltreatments_hash + profile.combobolustreatments_hash + profile.profiletreatments_hash + spec_profile; var returnValue = cache.get(cacheKey); if (returnValue) { From 74bfa726aa52391be61ebed2af51f59347651c2c Mon Sep 17 00:00:00 2001 From: Sulka Haro Date: Mon, 21 Oct 2019 20:27:25 +0300 Subject: [PATCH 2/3] Sort entries once on load and then rely on the sorting to find out the largest and smallest value --- lib/client/index.js | 6 +++++- lib/profilefunctions.js | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/client/index.js b/lib/client/index.js index ea0aa302bc1..cb7fce8c7c9 100644 --- a/lib/client/index.js +++ b/lib/client/index.js @@ -266,7 +266,7 @@ client.load = function load (serverSettings, callback) { client.dataExtent = function dataExtent () { if (client.entries.length > 0) { - return d3.extent(client.entries, client.entryToDate); + return[client.entryToDate(client.entries[0]), client.entryToDate(client.entries[client.entries.length-1])]; } else { return [new Date(client.now - times.hours(history).msecs), new Date(client.now)]; } @@ -1147,6 +1147,10 @@ client.load = function load (serverSettings, callback) { point.color = 'transparent'; } }); + + client.entries.sort(function sorter(a,b) { + return a.mills - b.mills; + }); } function dataUpdate (received) { diff --git a/lib/profilefunctions.js b/lib/profilefunctions.js index a079d0b419e..40ce9629030 100644 --- a/lib/profilefunctions.js +++ b/lib/profilefunctions.js @@ -6,7 +6,7 @@ var c = require('memory-cache'); var times = require('./times'); var crypto = require('crypto'); -var cacheTTL = 1000; +var cacheTTL = 5000; var prevBasalTreatment = null; var cache = new c.Cache(); From dc484faefb8d8c8241cc3f6776695fabc573dc1f Mon Sep 17 00:00:00 2001 From: Sulka Haro Date: Mon, 21 Oct 2019 20:40:33 +0300 Subject: [PATCH 3/3] Make the renderer reuse Date objects instead of instantiating a huge amount of dates all the time --- lib/client/renderer.js | 46 +++++++++++++++++++++++++----------------- 1 file changed, 27 insertions(+), 19 deletions(-) diff --git a/lib/client/renderer.js b/lib/client/renderer.js index 944d66da964..bff950d309f 100644 --- a/lib/client/renderer.js +++ b/lib/client/renderer.js @@ -11,6 +11,8 @@ var DEFAULT_FOCUS = times.hours(3).msecs , TOOLTIP_WIDTH = 150 //min-width + padding ; +const zeroDate = new Date(0); + function init (client, d3) { var renderer = {}; @@ -18,6 +20,12 @@ function init (client, d3) { var utils = client.utils; var translate = client.translate; + function getOrAddDate(entry) { + if (entry.date) return entry.date; + entry.date = new Date(entry.mills); + return entry.date; + } + //chart isn't created till the client gets data, so can grab the var at init function chart () { return client.chart; @@ -97,12 +105,12 @@ function init (client, d3) { sel.attr('cx', function(d) { if (!d) { console.error('Bad data', d); - return chart().xScale(new Date(0)); + return chart().xScale(zeroDate); } else if (!d.mills) { console.error('Bad data, no mills', d); - return chart().xScale(new Date(0)); + return chart().xScale(zeroDate); } else { - return chart().xScale(new Date(d.mills)); + return chart().xScale(getOrAddDate(d)); } }) .attr('cy', function(d) { @@ -172,7 +180,7 @@ function init (client, d3) { (d.type === 'forecast' && d.forecastType ? '
' + translate('Forecast Type') + ': ' + d.forecastType : '') + (rawbgInfo.value ? '
' + translate('Raw BG') + ': ' + rawbgInfo.value : '') + (rawbgInfo.noise ? '
' + translate('Noise') + ': ' + rawbgInfo.noise : '') + - '
' + translate('Time') + ': ' + client.formatTime(new Date(d.mills))) + '
' + translate('Time') + ': ' + client.formatTime(getOrAddDate(d))) .style('left', tooltipLeft()) .style('top', (d3.event.pageY + 15) + 'px'); } @@ -232,7 +240,7 @@ function init (client, d3) { } } - return '' + translate('Time') + ': ' + client.formatTime(new Date(d.mills)) + '
' + + return '' + translate('Time') + ': ' + client.formatTime(getOrAddDate(d)) + '
' + (d.eventType ? '' + translate('Treatment type') + ': ' + translate(client.careportal.resolveEventName(d.eventType)) + '
' : '') + (d.reason ? '' + translate('Reason') + ': ' + translate(d.reason) + '
' : '') + (d.glucose ? '' + translate('BG') + ': ' + d.glucose + (d.glucoseType ? ' (' + translate(d.glucoseType) + ')' : '') + '
' : '') + @@ -246,7 +254,7 @@ function init (client, d3) { } function announcementTooltip (d) { - return '' + translate('Time') + ': ' + client.formatTime(new Date(d.mills)) + '
' + + return '' + translate('Time') + ': ' + client.formatTime(getOrAddDate(d)) + '
' + (d.eventType ? '' + translate('Announcement') + '
' : '') + (d.notes && d.notes.length > 1 ? '' + translate('Message') + ': ' + d.notes + '
' : '') + (d.enteredBy ? '' + translate('Entered By') + ': ' + d.enteredBy + '
' : ''); @@ -275,7 +283,7 @@ function init (client, d3) { function updateTreatCircles (sel) { sel.attr('cx', function(d) { - return chart().xScale(new Date(d.mills)); + return chart().xScale(getOrAddDate(d)); }) .attr('cy', function(d) { return chart().yScale(client.sbx.scaleEntry(d)); @@ -369,26 +377,26 @@ function init (client, d3) { if (d.eventType === 'Temporary Target') { top = d.targetTop === d.targetBottom ? d.targetTop + rectHeight(d) : d.targetTop; } - return 'translate(' + chart().xScale(new Date(d.mills)) + ',' + chart().yScale(utils.scaleMgdl(top)) + ')'; + return 'translate(' + chart().xScale(getOrAddDate(d)) + ',' + chart().yScale(utils.scaleMgdl(top)) + ')'; } function treatmentRectWidth (d) { if (d.durationType === "indefinite") { - return chart().xScale(chart().xScale.domain()[1].getTime()) - chart().xScale(new Date(d.mills)); + return chart().xScale(chart().xScale.domain()[1].getTime()) - chart().xScale(getOrAddDate(d)); } else { - return chart().xScale(new Date(d.mills + times.mins(d.duration).msecs)) - chart().xScale(new Date(d.mills)); + return chart().xScale(new Date(d.mills + times.mins(d.duration).msecs)) - chart().xScale(getOrAddDate(d)); } } function treatmentTextTransform (d) { if (d.durationType === "indefinite") { var offset = 0; - if (chart().xScale(new Date(d.mills)) < chart().xScale(chart().xScale.domain()[0].getTime())) { - offset = chart().xScale(nowDate) - chart().xScale(new Date(d.mills)); + if (chart().xScale(getOrAddDate(d)) < chart().xScale(chart().xScale.domain()[0].getTime())) { + offset = chart().xScale(nowDate) - chart().xScale(getOrAddDate(d)); } return 'translate(' + offset + ',' + 10 + ')'; } else { - return 'translate(' + (chart().xScale(new Date(d.mills + times.mins(d.duration).msecs)) - chart().xScale(new Date(d.mills))) / 2 + ',' + 10 + ')'; + return 'translate(' + (chart().xScale(new Date(d.mills + times.mins(d.duration).msecs)) - chart().xScale(getOrAddDate(d))) / 2 + ',' + 10 + ')'; } } @@ -457,7 +465,7 @@ function init (client, d3) { function prepareContextCircles (sel) { var badData = []; - sel.attr('cx', function(d) { return chart().xScale2(new Date(d.mills)); }) + sel.attr('cx', function(d) { return chart().xScale2(getOrAddDate(d)); }) .attr('cy', function(d) { var scaled = client.sbx.scaleEntry(d); if (isNaN(scaled)) { @@ -623,7 +631,7 @@ function init (client, d3) { } client.tooltip.transition().duration(TOOLTIP_TRANS_MS).style('opacity', .9); - client.tooltip.html('' + translate('Time') + ': ' + client.formatTime(new Date(treatment.mills)) + '
' + '' + translate('Treatment type') + ': ' + translate(client.careportal.resolveEventName(treatment.eventType)) + '
' + + client.tooltip.html('' + translate('Time') + ': ' + client.formatTime(getOrAddDate(treatment)) + '
' + '' + translate('Treatment type') + ': ' + translate(client.careportal.resolveEventName(treatment.eventType)) + '
' + (treatment.carbs ? '' + translate('Carbs') + ': ' + treatment.carbs + '
' : '') + (treatment.protein ? '' + translate('Protein') + ': ' + treatment.protein + '
' : '') + (treatment.fat ? '' + translate('Fat') + ': ' + treatment.fat + '
' : '') + @@ -771,7 +779,7 @@ function init (client, d3) { chart().drag.append('line') .attr('class', 'arrow') .attr('marker-end', 'url(#arrow)') - .attr('x1', chart().xScale(new Date(treatment.mills))) + .attr('x1', chart().xScale(getOrAddDate(treatment))) .attr('y1', chart().yScale(client.sbx.scaleEntry(treatment))) .attr('x2', x) .attr('y2', y) @@ -915,7 +923,7 @@ function init (client, d3) { .enter() .append('g') .attr('class', 'draggable-treatment') - .attr('transform', 'translate(' + chart().xScale(new Date(treatment.mills)) + ', ' + chart().yScale(client.sbx.scaleEntry(treatment)) + ')') + .attr('transform', 'translate(' + chart().xScale(getOrAddDate(treatment)) + ', ' + chart().yScale(client.sbx.scaleEntry(treatment)) + ')') .on('mouseover', treatmentTooltip) .on('mouseout', hideTooltip); if (client.editMode) { @@ -997,7 +1005,7 @@ function init (client, d3) { //when the tests are run window isn't available var innerWidth = window && window.innerWidth || -1; // don't render the treatment if it's not visible - if (Math.abs(chart().xScale(new Date(treatment.mills))) > innerWidth) { + if (Math.abs(chart().xScale(getOrAddDate(treatment))) > innerWidth) { return; } @@ -1162,7 +1170,7 @@ function init (client, d3) { } function profileTooltip (d) { - return '' + translate('Time') + ': ' + client.formatTime(new Date(d.mills)) + '
' + + return '' + translate('Time') + ': ' + client.formatTime(getOrAddDate(d)) + '
' + (d.eventType ? '' + translate('Treatment type') + ': ' + translate(client.careportal.resolveEventName(d.eventType)) + '
' : '') + (d.endprofile ? '' + translate('End of profile') + ': ' + d.endprofile + '
' : '') + (d.profile ? '' + translate('Profile') + ': ' + d.profile + '
' : '') +