Skip to content

Commit

Permalink
refactored websocket alarms into notifications.js
Browse files Browse the repository at this point in the history
  • Loading branch information
jasoncalabrese committed Jun 7, 2015
1 parent 9a7d3e8 commit f52e80a
Show file tree
Hide file tree
Showing 4 changed files with 149 additions and 110 deletions.
5 changes: 5 additions & 0 deletions lib/bootevent.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ function boot (env) {
ctx.heartbeat = require('./ticker')(env, ctx);

ctx.data = require('./data')(env, ctx);
ctx.notifications = require('./notifications')(env, ctx);

ctx.heartbeat.on('tick', function(tick) {
console.info('tick', tick.now);
Expand All @@ -41,6 +42,10 @@ function boot (env) {
});
});

ctx.heartbeat.on('data-loaded', function() {
ctx.notifications.processData(env, ctx);
});

ctx.heartbeat.uptime( );

next( );
Expand Down
124 changes: 124 additions & 0 deletions lib/notifications.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
'use strict';

var ar2 = require('./plugins/ar2')();

var THIRTY_MINUTES = 30 * 60 * 1000;

var Alarm = function(_typeName, _threshold) {
this.typeName = _typeName;
this.silenceTime = THIRTY_MINUTES;
this.lastAckTime = 0;
this.threshold = _threshold;
};

// list of alarms with their thresholds
var alarms = {
'alarm' : new Alarm('Regular', 0.05),
'urgent_alarm': new Alarm('Urgent', 0.10)
};

function init (env, ctx) {
function notifications () {
return notifications;
}

//should only be used when auto acking the alarms after going back in range or when an error corrects
//setting the silence time to 1ms so the alarm will be retriggered as soon as the condition changes
//since this wasn't ack'd by a user action
function autoAckAlarms() {
var sendClear = false;
for (var type in alarms) {
if (alarms.hasOwnProperty(type)) {
var alarm = alarms[type];
if (alarm.lastEmitTime) {
console.info('auto acking ' + type);
notifications.ack(type, 1);
sendClear = true;
}
}
}
if (sendClear) {
ctx.heartbeat.emit('notification', {clear: true});
console.info('emitted notification clear');
}
}

function emitAlarm (type) {
var alarm = alarms[type];
if (ctx.data.lastUpdated > alarm.lastAckTime + alarm.silenceTime) {
ctx.heartbeat.emit('notification', {type: type});
alarm.lastEmitTime = ctx.data.lastUpdated;
console.info('emitted notification:' + type);
} else {
console.log(alarm.typeName + ' alarm is silenced for ' + Math.floor((alarm.silenceTime - (ctx.data.lastUpdated - alarm.lastAckTime)) / 60000) + ' minutes more');
}
}

notifications.processData = function processData ( ) {
var d = ctx.data;

console.log('running notifications.processData');

var lastSGV = d.sgvs.length > 0 ? d.sgvs[d.sgvs.length - 1].y : null;

if (lastSGV) {
var forecast = ar2.forecast(env, ctx);

var emitAlarmType = null;

if (env.alarm_types.indexOf('simple') > -1) {
if (lastSGV > env.thresholds.bg_high) {
emitAlarmType = 'urgent_alarm';
console.info(lastSGV + ' > ' + env.thresholds.bg_high + ' will emmit ' + emitAlarmType);
} else if (lastSGV > env.thresholds.bg_target_top) {
emitAlarmType = 'alarm';
console.info(lastSGV + ' > ' + env.thresholds.bg_target_top + ' will emmit ' + emitAlarmType);
} else if (lastSGV < env.thresholds.bg_low) {
emitAlarmType = 'urgent_alarm';
console.info(lastSGV + ' < ' + env.thresholds.bg_low + ' will emmit ' + emitAlarmType);
} else if (lastSGV < env.thresholds.bg_target_bottom) {
emitAlarmType = 'alarm';
console.info(lastSGV + ' < ' + env.thresholds.bg_target_bottom + ' will emmit ' + emitAlarmType);
}
}

if (!emitAlarmType && env.alarm_types.indexOf('predict') > -1) {
if (forecast.avgLoss > alarms['urgent_alarm'].threshold) {
emitAlarmType = 'urgent_alarm';
console.info('Avg Loss:' + forecast.avgLoss + ' > ' + alarms['urgent_alarm'].threshold + ' will emmit ' + emitAlarmType);
} else if (forecast.avgLoss > alarms['alarm'].threshold) {
emitAlarmType = 'alarm';
console.info('Avg Loss:' + forecast.avgLoss + ' > ' + alarms['alarm'].threshold + ' will emmit ' + emitAlarmType);
}
}

if (d.sgvs.length > 0 && d.sgvs[d.sgvs.length - 1].y < 39) {
emitAlarmType = 'urgent_alarm';
}

if (emitAlarmType) {
emitAlarm(emitAlarmType);
} else {
autoAckAlarms();
}
}
};

notifications.ack = function ack (type, time) {
var alarm = alarms[type];
if (alarm) {
console.info('Got an ack for: ', alarm, 'time: ' + time);
} else {
console.warn('Got an ack for an unknown alarm time');
return;
}
alarm.lastAckTime = new Date().getTime();
alarm.silenceTime = time ? time : THIRTY_MINUTES;
delete alarm.lastEmitTime;

};

return notifications();
}

