AnsweredAssumed Answered

Need to update all Digital point values and Change Digital State Set but I am seeing wrong digital values

Question asked by langfom on Jun 26, 2020
Latest reply on Jun 26, 2020 by langfom

Hi There

 

I am using AFSDK version 2.10.5.9050.  PI Server version 3.420.1182.

 

Currently all of our digital points in our PI System receive either 0 or 1.  We are moving to a new monitoring system which will send 0 to indicate an Error and 1 and 2 as the valid values.  Due to the monitoring system constraints I have been asked to align our PI data to the new system which requires the following: -

  • Update all existing digital point values to 1 or 2 i.e. existing 0 values become 1, existing 1 values become 2.
  • We currently have 60+ Digital State Sets across our digital tags e.g. Open/Close, On/Off, Alarm/Normal etc which will either need to be modified to add a 2 value and edit the existing 0 and 1 values or create a new set of Digital States.  Already a new SwitchStatus Digital Set has already been created in PI by the monitoring system software which contains 5 states.  All of our existing switch status points which were just Open/Closed will need to move across to this new SwitchStatus Digital Set.

 

We have ~200,000 Digital Points in our PI System with up to 13 years of data.  Thankfully the majority our Digital points don't change too often.

 

My original AF SDK prototype solution attempted to change the point's existing Digital State Set to the new SwitchStatus Digital Set and then update all of the existing AFValues to the required AFEnumerationValue from the SwitchStatus Digital State Set.  I experienced strange results doing this.

 

Then reading through the PI Square forum I came across this question what is the osisoft best practice for digital pi tags whose data sources states have changed or swapped keep in mind these pi tags have historized data that we want to keep

I put together the Option 2 solution but I still receive strange results. My program does the following (source code below)

  • Find a test PI Point and load DigitalSetName attribute
  • Query data for that point
  • Insert the data into a PostgreSQL database table
  • Delete the data for the test point from PI
  • Change the Digital State Set to the new SwitchStatus set for the test PI Point
  • Re-insert the data into PI from the PostgreSQL table with the existing int values mapped to the AFEnumerationValue from the SwitchStatus Digital Set.

 

In PI SMT, the result initially look ok in the Archive Editor but if I continue to refresh the data, the Values will change to incorrect values i.e. the system state ?2 and also the Closed values aren't correct to what I inserted

PI SMT - archive editor

The original data is shown below (from my PostgreSQL data)

value | valuestring | time
------+-------------+--------------+-------------------------------
1 | Closed | 2020-05-18 13:02:26.190002+10
1 | Closed | 2020-06-18 11:26:08.019012+10
1 | Closed | 2020-06-23 22:30:25.617004+10
0 | Open | 2020-06-26 14:15:40+10
1 | Closed | 2020-06-26 14:15:59+10

 

The PI OLEDB Tester tool shows a different value for the same point when compared to the above PI SMT screenshot.

PI OLEDB Tester output

What I don't understand is I have inserted AFEnumerationValue for offset 2 which should be closed but the first three values have a problem and reverted to System State value 2.  The bottom two rows have the correct status from the new Digital State.  What has gone wrong with the first 3 rows?

 

PI Datalink is also strange, for the same *-1y query, the first row doesn't with the oldest timestamp doesn't appear in the results, the number of values is 6 and the last two rows have picked up the name of the old Digital State Set and appended _Code2 to the name.

PI Datalink image

In Visual Studio when I query these data values, the list items 0, 1, 2, 4 have a M_Open_Close_Code2 EnumerationValue and null as the EnumerationSet.  Item 3 has the old EnumerationSet M_Open_Close.  Item 5 is the only value which shows to the new "SwitchStatus" EnumerationSet

Visual Studio Locals

 

Here is my code

 

 

string testTag = "A_Digital_Switch_Status_Tag";
string newStateName = "SwitchStatus";
DateTime dateTimeStart = DateTime.Now.AddYears(-1);

piserver = PIServer.FindPIServer("xxxxxxxx");
piserver.Connect();

var newDigitalSet = piserver.StateSets[newStateName];
AFTimeRange afTimeRange = new AFTimeRange(dateTimeStart, DateTime.Now);

var digPoint = PIPoint.FindPIPoint(piserver, testTag);
var attributes = new String[] { PICommonPointAttributes.DigitalSetName };
digPoint.LoadAttributes(attributes);

var existingValues = digPoint.RecordedValues(afTimeRange, OSIsoft.AF.Data.AFBoundaryType.Inside, "", false);

...
omitted code - insert existing PI values into PostgreSQL
...

//// digPoint.UpdateValues(afValues, AFUpdateOption.Remove);
// trying this way below to see if it helps
piserver.UpdateValues(existingValues, AFUpdateOption.Remove);

string existingDigitalSetName = digPoint.GetAttribute(PICommonPointAttributes.DigitalSetName).ToString();
if (!existingDigitalSetName.Equals(newDigitalSet.Name))
{
digPoint.SetAttribute(PICommonPointAttributes.DigitalSetName, newDigitalSet.Name);
digPoint.SaveAttributes(attributes);
digPoint.UnloadAllAttributes(null);
}

// Tried adding this...
Thread.Sleep(5000);

// Tried getting test point again to see if this helps
var insertPoint = PIPoint.FindPIPoint(piserver, testTag);

...
omitted code - query PostgreSQL to get data for test tag...
...

var afValues = new List<AFValue>();

NpgsqlDataReader dr = cmd.ExecuteReader();
while (dr.Read())
{
int piValue = (int)dr[0];
AFTime piTime = (DateTime)dr[1];
bool questionable = (bool)dr[2];
AFEnumerationValue enumValue = getMappedValue(piValue, newDigitalSet);
AFValue afValue = new AFValue(enumValue, piTime);
afValue.Questionable = questionable;
afValues.Add(afValue);
}

digPoint.UpdateValues(afValues, AFUpdateOption.Insert);


// below is the getMappedValue method
// as used to build AFValue list from PostgreSQL results

private AFEnumerationValue getMappedValue(int piValue, AFEnumerationSet newDigitalSet)
{
switch(piValue)
{
case 0:
return newDigitalSet[1]; // open
case 1:
return newDigitalSet[2]; // closed
default:
return newDigitalSet[0]; // error
}
}

 

Does anyone know if this can be done?

What is the best way to do this?

What am I doing wrong?

Have I missed a save, commit or flush of a cache somewhere in the code?

Should I disconnect, reconnect to the PI Server in between the data delete and the data insert?

 

Thanks in Advance

Mark

Outcomes