mirror of
https://gitlab.silvrtree.co.uk/martind2000/mdot_server.git
synced 2025-01-31 14:40:14 +00:00
506 lines
14 KiB
JavaScript
506 lines
14 KiB
JavaScript
'use strict';
|
|
/* global Backbone, _, $, AmCharts, notification */
|
|
/* global mainview */
|
|
/* jshint browser: true , devel: true*/
|
|
|
|
(function($) {
|
|
|
|
var GraphView;
|
|
var mqttConfig = {
|
|
orgId: 'qz0da4',
|
|
userName: 'a-qz0da4-dfwwdkmkzr',
|
|
appKey: '9txJEf3Cjy7hkSOvkv',
|
|
prefix: 'iot-2/type/mDot/id/'
|
|
};
|
|
|
|
var sendAuthentication = function(xhr) {
|
|
var user = 'a-qz0da4-dfwwdkmkzr'; // Your actual username
|
|
var pass = '9txJEf3Cjy7hkSOvkv'; // Your actual password
|
|
var token = user.concat(':', pass);
|
|
xhr.setRequestHeader('Authorization', ('Basic '.concat(btoa(token))));
|
|
|
|
console.log('Auth:', ('Basic '.concat(btoa(token))));
|
|
};
|
|
|
|
var EventsModel = Backbone.Model.extend({
|
|
initialize: function() {
|
|
_.bindAll(this, 'processAdded');
|
|
this.on('all', function(d) {
|
|
console.log('model:all', d);
|
|
this.temporal = {low: 0, high: 0};
|
|
});
|
|
this.on('remove', function() {
|
|
$('#output').empty();
|
|
});
|
|
|
|
this.on('add', function() {
|
|
this.processAdded();
|
|
});
|
|
|
|
},
|
|
findOccupancy: function(ts) {
|
|
/*
|
|
Get a branch from the date tree and see if the reduced set of records has a matching timestamp..
|
|
*/
|
|
let count = 0;
|
|
let tsDate = new Date(ts);
|
|
let tsMS = tsDate.getTime();
|
|
let branch = this.dateTree[tsDate.getFullYear().toString()][tsDate.getMonth().toString()][tsDate.getDate().toString()];
|
|
|
|
if (typeof branch === 'undefined') {return count;}
|
|
_(branch).each(function(item) {
|
|
if ((tsMS >= item.startMS) && (tsMS <= item.endMS)) {
|
|
count = item.count;
|
|
return count;
|
|
}
|
|
|
|
}, this);
|
|
return count;
|
|
},
|
|
buildSpeedDateTree: function(occupancy) {
|
|
/*
|
|
Builds a tree to help speed up occupancy searching
|
|
*/
|
|
var _tree = {};
|
|
_(occupancy).each(function(item) {
|
|
let newItem = item;
|
|
let day, month,year;
|
|
let _date = new Date(item.start);
|
|
|
|
newItem.startMS = new Date(item.start).getTime();
|
|
newItem.endMS = new Date(item.end).getTime();
|
|
|
|
day = _date.getDate().toString();
|
|
month = _date.getMonth().toString();
|
|
year = _date.getFullYear().toString();
|
|
|
|
if (!_tree.hasOwnProperty(year)) {
|
|
_tree[year] = {};
|
|
}
|
|
|
|
if (_tree.hasOwnProperty(year)) {
|
|
|
|
if (!_tree[year].hasOwnProperty(month)) {
|
|
_tree[year][month] = {};
|
|
}
|
|
|
|
if (_tree[year].hasOwnProperty(month)) {
|
|
if (!_tree[year][month].hasOwnProperty(day)) {
|
|
_tree[year][month][day] = [];
|
|
}
|
|
|
|
_tree[year][month][day].push(newItem);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}, this);
|
|
|
|
return _tree;
|
|
},
|
|
|
|
|
|
processAdded: function() {
|
|
console.log('Model:ProcessAdded');
|
|
console.time('processAdd');
|
|
var skipOccupancy = false;
|
|
var tempCollection = new Backbone.Collection();
|
|
|
|
var events;
|
|
|
|
_.invoke(DeviceCollection.toArray(), 'destroy');
|
|
|
|
events = this.get('events');
|
|
|
|
this.dateTree = this.buildSpeedDateTree(events.occupancy);
|
|
|
|
if (Object.keys(this.dateTree).length === 0) {
|
|
skipOccupancy = true;
|
|
}
|
|
|
|
_(events.data).each(function(i) {
|
|
let _occupancy = 0;
|
|
|
|
if (!skipOccupancy) {
|
|
_occupancy = this.findOccupancy(i.timestamp, events.occupancy);
|
|
}
|
|
|
|
tempCollection.add({
|
|
dt: Date.create(i.timestamp).addHours(1),
|
|
lux: i.lux,
|
|
temp: i.temp,
|
|
co2: i.co2,
|
|
humid: i.humidity,
|
|
noise: i.sound,
|
|
occupancy: _occupancy
|
|
});
|
|
}, this);
|
|
|
|
DeviceCollection.models = tempCollection.models;
|
|
|
|
console.timeEnd('processAdd');
|
|
DeviceCollection.trigger('update');
|
|
|
|
notification.notify('success', 'Data loaded');
|
|
|
|
}, decoder: function(data) {
|
|
var _obj = {};
|
|
var _data = window.atob(data).split('');
|
|
|
|
var bytes = _data.map(i => i.charCodeAt());
|
|
|
|
_obj.light = parseInt('0x' + ('0' + bytes[0]).substr(-2) + ('0' + bytes[1]).substr(
|
|
-2));
|
|
_obj.co2 = parseInt(_data[2] + _data[3] + _data[4] + _data[5] + _data[6],
|
|
10);
|
|
_obj.temp = (parseInt(_data[7] + _data[8] + _data[9] + _data[10] + _data[11],
|
|
10) - 1000) / 10;
|
|
_obj.humid = (parseInt(_data[12] + _data[13] + _data[14] + _data[15] + _data[16],
|
|
10) / 10);
|
|
_obj.noise = parseInt('0x' + ('0' + bytes[17]).substr(-2) + ('0' + bytes[18]).substr(
|
|
-2));
|
|
_obj.binData = bytes;
|
|
return _obj;
|
|
}, dateTime: function($date) {
|
|
var dateTime = new Date.create($date);
|
|
var date = dateTime.format('{yyyy}-{MM}-{dd}');
|
|
var time = dateTime.format('{HH}:{mm}:{ss}');
|
|
return {
|
|
dateTime: dateTime.format('{yyyy}-{MM}-{dd} {HH}:{mm}:{ss}'),
|
|
date: date,
|
|
time: time
|
|
};
|
|
}
|
|
});
|
|
|
|
var mDotCollection = Backbone.Collection.extend({
|
|
model: EventsModel,
|
|
url: 'https://qz0da4.internetofthings.ibmcloud.com/api/v0002/historian/types/mDot/devices/',
|
|
initialize: function() {
|
|
this.on('update', function() {
|
|
|
|
});
|
|
}
|
|
|
|
});
|
|
|
|
var ItemView = Backbone.View.extend({
|
|
tagName: 'div', className: 'item mui-container', initialize: function() {
|
|
this.template = _.template($('#item-template').html());
|
|
console.log('ItemView:Init');
|
|
}, render: function() {
|
|
|
|
console.log('ItemView:Render');
|
|
_(this.model.events).each(function(i) {
|
|
this.$el.append(this.template({item: i}));
|
|
}, this);
|
|
|
|
return this;
|
|
}
|
|
});
|
|
|
|
var MDOT = Backbone.View.extend({
|
|
model: EventsModel, el: $('#output'),
|
|
|
|
events: {
|
|
'click button#refresh': 'refresh'
|
|
}, initialize: function() {
|
|
_.bindAll(this, 'render', 'refresh', 'update');
|
|
this.collection.bind('change reset add remove', this.render, this);
|
|
|
|
this.template = _.template($('#loaded-template').html());
|
|
}, refresh: function() {
|
|
|
|
}, update: function() {
|
|
console.log('MDOT:update');
|
|
this.collection.each(function(model) {
|
|
|
|
});
|
|
|
|
}, render: function() {
|
|
console.log('MDOT:render');
|
|
$('#output').empty();
|
|
|
|
return this;
|
|
}
|
|
});
|
|
|
|
var MainModel = Backbone.Model.extend({});
|
|
|
|
var MainView = Backbone.View.extend({
|
|
el: $('#main'), template: _.template($('#main-template').html()), events: {
|
|
'change select#device': 'changeDevice',
|
|
'click button#refresh': 'updateDevice',
|
|
submit: function(event) {}
|
|
|
|
}, initialize: function() {
|
|
_.bindAll(this, 'render', 'changeDevice', 'updateDevice');
|
|
|
|
this.model.on('change', this.updateDevice);
|
|
console.log('MainView:', this);
|
|
this.render();
|
|
}, render: function() {
|
|
$(this.el).html(this.template());
|
|
return this;
|
|
}, changeDevice: function() {
|
|
var newDevice;
|
|
console.log('MainView:ChangeDevice');
|
|
newDevice = this.$el.find('#device')[0].value;
|
|
|
|
this.model.set('device', newDevice);
|
|
}, updateDevice: function() {
|
|
var fetchObj = {beforeSend: sendAuthentication};
|
|
console.log('MainView:Updatedevice');
|
|
notification.clearAll();
|
|
if (this.model.has('device')) {
|
|
this.collection.url = '/apiv2/mdot/' + this.model.get('device');
|
|
|
|
$('#output').empty();
|
|
notification.notify('info', 'Loading...');
|
|
this.collection.fetch(fetchObj);
|
|
} else {
|
|
console.error('Nothing to get!');
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
GraphView = Backbone.View.extend({
|
|
el: $('#graph'), initialize: function() {
|
|
this.modes = ['', 'lux', 'temp', 'co2', 'humid', 'noise'];
|
|
this.titles = [
|
|
'', 'Light Levels', 'Temperature', 'Co2 Levels', 'Humidity', 'Sound'
|
|
];
|
|
|
|
this.mode = 0;
|
|
|
|
// Config AMChart
|
|
this.chart = {};
|
|
this.categoryAxesSettings = new AmCharts.CategoryAxesSettings();
|
|
this.dataSet = new AmCharts.DataSet();
|
|
|
|
console.log('GraphView!');
|
|
_.bindAll(this, 'render', 'updateGraphV2', 'setupChart');
|
|
this.collection.on('update', function(d) {
|
|
console.log('GraphView Collection update trigger!!');
|
|
this.updateGraphV2();
|
|
}, this);
|
|
|
|
}, events: {
|
|
'change select#displaymode': 'changeMode'
|
|
}, setupChart: function() {
|
|
console.log('chart:SetupChart');
|
|
|
|
this.categoryAxesSettings.minPeriod = 'mm';
|
|
this.chart.categoryAxesSettings = this.categoryAxesSettings;
|
|
|
|
this.dataSet.color = '#b0de09';
|
|
|
|
this.chart.dataSets = [this.dataSet];
|
|
},
|
|
|
|
doChartV2: function(chartData) {
|
|
var self = this;
|
|
console.time('doChartV2');
|
|
self.chart = AmCharts.makeChart('chartdiv', {
|
|
type: 'serial',
|
|
theme: 'light',
|
|
|
|
legend: {
|
|
useGraphSettings: true,
|
|
color: '#fff',
|
|
switchColor: '#556374'
|
|
},
|
|
color: '#ffffff',
|
|
dataProvider: chartData,
|
|
dataDateFormat: 'YYYY-MM-DDTHH:NN:SS.QQQ',
|
|
synchronizeGrid: true,
|
|
valueAxes: [
|
|
{
|
|
id: 'lux',
|
|
axisColor: '#FFC802',
|
|
axisThickness: 2,
|
|
axisAlpha: 1,
|
|
position: 'left',
|
|
gridColor: '#556374'
|
|
},
|
|
{
|
|
id: 'co2',
|
|
axisColor: 'rgba(0,191,255,1)',
|
|
axisThickness: 2,
|
|
axisAlpha: 1,
|
|
position: 'right',
|
|
gridColor: '#556374'
|
|
},
|
|
{
|
|
id: 'temp',
|
|
axisColor: 'rgba(46,255,0,1)',
|
|
axisThickness: 2,
|
|
gridAlpha: 0,
|
|
offset: 50,
|
|
axisAlpha: 1,
|
|
position: 'left',
|
|
gridColor: '#556374'
|
|
},
|
|
{
|
|
id: 'humid',
|
|
axisColor: 'rgba(255,0,99,1)',
|
|
axisThickness: 2,
|
|
axisAlpha: 1,
|
|
offset: 50,
|
|
position: 'right',
|
|
gridColor: '#556374'
|
|
}/*,
|
|
{
|
|
id: 'noise',
|
|
axisColor: 'rgb(99, 157, 189)',
|
|
axisThickness: 2,
|
|
gridAlpha: 0,
|
|
offset: 100,
|
|
axisAlpha: 1,
|
|
position: 'left',
|
|
gridColor: '#556374'
|
|
}*/
|
|
,{
|
|
id: 'occupancy',
|
|
axisColor: '#aaaaaa',
|
|
axisThickness: 2,
|
|
gridAlpha: 0,
|
|
offset: 100,
|
|
axisAlpha: 1,
|
|
position: 'right',
|
|
gridColor: '#556374'
|
|
}
|
|
],
|
|
graphs: [{id: 'occ',
|
|
valueAxis: 'occupancy',
|
|
type: 'column',
|
|
clustered: false,
|
|
columnWidth: 1,
|
|
lineColor: '#aaaaaa',
|
|
title: 'Occupancy',
|
|
valueField: 'occupancy',
|
|
fillColor: '#888888',
|
|
fillAlphas: 0.2,
|
|
fillToAxis: 'x'
|
|
},
|
|
{
|
|
valueAxis: 'lux',
|
|
lineColor: '#FFC802',
|
|
title: 'Light Level',
|
|
valueField: 'lux',
|
|
fillAlphas: 0
|
|
},
|
|
{
|
|
valueAxis: 'co2',
|
|
lineColor: 'rgba(0,191,255,1)',
|
|
title: 'Co2',
|
|
valueField: 'co2',
|
|
fillAlphas: 0
|
|
},
|
|
{
|
|
valueAxis: 'temp',
|
|
lineColor: 'rgba(46,255,0,1)',
|
|
title: 'Temperature',
|
|
valueField: 'temp',
|
|
fillAlphas: 0
|
|
},
|
|
{
|
|
valueAxis: 'humid',
|
|
lineColor: 'rgba(255,0,99,1)',
|
|
title: 'Humidity',
|
|
valueField: 'humid',
|
|
fillAlphas: 0
|
|
}/*,
|
|
{
|
|
valueAxis: 'noise',
|
|
lineColor: 'rgb(99, 157, 189)',
|
|
title: 'Sound',
|
|
valueField: 'noise',
|
|
fillAlphas: 0
|
|
}*/
|
|
],
|
|
chartScrollbar: {
|
|
graph: 'occ',oppositeAxis: false,
|
|
offset: 30,
|
|
scrollbarHeight: 80,
|
|
backgroundAlpha: 0,
|
|
selectedBackgroundAlpha: 0.1,
|
|
selectedBackgroundColor: '#888888',
|
|
graphFillAlpha: 0,
|
|
graphLineAlpha: 0.5,
|
|
selectedGraphFillAlpha: 0,
|
|
selectedGraphLineAlpha: 1,
|
|
autoGridCount: true,
|
|
color: '#AAAAAA',
|
|
updateOnReleaseOnly: true
|
|
},
|
|
chartCursor: {
|
|
cursorPosition: 'mouse',
|
|
cursorColor: '#14bfff',
|
|
color: '#000'
|
|
},
|
|
categoryField: 'date',
|
|
categoryAxis: {
|
|
minPeriod: '15mm',
|
|
parseDates: true,
|
|
axisColor: 'rgba(255,255,255,0.8)',
|
|
minorGridEnabled: true,
|
|
gridColor: '#556374'
|
|
},
|
|
export: {
|
|
enabled: true, position: 'bottom-right'
|
|
}
|
|
});
|
|
|
|
$('#chartdiv').empty();
|
|
self.chart.write('chartdiv');
|
|
|
|
console.timeEnd('doChartV2');
|
|
}, updateGraphV2: function() {
|
|
console.time('updateGraphV2');
|
|
var self = this;
|
|
var chartData = [];
|
|
console.time('chartData');
|
|
_(this.collection.models).each(function(i) {
|
|
chartData.push({
|
|
date: i.get('dt'),
|
|
co2: i.get('co2'),
|
|
humid: i.get('humid'),
|
|
lux: i.get('lux'),
|
|
noise: i.get('noise'),
|
|
temp: i.get('temp'),
|
|
occupancy: i.get('occupancy')
|
|
});
|
|
});
|
|
console.timeEnd('chartData');
|
|
console.log('Record count:',chartData.length);
|
|
|
|
// Console.log(chartData);
|
|
self.doChartV2(chartData);
|
|
console.timeEnd('updateGraphV2');
|
|
}
|
|
|
|
});
|
|
|
|
notification.configProfile('global', {
|
|
stacking: false
|
|
});
|
|
var DeviceCollection = new Backbone.Collection;
|
|
var OccupancyCollection = new Backbone.Collection;
|
|
|
|
var mdotCollection = new mDotCollection();
|
|
|
|
var mainSettings = new MainModel();
|
|
var mainview = new MainView({
|
|
collection: mdotCollection, model: mainSettings
|
|
});
|
|
|
|
var mdot = new MDOT({collection: mdotCollection});
|
|
|
|
var grapher = new GraphView({collection: DeviceCollection});
|
|
|
|
})(jQuery);
|