Create alarm list component

Creating an alarm list component to show all active events count

July 15th, 2020


This is the 8th article of a Dashboard development series. You can check all the articles by clicking here

In this article, we are going to build the next component:

Alarm list component

This component shows the total consumed energy as a bar chart and the kW/ton ratio as a line chart. Let's start by creating the alarmList.js and alarmList.html files in the components/alarmList directory:

alarmList.js

/**
 * @copyright 2020 {@link http://infiniteautomation.com|Infinite Automation Systems, Inc.} All rights reserved.
 * @author Luis Güette
 */

define(['angular', 'require'], (angular, require) => {
    'use strict';

    class AlarmListController {
        static get $$ngIsClass() {
            return true;
        }

        static get $inject() {
            return [];
        }

        constructor() {

        }

        $onInit() {

        }
    }

    return {
        bindings: {

        },
        controller: AlarmListController,
        templateUrl: require.toUrl('./alarmList.html')
    };
});

alarmList.html

<h1>Alarm List</h1>

Then, we need to import it in hvac.js module:

hvac.js

/**
 * @copyright 2020 {@link http://infiniteautomation.com|Infinite Automation Systems, Inc.} All rights reserved.
 * @author Luis Güette
 */

define([
    'angular',
    'require',

    './pages/overview/overview.js',

    './components/map/map.js',
    './components/selectedUnitCard/selectedUnitCard.js',
    './components/unitsTable/unitsTable.js',
    './components/kpiIndicators/kpiIndicators.js',
    './components/energyChart/energyChart.js',
    './components/alarmList/alarmList.js',

    './services/unit.js'
], (
    angular,
    require,
    overview,
    map,
    selectedUnitCard,
    unitsTable,
    kpiIndicators,
    energyChart,
    alarmList,
    unitService
) => {
    'use strict';

    const hvacModule = angular
        .module('hvacModule', ['maUiApp'])
        .component('hvacOverview', overview)
        .component('hvacMap', map)
        .component('hvacSelectedUnitCard', selectedUnitCard)
        .component('hvacUnitsTable', unitsTable)
        .component('hvacKpiIndicators', kpiIndicators)
        .component('hvacAlarmList', alarmList)
        .component('hvacEnergyChart', energyChart)

        .factory('hvacUnit', unitService);

    hvacModule.config([
        'maUiMenuProvider',
        (maUiMenuProvider) => {
            maUiMenuProvider.registerMenuItems([
                {
                    name: 'ui.overview',
                    url: '/overview',
                    menuIcon: 'map',
                    template: '<hvac-overview></hvac-overview>',
                    menuText: 'Overview',
                    weight: 100,
                    params: {
                        dateBar: {
                            rollupControls: true
                        }
                    }
                },
            ]);
        }
    ]);

    return hvacModule;
}); // define

Let's call the hvac-energy-chart component in the overview.html file:

overview.html

<div class="left-container" layout="row" layout-wrap layout-align="space-between start">
    <div flex="100" flex-gt-sm="50" flex-gt-md="60">
        <hvac-kpi-indicators units-count="$ctrl.units.length"></hvac-kpi-indicators>

        <md-card>
            <md-card-header>
                <p md-colors="::{color: 'accent'}">Active Alarms</p>
            </md-card-header>

            <md-card-content>
                <hvac-map units="$ctrl.units" on-select-unit="$ctrl.onSelectUnit(unit)"></hvac-map>
            </md-card-content>
        </md-card>

        <hvac-selected-unit-card ng-if="$ctrl.selectedUnit" unit="$ctrl.selectedUnit"></hvac-selected-unit-card>
    </div>
    <div class="right-container" flex="100" flex-gt-sm="50" flex-gt-md="40">

        <div layout="row" layout-wrap layout-align="space-between stretch">
            <div class="energy-chart-container" flex="100" flex-gt-md="60">
                <md-card>
                    <md-card-header>
                        <p md-colors="::{color: 'accent'}">Energy vs kW/ton ratio</p>
                    </md-card-header>

                    <md-card-content>
                        <hvac-energy-chart></hvac-energy-chart>
                    </md-card-content>
                </md-card>
            </div>

            <div class="alarms-list-container" flex="100" flex-gt-md="40">
                <md-card flex="100">
                    <md-card-header>
                        <p md-colors="::{color: 'accent'}">Active Alarms</p>
                    </md-card-header>
                    <md-card-content>
                        <hvac-alarm-list></hvac-alarm-list>
                    </md-card-content>
                </md-card>
            </div>
        </div>

        <md-card>
            <md-card-header>
                <p md-colors="::{color: 'accent'}">Units</p>
            </md-card-header>

            <md-card-content>
                <hvac-units-table units="$ctrl.units"></hvac-units-table>
            </md-card-content>
        </md-card>
    </div>
