• phildunlap

    That one is intended to run every minute (.past(MINUTE, 1)) so for a meta point you could do Start of minute or 0 * * * * ? as your update event.

    posted in How-To read more
  • phildunlap

    That's not the latest for Mango. https://github.com/infiniteautomation/BACnet4J

    It doesn't seem to have 3.2.3 in the lib-opt folder, but if you read the RELEASE-NOTES you may see some pertinent stuff. You can get the 3.2.3 jar in the BACnet module.

    posted in BACnet4J general discussion read more
  • phildunlap

    Is there any output to the browser's console? You should be able to right click the page and select 'Inspect Element' then navigate to the console.

    posted in User help read more
  • phildunlap

    Hi Dan,

    You're looking for the <session-timeout> tag in Mango/web/WEB-iNF/web.xml

    i believe the value is in minutes.

    posted in User help read more
  • phildunlap

    Hi Dave,

    This is done through a meta point.

    Something like....

    var changeThreshhold = 5 //5 units per minute, let's say
    var data = p.past(MINUTE, 5)
    if( data.count == 0 ) {
      //Handle no data in period condition, raise event?
      return true;
    } else if ( data.count == 1 ) {
      //Handle only one piece of data in our range by getting another value
      var lv = p.lastValue(1);
      if ( lv == null ) //Only one data point, no cause for alarm. We may not need to check this
         return false
      return changeThreshhold > ( data.lastValue - lv.value )/((data.lastTime - lv.time)/60000);
    } else {
      return changeThreshhold > ( data.lastValue - data.firstValue )/((data.lastTime - data.firstTime)/60000);
    }
    

    Then having a state detector on the meta point checking for a "true" state. This isn't its own detector because the shrewd observer may see data sets where this logic is deeply flawed (specifically, data that isn't monotonic). For situations where your data varies in both directions, you may be more interested in changing the logic to sum the differences as you iterate the list, and alarm if you cross the threshhold, more like....

    var data = p.pointValuesSince(new Date().getTime() - 5*60*1000) //Five minutes in milliseconds
    if( data.length <= 1 ) //we have the same edge conditions for our range
       return false
    var sum = 0;
    var changeThreshhold = 5; //so, this would mean that if any point in the period it is more than
    for( var k = 0; k < data.length-1; k+=1 ) {
      sum += (data[k+1].value - data[k].value);
      if( sum < changeThreshhold || sum > changeThreshhold )
        return true;
    }
    return false;
    

    So, what it means to have changed over time can be funky depending on your data set. That second version should be equivalent to seeing if any values in a range are outside the acceptable rate of change... like,

    var data = p.pointValuesSince(new Date().getTime() - 5*60*1000) //Five minutes in milliseconds
    if( data.length <= 1 ) //we have the same edge conditions for our range
       return false
    var firstValue = data[0];
    var changeThreshhold = 5; //so, this would mean that if any point in the period it is more than
    for( var k = 1; k < data.length; k+=1 ) {
      if( firstValue.value - data[k].value > changeThreshhold || firstValue.value - data[k].value < changeThreshhold )
        return true;
    }
    return false;
    

    But this could still be flawed. If data[0].value == 0, data[1].value == -3, and data[2].value == 3, then none of these detectors would alarm, even though we experienced a change of 6 at one point. One would expect the next run of the point will have data[0] == -3, but having uniform data in time is an assumption we shouldn't rest our logic on!

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

    Hi Mircea,

    A "Broken pipe" is a general error for an abrupt disconnect of a TCP connection without warning by the other side.

    it looks like it occurred while streaming data from the Json Data table. What page were you using when you observed this? Can you repeat it?

    By itself a broken pipe isn't too significant but surely if you can reproduce it readily, then it definitely warrants investigation.

    posted in Mango Automation general Discussion read more
  • phildunlap

    Hi Gary,

    I think the problem you're experiencing is probably that there was a bug preventing the HTTP Receiver from being created or saved through the API. I fixed this and released a new version of the module, 1.6.2.

    If you've got that version, the easiest / best way to get the JSON model is by doing a GET for a data source you've made through the UI. But, here's the JSON for a an HTTP Receiver from my testing:

    {
      "xid": "httpReceiver2",
      "name": "HTTP Receiver",
      "enabled": false,
      "modelType": "HTTP_RECEIVER",
      "validationMessages": [],
      "deviceIdWhiteList": [
        "*"
      ],
      "setPointUrl": "",
      "ipWhiteList": [
        "*.*.*.*"
      ],
      "editPermission": "",
      "purgeSettings": {
        "override": false,
        "frequency": {
          "periods": 1,
          "type": "YEARS"
        }
      },
      "alarmLevels": {
        "SET_POINT_FAILURE": "URGENT"
      }
    }
    

    One must use the POST /rest/v1/data-sources to create data sources, and the PUT /rest/v1/data-sources/[XID] to update existing data sources.

    Thanks for bringing that bug to our attention!

    posted in User help read more
  • phildunlap

    Hi Mok Kiew,

    I've never written any VBA and very little VB personally, but I think this stackoverflow thread does some general API type stuff, and it does contain some information about setting headers: http://stackoverflow.com/questions/19553476/how-to-make-rest-call-with-vba-in-excel

    You'll want to do a GET to /rest/v1/login/admin or another user with the password in a header named "password" which will get you a response with a Set-Cookie header if successful. You'll have to pass that header's value as the "Cookie" header in subsequent requests.

    You may need to include the XSRF-TOKEN portion of the Cookie under the X-Xsrf-Token header, as well. If this is required you'll have a 403 Forbidden response when using just the Cookie header.

    Someone else may chime in with an example script. Good luck!

    posted in How-To read more
  • phildunlap

    Hi Jose,

    I'm sure I had a clearer picture in my mind of what I would need to write you when I said that. But, here's a python script to generate JSON you can import on the import/export page. You'll need to edit the paths in the script if you want to use it, and it'll intake a CSV file like:

    DP_123456,1003
    DP_654321,1004
    

    and output JSON for point links you can import. You may also need to modify the target point XID or other things like that. Here's the python:

    import json
    from StringIO import StringIO
    
    #use double slashes for windows, i.e. "C:\\path\\to\\new-point-links.csv"
    csvPointXids = open("/path/to/new-point-links.csv")
    #I will treat this as though it's a CSV with two columns, 0 is source point XID, 1 is the code for the point link body, like 1002
    
    basePointLink = """{
             "xid":"PL_17-3-1_gen_%(loopCounter)d",
             "sourcePointId":"%(sourceXid)s",
             "targetPointId":"almnr",
             "event":"CHANGE",
             "logLevel":"NONE",
             "disabled":false,
             "script":"return getPrefix() + '%(outputNumber)s' + source.value",
             "scriptPermissions":{
                "customPermissions":"",
                "dataPointReadPermissions":"superadmin",
                "dataPointSetPermissions":"superadmin",
                "dataSourcePermissions":"superadmin"
             },
             "writeAnnotation":false
          }"""
    	  
    #uncomment this if you have a header line to consume
    #csvPointXids.readline()
    
    outputConfig = {"pointLinks":[]}
    loopCounter = 1
    for line in csvPointXids.readlines() :
    	data = line.replace("\r","").replace("\n","").split(",")
    	if len(data) < 2 :
    		continue
    	pointJson = basePointLink % {"loopCounter": loopCounter, "sourceXid": data[0], "outputNumber": data[1]}
    	#print pointJson
    	outputConfig["pointLinks"].append( json.load( StringIO( pointJson ) ) )
    	loopCounter += 1
    	
    csvPointXids.close()
    
    #You need to edit this output path as well. C:\\path\\to\\output.json
    output = open("/path/to/output.json", "w+")
    output.write( json.dumps( outputConfig, indent=4, sort_keys=False, separators=(",",":") ) )
    output.close()
    

    posted in User help read more
  • phildunlap

    Hi Jose,

    I would bet that means there are no lines in your CSV or there is not a comma in the line separating the two fields. You can add a statement like

    print line
    

    to the for line in csvPointXids.readlines() : block before the if len(data). to see it print at the command line. If there are no lines in your file you will see no output. I think if you have the file path wrong in the initial open() it will error.

    It looks like I got the separators backwards (should be separators=(",",":"), so I edited that to fix. I did test that the script generates JSON (but I didn't import the json)

    posted in User help read more
  • phildunlap

    1. IP, hence the "IpNetwork" and "IpNetworkBuilder" instead of MstpNetwork
    2. if( localDevice.getNetwork() instanceof IpNetwork ) return "It's an IP network!";
    3. Create an MstpNetwork and pass it to new DefaultTransport( mstpNetwork );

    posted in BACnet4J general discussion read more
  • phildunlap

    Hi Silvia,

    It's a very good question! The answer is that as the UI is being redesigned, a fair bit of functionality hasn't migrated over yet. The "Data sources" menu item will link you back into the old UI, and you can navigate to the old system settings page using the icon bar. Or, you can navigate to the /system_settings.shtm URL.

    If you find there's a legacy page you visit especially often, you can link to it using a custom link menu item on the "Edit menu" page, linked to the full URL of the page.

    posted in Mango Automation read more
  • phildunlap

    Hello!

    I think what you're looking for is probably...

    <@subject>Hey - Stuff is happening with the automation system and I think you should know</@subject>

    somewhere in your FTL. For a complete list of subject line options I'll have to refer you to the code a little, as the SubjectDirective class has some parameter options. Aliases override the default (but keep the Critical - and - id portions as you discovered probably), and the last SubjectDirective will probably have the final say.

    https://github.com/infiniteautomation/ma-core-public/blob/main/Core/src/com/serotonin/m2m2/email/SubjectDirective.java

    posted in How-To read more
  • phildunlap

    If you wished to accumulate all values instead of just the logged averages, you could more simply have your script body be

    return my.value + p.value;

    The update event would still be start of minute to get an instant instead of the average. Or you could do a context update accumulator, but presumably there is a reason you're only wanting to accumulate the minute average, or I am misunderstanding.

    posted in Scripting general Discussion read more
  • phildunlap

    He made a great post a little while ago about how to include your own module definitions, if you find that you need to extend Mango in some way we haven't yet: https://forum.infiniteautomation.com/topic/2844/how-to-use-json-receiver-data-point/5

    posted in User help read more
  • phildunlap

    Hi George,

    There is! Runtime information is available in the StartsAndRuntimes object returned from the past() and previous() functions on data points in scripts.

    So, i could have a meta point like,

    var stats = p.past(DAY); //p is the variable name of the binary point in the context
    for(var k = 0; k < stats.data.length; k+=1) {
        if(stats.data[k].value === true)
            return my.value + stats.data[k].runtime/3600000; //runtime given in milliseconds
    }
    return my.value;
    

    running on a cron like 59 59 23 * * ? and it'll totalize once a day the hours run that day. Then you can put a limit detector on the meta point.

    posted in Mango Automation read more
  • phildunlap

    Have you let this run over night? With that cron pattern it should only run on the last second of the day to produce a value.

    posted in Mango Automation read more
  • phildunlap

    Hi mariealtin,

    Unfortunately there is not a way to currently reference information about the target or source points from within the script. It wouldn't be very much to add, so I'll bring it up to people.

    One could programmatically fill those in, if you're interested in a workaround. Simply keep using "[sourcename]" where you'd like the name in the script, perhaps like this,

    var x = source.value;
    if(x == 1.0)
        return '[sourcename]_1';
    else
        return '[sourcename]_0';
    

    Then export your pointLinks and dataPoints to JSON, and try out this python script:

    import json
    
    configFile = open("/path/to/config.json")
    config = json.load( configFile )
    configFile.close()
    
    dpXidMap = {}
    for dp in config["dataPoints"] :
        dpXidMap[dp["xid"]] = dp
    
    for pl in config["pointLinks"] :
        pl["script"] = pl["script"].replace("[sourcename]", dpXidMap[pl["sourcePointId"]]["name"])
    
    outputFile = open("/path/to/output.json", "w+")
    outputFile.write(json.dumps(config, separators=(",",": "), indent=4, sort_keys=False))
    outputFile.close()
    

    Which should do all that text replacement for you, and write JSON you can import to the output.json file.

    posted in Scripting general Discussion read more
  • phildunlap

    Their reply,

    Hi Phillip,

    Thanks for pointing this out. This property was originally documented on our site and listed as a beta/experimental feature (and it still is), but we removed references to it in the documentation because it was not quite ready for primetime as it worked in very basic use cases (note that it will ignore any user set minimum and maximum values). As we're currently focusing on our next major version, we don't have an ETA as to when we'll iron out any remaining issues with synchronizeGrid. You're more than welcome to keep using it if it works for you, but use at your own risk.

    As for enforcing the minimums and maximums, setting the value axis' strictMinMax (http://docs.amcharts.com/3/javascriptcharts/ValueAxis#strictMinMax) property to true should do the trick.

    Please let me know if you have any questions.

    Best,
    [Name of AMChart support person]

    posted in Mango feedback read more
  • phildunlap

    Try query="'deviceName=' + selectedDevice"

    posted in User help read more