maJsonStore (JSON store)
-
Hi,
With the dashboards I'm designing, I've managed to store my data points I want to look at in an array, which is geat for the one overview page, but now I'm looking at making detail page(s), which uses some of the same datapoints.
I'm wondering if the
ma-json-store
is a better place to save the details of the datapoints - it's actually an array of arrays which is formatted thus:DataPoints=[ {ID:'001',Location:'Lot 17',URL:'/ui/lot17',CustomerLoad:'001DP_POD-827990', PV:'001DP-8212690',PVMax:'DP_008082',GenRun:'DP_812',BattCharge:'684160',BattTemp:'4275',FuelLevel:'9842',GenOutput: '37690'} ,{ID:'002',Location:'Lot 860',URL:'/ui/lot860',CustomerLoad:'7990',PV:'2690',PVMax:'008082',GenRun:'2852',BattCharge:'44800',BattTemp:'444275',FuelLevel:'999842',GenOutput: '767690'} (etc, for another 10 or so points) ]
Where the second value is the XID of the datapoint I'm addressing (I've snipped a lot down so it doesn't look so messy!)
Before I plunge in and move everything around - I was curious if the JSON store could save the information in this state, or whether I'd need to change it's format somewhat - and also how I (or someone else) could administer these points when new MangoES are added. I've seen some rudimentary documentation on the component, but was also curious if there was some more detailed/advanced examples beyond the http://localhost:8080/ui/docs/ng-mango/ma-json-store ?
Thanks
Richard
-
I would highly recommend leveraging a dynamic watchlist based on data point tags to gather a list of data points for a displaying on a dashboard/page. See https://help.infiniteautomation.com/dynamic-dashboard-workflow.
Do not use data point XIDs to store information about a datapoint, use tags for this!
See here for how to add tags - https://forum.infiniteautomation.com/topic/4153/using-ma-state-params/10
Example naming schema
Site 1, voltage:name: 'Voltage', tags: {location: 'Site 1'}
Site 1, current:name: 'Current', tags: {location: 'Site 1'}
Site 2, voltage:name: 'Voltage', tags: {location: 'Site 2'}
Site 2, current:name: 'Current', tags: {location: 'Site 2'}
and so on
Your page basically uses the Watchlist to retrieve all data points belonging to a selected location, then you bind components on your page to a particular point in this array by filtering on the data point name.You can still store auxillary data about a site in the JSON store, e.g. set points, limits etc. There is another example located at http://localhost:8080/ui/examples/utilities/json-store
I would store the JSON data in an object where each key is the location string (same as what you store as the location tag value). e.g.
{ "Site 1": { limitA: 200, setPointB: -20.5 }, "Site 2": { limitA: 250, setPointB: -32.4 } }
-
Thank you so much; that looks like exactly what I want for my individual pages, so I'm off to have a play with watchlists and see what I can do there. I'm sure I'll have a heap of questions in the near future, so I'll try to add them to this thread to make it a resource for those reading in the future.
One question comes to mind for my "overview" page - can I cycle through the watchlist to plot all those points on a single page; I've got the page looking reasonable with the XID data at this stage:
Cheers
Richard
-
Hi Richard
The best way to cycle through the array that the watchlist gives you is to use the angular filter pipe. You can filter by any point attribute. such as deviceName, name or tag.
-
@craigweb said in maJsonStore (JSON store):
Hi Richard
The best way to cycle through the array that the watchlist gives you is to use the angular filter pipe. You can filter by any point attribute. such as deviceName, name or tag.
Thanks - is it possible to see some sample/example code (or if you have a site which outlines it) to use the filter?
Cheers
Richard
-
<div flex="100" flex-gt-md="33" layout="column" ng-if="designer.points | filter:{name:'Level'}:true | maFirst" ng-repeat="res in designer.points | filter:{name:'Level'}:false"> <md-card> <md-toolbar class="md-whiteframe-1dp md-hue-3"> <div class="md-toolbar-tools"> <h2 flex=""> <span>{{ res.deviceName }} </span> </h2> </div> </md-toolbar> <md-card-content> <div flex="100" layout="row"> <div layout="column" flex="50"> <h2> Level:</h2> <ma-point-value style=" width: 25%; font-weight: bolder; font-size: 25px;" enable-popup="right" point="res"></ma-point-value> </div> <div class="flex"> <img ng-src="/rest/v2/file-stores/default/img/Reservoir.svg" style="z-index:-2 width: 200px; height: 150px;"> </div> </div> </md-card-content> </md-card> </div>
My watchlist is a dynamic watchlist the queries all points where the "site" tag matches.
I then filter down to only have the points with name = "Level" then use ng-repeat to display them all in a md-card.
Your table might get a little complicated with nested ng-repeats. You might want to read up on the angular docs on how to use ng-repeat-end. https://docs.angularjs.org/api/ng/directive/ngRepeat
-
Thanks - these are working quite well, and I've been making some good progress - I was curious if tags can be set on the edge devices, or if they can only be set on the server?
Cheers
Richard
-
The edge device and the server runs the same software The ES just has limited hardware capacity. So yes they can be set there and the publisher/data source can be set to update any point properties on the server if they are changed on the ES.
-
@craigweb said in maJsonStore (JSON store):
<div flex="100" flex-gt-md="33" layout="column" ng-if="designer.points | filter:{name:'Level'}:true | maFirst" ng-repeat="res in designer.points | filter:{name:'Level'}:false">
My watchlist is a dynamic watchlist the queries all points where the "site" tag matches.
I then filter down to only have the points with name = "Level" then use ng-repeat to display them all in a md-card.
Your table might get a little complicated with nested ng-repeats. You might want to read up on the angular docs on how to use ng-repeat-end. https://docs.angularjs.org/api/ng/directive/ngRepeat
Does the
designer.points
map to all points, that then need to be filtered? Or, after adding the tags, do I also need to create a dynamic watchlist (as is kinda eluded to here: https://forum.infiniteautomation.com/topic/4153/using-ma-state-params/15#) to address?Thanks
Richard
-
This post is deleted! -
Designer.points
has all the points that are in the watchlist that I created, the watchlist points get brought into the page with a<ma-watch-list-get>
So yes you will need to make a watchlist.
The other option is to use a
<ma-point-query query="{name:query.filter, deviceName:query.filter}" limit="query.limit" start="(query.page-1)*query.limit" sort="query.order" points="points" promise="promise"></ma-point-query>
in your page, use {{points}} so show youthe object it returns.
-
So, just trying to tie together Craig's example and Jared's example above, I'm getting confused how to filter by the tags; so far I have:
<ma-point-query query="{deviceName:'SPS001'}" points="points" promise="promise"></ma-point-query> <!-- show me everything --> <!--div style="color: white;background-color:black; border: 1px solid red;" ng-if="points" ><pre>{{points}}</pre></div--> <div md-progress="promise" style="border: 1px solid blue; background-color: white; color: black;" ng-repeat="point in points"> <span>{{point.xid}}</span> <span style="background-color: yellow;">{{point.tags.Display}}</span> </div>
Which successfully shows me everything that matches the device name in the commented out (first) div, or the secondary DIV, which shows me the same points, but just the XID for the point and the tag for "Display" - but how do I get a specific point from the array if I want the "Display" equal to, say, "Battery Capacity" for this one site? I don't know if I need to put this filter within the
ma-point-query
section, or if I load the whole array intopoints
and then filter in theng-repeat
section ... nor the syntax, as thetags
seem to be at a secondary level of the initial query (as per the twospan
s, where one ispoint.xid
and the tags arepoint.tag.Display
).BTW: there is a typo on the page: /ui/docs/ng-mango/ma-point-query in the 'limit' description: "no limit by defualt"
Thanks
Richard
-
I can help you with the point query.
You can either be specific at what you want at the beginning, or take a multitude of points then filter them accordingly with a filter or usingng-if="point.tags.Display=='displayType'"
For your point query, you can work from the watchlist builder to generate an RQL query.
You'll get something like:<ma-point-query query="'eq(deviceName,SPS001)&in(tags.Display,Battery Capacity)'" points="points" promise="promise"></ma-point-query>
Hope that's of some help
Fox
-
Using the query builder as MattFox suggested is definitely a pro tip!
Your question seems to be around where best to do the filtering. IMO you will be better off doing one simple query to the backend and then doing all your filtering on the front end. That is if your page is going to have a lot of different components using those points. If the page is focused on only certain points then you could query for only those points. The choice is yours
The point query gives you an object with an array of points and all the points attributes, it is not live updating. So now you know that in your page you only have points from SPS001, you can now filter down to the specific point you want in sps001 as such. The syntax is like this because of the nested structure.
<ma-point-value point="points | filter:{tags:{Display: 'Battery Capacity'} }"></ma-point-value>
You can have multiple filters like such
<ma-point-value point="points | filter:{deviceName:'myDevice',tags:{Site: 'myTag'} }"></ma-point-value>
If you want to use an ng-repeat then you make your filter less precise that it gives multiple points that match the criteria. You have a couple of options for the order that the ng-repeat will use.
-
Unfortunately I'm still not getting the results I require; if I put the point query as:
<ma-point-query query="'eq(tags.SiteName,SPS001)&&(tags.Display,Customer Load)'" points="points" ></ma-point-query>
I'm getting nothing back, but if I put:
<ma-point-query query="'eq(tags.SiteName,SPS001)'" points="points" ></ma-point-query>
It only returns the tag
SiteName
and knocks out the tagDisplay
, so I can't further filter on it...Also, when I did get this working (in a different capacity, using the
SIteName
tag to filter a specific site), the data populated in myDIV
, but was destroyed almost instantaneously:<tr ng-repeat="site in sites.sites"> <td> <ma-point-query query="'&in(tags.SiteName,{{site.name}})'" points="points" ></ma-point-query> <div style="color: white;background-color:black; border: 1px solid red;" ng-if="points | filter:{tags:'Display'}" ><pre>{{points}}</pre></div> </td>
Is this where the
promise
keyword becomes important?Cheers
Richard
-
Hi Richard
The promise variable will have 3 states fulfilled, rejected and pending. It gives you the status of the query. So no is the answer to your question about the promise.
<ma-point-query query="'eq(tags.SiteName,SPS001)&&(tags.Display,Customer Load)'" points="points" ></ma-point-query>
There is no operatore for your second criteria. This should work
<ma-point-query query="'eq(tags.SiteName,SPS001)&eq(tags.Display,Customer Load)'" points="points" ></ma-point-query>
As Mattfox sugested useing the watchlist builder to make your RQL queries. Let me show you how. The nice thing about using this method is there is you can see the results immediately on the query preview tab.
As for what you are doing in the table. I think you might be running into scoping issues with using points. Hopefully, @Jared-Wiltshire can comment on that.
ng-if="points | filter:{tags:'Display'}"
Will not work and I'm sure there are plenty of errors in your browser's console when you use this. tags is an object, what you are doing there is filtering the points array for any object that has a tag with a value of string 'Display'As in my previous post if you want to filter with tags you need to supply a nested object the filter ie:
point="points | filter:{tags:{Display: 'Battery Capacity'} }"
If you want to use it in a ng-if you need to use maFirst
point="points | filter:{tags:{Display: 'Battery Capacity'} | maFirst}"
Lastly, I think that your pages are getting quite complex and you would be better off creating a user module where you have a controller to do all your logic and mapping of arrays,
-
@craigweb said in maJsonStore (JSON store):
Hi Richard
There is no operatore for your second criteria. This should work
<ma-point-query query="'eq(tags.SiteName,SPS001)&eq(tags.Display,Customer Load)'" points="points" ></ma-point-query>
Thanks - that was the syntax I was after; I couldn't see anything like i in the RQL syntax: https://cubicweb.readthedocs.io/en/3.26/book/annexes/rql/language/#rql
Is there somewhere that I can read about this??
Also I note that in my nested repeats, this will work, and display all the relevant info for a single point:
<ma-json-store xid="sitesDataXID" value="sites"></ma-json-store> <div style="border:2px solid blue;" ng-repeat="site in sites.sites"> <ma-point-query query="'eq(tags.SiteName,SPS001)&eq(tags.Display,Customer Load)'" points="points" ></ma-point-query> <!-- show me everything --> <div style="color: white;background-color:black; border: 1px solid red;">--{{ site.name }}--<pre>{{ points }}</pre>--</div> </div>
Yet if I change the tag.SiteName to
{{site.name}}
thus:<ma-json-store xid="sitesDataXID" value="sites"></ma-json-store> <div style="border:2px solid blue;" ng-repeat="site in sites.sites"> <ma-point-query query="'eq(tags.SiteName,{{site.name}})&eq(tags.Display,Customer Load)'" points="points" ></ma-point-query> <!-- show me everything --> <div style="color: white;background-color:black; border: 1px solid red;">--{{ site.name }}--<pre>{{ points }}</pre>--</div> </div>
I'm getting this:
Although the browsers (Firefox) code inspector displays the code as it should:
So - should that work as I have it, or does Angular not allow variable substitution in this manner? I al;so tried to do it with
' + {{site.name}} + '
and a few variations (but clearly not the right one!)ng-if="points | filter:{tags:'Display'}"
Will not work and I'm sure there are plenty of errorsMy apologies - I left this in with changing the page so much ...
As Mattfox sugested useing the watchlist builder to make your RQL queries. Let me show you how. The nice thing about using this method is there is you can see the results immediately on the query preview tab.
Thanks for that - I'm looking into how that all works ...
Cheers
Richard
-
@richard-mortimer said in maJsonStore (JSON store):
<div style="border:2px solid blue;" ng-repeat="site in sites.sites">
<ma-point-query query="'eq(tags.SiteName,SPS001)&eq(tags.Display,Customer Load)'" points="points" ></ma-point-query>
<!-- show me everything -->
<div style="color: white;background-color:black; border: 1px solid red;">--{{ site.name }}--<pre>{{ points }}</pre>--</div>
</div>Yikes, you're repeating your queries multiple times and calling the same data. We may need to start moving you into angularJS component territory.
However, if you're after that site name, you need to do this:<ma-json-store xid="sitesDataXID" value="sites"></ma-json-store> <div style="border:2px solid blue;" ng-repeat="site in sites.sites"> <ma-point-query query="'eq(tags.SiteName,'+site.name+')&eq(tags.Display,Customer Load)'" points="points[site.name]" ></ma-point-query> <!-- show me everything in this point! --> <div style="color: white;background-color:black; border: 1px solid red;">--{{ site.name }}--<pre>{{ points[site.name] }}</pre>--</div> </div>
if you have
points="points"
being used every time for each of your ma-point-query items, they'll continue to be updated and overwrite one another as they all share the same scope.
Storing them in an object using the site name as the key makes it a bit easier.
or alternatively!<ma-json-store xid="sitesDataXID" value="sites"></ma-json-store> <div style="border:2px solid blue;" ng-repeat="(index,site) in sites.sites"> <ma-point-query query="'eq(tags.SiteName,'+site.name+')&eq(tags.Display,Customer Load)'" points="points[index]" ></ma-point-query> <!-- show me everything in this point! --> <div style="color: white;background-color:black; border: 1px solid red;">--{{ site.name }}--<pre>{{ points[index] }}</pre>--</div> </div>
This will map everything by the index in your JSON store.
Let me know how you go.Fox
-
I agree with Mattfox and I think Richard will be more familiar since you can code pure javascript functions in the component controller to manipulate your data. instead of pseudo angular/javascript
I believe you are safe to use
points="points"
unlesspoints
is initiated somewhere else in the ng-repeats parent scope. As ng-repeat creates a scope for each iteration but will first look in the parent scope forpoints
-
Sorry to drag up an old subject, but I've got a curious thing happening within my repeats - and I'm not sure if it's an Angular issue, or I've done something to upset it ...
My code used two
ng-if
statements to determine if the value is output value is in watts or kilowatts, thus:<span ng-repeat="point in points[site.name]"><!-- Load --> <ma-get-point-value point-xid="{{ point.xid }}" point="output"></ma-get-point-value> <div ng-if="output.value > 1000">{{ (output.value / 1000) | number:1 }}kW</div> <div ng-if="output.value <= 1000">{{ output.value | number:0 }}W</div> </span><!-- end Load -->
The problem is - I'm sometimes getting two values returned, (for example 984W and 1kW) - if I directyly print the value on the screen via
{{ output.value }}
I'm seeing what I would expect to see (mostly integers, a couple of float types for some points).Am I doing something wrong here, or is there a better way to do this?
[edit] Here's one I managed to catch as it happened:
Thanks
Richard