extensions/web-chart/web-chart.js
Rama master · réplica de lectura del repositorio original.
define(['./web-chart.xml'], function(pageXml) {
function isInteger(value) {
return (typeof value === 'number') && isFinite(value) && (Math.floor(value) === value);
}
function isFloat(value) {
return (typeof value === 'number') && isFinite(value) && (Math.floor(value) !== value);
}
function countDigits(value) {
var s = String(value);
var i = s.indexOf('.');
return i > 0 ? s.length - i - 1 : 0;
}
function createNumberChartDataSets(dataPointSet, datasets, prefix) {
datasets = datasets || [];
prefix = prefix || '';
var avgSerie = [];
var minSerie = [];
var maxSerie = [];
var maxDigits = 0;
dataPointSet.forEach(function(item) {
var digits = countDigits(item.min);
if (digits > maxDigits) {
maxDigits = digits;
}
minSerie.push(item.min);
maxSerie.push(item.max);
});
dataPointSet.forEach(function(item) {
var avg = item.average;
if (isFloat(avg)) {
avg = avg.toFixed(maxDigits);
}
avgSerie.push(avg);
});
var hue = (Math.abs(hashString(prefix)) % 240) / 240;
var maxColor = hsvToRgb(hue, 0.7, 0.8);
var avgColor = hsvToRgb(hue, 0.5, 0.8);
var minColor = hsvToRgb(hue, 0.3, 0.8);
datasets.push({
label: prefix + 'Average',
backgroundColor: Chart.helpers.color(avgColor).alpha(0.5).rgbString(),
borderColor: avgColor,
fill: false,
data: avgSerie
}, {
label: prefix + 'Max',
hidden: true,
backgroundColor: Chart.helpers.color(maxColor).alpha(0.5).rgbString(),
borderColor: maxColor,
fill: false,
data: maxSerie
}, {
label: prefix + 'Min',
hidden: true,
backgroundColor: Chart.helpers.color(minColor).alpha(0.5).rgbString(),
borderColor: minColor,
fill: false,
data: minSerie
});
return datasets;
}
function createMappedChartDataSets(dataPointSet, datasets, prefix, map) {
datasets = datasets || [];
prefix = prefix || '';
var chgSerie = [];
var valSerie = [];
dataPointSet.forEach(function(item) {
chgSerie.push(item.changes);
valSerie.push(item.index);
});
var hue = (Math.abs(hashString(prefix) + 120) % 240) / 240;
var hue2 = (Math.abs(hashString(prefix) + 180) % 240) / 240;
var valColor = hsvToRgb(hue, 0.5, 0.8);
var chgColor = hsvToRgb(hue2, 0.5, 0.8);
datasets.push({
label: prefix + 'Values',
backgroundColor: Chart.helpers.color(valColor).alpha(0.5).rgbString(),
borderColor: valColor,
fill: false,
steppedLine: 'after',
pointStyle: 'rectRot',
//pointRadius: 3,
//yAxisID: 'map',
map: map,
data: valSerie
});
datasets.push({
label: prefix + 'Changes',
hidden: true,
backgroundColor: Chart.helpers.color(chgColor).alpha(0.5).rgbString(),
borderColor: chgColor,
fill: false,
steppedLine: 'after',
pointStyle: 'triangle',
data: chgSerie
});
return datasets;
}
function createChangesChartDataSets(dataPointSet, datasets, prefix) {
datasets = datasets || [];
prefix = prefix || '';
var chgSerie = [];
dataPointSet.forEach(function(item) {
chgSerie.push(item.changes);
});
var hue = (Math.abs(hashString(prefix) + 60) % 240) / 240;
var chgColor = hsvToRgb(hue, 0.5, 0.8);
datasets.push({
label: prefix + 'Changes',
backgroundColor: Chart.helpers.color(chgColor).alpha(0.5).rgbString(),
borderColor: chgColor,
fill: true,
data: chgSerie
});
return datasets;
}
function listDates(dataPointSet) {
var dates = [];
var labels = [];
dataPointSet.forEach(function(item) {
var date = new Date(item.time * 1000);
dates.push(date);
labels.push(date.toISOString().substring(11, 16));
});
return dates;
}
function createChartDataSets(dataPointSet, datasets, prefix) {
//console.log('createChartDataSets()', dataPointSet, datasets, prefix);
if (dataPointSet.length === 0) {
return [];
}
var firstItem = dataPointSet[0];
if ('map' in firstItem) {
return createMappedChartDataSets(dataPointSet, datasets, prefix, firstItem.map);
}
if ((firstItem.type === 'number') || (firstItem.type === 'integer')) {
return createNumberChartDataSets(dataPointSet, datasets, prefix);
}
// TODO Remove
var lastItem = dataPointSet[dataPointSet.length - 1];
if ('average' in lastItem) {
return createNumberChartDataSets(dataPointSet, datasets, prefix);
}
if ('changes' in lastItem) {
return createChangesChartDataSets(dataPointSet, datasets, prefix);
}
return [];
}
function loadChart() {
if (typeof Chart === 'undefined') {
return loadScripts('static/moment.js', 'static/chart/Chart.min.js');
}
return Promise.resolve();
}
/************************************************************
* Chart
************************************************************/
var vue = new Vue({
template: pageXml,
data: {
chartType: 'line',
chartTension: 0,
chartBeginAtZero: true,
path: '',
paths: [],
toDays: 0,
things: [],
duration: 43200,
period: 0,
chart: null
},
methods: {
getHistoricalDataHeaders: function() {
var duration = parseInt(this.duration, 10);
var period = parseInt(this.period, 10);
if (period <= 0) {
period = duration / 120; // how many data points
period = Math.floor(period / 300) * 300; // round to 5 min
period = Math.max(900, Math.min(period, SEC_IN_DAY)); // between 15 min to one day
//console.log('auto period is ' + period);
}
var toDays = parseInt(this.toDays, 10);
var toDate = new Date(Date.now() - (toDays * SEC_IN_DAY * 1000));
var nextHour = new Date(toDate.getFullYear(), toDate.getMonth(), toDate.getDate(), toDate.getHours() + 1);
var tomorrow = new Date(toDate.getFullYear(), toDate.getMonth(), toDate.getDate() + 1);
var toTime = dateToSec(duration > SEC_IN_DAY ? tomorrow : nextHour);
var fromTime = toTime - duration;
return {
"X-FROM-TIME": fromTime,
"X-TO-TIME": toTime,
"X-PERIOD": period
};
},
loadMultiHistoricalData: function(paths) {
this.paths = paths;
var headers = this.getHistoricalDataHeaders();
headers["X-PATHS"] = paths.join(',');
var self = this;
fetch('/engine/historicalData/', {
method: 'GET',
headers: headers
}).then(getResponseJson).then(function(dataPointSets) {
//console.log('fetch()', response);
var dates = null;
var datasets = [];
dataPointSets.forEach(function(dataPointSet, i) {
if (dates === null) {
dates = listDates(dataPointSet);
}
createChartDataSets(dataPointSet, datasets, '' + (i + 1) + ' ');
});
self.createChart(dates, datasets);
});
},
loadHistoricalData: function(path) {
this.path = path;
var self = this;
loadChart().then(function() {
return fetch('/engine/historicalData/' + path, {
method: 'GET',
headers: self.getHistoricalDataHeaders()
});
}).then(getResponseJson).then(function(dataPointSet) {
//console.log('fetch()', response);
self.createChart(listDates(dataPointSet), createChartDataSets(dataPointSet));
});
},
openPath: function() {
//console.info('openPath() ' + this.path);
if (this.path) {
app.toPage('data-chart', this.path);
}
},
onShow: function(path) {
if (path) {
this.loadHistoricalData(path);
}
//console.log('onShow(' + path + ') path = "' + this.path + '"');
var self = this;
app.getThings().then(function(things) {
self.things = things;
});
},
cleanMultiHistoricalData: function() {
this.paths = [];
},
pushMultiHistoricalData: function() {
if (this.path) {
this.paths.push(this.path);
this.path = '';
this.reloadHistoricalData();
}
},
reloadHistoricalData: function() {
if (this.path) {
this.loadHistoricalData(this.path);
} else if (this.paths.length > 0) {
this.loadMultiHistoricalData(this.paths);
}
},
createChart: function(dates, datasets) {
if (this.chart) {
this.chart.destroy();
}
//var lastIndex = title.lastIndexOf('/');
//if (lastIndex > 0) { title = title.substring(lastIndex + 1); }
var chartTension = parseFloat(this.chartTension);
var chartBeginAtZero = parseBoolean(this.chartBeginAtZero);
var yAxes = [{
scaleLabel: {
labelString: 'Value',
//display: true
},
//display: true,
//position: 'left',
ticks: {
//suggestedMin: 10,
//callback: ticksCallback,
//suggestedMax: suggestedMax,
beginAtZero: chartBeginAtZero
}
}];
datasets.forEach(function(dataset, index) {
if ('map' in dataset) {
// https://www.chartjs.org/samples/latest/scales/non-numeric-y.html
var map = dataset.map;
delete dataset.map;
dataset.yAxisID = 'map';
yAxes.push({
id: dataset.yAxisID,
//type: 'category',
//display: true,
position: 'right',
ticks: {
beginAtZero: true,
callback: function(value) {
if (isInteger(value) && (value >= 1) && (value <= map.length)) {
return map[value - 1];
}
return undefined;
},
suggestedMax: 3
}
});
}
});
//console.info('chart', datasets, yAxes);
this.chart = new Chart('chart-data-view-canvas', {
type: this.chartType,
data: {
labels: dates,
datasets: datasets
},
options: {
//responsive: true,
elements: {
line: {
tension: chartTension
},
rectangle: {
borderWidth: 1
}
},
legend: {
display: true,
position: 'bottom'
},
scales: {
xAxes: [{
type: 'time',
time: {
parser: 'MM/DD/YYYY HH:mm',
tooltipFormat: 'll HH:mm'
},
scaleLabel: {
display: true,
labelString: 'Date'
}
}],
yAxes: yAxes
}
}
});
}
}
});
addPageComponent(vue, 'chart-line', true);
});