(function () { 'use strict'; angular .module('hyperion') .controller('system.DashboardController', ['$http', '$state', '$stateParams', '$rootScope', '$scope', '$timeout', '$sessionStorage', '$location', '$filter', 'timerange', 'sysClock', 'CONFIG', '$q', 'authService', function($http, $state, $stateParams, $rootScope, $scope, $timeout, $sessionStorage, $location, $filter, timerange, sysClock, CONFIG, $q, authService){ $rootScope.$watch('project', function (newValue, oldValue, scope) { if(newValue!==oldValue){ load_data().catch(error => { // dashboard data console.warn(error); }); } }); // set #content div margin if($sessionStorage.currentUser !== undefined) { if($sessionStorage.currentUser.smallMenu) { $("#content").css('margin-left', 50); } } var scopeIsActive = true; $scope.$on('$destroy', function() { scopeIsActive = false; }); // Charts Loading $scope.loading_top_charts = true; // combination of chart_powertemp + chart_powertemp_pie $scope.loading_modules = true; var time = timerange.calc($sessionStorage.currentUser.timerange); $scope.$on('trUpdate', function(event, payload) { // time range update event console.log('trUpdate event: ' + $sessionStorage.currentUser.timerange); time = timerange.calc($sessionStorage.currentUser.timerange); load_data().catch(error => { // dashboard data console.warn(error); }); }); function load_data() { return new Promise((resolve, reject) => { $scope.isLoading = true; // show ajax loader (global) $scope.loading_top_charts = true; // loading top charts $scope.loading_modules = true; // loading modules information // show ajax loaders $scope.project = { locations_total: null, equipment_total: null, events_total: null, events_warning: null, events_alert: null, power_consumption: null, co2_emission: null }; let time_start = moment(time.start).format('YYYY-MM-DDTHH:mm:ss'); let time_end = moment(time.today).format('YYYY-MM-DDTHH:mm:ss'); console.log('date_start: ' + time_start); console.log('date_end: ' + time_end); console.log('$rootScope.project: ', $rootScope.project); const promise_project = new Promise((resolve, reject) => { authService.getJWTAuth().then(authHeader => { var request = {'query': 'query { ' + 'projects(id:"' + $rootScope.project + '", time_start:"' + time_start + '", time_end:"' + time_end + '", timezone:"' + sysClock.getTimeZone() + '") { ' + 'id ' + 'name ' + 'locations_total ' + 'equipment_total ' + 'events_notice ' + 'events_warning ' + 'events_alert ' + 'power_consumption ' + 'co2_emission ' + '} ' + '}' }; $http({ method: 'POST', url: CONFIG.APP_API, data: request, headers: authHeader }).then( function(response){ // resolve $scope.project = response.data.data.projects[0]; $scope.project.events_total = parseInt($scope.project.events_notice?$scope.project.events_notice:0) + parseInt($scope.project.events_warning?$scope.project.events_warning:0) + parseInt($scope.project.events_alert?$scope.project.events_alert:0); $scope.project.events_notice = $scope.project.events_notice?$scope.project.events_notice:0; $scope.project.events_warning = $scope.project.events_warning?$scope.project.events_warning:0; $scope.project.events_alert = $scope.project.events_alert?$scope.project.events_alert:0; $scope.project.power_consumption = $scope.project.power_consumption?$scope.project.power_consumption:0; $scope.project.co2_emission = $scope.project.co2_emission?$scope.project.co2_emission:0; console.log($scope.project); resolve(null); },function(error) { // failure console.error(error); reject(); } ); }); }); const promise_modules = new Promise((resolve, reject) => { authService.getJWTAuth().then(authHeader => { var request = {'query': 'query { ' + 'module_event_daily(project:"' + $rootScope.project + '", time_start:"' + time_start + '", time_end:"' + time_end + '", timezone:"' + sysClock.getTimeZone() + '") { ' + 'id ' + 'name ' + 'icon ' + 'type_ids ' + 'devices ' + 'events_total ' + 'events_notice ' + 'events_warning ' + 'events_alert ' + 'events ' + '} ' + '}' }; $http({ method: 'POST', url: CONFIG.APP_API, data: request, headers: authHeader }).then( function(response){ // resolve $scope.modules = response.data.data.module_event_daily; $scope.loading_modules = false; resolve(null); },function(error) { // failure console.error(error); reject(null); } ); }); }); Promise.all([ promise_project, promise_modules ]) .then(() => { console.log('Promise.all'); $scope.isLoading = false; // hide ajax loader $scope.modules.forEach(module => { let chart_data = []; module.events.forEach(element => { chart_data.push({x: element.epoch, y: element.total}); }) new Highcharts.Chart({ chart: { type: 'column', renderTo: 'chart_' + module.name + '_events', height: 55, borderWidth: 0, marginTop: 0, marginRight: 10, marginLeft: 10, marginBottom: 0, spacingLeft: 0, spacingRight: 0, spacingBottom: 0, spacingTop: 0, backgroundColor: 'transparent' }, time: { useUTC: true}, title: { text: null }, subtitle: { text: null }, exporting: { enabled: false }, credits: { enabled: false }, legend: { enabled:false }, tooltip: { outside: true, useHTML: true, backgroundColor: '#fff', borderWidth: 0, formatter: function () { return '
'+moment(this.point.x).format('ll') + '
' + this.point.y + ' events
'; } }, xAxis: { labels: { enabled: false }, type: 'datetime', tickInterval: 86400000, // 24 * 3600 * 1000 = 1 day }, yAxis: { labels: { enabled: false }, gridLineWidth: 0 }, plotOptions: { column: { stacking: 'normal', pointPadding: 0, borderWidth: 0, dataLabels: { enabled: false } }, series: { name: 'Events', minPointLength: 3, borderWidth: 0, dataLabels: { enabled: false }, marker: { enabled: false } } }, series: [{ data: chart_data }] }, function(chart){ }); }); create_chart_objects(); // create charts & load data resolve(null); }); }); } $timeout(() => { load_data() .catch(error => { console.warn(error); }); }, 100); $scope.viewLocation = function(location_id) { $location.path('/system/locations/view/'+location_id); }; //////////////////////////////////////////////////////////////////////////////////////////////////////////// // HIGHCHARTS CONFIG Highcharts.setOptions({ time: { timezone: sysClock.getTimeZone() } }); Highcharts.dateFormats = { 'Z': () => { return moment.tz(sysClock.getTimeZone()).format('Z'); } }; const chart_power_consumption_pie_colors = Highcharts.getOptions().colors.map((c, i) => // Start out with a darkened base color (negative brighten), and end up with a much brighter color Highcharts.color('#0197FE') .brighten((i - 3) / 7) .get() ); //////////////////////////////////////////////////////////////////////////////////////////////////////////// // GLOBAL Chart Data Functions const chart_clean = (chart) => { // remove data servies (empty graph) while(chart.series.length) { chart.series[0].remove(); } } const load_daily_data = () => { // prepare loading 1d aggregation data to graph return new Promise((resolve) => { $scope.chart_drilldown = false; // mark drolldown false $scope.chart_drilldown_timestamp = null; // remove drilldown timestamp chart_clean(chart_powertemp_consumption); // remove data servies (empty graph) //$scope.chart_consumption_title = 'Last ' + $sessionStorage.currentUser.timerange + ' Days Power Consumption'; // set graph title /* let tickInterval = 86400000; // 3600 * 24 * 1000 = 1 day (tickInterval = 1d in milliseconds) let time_start_truncated = moment.utc(time.start).startOf('day').valueOf(); // round up to beginning of the 1st day let time_end_truncated = moment.utc(time.today).add(1, 'days').startOf('day').subtract(1, 'seconds').valueOf(); // round down to end of the last day console.log('>> time_start_truncated:', time_start_truncated); console.log('>> time_end_truncated:', time_end_truncated); chart_powertemp_consumption.xAxis.forEach((xaxis, i) => { // set graph X Axis (time:days) display range xaxis.options.tickInterval = tickInterval; xaxis.setExtremes(time_start_truncated, time_end_truncated, true); }); chart_events_heatmap.xAxis.forEach((xaxis, i) => { // set graph X Axis (time:days) display range xaxis.options.tickInterval = tickInterval; xaxis.setExtremes(time_start_truncated, time_end_truncated, true); }); let time_start = moment.utc(time_start_truncated).format('YYYY-MM-DDTHH:mm:ss'); let time_end = moment.utc(time_end_truncated).format('YYYY-MM-DDTHH:mm:ss'); */ let time_start = moment.utc(time.start).format('YYYY-MM-DDTHH:mm:ss'); let time_end = moment.utc(time.today).format('YYYY-MM-DDTHH:mm:ss'); console.log('>> time_start:', time_start); console.log('>> time_end:', time_end); console.log('**** ' + moment.tz(sysClock.getTimeZone()).format('YYYY-MM-DDTHH:mm:ss')); Promise.all([ chart_add_power_daily(time_start, time_end), chart_add_power_module(time_start, time_end), chart_add_events(time_start, time_end) ]) .then(() => { console.log('Promise.all'); resolve(null); }); }); } //////////////////////////////////////////////////////////////////////////////////////////////////////////// const chart_add_power_daily = (time_start, time_end) => { console.log('chart_add_power_daily'); return new Promise((resolve) => { let power_series = { name: "Power Consumption", type: "column", marker: { symbol: 'circle' }, yAxis: 0, zIndex: 1, tooltip: { valueSuffix: 'kWh' }, color: '#0197fe', data: [] }; authService.getJWTAuth().then(authHeader => { var request = {'query': 'query { ' + 'power_daily(object:"' + $rootScope.project + '", time_start:"' + time_start + '", time_end:"' + time_end + '", timezone:"' + sysClock.getTimeZone() + '") { ' + 'step ' + 'epoch ' + 'p_avg ' + '} ' + '}' }; $http({ method: 'POST', url: CONFIG.APP_API, data: request, headers: authHeader }).then( (response) => { // resolve response.data.data.power_daily.forEach(day => { power_series.data.push({ x: parseInt(day.epoch, 10), y: (day.p_avg?day.p_avg:0) }); }); chart_powertemp_consumption.addSeries(power_series); console.log(power_series.data); resolve(null); },(error) => { // failure console.error(error); } ); }); }); } //////////////////////////////////////////////////////////////////////////////////////////////////////////// const chart_add_power_module = (time_start, time_end) => { console.log('chart_add_power_module'); return new Promise((resolve) => { let power_module = { name: "Power Consumption", data: [] }; let total = 0; authService.getJWTAuth().then(authHeader => { var request = {'query': 'query { ' + 'power_module(object:"' + $rootScope.project + '", time_start:"' + time_start + '", time_end:"' + time_end + '", timezone:"' + sysClock.getTimeZone() + '") { ' + 'module ' + 'p_avg ' + '} ' + '}' }; $http({ method: 'POST', url: CONFIG.APP_API, data: request, headers: authHeader }).then( (response) => { // resolve response.data.data.power_module.forEach(module => { total += (module.p_avg?module.p_avg:0); }); console.log(total); if(total) { response.data.data.power_module.forEach(module => { power_module.data.push({ name: module.module, value: (module.p_avg?module.p_avg:0), y: ((module.p_avg?(module.p_avg/total):0))*100 }); }); } else { power_module.data.push({name: 'none',y: 100}); } console.log(power_module.data); chart_power_pie.addSeries(power_module); console.log('resp'); resolve(null); },(error) => { // failure console.error(error); } ); }); }); } //////////////////////////////////////////////////////////////////////////////////////////////////////////// const chart_add_events = (time_start, time_end) => { console.log('chart_add_events'); return new Promise((resolve) => { let event_series = { name: 'events data', borderWidth: 0.2, borderColor: '#ffffff', colsize: 86400000, // one day data: [], dataLabels: { enabled: false, // <-- show number of events } }; authService.getJWTAuth().then(authHeader => { var request = {'query': 'query { ' + 'event_daily(object:"' + $rootScope.project + '", time_start:"' + time_start + '", time_end:"' + time_end + '", timezone:"' + sysClock.getTimeZone() + '") { ' + 'step ' + 'epoch ' + 'total ' + 'notice ' + 'warning ' + 'alert ' + '} ' + '}' }; $http({ method: 'POST', url: CONFIG.APP_API, data: request, headers: authHeader }).then( (response) => { // resolve console.log('response', response); let total = 0; let max_total = 0; response.data.data.event_daily.forEach(day => { total = (day.total?day.total:0); console.log('total: ' + total); max_total = total>max_total?total:max_total; event_series.data.push([ parseInt(day.epoch), 0, total ]); }); chart_events_heatmap.addSeries(event_series); console.log('max_total: ' + max_total); console.log(chart_events_heatmap.colorAxis) if(max_total == 0) { chart_events_heatmap.colorAxis[0].update({ min: 0, minColor: 'transparent', maxColor: 'transparent' }); } resolve(null); },(error) => { // failure console.error(error); } ); }); }); } //////////////////////////////////////////////////////////////////////////////////////////////////////////// // CHARTS: Power Consumption + Pie var chart_powertemp_consumption = null; // chart object var chart_power_pie = null; // chart object var chart_events_heatmap = null; // chart object const create_chart_objects = () => { const obj_chart_powertemp = new Promise((resolve, reject) => { new Highcharts.Chart({ chart: { renderTo: 'chart_powertemp', height: 200, minHeight: 200, borderWidth: 0, marginTop: 0, marginRight: 0, marginLeft: 0, marginBottom: 0, spacingLeft: 0, spacingRight: 0, spacingBottom: 0, spacingTop: 0, backgroundColor: 'transparent', plotBackgroundColor: null, plotBorderWidth: null, plotShadow: false, }, time: { useUTC: true }, title: { text: null }, subtitle: { text: null }, exporting: { enabled: false }, credits: { enabled: false }, legend: { enabled: false }, accessibility: { announceNewData: { enabled: true } }, xAxis: { type: 'datetime', tickInterval: 86400000, // 24 * 3600 * 1000 = 1 day }, yAxis: [{ // Power Consumption labels: { format: '{value}kWh', style: { color: Highcharts.getOptions().colors[0] } }, title: { text: 'Power Consumption', style: { color: Highcharts.getOptions().colors[0] } }, gridLineWidth: 0, min: 0, minRange: 2 }], tooltip: { shared: true, outside: true, useHTML: true, backgroundColor: '#fff', borderWidth: 0 }, plotOptions: { column: { stacking: 'normal', pointPadding: 0, borderWidth: 0, dataLabels: { enabled: false } }, series: { cursor: 'pointer', pointPadding: 0.07, groupPadding: 0.07, borderWidth: 0.07, minPointLength: 3, shadow: false, dataLabels: { enabled: false }, marker: { enabled: false }, } }, series: [] }, (chart) => { chart_powertemp_consumption = chart; resolve(); }); }); const obj_chart_power_pie = new Promise((resolve, reject) => { new Highcharts.Chart({ chart: { type: 'pie', renderTo: 'chart_power_pie', height: 200, borderWidth: 0, marginTop: 0, marginRight: 0, marginLeft: 0, marginBottom: 0, spacingLeft: 0, spacingRight: 0, spacingBottom: 0, spacingTop: 0, backgroundColor: 'transparent', plotBackgroundColor: null, plotBorderWidth: null, plotShadow: false, }, time: {useUTC: true}, title: {text: null}, subtitle: {text: null}, legend: {enabled: true}, exporting: {enabled: false}, credits: {enabled: false}, plotOptions: { pie: { borderRadius: 5, borderWidth: 0, colors: chart_power_consumption_pie_colors, dataLabels: { enabled: false, }, center: ['50%', '50%'] }, series: { dataSorting: { enabled: true }, innerSize: '50%', dataLabels: { enabled: false, format: '{point.name}: {point.y:.1f}%' } } }, tooltip: { //pointFormat: '{series.name}: {point.percentage:.1f}%' headerFormat: null, pointFormat: '{point.name}{point.y:.2f}%
of total Energy Consumption
', backgroundColor: '#fff' }, series: [] }, function(chart){ chart_power_pie = chart; resolve(); }); }); const obj_chart_events_heatmap = new Promise((resolve, reject) => { new Highcharts.Chart({ chart: { type: 'heatmap', renderTo: 'chart_events', height: 15, borderWidth: 0, marginTop: 0, marginRight: 0, marginLeft: 0, marginBottom: 0, spacingLeft: 0, spacingRight: 0, spacingBottom: 0, spacingTop: 0, backgroundColor: 'transparent', plotBackgroundColor: null, plotBorderWidth: null, plotShadow: false, events: { addSeries: function () { /* var label = this.renderer.label('A series was added, about to redraw chart', 100, 120) .attr({ fill: Highcharts.getOptions().colors[0], padding: 10, r: 5, zIndex: 8 }) .css({ color: '#FFFFFF' }) .add(); setTimeout(function () { label.fadeOut(); }, 1000); */ }/* addSeries: function() { console.log('----------------------------------------------'); console.log('chart_events_heatmap fn:addSeries:'); console.log('series count:', this.series.length); if(this.series.length) { this.series[0].data.forEach(element => { console.log(element.value); if(element.value == 0) { element.update({color: 'transparent'}); } }); } } */ } }, time: { useUTC: true }, title: { text: null }, subtitle: { text: null }, tooltip: { enabled: false }, exporting: { enabled: false }, credits: { enabled: false }, legend: { enabled: false }, accessibility: { announceNewData: { enabled: true } }, xAxis: { type: 'datetime', tickInterval: 86400000, // 24 * 3600 * 1000 = 1 day }, yAxis: { categories: ['POWER'], title: null, minPadding: 0, maxPadding: 0, startOnTick: false, endOnTick: false, tickWidth: 1, gridLineWidth: 0 }, colorAxis: { min: 0, stops: [ [0, '#27ae60'], [0.0001, '#ffc312'], [1, '#ea2027'] ] }, legend: { enabled: false }, plotOptions: { heatmap: { dataLabels: { enabled: false }, }, series: { dataLabels: { enabled: false }, point: { events: { click: function(event) { //link.redirect('#!/system/events', '/system/events/' + moment(this.x).format('YYYY-MM-DD')); } } } } }, tooltip: { outside: true, useHTML: true, backgroundColor: '#fff', borderWidth: 0, formatter: function () { return '
'+moment(this.point.x).format('ll') + '
' + this.point.value + ' events
'; } }, series: [] }, function(chart){ chart_events_heatmap = chart; resolve(); }); }); Promise.all([ obj_chart_powertemp, obj_chart_power_pie, obj_chart_events_heatmap ]) .then(() => { console.log('Promise.all'); load_daily_data() .then(() => { $scope.loading_top_charts = false; }); }); }; //////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////// }]) })();