Please Note This forum exists for community support for the Mango product family and the Radix IoT Platform. Although Radix IoT employees participate in this forum from time to time, there is no guarantee of a response to anything posted here, nor can Radix IoT, LLC guarantee the accuracy of any information expressed or conveyed. Specific project questions from customers with active support contracts are asked to send requests to support@radixiot.com.

Radix IoT Website Mango 3 Documentation Website Mango 4 Documentation Website

Pop up window (face plate)


  • In old Siemens Desigo we called it Genie and superGenie while in PLC they call it face plate. The face plate is a pop up window that embodied the override command, Alarm status, acknowledgment and rest, and possible some setpoint if the devices under monitor has multiple aggregates. Once you click outside of the floating face plate it will close automatically. In this way we able to hide the details to make the graphics neat and clean and once needed can be expose by the user by clicking the graphic item in the GUI screen. Hope this be consider in the next release of Mango Automation.


  • If I understand it correctly you would like to create some dialog window where you can override some settings or set some point values and then if user clicks outside of the window the window will close automatically.

    I would use md-dialog for something like that which Mango has...
    https://material.angularjs.org/latest/demo/dialog

    This however needs programming to be customized to your needs.

    Thomas


  • @thomaseinasto

    Yeah I think this is the right solution, unfortunately I come from Siemens Automation background and need to study the AngularJS as you mention in your comment., If you could share a simple example would be great.


  • Not to say something but the link has the source code of all the dialogs if you click on top right corner of each example md-card. To make it more dynamic you should program it with Angular components/directives.


  • I believe the easiest way for you will be to use a <md-tooltip >***put the markup that you want to see in here ***</md-tooltip> on the element that you want to have the pop up. As you get more comfortable with angularJS. You'l see that the best way forward is to make your own modules "reusable graphic libraries". Although mango does not come with all the pre-made libraries that Siemans has. It is far more customisable.


  • What you all are not pointing out to Joey is that he needs to write a controller to handle all of the actions required. Simply pointing to a resource is useless if they are not familiar with programming in AngularJS and javascript.
    Joey if you are comfortable with the idea of custom code used to extend your system, I will provide you with what you need to do. Although you will need to understand how a component functions with the view and neec to know what variables you need to update/set for your page.
    Start here to see if you can make this work - if successful i'll fill out the blanks for you to make a reasonably generic popup panel
    https://help.infiniteautomation.com/getting-started-with-a-user-module/

    We can make it look pretty after that.


  • Hi @mattfox ,

    Actually I'm studying right now AngularJS and starting to understand the Model,View and Controller . Most of the time I learn by inspecting the code and running it to see the results. What actually the face plate has are as follows:

    1. Setpoint, 2 Minimum Limit, 3. Maximum Limit (limits are used for setting the low and/or high limit alarm), 4. Override bits (set to manual or Automatic mode). Basically all those points can be define as virtual points so it practically take the points after selecting the data source.

    Hope you could share the pop up panel and I'll try to work on it.

    Thanks

    Joey


  • @joey-uson
    So from what I'm seeing here, it's a static one off popup for setting a point's value between a min and max limit.
    This can be implemented either with a dropdown select or a range slider (this is assuming all values are numeric) a point to set this value to, and a toggled point for stating whether you are running manually or not. At least with a dropdown, you can set all possible values (but then this starts getting messy if you need a range).
    The other is to check what is inputted into your set value box on change and ensure it is numerical and between the ranges set for your min and max.
    Does this sound about right?
    I was thinking of something generic for you. You insert the points you want to set as parameters and it spits them out into a separate template you have in your overrides folder.
    So in total, 2 datapoints, and min and max attributes on a per point basis. let me know if i'm missing anything (ie the min and max are also configured by a point)


  • Hi @MattFox

    Are you still able to post a sample of a modal popup? I was able to get the user module example created and working but I have yet been able to find an example of a modal that seems to fit within the framework.

    The intention of the modal is to allow users to add information about a sensor, such as GPS coordinates, add alarm setpoints, calibration values, etc. This data would then be saved to either a (virtual) data point or the JSON store.

    Thanks
    Ian


  • Sure happy to. I won't be near a computer today due to having to work off site. I will however quite happily smack out tomorrow for you both. If that's ok.
    Thanks for giving me your requirements Ian, I'll whack it out fairly quickly.

    Fox


  • Thanks, that would be great. Don't go to too much trouble on it. :)


  • 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


  • For anyone following this thread:
    found a small typo on the useModule.js code. The try block needs to be closed with a }.

    @MattFox Thank you very much for doing this. I've been wanting to dive into modules for sometime now but have never been able to find the time. This will really help with the learning curve.

    There is just one thing that has got me here. When I try add a 2nd userModule my previous one disappears. Could someone help me with how to do this correctly, it will be very much appreciated.


  • Thanks regarding the typo, I'll take a look and will correct it.
    As for your user module. You only have one. You append all of your components and controllers to it.
    I use a try catch to see where it fails. if you wrap them around each module then you can successfully determine how things are failing without breaking the entire module and losing all of your components/controllers...
    Happy to take a look for you. Otherwise feel free to private message me, same goes for Ian and Joey.

    EDIT:have corrected by adding missing brace


  • @MattFox Thanks for that tip, I edit the userModule.js like this to add a second module. don't get any console errors.

    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);}	
    
    try{userModule.component('myComponent', {
        bindings: {
            name: '@?'
        },
        template: '<span>Hello {{$ctrl.name}}</span>'
    });
    }
    catch(e){console.log(e);}
    
        return userModule;
    });
    

  • Well if there is nothing being printed in the console, that's a sign of something working....
    Do note that console.log() outputs as an informative text. If you're using chrome, ensure under the console for default levels that it's ticked:
    0_1536033304366_69b04a4b-7b42-4aae-95a8-6b49c32ada99-image.png

    Secondly, check on the page you have myComponent on and check the console there to ensure there is not some silly form of a clash etc...

    Fox


  • Thanks Matt, I was able to get your example working. Now to play around with retrieving and saving data to a data point or JSON store.

    One thing that confused me was the folder structure that was required. I followed the (Jared's) tutorial that you linked which created the userModule.js in /rest/v2/file-stores/public/userModule.js. You mentioned that within this directory I need to create:
    /components/settingsModal.js
    /views/modalDemo.html

    Also, you mentioned creating these directories and files under: /mango/overrides/web/modules/mangoUI/web/dev/

    It seemed odd that the files would (appear to) be duplicated in these 2 locations.

    What I found to work was that the settingsModal.js was located in:
    /rest/v2/file-stores/public/components/settingsModal.js (based on the path defined in the userModal.js)
    and the modalDemo.html was located in:
    /mango/overrides/web/modules/mangoUI/web/dev/views/modalDemo.html (based on the path defined in settingsModal.js)

    Is this correct? It would seem to be bit of a maintenance pain to have the files located in different contexts.

    This example was great. Thanks again

    Ian


  • So sorry Ian, didn't mean to perplex you.
    I store all of my files in /opt/mango/overrides/web/modules/mangoUI/web/dev directories.
    0_1536107492653_afcae342-46ab-4138-9e67-6038b10693e4-image.png
    This way I don't need to concern myself with changes or being overwritten with mango updates.

    So by this I mean userModule.js is
    /opt/mango/overrides/web/modules/mangoUI/web/dev/userModule.js

    the modalDemo.html:
    /opt/mango/overrides/web/modules/mangoUI/web/dev/views/modalDemo.html

    and the settingsModal.js file is in
    /opt/mango/overrides/web/modules/mangoUI/web/dev/components/settingsModal.js

    The reason I do this is to better segment my code. You can put it all in one directory if you'd rather, but as I have done a lot of MVC programming in my PHP days I'm used to separating my model, controller and views for consistency.

    @iperry said in Pop up window (face plate):

    Now to play around with retrieving and saving data to a data point or JSON store.

    If you look under the settingsModal.js I've done some code for you to do that.
    Just add a button to the view and call the method with $ctrl.saveToStore( ) or for a virtual point,
    make sure you simply set your virtual point in the savePoint attribute and call the. $ctrl.saveToPoint() method.

    Hmm, should remove that (data) argument for the JSON store method...

    Anyway! Once you've got that saving we can look at loading the data and re-populating your fields.


  • Ahh, thanks. I wasn't sure if you could specify where the userModule.js was other than from the public store. I would rather use the overrides directory(s) for my code so I will change the path. I had tried to use this directory before but didn't get the path from the ui quite right.

    I like the way you segment your code. I have been working with a few MVC frameworks over the years as it fits well with the way I code. Sometimes can be fun trying to trace through code but the modularity is great.

    I saw your segments for interfacing with the json store and data points. Those are my next stop in this trip. :)

    Ian


  • For anyone that finds this thread, you can create a dialog window in Mango 3.6 very easily -

    <ma-button label="Show dialog" ng-click="showDialog = {}"></ma-button>
    
    <ma-dialog show-dialog="showDialog">
        <div>This is the dialog content</div>
    </ma-dialog>