diff --git a/css/dropdown.css b/css/dropdown.css index 77c50ff8965..0c2f5ec4f3d 100644 --- a/css/dropdown.css +++ b/css/dropdown.css @@ -4,22 +4,23 @@ padding: 0; position: absolute; visibility: hidden; + right: 8px; } .dropdown-menu li { list-style: none; float: none; - display: inline + display: inline; } .dropdown-menu li a { display: block; - padding: 5px 12px; text-decoration: none; white-space: nowrap; width: auto; background: #BBBBBB; - color: #24313C + color: #24313C; + padding: 2px 10px; } .dropdown-menu li a:hover { diff --git a/css/main.css b/css/main.css index 74123061ca0..a19f8707eff 100644 --- a/css/main.css +++ b/css/main.css @@ -42,6 +42,11 @@ body { height: 100%; } +#chartContainer svg { + width: 100%; + height: 100%; +} + .container { overflow: hidden; height: auto; @@ -85,13 +90,16 @@ body { .span1{width: 20%; float: left; padding-top: 3%; line-height: 50%;} -.span2{width: 40%; margin-left: 35%} +.span2{margin-left: 35%; float: right;} -.bgButton { +#noButton { + padding: 2px; +} + +#bgButton { background: #ff2035; color: #000000; width: auto; - margin: 3px auto; border: 2px solid #DDD; border-right: 2px solid #ccc; border-bottom: 2px solid #ccc; @@ -108,10 +116,11 @@ body { -ms-user-select: none; user-select: none; cursor: default; + margin-right: 37px; } .bgButton:active { - margin: 10px auto; + background: #850406; border: 2px solid #999; box-shadow: none; -moz-box-shadow: none; @@ -124,6 +133,12 @@ body { margin: 10px auto; } -#silenceBtn { - font-size: 200%; +#silenceBtn, #silenceBtn * { + font-size: 70%; +} + +#testAlarms { + font-size: 20%; + color: blue; + text-decoration: none; } diff --git a/index.html b/index.html index eba721331b1..f3954a2ea7a 100644 --- a/index.html +++ b/index.html @@ -9,18 +9,20 @@
-
- ???
- 0 watcher(s) +
+
---
+
+ --- +
-
-
- ??? - mg/dL +
+
+ --- + -✓♪
-
- - + diff --git a/js/client.js b/js/client.js index 8f788e34354..64b0b7b3c41 100644 --- a/js/client.js +++ b/js/client.js @@ -2,8 +2,8 @@ "use strict"; var treatments, - padding = { top: 20, right: 10, bottom: 30, left: 10}, - opacity = {current: 1, DAY: 1, NIGHT: 0.5}, + padding = {top: 20, right: 10, bottom: 80, left: 10}, + opacity = {current: 1, DAY: 1, NIGHT: 0.8}, now = Date.now(), data = [], dateFn = function (d) { return new Date(d.date)}, @@ -19,7 +19,13 @@ brushTimer, brushInProgress = false, clip, - FOCUS_DATA_RANGE_MS = 12600000; // 3.5 hours of actual data + FOCUS_DATA_RANGE_MS = 12600000, // 3.5 hours of actual data + audio = document.getElementById('audio'), + alarmInProgress = false, + currentAlarmType = null, + alarmSound = 'alarm.mp3', + urgentAlarmSound = 'alarm2.mp3'; + // create svg and g to contain the chart contents var charts = d3.select('#chartContainer').append('svg') @@ -98,9 +104,9 @@ // get the desired opacity for context chart based on the brush extent function highlightBrushPoints(data) { if (data.date.getTime() >= brush.extent()[0].getTime() && data.date.getTime() <= brush.extent()[1].getTime()) { - return 1 + return 1; } else { - return 0.2 + return 0.5; } } @@ -574,8 +580,30 @@ if (d.length > 1) { // change the next line so that it uses the prediction if the signal gets lost (max 1/2 hr) if (d[0].length) { - $('#currentBG').text(d[0][d[0].length - 1].y); - $('#bgValue').text(d[0][d[0].length - 1].y); + var current = d[0][d[0].length - 1]; + var secsSinceLast = (Date.now() - new Date(current.x).getTime()) / 1000; + var currentBG = current.y; + + //TODO: currently these are filtered on the server + //TODO: use icons for these magic values + switch (current.y) { + case 0: currentBG = '??0'; break; //None + case 1: currentBG = '?SN'; break; //SENSOR_NOT_ACTIVE + case 2: currentBG = '??2'; break; //MINIMAL_DEVIATION + case 3: currentBG = '?NA'; break; //NO_ANTENNA + case 5: currentBG = '?NC'; break; //SENSOR_NOT_CALIBRATED + case 6: currentBG = '?CD'; break; //COUNTS_DEVIATION + case 7: currentBG = '??7'; break; //? + case 8: currentBG = '??8'; break; //? + case 9: currentBG = '?AD'; break; //ABSOLUTE_DEVIATION + case 10: currentBG = '?PD'; break; //POWER_DEVIATION + case 12: currentBG = '?RF'; break; //BAD_RF + } + + $('#lastEntry').text(timeAgo(secsSinceLast)).toggleClass('current', secsSinceLast < 10 * 60); + $('.container .currentBG').text(currentBG); + $('.container .currentDirection').html(current.direction); + $('.container .current').toggleClass('high', current.y > 180).toggleClass('low', current.y < 70) } data = d[0].map(function (obj) { return { date: new Date(obj.x), sgv: obj.y, color: 'grey'} }); data = data.concat(d[1].map(function (obj) { return { date: new Date(obj.x), sgv: obj.y, color: 'blue'} })); @@ -619,23 +647,26 @@ $('#watchers').text(watchers); }); - // load alarms - var alarmSound = document.getElementById('audio'); - var urgentAlarmSound = document.getElementById('audio2'); - - // alarm state - var alarmInProgress = false; - var currentAlarmType = null; + $('#testAlarms').click(function(event) { + event.preventDefault(); + audio.src = 'audio/alarm.mp3'; + audio.load(); + audio.play(); + setTimeout(function() { + audio.pause(); + }, 4000); + }); - function generateAlarm(alarmType) { + function generateAlarm(file) { alarmInProgress = true; - alarmType.load(); - alarmType.play(); + audio.src = 'audio/' + file; + audio.load(); + audio.play(); var element = document.getElementById('bgButton'); element.hidden = ''; var element1 = document.getElementById('noButton'); element1.hidden = 'true'; - $('#bgValue').text($('#currentBG').text()); + $('.container .currentBG').text(); } function stopAlarm(isClient, silenceTime) { @@ -644,15 +675,39 @@ element.hidden = 'true'; element = document.getElementById('noButton'); element.hidden = ''; - alarmSound.pause(); - urgentAlarmSound.pause(); + audio.pause(); // only emit ack if client invoke by button press if (isClient) { - socket.emit('ack', currentAlarmType, silenceTime); + socket.emit('ack', currentAlarmType || 'alarm', silenceTime); } } - //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + function timeAgo(offset) { + var parts = {}, + MINUTE = 60, + HOUR = 3600, + DAY = 86400, + WEEK = 604800; + + if (offset <= MINUTE) parts = { lablel: 'now' }; + if (offset <= MINUTE * 2) parts = { label: '1 min ago' }; + else if (offset < (MINUTE * 60)) parts = { value: Math.round(Math.abs(offset / MINUTE)), label: 'mins' }; + else if (offset < (HOUR * 2)) parts = { label: '1 hr ago' }; + else if (offset < (HOUR * 24)) parts = { value: Math.round(Math.abs(offset / HOUR)), label: 'hrs' }; + else if (offset < DAY) parts = { label: '1 day ago' }; + else if (offset < (DAY * 7)) parts = { value: Math.round(Math.abs(offset / DAY)), label: 'day' }; + else if (offset < (WEEK * 52)) parts = { value: Math.round(Math.abs(offset / WEEK)), label: 'week' }; + else parts = { label: 'a long time ago' }; + + if (parts.value) + return parts.value + ' ' + parts.label + ' ago'; + else + return parts.label; + + } + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //draw a compact visualization of a treatment (carbs, insulin) diff --git a/server.js b/server.js index b99372ce933..e293b6aeea0 100644 --- a/server.js +++ b/server.js @@ -101,6 +101,23 @@ DB.collection = DB.collection || process.env.CUSTOMCONNSTR_mongo_collection; var DB_URL = DB.url; var DB_COLLECTION = DB.collection; +var dir2Char = { + 'NONE': '⇼', + 'DoubleUp': '⇈', + 'SingleUp': '↑', + 'FortyFiveUp': '↗', + 'Flat': '→', + 'FortyFiveDown': '↘', + 'SingleDown': '↓', + 'DoubleDown': '⇊', + 'NOT COMPUTABLE': '-', + 'RATE OUT OF RANGE': '↮' +}; + +function directionToChar(direction) { + return dir2Char[direction] || '-'; +} + var Alarm = function(_typeName, _threshold) { this.typeName = _typeName; this.silenceTime = FORTY_MINUTES; @@ -139,6 +156,7 @@ function update() { obj.y = element.sgv; obj.x = element.date; obj.d = element.dateString; + obj.direction = directionToChar(element.direction); cgmData.push(obj); } }); @@ -217,8 +235,9 @@ function loadData() { // compute current loss var avgLoss = 0; - for (var i = 0; i <= 6; i++) { - avgLoss += 1 / 6 * Math.pow(log10(predicted[i].y / 120), 2); + var size = Math.min(predicted.length - 1, 6); + for (var j = 0; j <= size; j++) { + avgLoss += 1 / size * Math.pow(log10(predicted[j].y / 120), 2); } if (avgLoss > alarms['urgent_alarm'].threshold) { @@ -231,6 +250,8 @@ function loadData() { // get data from database and setup to update every minute function kickstart ( ) { + //TODO: test server to see how data is stored (timestamps, entry values, etc) + //TODO: check server settings to configure alerts, entry units, etc update( ); return update; }