7 Replies Latest reply on Mar 19, 2018 6:14 PM by neilg

    How does AF attributes subscription work?


      I signed one AF attribute (A) to my application, and this attribute is pointed to a PI tag (B). When i change the PI tag (B) of the AF attribute for a PI tag (C), my application doesn't get the values of the new tag on the update events, the application continue to get the value of the old tag.


      1 - When I sign an AF attribute and it changes, the signed object remains the same? In other words, the datapipe/eventpipe is not capable of detecting this change?

      2 - It seems that if I sign an AF attribute that is pointing to a PI tag, it signs a full "path" to the value of the PI tag. I wanna know if is it true?

      3 - Also, I want to know, even if I refresh the AF database this "path" will change to the new one.


      Please aks me if you need more informations.


      I hope you can help me.

        • Re: How does AF attributes subscription work?

          Hi Bruno,


          Thanks for posting your question.  In order to better understand your situation, I have some follow up questions

          1. After you change the AF attribute A to point to PI tag C, does the application check in the changes?

          2. After you change the configuration, does the application sign up for updates again? 

          3. What call are you making to sign up for updates for the AF Attribute?


          Lastly, would you mind posting the code here?

            • Re: How does AF attributes subscription work?



              1 - This change is made in PI System Explorern, not in the application. The application only recieve new values from PI/AF.


              2 - No. The aplication don't sign up the attibutes again, because the application don't know when a the attibute changes on  PI System Explorern.


              3 -  Sign Up method

              public void Subscribe(IEnumerable<variable> variables)


                          foreach (var variable in variables)


                              var key = variable.CodigoVariavel;

                              if (!varDictionary.ContainsKey(key))


                                  varDictionary[key] = new List<variable>();




                          var groupedvar = variables.GroupBy(v => v.Connection.ConnectionString);

                          foreach (var group in groupedvar)


                              var context = GetServerContext(group.Key);

                              var pathNames = variables.Select(v => v.VarPath);

                              var attributes = AFServerRepository.FindAttributes(pathNames, true, "Subscribe");

                              var afSingups = context.DataPipe.AddSignups(attributes);


                                     if (timer == null)


                              timer = new Timer(int.Parse(ConfigurationManager.AppSettings["AFServerEvent.ProcessUpdatesPeriod"] ?? "10000", CultureInfo.CurrentCulture));

                              timer.Elapsed += ProcessUpdates;

                              timer.AutoReset = false;





              Process Updates

              private void ProcessUpdates(object sender, EventArgs arguments)


                          string currentKey = "Before foreach in updateEvents.Results and key attribuition";

                          foreach (var context in serversContexts)




                                  var updateEvents = context.Value.DataPipe.GetUpdateEvents();

                                  var baseDate = DateTime.Now;

                                  foreach (var updateEvent in updateEvents.Results)


                                      var key = updateEvent.Value.Attribute.GetPath();

                                      currentKey = key;

                                      IList<Variable> variables;

                                      if (variaveisDictionary.TryGetValue(key, out variables))


                                          var value = AFServerRepository.GetValue(updateEvent.Value);

                                          foreach (var Variable in variables)


                                              if (Variable.Date == null || updateEvent.Value.Timestamp.LocalTime > Variable.Date)


                                                  Variable.Value = value;

                                                  Variable.Date = updateEvent.Value.Timestamp.LocalTime;

                                                  Variable.ReadError= !updateEvent.Value.IsGood;

                                                  MessageSender.Instance.Send(new VariableOnNewValueEvent(Variable), baseDate);






                                          var message = string.Format(CultureInfo.CurrentCulture, "AFServerEvents - Updates - {0} - ERRO: The given key was not present in the dictionary.", currentKey);


                                          ErrorEvents.Raise(new DomainException(message));




                              catch (Exception e)


                                  var message = string.Format(CultureInfo.CurrentCulture, "AFServerEvents - Updates - {0} - ERRO: {1}", currentKey, e.Message);






                • Re: How does AF attributes subscription work?

                  Thanks for the code, Bruno.  I've moved this thread to the PI Developers Club, since it's more of a coding question with the PI AF SDK than a general PI question.


                  I've verified the behavior you're seeing on my own system - if you change the PI Point that an AF Attribute is pointing to, the data pipe will not automatically update to reflect the new configuration.  You can verify this with code or with PSE.  In PSE:

                  1. Go to the PIPoint Data Reference attribute > right click > Time Series Data > Data Pipe tab.  Note the values there.

                  2. Keeping the Time Series Data window open, change the PIPoint that the attribute is pointing at.  Check it in.

                  3. Click Update on the Time Series Data window. You'll note that the values do not update.


                  You have to use the AFDataPipe.ObservePendingChanges Method and the AFDataPipe.ProcessAppliedChanges Method in order to get updates on the attributes already added to the AF DataPipe.  I was able to get the AFDataPipe to get values for PIPoint attributes even after changing the configuration with this code:


                  using System;
                  using System.Collections.Generic;
                  using OSIsoft.AF;
                  using OSIsoft.AF.Asset;
                  using OSIsoft.AF.Data;
                  using OSIsoft.AF.PI;
                  namespace test
                      class Program
                          static void Main(string[] args)
                              PISystems afsrvs = new PISystems();
                              PISystem afsrv = afsrvs["kf-af285"];
                              AFDatabase afdb = afsrv.Databases["datapipe"];
                              var pisrv = PIServer.FindPIServer("kf-pi1");
                              //find Element1|Attribute1
                              var elements = AFElement.FindElements(afdb, null, null, AFSearchField.Name, true, AFSortField.Name, AFSortOrder.Ascending, 10);
                              var elem1 = elements["Element1"];
                              var attrs = AFAttribute.FindAttributes("Attribute1", elem1);
                              //create dbCookie for changed items
                              object dbCookie;
                              afdb.FindChangedItems(false, int.MaxValue, null, out dbCookie);
                              //create datapipe
                              var datapipe = new AFDataPipe();
                              var errors = datapipe.AddSignups(attrs);
                              var changedObjects = new List<AFChangeInfo>();
                              //get events from AFDataPipe.  Code from https://pisquare.osisoft.com/thread/11797?q=afdatapipe
                              while (true) // this statement keepes the task (thread) alive
                                  bool bMoreEvents = true;
                                  while (bMoreEvents) // we need this loop to be certain that we empty the data pipe each time 
                                      //find changed objects in the AF database. Can also be done on a PI AF Server (PI System) level.
                                      changedObjects.AddRange(afdb.FindChangedItems(false, int.MaxValue, dbCookie, out dbCookie));
                                      var pendingChanges = datapipe.ObservePendingChanges(changedObjects);
                                      //refreshes objects that were changed, then datapipe processes changes
                                      AFChangeInfo.Refresh(afsrv, changedObjects);
                                      //get updates for all objects in datapipe
                                      var updates = datapipe.GetUpdateEvents(out bMoreEvents);
                                      foreach (var dataPipeEvent in updates.Results)
                                          //print values from datapipe.
                                          Console.WriteLine("AFDataPipe event - Attribute Name: {0}, Tag Name: {1}, Action Type: {2}, Value {3}, TimeStamp: {4}", dataPipeEvent.Value.Attribute.Name, dataPipeEvent.Value.PIPoint.Name, dataPipeEvent.Action.ToString(), dataPipeEvent.Value.Value, dataPipeEvent.Value.Timestamp.ToString());


                  Here's what the output looks like when I changed from cdt158 > sinusoid > cdm158.


                  AFDataPipe event - Attribute Name: Attribute1, Tag Name: CDT158, Action Type: Add, Value 61, TimeStamp: 11/3/2016 3:08:34 PM

                  AFDataPipe event - Attribute Name: Attribute1, Tag Name: CDT158, Action Type: Add, Value 62, TimeStamp: 11/3/2016 3:08:41 PM

                  AFDataPipe event - Attribute Name: Attribute1, Tag Name: SINUSOID, Action Type: Add, Value 100, TimeStamp: 11/3/2016 3:08:51 PM

                  AFDataPipe event - Attribute Name: Attribute1, Tag Name: SINUSOID, Action Type: Add, Value 99, TimeStamp: 11/3/2016 3:12:47 PM

                  AFDataPipe event - Attribute Name: Attribute1, Tag Name: SINUSOID, Action Type: Add, Value 77, TimeStamp: 11/3/2016 3:12:51 PM

                  AFDataPipe event - Attribute Name: Attribute1, Tag Name: CDM158, Action Type: Add, Value Cascade, TimeStamp: 11/3/2016 3:12:44 PM

                  AFDataPipe event - Attribute Name: Attribute1, Tag Name: CDM158, Action Type: Add, Value Auto, TimeStamp: 11/3/2016 3:13:14 PM


                  Let me know if you have any questions.



                  AFChangeInfo Structure

                  AFChangeInfo.Refresh Method

                  How to use the PIDataPipe or the AFDataPipe

                  4 of 4 people found this helpful
                    • Re: How does AF attributes subscription work?

                      Thanks for your help.

                      • Re: How does AF attributes subscription work?

                        Hi Keith, Sorry for bumping an old post. I tried this but this does not work for the following scenario.

                        I am putting an example case here.
                        I have 3 weather stations, WS1, WS2, WS3. The weather station telemetry tags and AF Attributes are named as M.WS1.MEASURED_WIND_SPEED, M.WS2.MEASURED_WIND_SPEED, etc.

                        There is an attribute called "WS ID TO USE". This ID is populated by an AF Analyses which gets populated to WS1 or WS2 or WS3. The logic is if WS1 data goes stale use WS2 and so on.
                        The WIND_SPEED AF Attributes refer to the "WS ID TO USE" (in the config string) attribute to name the PI tag to use. Initially the attribute is linked to WS1. It goes stale and the AF Analyses updates the ID to WS2 and hence the WIND_SPEED attribute starts reflecting the tag for WS2. When printing out the tag name the correct name comes up from the event on the datapipe but the data is still coming from WS1 WIND SPEED tag which was the one initially signed up.


                        Also the pending changes collection does not seem to have any values in this type of change.