10 Replies Latest reply on Sep 13, 2017 8:18 AM by Roger Palmen

    Custom Coresight/PI Vision Symbol to Calculate Values

    kbobeck

      With the release of PI Vision and its symbols library, we are trying to move away from ProcessBook. However, one major feature PI Vision still seems to be missing is the ability to do calculations, like PB's 'PI Calc' ability. For example, we wanted to do simple addition of two tag values: generated power from a CHP plant + imported power from the utility co. = Total Power. The way things currently are, this requires either a) creating a ProcessBook display and importing it into PI Vision or b) adding a calculation in AF using Asset Analytics or an attribute formula. Both options require access to a work station with PB and/or System Explorer installed on it, whereas PI Vision can be accessed from anywhere.

       

      I've seen a couple of custom symbols for simple values, but nothing for calculations. After some searching, PI Web API seems to be the best (or only) way to go, so I've come up with https://<server>/piwebapi/calculation/summary/?webID=s0JU52ag55YUORaOBzWLzbswT1NJVEVTVDAyMA&time=*&expression='CHP.Power'%2B'Utility.Power'&summaryType=total as my request . However, I've taken a look at Using PI Web API Client library in PI Vision 2016 R2 and 2017 and I don't understand which portion of the code decides what the request is. Additionally, according to Question - Coresight Symbol Extension with Multiple data source using AF heading as input., a Value symbol can only have a single data source. I envision dragging and dropping one attribute and then another on top of it (like you would to add two attributes to a trend) to populate which tags to add, but I don't want a table - I want it to be a single value.

       

      As my knowledge of PI Web API dates back to about 10am this morning, I'm hoping someone could point me in the right direction. Where does the request go within the code for a custom symbol? Is there a way to make it look like a value, even though they can only have a single data source?

        • Re: Custom Coresight/PI Vision Symbol to Calculate Values
          Kenji Hashimoto

          It is very good question.

          For getting calculation results, I also recommend to use analysis or formula within AF. But if you want to get it from PI Web API, following blog post is helpful.

          PI Coresight 2016 R2 Put Value Custom Symbol by PI Web API

          It calls PI Web API to write value within PI Vision. You can change it to calculate A+B.

          Also I recommend to vote here. (Customer Feedback - PI Vision + Dataset)

          Create on the fly calculations – Customer Feedback for OSIsoft & the PI System

          4 of 4 people found this helpful
          • Re: Custom Coresight/PI Vision Symbol to Calculate Values
            Roger Palmen

            In general, a good architecture keeps the visualisation and other stuff like business rules separate. So having calculations in a PI Vision display is not the way to go.

            Of course you could build a custom symbol that processes the data. For simple calculations to build your desired visualisation this could be OK, but in general this is a no-no.

             

            Edit: so my advise goes alongside that of Kenji: do the calculations in AF, and leave only the visualisation in PI Vision

            1 of 1 people found this helpful
              • Re: Custom Coresight/PI Vision Symbol to Calculate Values
                kbobeck

                Hi Roger,

                 

                I guess I'm hoping it can be a stop-gap until such capabilities are native to PI Vision. At the very least, even if I never implement it in a production environment, trying to figure this out is becoming an excellent way to understand PI Vision extensibility and how the Web API works

                  • Re: Custom Coresight/PI Vision Symbol to Calculate Values
                    Lonnie Bowling

                    I kind of (respectfully) disagree with the idea that all calculations should be done in AF. Sometimes calculations are so trivial and varied, like simple sums in this case, that AF would be overkill, and could become bloated. There is a place for doing stuff on the UI side, or with a PI Web API call. Sometimes breaking the rules actually is easier and faster and works just as well. I try not to get to set in one way or the other. Don't get me wrong, I agree that most calculations should and would be best to keep in AF, but not 100% of the time. I think in this case, what Kelsey is looking to do, is logical and has some nice benefits.

                     

                    Lonnie

                    1 of 1 people found this helpful
                      • Re: Custom Coresight/PI Vision Symbol to Calculate Values
                        LindseyHatfield

                        I agree with Lonnie as well, the SCADA system collects the data and the AF server uses it and does all sorts of calculations, data cleansing, analysis and notifications etc.

                        When users of the data are looking at the figures and comparing information they have to do on the fly additions and comparisons between sites etc.

                        These may only be used once or twice a year for a couple of sites, so to create additions for all these items for the hundreds of sites and saving to more points is a major waste.

                        On top of that the users that are using coresight do not have access to the AF servers and configurations so that becomes another teams services.

                         

                        I am interested as well and will also have a go at building something like this.

                         

                        Lindsey

                  • Re: Custom Coresight/PI Vision Symbol to Calculate Values
                    Marcos Vainer Loeff

                    Hello Kelsey,

                     

                     

                    I have developed a custom symbol for you with PI Web API Client library for AngularJS. I really think that using this library is easier since you don't need to generate the URL manually. The library does it for you automatically. Just be careful to update the source code from PI Vision after every update according to the blog post. Don't worry it is a very simple procedure.

                     

                     

                    Sorry for the delay but the code it below:

                     

                    (function (PV) {
                        function symbolVis() { }
                        PV.deriveVisualizationFromBase(symbolVis);
                    
                    
                        var definition = {
                            typeName: 'piwebapitest',
                            datasourceBehavior: PV.Extensibility.Enums.DatasourceBehaviors.Multiple,
                            inject: ['piwebapi'],
                            getDefaultConfig: function () {
                                return {
                                    DataShape: 'Table',
                                    Height: 400,
                                    Width: 400,
                                    MarkerColor: 'rgb(255,0,0)'
                                };
                            },
                            visObjectType: symbolVis
                        };
                    
                    
                    
                    
                        symbolVis.prototype.init = function init(scope, elem, piwebapi) {
                    
                    piwebapi.SetServiceBaseUrl("https://marc-web-sql.marc.net/piwebapi");
                            piwebapi.SetKerberosAuth();
                            piwebapi.CreateObjects();
                    
                    
                            console.log('Starting init scope: ' + scope);
                            console.log('Starting init scope: ' + elem);
                    
                    this.onDataUpdate = dataUpdate;
                            this.onConfigChange = configChanged;
                            this.onResize = resize;
                    
                    
                            var container = elem.find('#container')[0];
                            var id = "test_" + Math.random().toString(36).substr(2, 16);
                            container.id = id;
                    
                    
                    
                    
                            function configChanged(config, oldConfig) {
                    console.log('configChange called');
                            };
                    
                    
                            function resize(width, height) {
                    console.log('resized called');
                            } 
                    
                    scope.streamWebId = null;
                    
                            function dataUpdate(data) {
                    if ((data == null) || (data.Rows.length == 0)) {
                                    return;
                                }
                    
                    var firstDataRow = data.Rows[0];
                    if (firstDataRow.Path)
                    {
                    var path = firstDataRow.Path.substring(3, firstDataRow.Path.length);
                    if (firstDataRow.Path.substring(0, 2) == "af")
                    {
                    path = path.split("|")[0];
                    piwebapi.element.elementGetByPath(path).then(function (response) {
                    scope.streamWebId = response.data.WebId;
                    });
                    }
                    if (firstDataRow.Path.substring(0, 2) == "pi")
                    {
                    piwebapi.point.pointGetByPath(path).then(function (response) {
                    scope.streamWebId = response.data.WebId;
                    });
                    }
                    
                    }
                    
                    if (scope.streamWebId != null)
                    {
                    
                    calculationBasis = 'TimeWeighted';
                    endTime = "*";
                    expression = "2*'Latitude'"; 
                    sampleInterval = undefined;
                    sampleType = undefined;
                    selectedFields = undefined;
                    startTime = "*-1d";
                    summaryDuration = undefined;
                    summaryType = "total";
                    timeType = undefined;
                    webId = scope.streamWebId;
                    piwebapi.calculation.calculationGetSummary(calculationBasis, endTime, expression, sampleInterval, sampleType, selectedFields, startTime, summaryDuration, summaryType, timeType, webId, null).then(function (response) {
                    scope.data = response.data;
                    });
                    }
                            }
                        }
                    
                    
                    
                    
                    
                    
                    
                    
                        PV.symbolCatalog.register(definition);
                    })(window.PIVisualization);
                    
                      • Re: Custom Coresight/PI Vision Symbol to Calculate Values
                        kbobeck

                        Hi Marcos,

                         

                        Thanks for putting in the effort to develop this and sorry for the late reply! I tried to use the symbol but I am not able to get anything to display in PI Vision, despite creating an HTML template and adding a static label (Total: ) to help confirm whether it was working. Any suggestions? Additionally, how would you go about avoiding hardcoding the expression? My goal is to drag/drop one attribute to create the symbol, then drag/drop another attribute on top to add it to the symbol and therefore add the two together. I was trying the below method (before you provided your code) and setting expression = resultString, but not getting anywhere:

                         

                                scope.items = [];
                                function dataUpdate(data) {
                                    if (data) {
                                        for (var i = 0; i < data.Rows.length; i++) {
                                            if (scope.items[i]) {
                                                // Update the time and date
                                                scope.items[i].Time = data.Rows[i].Time;
                                                scope.items[i].Value = parseFloat(data.Rows[i].Value);
                                                scope.items[i].Path = data.Rows[i].Path;
                                            } else {
                                                // Create a new data object, extracting attributes from the data update
                                                var newDataObject = {
                                                    "Time": data.Rows[i].Time,
                                                    "Value": parseFloat(data.Rows[i].Value),
                                                    "Path": data.Rows[i].Path
                                                };
                                                scope.items[i] = newDataObject;
                                            }
                                        }
                                    }
                                }
                                var resultString = "";
                                scope.calcResults = function () {
                                    for (var i = 0; i < scope.items.length; i++) {
                                        resultString += scope.items[i].path + "'%2B'";
                                        resultString = resultString.replace("%2B'", '');
                                    }
                                };
                        

                         

                         

                        Thanks!

                        Kelsey

                          • Re: Custom Coresight/PI Vision Symbol to Calculate Values
                            Roger Palmen

                            An approach i use for symbols that need more data than just one attribute is to link them to the element. If you use an element as the source of a symbol, all the attributes of the element are added to the source list of the symbol.

                             

                            E.g. i have a gauge where i configure the scaling and limits with a set of attributes. Based on the names of the attributes i map these to the process value, limit, scale values, etc, on the symbol. You could do the same for this symbol and use additional attributes to contain the configuration of the expression you need.