</div>

Reload the page, and you should see something like this:

Overview page

First, we need to get the events count, we will do this in alarmList.js:

alarmList.js

/**
 * @copyright 2020 {@link http://infiniteautomation.com|Infinite Automation Systems, Inc.} All rights reserved.
 * @author Luis Güette
 */

define(['angular', 'require'], (angular, require) => {
    'use strict';

    const EVENT_KEYS = {
        'NONE': 'none',
        'INFORMATION': 'information',
        'IMPORTANT': 'important',
        'WARNING': 'warning',
        'URGENT': 'urgent',
        'CRITICAL': 'critical',
        'SAFETY': 'safety',
    }

    class AlarmListController {
        static get $$ngIsClass() {
            return true;
        }

        static get $inject() {
            return ['maEvents', '$scope'];
        }

        constructor(Events, $scope) {
            this.Events = Events;
            this.$scope = $scope;
        }

        $onInit() {
            this.getEvents();
        }

        getEvents() {
            this.Events
                .buildQuery()
                .or()
                .eq('active', true)
                .limit(1000)
                .query()
                .then(events => {
                    this.eventsCount = this.mapEvents(events);
                    this.subscribeToEvents();
                });
        }

        mapEvents(events) {
            return events.reduce((result, event) => {
                const shortName = EVENT_KEYS[event.alarmLevel];

                if (Object.keys(EVENT_KEYS).includes(event.alarmLevel)) {
                    result[shortName] += 1;
                }

                return result;
            }, {
                none: 0,
                information: 0,
                important: 0,
                warning: 0,
                urgent: 0,
                critical: 0,
                safety: 0,
            });
        }

        subscribeToEvents () {
            this.Events.notificationManager.subscribe((event, mangoEvent) => {
                if (mangoEvent.id < 0) {
                    return;
                }

                const shortName = EVENT_KEYS[mangoEvent.alarmLevel];

                if (event.name === 'RAISED' && mangoEvent.active) {
                    this.eventsCount[shortName] += 1;
                }

                if (event.name === 'RETURN_TO_NORMAL' && !mangoEvent.active) {
                    this.eventsCount[shortName] -= 1;
                }

            }, this.$scope, ['RAISED', 'RETURN_TO_NORMAL']);
        }
    }

    return {
        bindings: {

        },
        controller: AlarmListController,
        templateUrl: require.toUrl('./alarmList.html')
    };
});
  • With getEvents() we get all the active events and calculate the counts by the alarm level.
  • Then we call subscribeToEvents(), which will update the counts in real time if we have new alarms or if any alarm has returned to normal state

Now, we update the alarmList.html and the hvac.css to show this data:

alarmList.html

