• Jared Wiltshire

    @mattfox said in Filter Datapoint List Output:

    Yeah I can relate there. I group by deviceName personally. Perhaps a format option?
    point-list-format="dname - name - tag"
    or somethign to that effect. that way we can choose what to show. maybe even a callback function we can add to it so we can format the output ourselves...

    Yeah definitely will still be an option to format the labels via an attribute. I was talking more about adding a default setting for point lists which are on built in pages.

    @mattfox said in Filter Datapoint List Output:

    Selfish request here, but would you be willing to share the source so i can manually format it to my liking and create a separate directive?

    The source is all on Github -

    https://github.com/infiniteautomation/ma-dashboards/blob/main/UI/web-src/ngMango/directives/filteringPointList.js
    https://github.com/infiniteautomation/ma-dashboards/blob/main/UI/web-src/ngMango/directives/filteringPointList.html

    posted in Wishlist read more
  • Jared Wiltshire

    I also had a play with creating a full wind rose in a SVG, posted below if anyone wants it. What it looks like
    0_1542732146913_650b6d07-f285-4ead-903e-1b513c4cd80b-image.png

    <style>
        g.grid circle {
            fill: transparent;
            stroke: grey;
            stroke-width: 1px;
            stroke-dasharray:10,10;
            stroke-opacity: 0.5;
        }
    
        g.direction-cones circle {
            clip-path: url(#cone);
            transform: scale(0);
        }
        
        g.direction-cones circle.scale-1 {
            fill: darkblue;
        }
        
        g.direction-cones circle.scale-2 {
            fill: darkgreen;
        }
        
        g.direction-cones circle.scale-3 {
            fill: green;
        }
        
        g.direction-lines line {
            stroke: grey;
            stroke-width: 1px;
        }
        
        g.direction-text text {
            fill: white;
            text-anchor: middle;
            alignment-baseline: middle;
            font-size: 16px;
        }
        
        #center {
            fill: white;
        }
        
        #north circle.scale-1 {
            transform: scale(0.2);
        }
        #north circle.scale-2 {
            transform: scale(0.7);
        }
        #north circle.scale-3 {
            transform: scale(0.80);
        }
        
        #north-east circle.scale-1 {
            transform: scale(0.25);
        }
        #north-east circle.scale-2 {
            transform: scale(0.5);
        }
        #north-east circle.scale-3 {
            transform: scale(0.70);
        }
        
        #east circle.scale-1 {
            transform: scale(0.5);
        }
        #east circle.scale-2 {
            transform: scale(0.7);
        }
        #east circle.scale-3 {
            transform: scale(0.75);
        }
    
    </style>
    
    <svg xmlns="http://www.w3.org/2000/svg" viewBox="-120 -120 240 240" width="400" height="400">
    
        <g class="grid">
            <circle r="100" />
            <circle r="75" />
            <circle r="50" />
            <circle r="25" />
        </g>
        
        <g class="direction-lines">
            <line x1="0" y1="0" x2="100" y2="0" transform="rotate(0)" />
            <line x1="0" y1="0" x2="100" y2="0" transform="rotate(90)" />
            <line x1="0" y1="0" x2="100" y2="0" transform="rotate(180)" />
            <line x1="0" y1="0" x2="100" y2="0" transform="rotate(270)" />
        </g>
        
        <g class="direction-text">
            <text transform="translate(0,-110)">N</text>
            <text transform="translate(110,0)">E</text>
            <text transform="translate(0,110)">S</text>
            <text transform="translate(-110,0)">W</text>
        </g>
    
        <clipPath id="cone" clip-path="url(#cone-end)">
            <rect width="100" height="100" transform="rotate(-15)"></rect>
        </clipPath>
        
        <clipPath id="cone-end">
            <rect width="100" height="100" transform="rotate(-75)"></rect>
        </clipPath>
        
        <g class="direction-cones" transform="rotate(-90)">
            <g id="north" transform="rotate(0)">
                <circle class="scale-3" r="100" />
                <circle class="scale-2" r="100" />
                <circle class="scale-1" r="100" />
            </g>
            <g id="north-east" transform="rotate(45)">
                <circle class="scale-3" r="100" />
                <circle class="scale-2" r="100" />
                <circle class="scale-1" r="100" />
            </g>
            <g id="east" transform="rotate(90)">
                <circle class="scale-3" r="100" />
                <circle class="scale-2" r="100" />
                <circle class="scale-1" r="100" />
            </g>
            <g id="south-east" transform="rotate(135)">
                <circle class="scale-3" r="100" />
                <circle class="scale-2" r="100" />
                <circle class="scale-1" r="100" />
            </g>
            <g id="south" transform="rotate(180)">
                <circle class="scale-3" r="100" />
                <circle class="scale-2" r="100" />
                <circle class="scale-1" r="100" />
            </g>
            <g id="south-west" transform="rotate(225)">
                <circle class="scale-3" r="100" />
                <circle class="scale-2" r="100" />
                <circle class="scale-1" r="100" />
            </g>
            <g id="west" transform="rotate(270)">
                <circle class="scale-3" r="100" />
                <circle class="scale-2" r="100" />
                <circle class="scale-1" r="100" />
            </g>
            <g id="north-west" transform="rotate(315)">
                <circle class="scale-3" r="100" />
                <circle class="scale-2" r="100" />
                <circle class="scale-1" r="100" />
            </g>
        </g>
    
        <circle id="center" r="5" />
    </svg>
    

    posted in How-To read more
  • Jared Wiltshire

    For anyone else playing for this, its worth noting that if you give a series-x-point attribute it will set the color, type and title according to the point's plot color, plot type and point name respectively. These can be changed on the data point edit page in Mango.

    posted in Dashboard Designer & Custom AngularJS Pages read more
  • Jared Wiltshire

    Thanks for helping out Thomas.

    Here's a snippet which should help you both out, it uses two select components to choose two levels from the point hierarchy then gets the array of points which you can use for a serial chart for example.

    <ma-point-hierarchy path="[]" hierarchy="rootFolder"></ma-point-hierarchy>
    <md-select ng-model="subfolder">
        <md-option ng-value="sf1" ng-repeat="sf1 in rootFolder.subfolders">{{sf1.name}}</md-option>
    </md-select>
    <md-select ng-model="points">
        <md-option ng-value="sf2.points" ng-repeat="sf2 in subfolder.subfolders">{{sf2.name}}</md-option>
    </md-select>
    
    <pre>{{points|json}}</pre>
    

    posted in Dashboard Designer & Custom AngularJS Pages read more
  • Jared Wiltshire

    When you update your dashboards module to the latest version you should also upgrade your custom dashboard in order to use the latest functionality. Each template references a particular version of the dashboards module code, you can see this at the start of the app.js file.

    In order to update your dashboard you should copy any files which you have not changed from the latest version of the template into your dashboard folder in the overrides folder. Then compare the differences and make changes to the files which you have modified (typically app.js where you have added your custom menu items to the adminTemplate).

    For the adminTemplate, typically you should copy and overwrite the index.html file, the directives folder, views/login.html, views/dashboard/main.html and views/dashboard/errors.html. Make sure you update the two references to adminTemplate in index.html to the name of your dashboard folder you created in overrides.

    You will need to manually update your app.js and re-insert your menu items. Typically this will be the easiest way as the only thing you will have changed is the menu-items. Here is a diff file showing the differences in app.js which may help you to upgrade - https://gist.github.com/jazdw/8a28f156fa3a39d1712b929be8866248

    Note
    The dashboards v3.2 module has functionality to create custom pages and add them to the menu structure directly from the web interface, you may wish to just copy the content of your .html file pages into a custom page. This will avoid the need to manipulate files in the future but will not be a suitable option for people who need advanced pages with their own JavaScript.

    posted in Dashboard Designer & Custom AngularJS Pages read more
  • Jared Wiltshire

    Hey @dcaron
    The dashboards do indeed operate a little differently now and we need to update the tutorials.

    The adminTemplate now has the HTML 5 mode URLs enabled in app.js (i.e. no /#/... urls). You should set the base url in index.html to
    <base href="/user-dashboards/custom1/"> and access your dashboards using /user-dashboards/custom1/home or /user-dashboards/custom1/visualizations/charts

    If you want you can disable the HTML 5 mode URLs by setting $locationProvider.html5Mode(false); in app.js, you would then set <base href="/modules/dashboards/web/custom1/"> in index.html and your URLs would look like /modules/dashboards/web/custom1/#/home and /modules/dashboards/web/custom1/#/visualizations/charts

    I'd highly recommend the first option, it gives much shorter nicer URLs.

    posted in User help read more
  • Jared Wiltshire

    Hey @atkins-chrisw
    Add this to your app.js -

    mdAdminApp.directive('getJson', ['$http', function($http) {
        return {
            scope: {
                url: '@',
                output: '='
            },
            link: function($scope, $element, $attrs) {
                $http({
                    method: 'GET',
                    url: $scope.url
                }).then(function(result) {
                    $scope.output = result.data;
                });
            }
        }
    }]);
    

    You can then use something like this on your page -

    <get-json url="/modules/dashboards/web/mdAdmin/manifest.json" output="myData"></get-json>
    <pre ng-bind="myData|json"></pre>
    

    posted in Dashboard Designer & Custom AngularJS Pages read more
  • Jared Wiltshire

    @jefero
    Have a look at the API documentation for ma-point-values you can pass in a point's XID as an attribute.

    posted in User help read more
  • Jared Wiltshire

    Hi @DieselD try the following syntax

    <md-input-container>
        <label>Filtered point list using RQL</label>
        <ma-point-list ng-model="myPoint3" query="'in(name,' + nameFilter.join(',') + ')'"></ma-point-list>
    </md-input-container>
    

    posted in Dashboard Designer & Custom AngularJS Pages read more
  • Jared Wiltshire

    @silvia The email settings will be in the new UI as of Mango 3.1 which is scheduled for release today.

    posted in Mango Automation read more
  • Jared Wiltshire

    @silvia thanks fixed for next release. I think we will also have to try and compile each element individually and catch the errors in the future so this sort of scenario doesn't repeat itself.

    posted in Mango feedback read more
  • Jared Wiltshire

    Hi @gary

    Assuming you are using Mango v3, you can register a user AngularJS module from the UI settings page /ui/administration/ui-settings (its at the bottom)

    If you click the paper clip icon you can upload a JS file
    0_1497981124008_fd4cea7d-e189-4d91-b0f3-52374b92636b-image.png

    0_1497981204566_1f33c76e-374c-4a31-b1ce-c682936020dc-image.png

    Heres a sample user module JS file

    define(['angular', 'require'], function(angular, require) {
    'use strict';
    
    var userModule = angular.module('userModule', ['maUiApp'])
    .directive('userHello', [function() {
        return {
            template: 'Hello, url is ' + require.toUrl('.') + ' {{$ctrl.testText}}',
            controller: angular.noop,
            controllerAs: '$ctrl',
            bindToController: {
            	testText: '@'
            }
        }
    }]);
    
    return userModule;
    
    }); // define
    

    posted in User help read more
  • Jared Wiltshire

    OK, I'd like to present a different solution which should be a little cleaner.

    This uses our built in directive <ma-now> (basically a clock that updates on a set interval)

    <ma-get-point-value point-xid="DP_698831" point="myPoint"></ma-get-point-value>
    <ma-now update-interval="5 seconds" on-change="tick = !tick"></ma-now>
    
    <p ng-if="tick">
        The point name is "{{myPoint.name}}" and its value is {{myPoint.value | number:2}}.
    </p>
    <p ng-if="!tick">
        The point name is "{{myPoint.name}}" and half its value is {{myPoint.value / 2 | number:2}}.
    </p>
    

    If you need more than 2 states, here's another example that will cycle through 5 different states

    <ma-get-point-value point-xid="DP_698831" point="myPoint"></ma-get-point-value>
    <ma-now update-interval="5 seconds" ng-init="tick = 1" on-change="tick = tick < 5 ? tick + 1 : 1"></ma-now>
    
    Tick number {{tick}}
    
    <div ng-switch="tick">
        <p ng-switch-when="1">
            The point name is "{{myPoint.name}}" and its value is {{myPoint.value | number:2}}.
        </p>
        <p ng-switch-when="2">
            The point name is "{{myPoint.name}}" and half its value is {{myPoint.value / 2 | number:2}}.
        </p>
    </div>
    

    posted in Dashboard Designer & Custom AngularJS Pages read more
  • Jared Wiltshire

    @mattfox said in Has anyone used "Let's Encrypt" for HTTPS - SSL? Any Suggestions?:

    In my experience of using let's encrypt with a java based web platform. I utilised apache and a proxy to tie the two together. From the outside it looked like you were accessing via HTTPS 443 but internally it would connect to whatever port the system ran on. Saved a lot of fluffing with making a java compatible cert and meant you weren't fiddling with java options that could cause instability if configured incorrectly.

    This is certainly an option and will make it easier to use Lets Encrypt as their client allows updating Apache's certificate automatically.

    posted in User help read more
  • Jared Wiltshire

    @silvia said in Change colour depending on two variables (SVG custom dashboard):

    I'd appreciate any thoughts, as this is starting to drive me slightly insane!

    Haha I know the feeling.

    @silvia said in Change colour depending on two variables (SVG custom dashboard):

    I'm not using the UI module, I'm creating my dashboard through the 'Edit pages', as it offers much more flexibility than through the dashboard designer.

    No problems there, the dashboard designer makes it easy to edit attributes and lay things out but markup is more powerful for sure. We had an old module called dashboards prior to v3,x which was replaced by the mangoUI module in v3.x I just wanted to make sure that you weren't using the old module. It sounds like you are using the mangoUI module, so all good.

    @silvia said in Change colour depending on two variables (SVG custom dashboard):

    I've tried the same sort of structure:

    So that syntax, where the expression evaluates to an object is specific to the ngClass directive. The ngBind directive's expression should evaluate to a string.

    @silvia said in Change colour depending on two variables (SVG custom dashboard):

    I've also tried ternary operators:

    Ternary operators should work, the only issue I can see with this is that there is a curly brace at the start of the expression.

    Your code should look like

    <ma-svg>
        <div ma-selector="#meterState001 tspan" ng-bind="plug1.value ? (switch1.value ? 'Charging' : 'Plugged In') : (switch1.value ? 'Error' : 'Empty')"></div>
    </ma-svg>
    

    @silvia said in Change colour depending on two variables (SVG custom dashboard):

    But it only reads the last ng-if and it ignores the others.

    Yep, it will read each in order and replace the ng-if attribute on the svg element.

    @silvia said in Change colour depending on two variables (SVG custom dashboard):

    I've also tried ng-switch:

    Yep, this also wont work. The ma-switch directive expects a list of elements with ma-selector attributes.

    posted in User help read more
  • Jared Wiltshire

    /web/exception/404.jsp

    Put a copy in overrides and edit it there otherwise your changes will be lost when you update Mango.

    posted in How-To read more
  • Jared Wiltshire

    Hey Matt,

    Took me a while to figure this out but we have synchronizeGrid turned on in the AmCharts options. Its documented here https://www.amcharts.com/demos/multiple-value-axes/

    Try changing your options to
    options="{synchronizeGrid: false, valueAxes:[{ minimum:0, maximum:100,title:'Feed Level (%)'}]}"

    Note that there are various other ValueAxis options that may affect the minimum and maximum. So take a look at https://docs.amcharts.com/3/javascriptcharts/ValueAxis if you have any issues.

    Jared

    posted in Mango feedback read more
  • Jared Wiltshire

    If you don't need to log the data and you just want to display the weather on your dashboard then you can definitely just add a widget.

    The fiddle you linked needed some changes for the latest version of AngularJS (1.6.4) but I got it working as an example of how to add custom directives to the user module.

    To add the a user AngularJS module go to Administration... UI Settings then at the bottom click the icon next to User module URL. Click the plus icon (Create new file) then copy paste this code (taken from the JSFiddle).

    The code had to be modified for SCE, strict dependency injection and some changes to the way JSONP requests work.

    define(['angular', 'require'], function(angular, require) {
    'use strict';
    
    var userModule = angular.module('userModule', ['maUiApp']);
    
    userModule.config(['$sceDelegateProvider', function($sceDelegateProvider) {
        var whiteList = $sceDelegateProvider.resourceUrlWhitelist();
        whiteList.push('http://api.openweathermap.org/**');
        $sceDelegateProvider.resourceUrlWhitelist(whiteList);
    }]);
    
    userModule.factory('weatherService', ['$http', '$sce', function($http, $sce) {
        return { 
          getWeather: function() {
            var weather = { temp: {}, clouds: null };
            $http.jsonp('http://api.openweathermap.org/data/2.5/weather?q=Salzburg,at&units=metric&APPID=f9dbd911bc01df1d9ce563b2ba4d3209', {jsonpCallbackParam: 'callback'}).then(function(data) {
                if (data && data.data) {
                    data = data.data;
                    if (data.main) {
                        weather.temp.current = data.main.temp;
                        weather.temp.min = data.main.temp_min;
                        weather.temp.max = data.main.temp_max;
                    }
                    weather.clouds = data.clouds ? data.clouds.all : undefined;
                }
            });
    
            return weather;
          }
        }; 
    }]);
    
    userModule.filter('temp', ['$filter', function($filter) {
        return function(input, precision) {
            if (!precision) {
                precision = 1;
            }
            var numberFilter = $filter('number');
            return numberFilter(input, precision) + '\u00B0C';
        };
    }]);
    
    userModule.controller('WeatherCtrl', ['$scope', 'weatherService', function ($scope, weatherService) {
        $scope.weather = weatherService.getWeather();
    }]);
    
    userModule.directive('weatherIcon', function() {
        return {
            restrict: 'E', replace: true,
            scope: {
                cloudiness: '@'
            },
            controller: ['$scope', function($scope) {
                $scope.imgurl = function() {
                    var baseUrl = 'https://ssl.gstatic.com/onebox/weather/128/';
                    if ($scope.cloudiness < 20) {
                        return baseUrl + 'sunny.png';
                    } else if ($scope.cloudiness < 90) {
                       return baseUrl + 'partly_cloudy.png';
                    } else {
                        return baseUrl + 'cloudy.png';
                    }
                };
            }],
            template: '<div style="float:left"><img ng-src="{{ imgurl() }}"></div>'
        };
    });
    
    return userModule;
    
    }); // define
    

    The markup will look like this (you dont need the ng-app).

    <div ng-controller="WeatherCtrl">
        <h2>Weather in Salzburg, Austria</h2>
        <weather-icon cloudiness="{{ weather.clouds }}"></weather-icon>
        <h3>Current: {{ weather.temp.current | temp:2 }}</h3>
        min: {{ weather.temp.min | temp }}, max: {{ weather.temp.max | temp }}
    </div>
    

    posted in How-To read more
  • Jared Wiltshire

    Actually I realized you can do something like this pretty easily using our ma-bar-display directive and AngularJS ng-repeat. Try this, change designer.points to the name of your point array:

        <div layout="" layout-align="space-around center">
            <div ng-repeat="point in designer.points track by point.xid" layout="column" layout-align="start center">
                <ma-bar-display style="height: 100px; width: 40px;" ng-style="{color: point.chartColour}" point="point" maximum="2" direction="bottom-to-top"></ma-bar-display>
                <span>{{point.name}}</span>
            </div>
        </div>
    

    posted in Dashboard Designer & Custom AngularJS Pages read more
  • Jared Wiltshire

    Yes its possible, here's an example.

    Add this style to your page (we need the default duration to be 0s so we have to add our own class).

    <style>
    .my-spin-clockwise {
      transform-origin: 50% 50%;
      animation: spin-clockwise 0s linear infinite;
    }
    </style>
    

    You can then change the duration of the animation like this.

    <img src="/fan.svg" class="my-spin-clockwise" ng-style="{'animation-duration': 50 / point.value + 's'}">
    

    However the animation restarts whenever the duration changes so you may want to round the point value like so

    <img src="/fan.svg" class="my-spin-clockwise" ng-style="{'animation-duration': 50 / (point.value - point.value % 10) + 's'}">
    

    posted in Dashboard Designer & Custom AngularJS Pages read more