• phildunlap

    Hi matwolff, welcome to the forum!

    I wonder, are you sending all the headers in the example? Are you getting a JSON response like you were successful?

    I was able to curl a login and save the response headers to a /home/mango/cookie-headers file with

    curl -X POST http://localhost:80/rest/v2/login --header "Accept:application/json" --header "Content-Type:application/json;charse=UTF-8" --header "X-XSRF-TOKEN:74cf354a-e871-48b6-a1c2-bebb93d00120" --header "Cookie:XSRF-TOKEN=74cf354a-e871-48b6-a1c2-bebb93d00120" --data '{"username":"admin","password":"admin"}' -D /home/mango/cookie-headers

    posted in User help read more
  • phildunlap

    Having too many points for your license will not cause points not to chart.

    One way to get a 400 BadRequest back is to request a rollup or simplify of an alphanumeric or image point, or request and unsupported rollup type in the date bar. So, can you try with the Date bar's rollup type set to 'None'?

    posted in User help read more
  • phildunlap

    Thanks Fox!

    Just to ensure for timkk880, when he says,

    moment format

    he means Moment.js format

    posted in Mango Automation general Discussion read more
  • phildunlap

    It may depend on your device. For the MangoES, which is a Linux ARM device, we're using the scripts in Idrive_ARM (which has the other directory inside it).

    posted in How-To read more
  • phildunlap

    Hmm. It sounds like one or more modules has a module.properties file that is most likely 0 bytes long. I saw this on someone's system after an update. Perhaps try removing all your modules, and then using the /modules.shtm page to install the modules you need.

    posted in Mango Automation Installation read more
  • phildunlap

    Or you could use a component. It's about the same, but you're less likely to be troubled by infinite digest loop issues during development. Here's the markup:

    <ma-point-query query="{$and: true, deviceName:'Mango Internal', name:'oint'}" limit="3" points="points"></ma-point-query>
    
    <br/>
    <ma-point-statistics points="points" rendered=false from="dateBar.from" to="dateBar.to" statistics="statsObj"></ma-point-statistics>
    <integral-collator points="points" statistics="statsObj" output-var="outputArray2"></integral-collator>
    <ma-serial-chart  style="height: 300px; width: 100%" series-1-values="outputArray2" default-type="column" 
    options='{"categoryAxis":{"parseDates":false}}'></ma-serial-chart>
    

    And now we'll need to define this component in our user module,

    define(['angular', 'require'], function(angular, require) {
    'use strict';
    
    var userModule = angular.module('userModule', ['maUiApp']);
    
    class IntegralCollatorController {
        static get $$ngIsClass() { return true; }
        
        $onChanges(changes) {
            if (changes.points || changes.statistics && (this.points && this.statistics)) {
                this.calculateOutput();
            }
        }
        
        calculateOutput() {
            if(!this.points || !this.statistics)
                return;
                
            var output = [];
            var minLength = this.points.length < this.statistics.length ? this.points.length : this.statistics.length;
            for(var k = 0; k < minLength; k+=1) {
                output.push({"value": this.statistics[k].integral.value, "timestamp": this.points[k].deviceName + " - " + this.points[k].name});
            }
            //TODO sort output
            this.outputVar = output;
        }
    }
    
    userModule.component('integralCollator', {
        bindings: {
            points: '<',
            statistics: '<',
            outputVar: '='
        },
        controller: IntegralCollatorController
    });
    
    return userModule;
    
    }); // define
    

    This is probably the right solution, but both techniques are interesting.

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

    Hi alexzer,

    What follows is perhaps not the ideal solution, but it does work (you may want to change the inputs back).

    <ma-point-query query="{$and: true, deviceName:'Mango Internal', name:'oint'}" limit="3" points="points"></ma-point-query>
    
    <br/>
    <ma-point-statistics points="points" rendered=false from="dateBar.from" to="dateBar.to" statistics="statsObj"></ma-point-statistics>
    <ma-calc input="outputArray|collateIntegrals:points:statsObj" output="outputArray"></ma-calc>
    <ma-serial-chart  style="height: 300px; width: 100%" series-1-values="outputArray" default-type="column" 
    options='{"categoryAxis":{"parseDates":false}}'></ma-serial-chart>
    

    and this required a filter in a user module again: https://help.infiniteautomation.com/getting-started-with-a-user-module/

    Here's my user module from this question now:

    define(['angular', 'require'], function(angular, require) {
    'use strict';
    
    var userModule = angular.module('userModule', ['maUiApp']);
    
    userModule.filter('collateIntegrals', ['$filter', function($filter) {
        return function(input, points, statistics) {
            if(!input)
                input = [];
            
            if(!points || !statistics)
                return;
                
            var output = [];
            var minLength = points.length < statistics.length ? points.length : statistics.length;
            for(var k = 0; k < minLength; k+=1) {
                output.push({"value": statistics[k].integral.value, "timestamp": points[k].deviceName + " - " + points[k].name});
            }
            if(output.length != input.length) {
                return output;
            } for(var k = 0; k < output.length; k+=1)
                if(output[k].value !== input[k].value || output[k].timestamp !== input[k].timestamp) {
                    return output;
                }
            return input; //Prevent infinite angular digest loop
        };
    }]);
    
    userModule.filter('fromJson', ['$filter', function($filter) {
        return function(input) {
            //console.log(input);
            try {
                return JSON.parse(input);
            } catch {
                console.log("Parsing error in fromJson filter for input: " + input);
                return null;
            }
        };
    }]);
    
    userModule.filter('sortByNumField', ['$filter', function($filter) {
        return function(input, field, asc) {
            try {
                return input.sort(function(a,b){ 
                    if(asc) return a[field]-b[field];
                    return b[field]-a[field];
                });
            } catch {
                console.log("Error sorting input: " + input);
                return input;
            }
        };
    }]);
    
    return userModule;
    
    }); // define
    

    Notice that series-1-values is the ouput array from the filter, so all points will be in a single series. It's possible this would be easier as a custom component, and example of which is in the next post....

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

    I don't think you can have two colors on the same series in a serial chart. So, I would think the easiest way would be storing the positive values in one point, and the negative values in the other. You would probably have to put zeros in the other point (whichever does receive the real value) so that there isn't linear interpolation by the chart that messes up the coloring. Or you could write a filter for turning all the negatives to 0, and another to turn all positives to 0 and negatives to positives, then use one one point's values filtered in two different ways

    posted in How-To read more
  • phildunlap

    :-/ When I say the UI, I mean the webserver, not the command line. I guess you didn't chmod +x all the scripts when you did your installation? I suspect your invoking the upgrade.sh script directly didn't hurt anything, since it didn't know where Mango was installed, but we've never instructed anyone to do that.

    You may still have the updated m2m2-core-3.4.1.zip in your Mango/ directory. If you attempted to start Mango again it would perform the upgrade.

    Otherwise,

    You should resolve this by downloading the enterprise bundle here: https://store.infiniteautomation.com/core

    1. Place the downloaded file in your Mango/ directory with the name m2m2-core-3.4.1.zip ,
    2. chmod +x Mango/bin/*.sh
    3. You may need to chown -R yourOsUser:yourOsGroup Mango/ if you copy the files over with a different permission that the user that launches Mango doesn't have.
    4. cd Mango/bin; ./ma.sh start

    The start scripts will check for upgrades in the Mango/ directory.

    posted in User help read more
  • phildunlap

    Hi Leandro,

    Sorry to be delayed in getting back to you. Have you attempted changing the env.properties associated with CORS? I believe the relevant section of the code for that part of the configuration happens here: https://github.com/infiniteautomation/ma-core-public/blob/main/Core/src/com/serotonin/m2m2/web/mvc/spring/MangoWebSocketConfiguration.java#L77

    The console on the left is with the env.properties

    rest.cors.enabled=false
    rest.cors.allowedOrigins=
    

    while the console on the right has the properties

    rest.cors.enabled=true
    rest.cors.allowedOrigins=localhost:8080,127.0.0.1:8080
    

    0_1526596998184_corsConsoles.png

    Note that in the right console I am still getting a 403 error for my request to 127.0.0.1:8080, probably because I didn't login on that hostname and don't have a valid session in an accessible cookie to send as a header in the websocket request. The reason I am getting 405s is that I didn't try to register for a real websocket, I was merely testing if the origin would be permitted, and it was. I can provide a more complete example if requested where the logging in gets done and a session cookie is available to the browser's JavaScript.

    Hope that's helpful!

    posted in Mango Automation read more