Created
May 11, 2015 21:57
-
-
Save TrevorBasinger/063f9a4e8a4e249e2bf9 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* jshint laxbreak: true, laxcomma: true */ | |
(function (R, F, M, A) { | |
var log = function (x) { console.log (x); return x; }, | |
mkSensorUrl = function (unit) { return 'http://10.50.2.213:8080/v2/units/' + unit + '/sensors'; }, | |
mkChart = R.curry (function (el, label) { | |
return new Highcharts.Chart({ chart: { renderTo: el | |
, defaultSeriesType: 'line' } | |
, title: { text: 'Live '+label+' data' } | |
, xAxis: { type: 'datetime' | |
, tickLength: 1 } | |
, yAxis: { minPadding: 0.2 | |
, maxPadding: 0.2 | |
, title: { text: 'Readings' | |
, margin: 80 } } | |
, plotOptions: { series: { showCheckbox: false | |
, visible: false | |
, events: { show: function () { | |
var chart = this.chart, | |
series = chart.series, | |
i = series.length, | |
otherSeries; | |
while (i--) { | |
otherSeries = series[i]; | |
if (otherSeries != this && otherSeries.visible) { | |
otherSeries.hide(); | |
} | |
} | |
}, | |
legendItemClick: function () { | |
if (this.visible) { | |
return false; | |
} | |
}}}} | |
, series: []}); | |
}), | |
hasConstants = R.compose (R.lt (0), R.length, R.prop ('mappedto'), R.head), | |
notHasConstants = R.compose (R.not, R.lt (0), R.length, R.prop ('mappedto'), R.head), | |
getSensors = R.curry (function (unit) { | |
return new F (function (reject, resolve) { | |
$.ajax ({ | |
method: 'GET', | |
url: mkSensorUrl (unit), | |
headers: { 'x-api-key': '7d798801e37f726fe8ab2fd36673b3a1' }, | |
success: resolve, | |
error: reject, | |
}); | |
}).map (R.prop ('results')) ; | |
}), | |
nest = R.flip (R.append)([]), | |
// getConstants :: String -> Future [Fault] | |
getConstants = R.curry (function (unit, c) { | |
return new F (function (reject, resolve) { | |
$.ajax ({ | |
method: 'GET', | |
url: mkSensorUrl (unit) + '/' + c, | |
headers: { 'x-api-key': '7d798801e37f726fe8ab2fd36673b3a1' }, | |
success: resolve, | |
error: reject, | |
}); | |
}).map (R.prop ('results')) | |
// Because ramda 'toPairs' isn't working correctly... I'm sorry future me. For everything. | |
.map (function (xs) { var x = {}; x[c] = xs; return x; }); | |
}), | |
// updateSeriesWithMaxLen :: Time -> Int -> SeriesRecord -> Tuple (Sensor, Int) -> SeriesRecord | |
updateSeriesWithMaxLen = R.curry (function (dt, maxLen, acc, tup) { | |
var name = R.replace (' ', '-', R.toLower (tup[0].sensorname)); | |
var label = tup[0].sensorname; | |
if (!acc[name]) { acc[name] = {name: label, data: []}; } | |
acc[name].data = R.append ([dt, tup[1]], acc[name].data); | |
if (acc[name].length > maxLen) | |
{ acc[name].data = R.slice (acc[name].data.length - maxLen, acc[name].data.length, acc[name].data); } | |
return acc; | |
}), | |
nil = null; | |
var FloFlo = A.module ('FloFlo', []); | |
FloFlo.factory ('LiveData', ['$rootScope', function ($rootScope) { | |
var socket = io (); | |
socket.on ('message', function (msg) { $rootScope.$broadcast ('message', msg); }); | |
return { join: function (channel) { socket.emit ('join', channel); } | |
, leave: function (channel) { socket.emit ('leave', channel); } }; | |
}]); | |
FloFlo.controller ('FloTroller', ['$rootScope', '$scope', 'LiveData', function ($rs, $s, LD) { | |
$s.seriesRecord = {}; | |
$s.sensors = []; | |
$s.constants = null; | |
$s.getConstVal = function (c, n) { | |
var cs = R.filter (R.compose (R.eq (n), R.prop ('code')), $s.constants[c]); | |
return cs[0].description; | |
}; | |
$s.joinUnit = function (unit) { | |
getSensors (unit).fork (log, function (xs) { | |
$s.sensors = xs; | |
LD.join ('scada#'+ unit +'#'); | |
}); | |
}; | |
$s.leaveUnit = function (unit) { LD.leave ('scada#'+ unit +'#'); $s.seriesRecord = {}; }; | |
$s.$on ('message', function (e, m) { | |
var dt = new Date (m.date).getTime(), | |
values = R.map (function (s) { return [s, R.nth (s.address, m.values)]; }, $s.sensors), | |
rangeTups = R.filter (notHasConstants, values); | |
$s.constTups = R.filter (hasConstants, values); | |
var f = R.compose (getConstants (m.unit), R.prop ('mappedto'), R.head); | |
if (!$s.constants) { | |
var futures = R.map (f, $s.constTups); | |
var ff = M.sequence (F, futures); | |
ff.fork (log, function (xs) { $s.constants = log (R.mergeAll (xs)); $s.$apply(); }); | |
} | |
R.reduce (updateSeriesWithMaxLen (dt, 20), $s.seriesRecord, rangeTups); | |
$s.$apply(); // Why? Cause fuck angular, that's why! | |
if (!$s.chart) { $s.chart = mkChart ('floChart', m.unit); } | |
if ($s.chart.series.length !== R.length (R.keys ($s.seriesRecord))) | |
{ R.mapObj (function (x) { $s.chart.addSeries (x); }, $s.seriesRecord); } | |
R.mapIndexed (function (x, i, o) { | |
var shift = $s.chart.series[i].data.length > 20; | |
$s.chart.series[i].addPoint (R.last (x[1].data), true, shift); | |
}, R.toPairs ($s.seriesRecord)); | |
}); | |
}]); | |
FloFlo.directive ('floUnitInput', function () { | |
return { | |
restrict: 'E', | |
templateUrl: 'unitinput.html', | |
controller: 'FloTroller' | |
}; | |
}); | |
})(R, Future, Monads, angular); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment