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

Best way to structure for multiple instances of device


  • My application consists of several similar devices.

    I am trying to figure out the most efficient way to implement this while minimizing duplication of code.

    For each device, I have a remote I/O (ADAM 6060 or similar) for controlling the device. There are also a few variables that are not connected to a remote I/O point, but are needed to keep track of operating modes and sequences.

    My current implementation consists of a virtual data source that includes both the remote I/O points and the mode and sequence variables, and a Modbus I/P data source for controlling the device. Point links are used to connect data points in the virtual data source to the points in the Modbus I/P data source.

    Additionally, scripts are used for control logic and sequencing.

    This is all working pretty well - for one device. What I am looking for is a method of implementation that avoids having to create all of the moving parts for each device - virtual data source, Modbus I/P data source, point links, and scripts. Or, at least a way that doesn't require that all of the moving parts being recreated from scratch.


  • Hi pyeager,

    I'm not sure I fully understand the why of the setup. Do the point links have scripts to transform the values, or are they simple point links? If simple point links, why not just set to the Modbus I/P points directly? Is this for the extra "variables not connected to a remote I/O point?"

    As far as recreating from scratch, there are tools that can make this easier. The Template Configuration module (only in the old UI currently) provides a way to template and process base JSON exports.

    No matter what you'll be creating a new Modbus device. This can potentially be a very easy find / replace operation on a JSON export. You'll probably want the script for each. You could abstract functions into Global Scripts for maintenance reasons, should you ever need to change them.

    Not knowing more about the situation, I hesitate to advise. If all the parts are necessary and you're going to be doing this a lot, I would consider templating the JSON either via the aforementioned module or via your own markup to make a find / replace / import process more convenient.


  • @phildunlap said in Best way to structure for multiple instances of device:

    If simple point links, why not just set to the Modbus I/P points directly? Is this for the extra "variables not connected to a remote I/O point?"

    Yes. The virtual data source exists as a place for variables that don't map directly to Modbus I/P points.

    I suspect that I can simply copy both the virtual and Modbus I/P data sources, and then try to make global scripts "glue" things together.


  • I would recommend using points on the Scripting data sources you're using, possibly. They're pretty much the same as no-change virtual points that are already in the script's context. But, again, I do not feel I understand the objectives well enough to make clear advisements.


  • Phil:
    Thanks for taking the time to discuss my challenge.

    Our initial implementation will probably include a dozen machines under control of Mango. A full implementation will include somewhere in the range of 100 machines.

    The Modbus I/P data sources are sufficient for controlling the machine, however there are modes, settings, and state variables per machine that must live somewhere. There are also process variables that are shared by groups of machines. A virtual data source per machine seems to be the logical place to put those as I understand the available options. Point links and scripts tie the virtual data source to physical machine (via the Modbus I/P data source) and shared process variables.

    Please let me know if I am missing something.

    I have been working on a script to iterate over a set of data sources, which I hope is the beginning of a scripted data source to implement control logic and sequencing. While I have been able to use DataSourceQuery to obtain a list of data sources, I am having trouble accessing the associated list of data points.

    This code finds the desired set of data sources:

    var sources = DataSourceQuery.query('like(name,polecat*VIRT)&limit(10)');
    for(var i=0; i<sources.size(); i++ ) {
        source = sources.get(i);
        print( source.name );
    }
    

    However, I have been unable to modify it to access the data points, which documentation indicates should be available through source.points.

    What am I missing?

    Edit: In the documentation at [https://help.infiniteautomation.com/about-mango-java-script], I find this:

    *Data Source Queries
    Data sources can be queried in much the same way. There is one method, DataSourceQuery.query(stringRql); A returned object will contain:

    name - String
    xid - String
    enabled - boolean
    type - String
    points - List<DataPointWrapper>*

    I find that I can reference name, xid, and enabled in my script, but type and points are undefined.


  • After further investigation, I find that type doesn't exist, but typeName does. Perhaps a documentation bug?

    While dumping one of the objects returned from DataSourceQuery, I find that points actually does exist, but when I try to reference it, I get "undefined".


  • Note: I'm using 3.5.6 until we're ready to upgrade..
    Your query will likely work better if you iterate rather than using get:

    var sources = DataSourceQuery.query('like(name,polecat*VIRT)&limit(10)');
    //I'm not confident with the like(name,polecat*VIRT), but hey if it works then good
    for(var i=0; i<sources.length; i++ ) {
        source = sources[ i ];
        print( source);
    }
    

    You should be able to print(source) and see all the associated datapoints in that datasource, regardless if the datasource is enabled or not. P.s I also found it to be typeName for the type of datasource. Most likely not been updated.

    My advice here that I can give you would be to look at tagging all of the datapoints instead and then using an RQL query to pull them in. Not only will it enable you to better index your point information but will also give you plenty of control over what points you want to pull.


  • Thanks for bringing that to our attention! It looks like everything should be working, but there is no getter in the DataSourceWrapper for the points, so its scope is private! It appears to have been this way since it was originally added, but I have fixed it for next release.

    For now, I think the easiest way to work around that would be a function in a global script to do the necessary reflecting to get out the points list. Like...

    function reflectPointsList(dataSourceWrapper) {
        var pointsListField = dataSourceWrapper.getClass().getDeclaredField("points");
        pointsListField.setAccessible(true);
        return pointsListField.get(dataSourceWrapper);
    }
    
    var dsw = DataSourceQuery.byXid("DS_XID");
    var points = reflectPointsList(dsw);
    print(points)
    

  • @pyeager said in Best way to structure for multiple instances of device:

    After further investigation, I find that type doesn't exist, but typeName does. Perhaps a documentation bug?

    Yes, thanks for bringing this to our attention, as well!


  • Thanks, Phil! That should get things moving.

    One more thing for your attention....

    Here is some example code from [https://help.infiniteautomation.com/about-mango-java-script]

    var points = DataPointQuery.query('like(name, *boiler*)&limit(10)');
    var average = 0.0;
    
    LOG.info('Found ' + points.size() + ' points to compute average.');
    
    for(var i=0; i<points.size(); i++){
        if(points.get(i).runtime !== null){
          LOG.info('Adding ' + points.get(i).runtime.value + ' to the average.');
          average = average + points.get(i).runtime.value;
        }
    }
    
    return average/points.size();
    

    It might be considered a "nitpick", but if any of the data points are disabled, the computation will be incorrect.

    Why? Because the last line divides by the number of points, not the number of enabled points.


  • @mattfox thanks for the suggestion regarding iteration. It looks much cleaner your way. I just followed the method used in the Mango documentation. Haven't done much javascript for quite a while!


  • Great!

    Yeah, that may be nitpicky ;). You could so easily count the number of enabled points when you're checking the runtime not being null...


  • Happy to help, my javascript ain't too bad IMO. I've got nothing on Jared or Phil mind you but always here if needed.


  • @mattfox said in Best way to structure for multiple instances of device:

    My advice here that I can give you would be to look at tagging all of the datapoints instead and then using an RQL query to pull them in. Not only will it enable you to better index your point information but will also give you plenty of control over what points you want to pull.

    Thanks for the suggestion, Matt!

    However, I have a question.

    If querying the data source gets me all the points I need, where is the advantage?


  • @pyeager

    @pyeager said in Best way to structure for multiple instances of device:

    If querying the data source gets me all the points I need, where is the advantage?

    In this case you may have additional info regarding location or logger brand etc etc.
    Which if in the case of all being in the data sources then that's fine.

    My other reasoning is prior to tags being introduced, I used to do the same thing and link by name or data sources etc Which is fine for single site installs. But what if you find you suddenly need only some particular devices, or a sensor or something is installed which feeds back in different units or magnitude? Or even you're using a mango unit on multiple locations and need different level access? User permissions can get you so far but you cannot always rely the name of the device being consistently named.

    My other point is being able to also create generic dashboards that pull data by query rather than individually mapping them. You may be able to individually name them differently, but what if you have the same issues as above? This is where tags are handy.

    This is my 10 cents. (2 cents plus inflation and tax etc etc..) And in my case I have dozens of sites and users to take into account with their own locations (some shared) and not necessarily using the same hardware we have.


  • @mattfox @phildunlap

    Where is DataSourceQuery documented? I haven't yet found full documentation for that or much else. I've been figuring things out from the examples.


  • Get yourself into the mangoUI and go to Administration -> Watchlist builder then select query as the watchlist type. It will allow you to put together a query and teach you what parameters are available. That was my go to for the first month or so when I started learning RQL.

    EDIT: As for the datasource query info, I've not got much. It honestly looks like you use it just to pull datasource property information in order to pull points by it's XID / id.


  • @phildunlap

    function reflectPointsList(dataSourceWrapper)
    

    works perfectly.

    Do I understand correctly that it is a workaround until the next release?


  • Correct, although it should continue to work even in subsequent releases. But, once 3.6.2 is released the concerns raised in this thread will have been fixed,

    1. points list not available via .points
    2. JavaScript help says type not typeName while .typeName is correct

  • This post is deleted!