• MattFox MattFox

    Correct, look under API Docs -> Components -> maSerialChart
    They have a complete guideline of what attribute tags to use to apply values. You will want the series-x-values where the x is the chart number.
    The Chart update rate is affected by the maPointValues components which you can also read about. The realtime="true" attribute will allow datapoints to update into the chart at the rate they change or are updated.
    If you don't want to average, make sure you set the rollup type to NONE. Alternatively, use the rollup-interval to set the interval duration between datapoints in the chart, note that changing from the live updating rate may mean you;ll need to average.

    posted in Dashboard Designer & Custom AngularJS Pages read more
  • MattFox MattFox

    This will be a handy post for those who are running cloud hosted solutions as there isn't a mangoES settings section for custom installs.

    posted in User help read more
  • MattFox MattFox

    OK!
    As a starting point to work from. I'll work with you so you learn how it all works as you go...
    (All code is placed in the /opt/mango/overrides/web... directories)
    /modules/mangoUI/web/dev/components/settingsModal.js

    define(['angular', 'require'], function(angular, require) {
    'use strict';
    
    /* Date: 1/9/18
    	   Author: Matt "Fox" Fox
    	   Desc: component written to launch a modal dialog with either a built in template or just spit out a list of points to save one by one.
    	*/
    	settingsModalController.$inject = ['$mdDialog','maJsonStore'];
    		function settingsModalController ($mdDialog,maJsonStore)
    		{
    			var ctrl = this;
    			ctrl.mdDialog = $mdDialog;
    			ctrl.parsedOptions=[];
    			
    			ctrl.values={};
    			
    			this.$onInit = function()
    			{
    				if(this.points && !angular.isArray(this.points))
    				{
    					console.log('Error: points field not an array of point objects');
    				}
    				if(this.pointOptions && !angular.isArray(this.pointOptions) )
    				{
    					console.log('Error: points options field not an array');
    				}
    				ctrl.clickOffClose = ctrl.clickOffClose===true?true:false;
    				
    				
    				
    				
    				
    			};
    			
    			this.$onChanges = function(e)
    			{
    				console.log(e);
    				checkPoints();
    			}
    			
    			function checkPoints()
    			{
    				ctrl.parsedOptions=[];
    				if(ctrl.points && angular.isArray(ctrl.points))
    				{
    					if(ctrl.pointOptions && angular.isArray(ctrl.pointOptions) )
    					{
    						var ptName,ptDesc;
    						for(var i=0; i<ctrl.points.length; i++)
    						{
    							ptName=ctrl.points[ i ].name;
    							ptDesc='';
    							if(ctrl.pointOptions[ i ] && ctrl.pointOptions[ i ].hasOwnProperty('name') )
    							{
    								ptName = ctrl.pointOptions[ i ].name;
    							}
    							if(ctrl.pointOptions[ i ] && ctrl.pointOptions[ i ].hasOwnProperty('desc') )
    							{
    								ptDesc = ctrl.pointOptions[ i ].desc;
    							}
    							ctrl.parsedOptions.push( {name:ptName,desc:ptDesc} );
    						}
    					}
    					else
    					{
    						for(var i=0; i<ctrl.points.length; i++)
    						{
    							ptName=ctrl.points[ i ].name;
    							ptDesc='';
    						
    							ctrl.parsedOptions.push( {name:ptName,desc:ptDesc} );
    						}
    					}
    				}
    				else if(ctrl.pointOptions && angular.isArray(ctrl.pointOptions) && !ctrl.points )
    				{	
    					var ptId, ptName, ptDesc;
    					for(var i=0; i<ctrl.pointOptions.length; i++)
    					{
    						if(ctrl.pointOptions[ i ] && !ctrl.pointOptions[ i ].hasOwnProperty('name') )
    						{
    							// ctrl.pointOptions=[];
    							ctrl.parsedOptions=[];
    							return;
    						}
    						ptId=i;
    						ptDesc='';
    						ptName=ctrl.pointOptions[ i ].name;
    						if(ctrl.pointOptions[ i ] && ctrl.pointOptions[ i ].hasOwnProperty('id') )
    						{
    							ptId = ctrl.pointOptions[ i ].id;
    						}
    						if(ctrl.pointOptions[ i ] && ctrl.pointOptions[ i ].hasOwnProperty('desc') )
    						{
    							ptDesc = ctrl.pointOptions[ i ].desc;
    						}						
    						ctrl.parsedOptions.push( {id:ptId,name:ptName,desc:ptDesc} );
    					}
    				}
    			}
    			
    			/* DIALOG CODE */
    			ctrl.showDialogue = function(ev) {
    			   // ctrl.updateModals();
    			   if( ctrl.altTemplateUrl!==undefined  )
    			   {
    				   try
    				   {
    					   
    					ctrl.mdDialog.show({
    					  templateUrl:ctrl.altTemplateUrl, 
    					  parent: angular.element(document.body),
    					  targetEvent: ev,
    					  clickOutsideToClose: ctrl.clickOffClose
    					}).then(
    						function()
    						{
    							console.log('function if closed/hidden manually'); 
    						},
    						function()
    						{
    							console.log('function to fire if closed from clicking outside/cancelled');
    						}
    					);
    				   }catch(e){console.warn(e);}
    			   }
    			   else
    			   {
    				   try{
    					   
    					ctrl.mdDialog.show({
    					  contentElement: '#settingsModal',
    					  parent: angular.element(document.body),
    					  targetEvent: ev,
    					  clickOutsideToClose: ctrl.clickOffClose
    					}).then(
    						function()
    						{
    							console.log('function if closed manually'); 
    						},
    						function()
    						{
    							console.log('function to fire if closed from clicking outside/cancelled');
    						}
    					);
    				   }catch(e){console.warn(e);}
    				}
    			};
    			
    			ctrl.hideDialogue = function() {
    				ctrl.mdDialog.hide();
    			};
    
    			ctrl.cancel = function() {	  
    				ctrl.mdDialog.cancel();
    			};
    			/* DIALOG CODE END*/
    			
    			/* JSON Store Stuff: */
    			ctrl.saveToStore = function(data=null)
    			{
    				
    				var item = maJsonStore.newItem( ctrl.jsonStore.toLowerCase() );
    				item.jsonData = data===null ? ctrl.values : data;
    				item.readPermission= "user";
    				item.editPermission= "user"; //can change this at your leisure or we can break it out into a setting...
    				
    				var r = item.$save();
    				
    				if(r.status)
    				{ 
    				}
    				else
    				{ 
    				}
    			};
    			
    			ctrl.loadFromStore = function()
    			{
    				maJsonStore.get({xid: ctrl.jsonStore.toLowerCase() }).$promise.then(function(item) {
    				ctrl.values = item.jsonData;
    			});
    			/* JSON Store Stuff END: */
    			};
    			
    			/* Virtual Point Save stuff */
    			ctrl.saveToPoint = function()
    			{
    				if(ctrl.savePoint===undefined)
    				{
    					alert('Point to save to not set as attribute!');
    					return;
    				}
    				ctrl.savePoint.setValue( JSON.stringify(ctrl.values) );
    			};	
    			
    			/* Virtual Point Save stuff END */
    	}
    		
    		
    		
    	
    	var settingsModal={
    		bindings:{
    			jsonStore: '<?', 		//Store name to save settings to
    			savePoint: '<?', 		//alternatively, save my JSON to a virtual pt, MUST BE TYPE DATAPOINT...
    			altTemplateUrl:'=?', 	//If we want to use the popup to show something else: use this template...
    			modalTitle:'=?', 		//seemed like a good idea at the time... will only work if altTemplateUrl is empty
    			clickOffClose:'=?', 	//set to true to click outside modal to shut it
    			pointOptions:'<?', 	//Alternate names for points,  (object array) [{id:'',name:'',desc:''}], id used for key name if using json/virtual point save
    			points: '<?',			 //Array of points to set individually
    		},
    		controller: settingsModalController,
    		templateUrl:'/modules/mangoUI/web/dev/views/modalDemo.html'
    	};
    	
    	return settingsModal;
    });
    

    and the template markup to add:
    /modules/mangoUI/web/dev/views/modalDemo.html

     <ma-button  hue="hue-2" palette="primary" label="Open Dialogue" ng-click="$ctrl.showDialogue()" raised="true"></ma-button>
     <div ng-if="!$ctrl.altTemplateUrl" style="visibility: hidden">
        <div class="md-dialog-container" id="settingsModal">
          <md-dialog layout-padding>
    			<md-dialog-actions><md-button ng-if="!$ctrl.clickOffClose" ng-click="$ctrl.cancel()" class="md-primary">x</md-button></md-dialog-actions>
     <div ng-if="!$ctrl.points && !$ctrl.parsedOptions">
    		Custom markup needed!
    		</div>
    		 
           <h1 ng-if="$ctrl.modalTitle!==undefined">{{$ctrl.modalTitle}}</h1> <small ng-if="$ctrl.clickOffClose">Click outside dialogue to close</small>
    		
    		<div ng-if="$ctrl.points">
    		<md-input-container ng-repeat="(index,point) in $ctrl.points" style="display:block;width:100%;">
    	   {{$ctrl.parsedOptions[index].name}}
    			<br/>	
    	   {{$ctrl.parsedOptions[index].desc}}
    			<br/>	
    			
    		<span style="display:inline-block;">Current Value: <ma-point-value style="display:inline-block;" point="point" ></ma-point-value></span>
    		 <ma-set-point-value  point="point" show-button="true"></ma-set-point-value> 
    		</md-input-container>
    		</div>
    		<div ng-if="!$ctrl.points && $ctrl.parsedOptions.length>0">
    			<md-input-container ng-repeat="input in $ctrl.parsedOptions">
    			{{input.name}}<br/>{{input.desc}}
    			<input ng-model="$ctrl.values[ input.id ]"/>
    			</md-input-container>
    		</div>
    		
          </md-dialog>
        </div>
      </div>
    

    More than happy to help you work through this (it will be a bit of a crash course)
    I've not added the buttons on the template for saving using a virtual point or JSON store, but that can be added quite easily.
    Basically you can generate a popup which will populate with inputs that can be named, given an ID and a description if necessary. This can also be used to override point names to more 'user friendly' equivalents for your users if they're filling out items in the dialogue box.

    inside the userModule.js directory (assuming that's the name of the file you've followed from Jared's tutorial) you'll want this structure:

    /userModule.js
    /components/settingsModal.js
    /views/modalDemo.html

    and inside userModule.js you'll want this:

    define(['angular', 'require','./components/settingsModal.js'], function(angular, require,settingsModal) {
        'use strict';
     var userModule = angular.module('userModule', ['maUiApp']);
    try {	
    	userModule.component('settingsModal', settingsModal);
    }
    catch(e){console.log(e);}	
        return userModule;
    });
    

    Markup in the dashboard page builder will be:

    <settings-modal point-options="[{id:'test',name:'input 1',desc:'input test 1'},{id:'test2',name:'input 2',desc:'input test 2'}]" points="vp" click-off-close="true"></settings-modal>
    

    See if you can get the popup showing for you first. I'm using 3.4.1 so I'm not completely sure if this will be affected in your version of mangoUI...

    Fox

    EDIT: sorry I'll be sure to add some more code comments later...
    New Edit: added data to save to store as an optional argument

    posted in Wishlist read more
  • MattFox MattFox

    Fixed, just mixed up the full path of the file with the userModule URL after closer inspection.

    posted in User help read more
  • MattFox MattFox

    /ui/docs/ng-mango/ma-serial-chart
    

    Read the docs under API Docs -> components -> maSerialChart

    there's an attribute you can set to alter the format:

    time-format
    (optional)
    string	
    The moment.js time format to be used in displaying timestamps on the X axis.
    

    Put your moment.js format in there and that will let you set the format to 'DD mm' etc

    posted in Mango Automation general Discussion read more
  • MattFox MattFox

    Congrats for making it through the other side. Would be good to have a chat considering currently our system doesn't scale well with mango. If the merger can help with this that'd be grand. It's the bottleneck in our system and I'm struggling to streamline what we have.

    Fox

    posted in Announcements read more
  • MattFox MattFox

    Seeing how amcharts have kindly given the source code on how to do this; you would need to get your required data points. Use a get point values query for the respective data points required. Use a "1 YEARS" AVERAGE/SUM rollup if you have multiple values for a given year depending on what you are calculating.

    Next write an angular component/directive which creates a time-line pie instance, and format the incoming point values with their points into the
    "[ 'year' : [ { 'value' : 'datapointValue1', 'label': 'point 1 name'}, { 'value' : 'datapointValue2, 'label': 'point 2 name'} ]" format.

    The supplied source code on amcharts will enable you to create animate effects etc.
    To get yourself started, I suggest you look at Jared's pie chart directive then build on top of that in a new file, just be sure to change the name from pieChart to timePieChart so you don't cause any problems with the native UI components.

    Hope that helps!

    Fox

    posted in Dashboard Designer & Custom AngularJS Pages read more
  • MattFox MattFox

    @psysak If youn mean you have a mango unit publishing data and you want to be able to set back the other way, The only way to do so is to create a publisher going back the other way. That way when you set the datapoint on the mango unit receiving the published data the changes are pushed back to the initial mango unit doing the publishing. This does mean you would have a new datasource to capture and hold the set data.

    posted in User help read more
  • MattFox MattFox

    Not a great deal to share really! I used nodeJS and the aws publishing library to send to a third party. Once set up, you just run the nodeJS with the express library or another restful style service then use the body of the request to be published out of subscribed mqqt topic.

    Sorry if it's a bit vague! If you want a how to I'll write a tutorial next week when I'm back at my work computer. Hope it doesn't step on Joel's toes..

    Fox

    posted in Wishlist read more
  • MattFox MattFox

    Ok first up. Show all of your markup in code tags. First it appeared you were trying to derive values from what you had stored in datapoints. I see you were confused and actually meant its JSON store data for a watchlist.
    Use ma point query and build a RQL query using the watchlist builder. This way you can pull only what you want.

    <ma-point-query query="'and(eq(name,Temperature),eq(tags.Sensor Type,shioiaq2))&limit(2)'" points="tmpPts"> </ma-point-query>
    
    <div ng-repeat="pt in tmpPts">
    <ma-point-values .... >
    
    <ma-serial-chart .... >
    </div>
    
    

    Once you markup is visible we can get rid of your digest error.

    Fox

    posted in Mango General read more
  • MattFox MattFox

    correct, you need to user the strictMinMax:true property to force the axes to be a given range

    posted in Dashboard Designer & Custom AngularJS Pages read more
  • MattFox MattFox

    I can tell you that the ma-gauge-chart tag is an angularJS component so it will not show anything between the tags, and thus not be rendered.
    Units usually show if you have set the unit field on the individual datapoint under /data_sources.shtm.
    Feel free to take screenshots and describe further what you are trying to accomplish.

    Fox

    posted in User help read more
  • MattFox MattFox

    6 Core xeon 15GB server
    Given Mango 10G to run with, htop seems to show it asking for 14 in a virtual sense, not sure if that means I need to crank it up or not..
    377 Datasources, A little over 8200 points, About half of those being updated by API from a third party system. All points are updating ranging from 20 seconds to 15 minutes. I'd argue about 500 points are meta points.
    193 users, I've always got about 2/3 thirds having active sessions on the system.
    CPU hits approx 20% every once in a while. I've got 700 max threads running in settings to ensure mango can just smack everything out as it needs. But after having issues last week with tags being saved to show status information I was beginning to wonder if there's more I'm missing....

    posted in Mango feedback read more
  • MattFox MattFox

    Anytime, well done and good luck. Always here if needed.

    Fox

    posted in Mango General read more
  • MattFox MattFox

    OK! Here is what the code would be like:
    You need to have a directory structure like this to make it work with my code:
    Use winscp to connect with SSH to your mango install.
    -/opt/mango/overrides/web/modules/mangoUI/web/userModule
    |->/opt/mango/overrides/web/modules/mangoUI/web/userModule/userModule.js
    |->->/opt/mango/overrides/web/modules/mangoUI/web/userModule/directives/gaugeChart.js

    Once configured, make sure you add userModule.js to the administration->UI Settings page under user module url.
    enter this:

    /modules/mangoUI/web/userModule/userModule.js
    

    in the userModule.js file paste this code:

    define(['angular', 'require','./directives/gaugeChart.js'], function(angular, require,gaugeChart) {
    var userModule = angular.module('userModule', ['maUiApp']);
    try
    {
    userModule.directive('gaugeChart', gaugeChart);
     return userModule;
    }catch(e){console.log(e);}
    });
    

    In the directives file, use the code in the post above.

    Now, to use this in your dashboard, simply do the same as above, except the tag would just be 'gauge-chart'

     <gauge-chart id="44cf2d0c-c777-41e0-b20d-ea11a3f9f0cb" style="position: absolute; width: 200px; height: 200px; left: 145px; top: 8px;" point-xid="DP_866585" start="180" end="260" 
      options="{
    	axes: [ {
        axisThickness: 1,
        axisAlpha: 0.2,
        tickAlpha: 0.2,
        valueInterval: 5,
        bands: [ {
          color: '#cc4748',
          endValue: 191,
          startValue: 180
        }, {
          color: '#fdd400',
          endValue: 202,
          startValue: 191
        }, {
          color: '#84b761',
          endValue: 232,
          innerRadius: '95%',
          startValue: 202
        }, {
          color: '#fdd400',
          endValue: 232,
          startValue: 234
        }, {
          color: '#cc4748',
          endValue: 260,
          startValue: 234
        }],
        bottomText: '[[value]] km/h',
        bottomTextYOffset: 0,
        startValue:180,
        endValue: 260
      } ]
    }
    "></gauge-chart>
    

    posted in Dashboard Designer & Custom AngularJS Pages read more
  • MattFox MattFox

    /[mangodir]/overrides/web/modules/mangoUI/web/
    

    For icons in the mangoUI mobile view you'll need:

    /[mangodir]/overrides/web/modules/mangoUI/web/ui/manifest.json
    

    For altering favicons:

    /[mangodir]/overrides/web/modules/mangoUI/web/ui/index.html
    

    posted in MangoES Hardware read more
  • MattFox MattFox

    @phildunlap said in Create event detector that applies to all/specific datapoints:

    You'd have a bunch of options to hack it up.

    You had me at hack
    I've got a status dashboard doing something similar already. Although I can likely extend what you've done here and implement an email with a template listing all iffy datapoints. It's not an alarm per se but it sure is a darn good start.
    I'll put it in the wishlist. I think it'd be good to datasource/point template wide alarm settings that individual points have.
    Inidividual point alarms override template -> template override datasource. That's my idea.

    Thanks again Phil!

    Fox

    posted in User help read more
  • MattFox MattFox

    Thanks for doing this on such short notice Jared

    posted in Dashboard Designer & Custom AngularJS Pages read more
  • MattFox MattFox

    @jay1089 Yep, count to 30 then fire it back up, perhaps monitor the LED's on the case - I assume here it's similar to a V2 with an aluminium cast case with 4 LEDs on the front.

    posted in MangoES Hardware read more