module.exports = init;
122 changes: 14 additions & 108 deletions lib/websocket.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
'use strict';

var ar2 = require('./plugins/ar2')();
var _ = require('lodash');
var ObjectID = require('mongodb').ObjectID;

Expand All @@ -20,14 +19,12 @@ function sort (values) {
});
}

function init (server) {
function init (env, ctx, server) {

function websocket ( ) {
return websocket;
}

var FORTY_MINUTES = 2400000;

var lastUpdated = 0;
var patientData = {};
var patientDataUpdate = {};
Expand Down Expand Up @@ -67,92 +64,28 @@ function init (server) {
io.sockets.socket(socket.id).emit('dataUpdate',patientData);
io.sockets.emit('clients', ++watchers);
socket.on('ack', function(alarmType, silenceTime) {
ackAlarm(alarmType, silenceTime);
ctx.notifications.ack(alarmType, silenceTime);
if (alarmType == 'urgent_alarm') {
//also clean normal alarm so we don't get a double alarm as BG comes back into range
ackAlarm('alarm', silenceTime);
ctx.notifications.ack('alarm', silenceTime);
}
io.sockets.emit('clear_alarm', true);
console.log('alarm cleared');
});
socket.on('disconnect', function () {
io.sockets.emit('clients', --watchers);
});
});
}

///////////////////////////////////////////////////
// data handling functions
///////////////////////////////////////////////////

var Alarm = function(_typeName, _threshold) {
this.typeName = _typeName;
this.silenceTime = FORTY_MINUTES;
this.lastAckTime = 0;
this.threshold = _threshold;
};

// list of alarms with their thresholds
var alarms = {
'alarm' : new Alarm('Regular', 0.05),
'urgent_alarm': new Alarm('Urgent', 0.10)
};

function ackAlarm(alarmType, silenceTime) {
var alarm = alarms[alarmType];
if (!alarm) {
console.warn('Got an ack for an unknown alarm time');
return;
}
alarm.lastAckTime = new Date().getTime();
alarm.silenceTime = silenceTime ? silenceTime : FORTY_MINUTES;
delete alarm.lastEmitTime;
}

//should only be used when auto acking the alarms after going back in range or when an error corrects
//setting the silence time to 1ms so the alarm will be retriggered as soon as the condition changes
//since this wasn't ack'd by a user action
function autoAckAlarms() {
var sendClear = false;
for (var alarmType in alarms) {
if (alarms.hasOwnProperty(alarmType)) {
var alarm = alarms[alarmType];
if (alarm.lastEmitTime) {
console.info('auto acking ' + alarmType);
ackAlarm(alarmType, 1);
sendClear = true;
}
}
}
if (sendClear) {
io.sockets.emit('clear_alarm', true);
console.info('emitted clear_alarm to all clients');
}
}