<div>
    <div class="header" layout="row" layout-align="space-between center">
        <span>Alarm Level</span>
        <span>Count</span>
    </div>

    <div class="body">
        <div layout="row" layout-align="space-between center">
            <span>
                <md-icon class="fa fa-flag fa-lg ma-alarm-flag ma-alarm-level-life-safety"></md-icon>
                Safety
            </span>
            <span ng-bind="$ctrl.eventsCount.safety"></span>
        </div>
        <div layout="row" layout-align="space-between center">
            <span>
                <md-icon class="fa fa-flag fa-lg ma-alarm-flag ma-alarm-level-critical"></md-icon>
                Critical
            </span>
            <span ng-bind="$ctrl.eventsCount.critical"></span>
        </div>
        <div layout="row" layout-align="space-between center">
            <span>
                <md-icon class="fa fa-flag fa-lg ma-alarm-flag ma-alarm-level-urgent"></md-icon>
                Urgent
            </span>
            <span ng-bind="$ctrl.eventsCount.urgent"></span>
        </div>
        <div layout="row" layout-align="space-between center">
            <span>
                <md-icon class="fa fa-flag fa-lg ma-alarm-flag ma-alarm-level-warning"></md-icon>
                Warning
            </span>
            <span ng-bind="$ctrl.eventsCount.warning"></span>
        </div>
        <div layout="row" layout-align="space-between center">
            <span>
                <md-icon class="fa fa-flag fa-lg ma-alarm-flag ma-alarm-level-important"></md-icon>
                Important
            </span>
            <span ng-bind="$ctrl.eventsCount.important"></span>
        </div>
        <div layout="row" layout-align="space-between center">
            <span>
                <md-icon class="fa fa-flag fa-lg ma-alarm-flag ma-alarm-level-information"></md-icon>
                Information
            </span>
            <span ng-bind="$ctrl.eventsCount.information"></span>
        </div>
    </div>
</div>
  • We use the same flag colors that Mango use by default

hvac.css

...
hvac-alarm-list div .header {
    padding: 0.5rem 0;
    border-bottom: var(--ma-primary-700) solid 1px;
    color: var(--ma-accent);
    font-size: 1.25rem;
    text-transform: uppercase;
    font-weight: 700;
}

hvac-alarm-list div .body {
    margin-top: 0.5rem;
    color: var(--ma-primary-700);
    font-size: 1.5rem;
    text-transform: uppercase;
}

hvac-alarm-list div .body div span:last-child {
    font-weight: 700;
}
...

Finally, let's update the overview.html to add a link to the events page:

overview.html

 <div class="left-container" layout="row" layout-wrap layout-align="space-between start">
    <div flex="100" flex-gt-sm="50" flex-gt-md="60">
        <hvac-kpi-indicators units-count="$ctrl.units.length"></hvac-kpi-indicators>

        <md-card>
            <md-card-header>
                <p md-colors="::{color: 'accent'}">Active Alarms</p>
            </md-card-header>

            <md-card-content>
                <hvac-map units="$ctrl.units" on-select-unit="$ctrl.onSelectUnit(unit)"></hvac-map>
            </md-card-content>
        </md-card>

        <hvac-selected-unit-card ng-if="$ctrl.selectedUnit" unit="$ctrl.selectedUnit"></hvac-selected-unit-card>
    </div>
    <div class="right-container" flex="100" flex-gt-sm="50" flex-gt-md="40">

        <div layout="row" layout-wrap layout-align="space-between stretch">
            <div class="energy-chart-container" flex="100" flex-gt-md="60">
                <md-card>
                    <md-card-header>
                        <p md-colors="::{color: 'accent'}">Energy vs kW/ton ratio</p>
                    </md-card-header>

                    <md-card-content>
                        <hvac-energy-chart></hvac-energy-chart>
                    </md-card-content>
                </md-card>
            </div>

            <div class="alarms-list-container" flex="100" flex-gt-md="40">
                <md-card flex="100">
                    <md-card-header layout="row" layout-align="space-between center">
                        <p md-colors="::{color: 'accent'}">Active Alarms</p>
                        <a ui-sref="ui.events">
                            <md-icon>visibility</md-icon>
                        </a>
                    </md-card-header>
                    <md-card-content>
                        <hvac-alarm-list></hvac-alarm-list>
                    </md-card-content>
                </md-card>
            </div>
        </div>

        <md-card>
            <md-card-header>
                <p md-colors="::{color: 'accent'}">Units</p>
            </md-card-header>

            <md-card-content>
                <hvac-units-table units="$ctrl.units"></hvac-units-table>
            </md-card-content>
        </md-card>
    </div>
</div>

and let's add some styles to hvac.css:

hvac.css

...
hvac-overview .right-container .alarms-list-container a {
   border-bottom-color: transparent;
}

hvac-overview .right-container .alarms-list-container a md-icon {
   color: var(--ma-accent);
}
...

Overview page

And we ar done! We have built an overview dashboard. You can download de project files from here

Copyright © 2020 Radix IoT, LLC.