function emitAlarm (alarmType) {
var alarm = alarms[alarmType];
if (lastUpdated > alarm.lastAckTime + alarm.silenceTime) {
io.sockets.emit(alarmType);
alarm.lastEmitTime = lastUpdated;
console.info('emitted ' + alarmType + ' to all clients');
} else {
console.log(alarm.typeName + ' alarm is silenced for ' + Math.floor((alarm.silenceTime - (lastUpdated - alarm.lastAckTime)) / 60000) + ' minutes more');
}
}

websocket.processData = function processData (env, ctx) {
websocket.processData = function processData ( ) {

var d = ctx.data;
lastUpdated = d.lastUpdated;

console.log('running websocket.loadData');
console.log('running websocket.processData');

var lastSGV = d.sgvs.length > 0 ? d.sgvs[d.sgvs.length - 1].y : null;

if (lastSGV) {
var forecast = ar2.forecast(env, ctx);

if (patientData.sgvs) {
var delta = calculateDelta(d);
if (delta.delta) {
Expand All @@ -170,43 +103,16 @@ function init (server) {
}
});

var emitAlarmType = null;

if (env.alarm_types.indexOf('simple') > -1) {
if (lastSGV > env.thresholds.bg_high) {
emitAlarmType = 'urgent_alarm';
console.info(lastSGV + ' > ' + env.thresholds.bg_high + ' will emmit ' + emitAlarmType);
} else if (lastSGV > env.thresholds.bg_target_top) {
emitAlarmType = 'alarm';
console.info(lastSGV + ' > ' + env.thresholds.bg_target_top + ' will emmit ' + emitAlarmType);
} else if (lastSGV < env.thresholds.bg_low) {
emitAlarmType = 'urgent_alarm';
console.info(lastSGV + ' < ' + env.thresholds.bg_low + ' will emmit ' + emitAlarmType);
} else if (lastSGV < env.thresholds.bg_target_bottom) {
emitAlarmType = 'alarm';
console.info(lastSGV + ' < ' + env.thresholds.bg_target_bottom + ' will emmit ' + emitAlarmType);
}
}

if (!emitAlarmType && env.alarm_types.indexOf('predict') > -1) {
if (forecast.avgLoss > alarms['urgent_alarm'].threshold) {
emitAlarmType = 'urgent_alarm';
console.info('Avg Loss:' + forecast.avgLoss + ' > ' + alarms['urgent_alarm'].threshold + ' will emmit ' + emitAlarmType);
} else if (forecast.avgLoss > alarms['alarm'].threshold) {
emitAlarmType = 'alarm';
console.info('Avg Loss:' + forecast.avgLoss + ' > ' + alarms['alarm'].threshold + ' will emmit ' + emitAlarmType);
}
}

if (d.sgvs.length > 0 && d.sgvs[d.sgvs.length - 1].y < 39) {
emitAlarmType = 'urgent_alarm';
}
}
};

if (emitAlarmType) {
emitAlarm(emitAlarmType);
} else {
autoAckAlarms();
}
websocket.emitNotification = function emitNotification (info) {
if (info.clear) {
io.sockets.emit('clear_alarm', true);
console.info('emitted clear_alarm to all clients');
} else if (info.type) {
io.sockets.emit(info.type);
console.info('emitted ' + info.type + ' to all clients');
}
};

Expand Down
8 changes: 6 additions & 2 deletions server.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,14 @@ bootevent(env).boot(function booted (ctx) {
///////////////////////////////////////////////////
// setup socket io for data and message transmission
///////////////////////////////////////////////////
var websocket = require('./lib/websocket')(server);
var websocket = require('./lib/websocket')(env, ctx, server);

ctx.heartbeat.on('data-loaded', function() {
websocket.processData(env, ctx);
websocket.processData();
});

ctx.heartbeat.on('notification', function(info) {
websocket.emitNotification(info);
});

})
Expand Down

0 comments on commit f52e80a

Please sign in to comment.