Hello everybody! Today I'm going to talk about my favorite programming language: Python. More specifically, how to integrate Python and our beloved AF SDK in order to extract data from PI. PI and Python? Let's call it PIthon.

Some of you may be wondering why would one do that. (If you are not, jump to the next paragraph). Well, Python has a very active community and is a well established programming language. It's multi-platform and has an uncountable number of libraries and tools. It's also heavily used for big data analytics when used together with libraries like SciPy and NumPy.

First, let me explain why I'm not using IronPython. If you don't know what it is, let me get its description: "IronPython is an implementation of the Python programming language targeting the .NET Framework and Mono.". See the problem? It's an implementation (C# if you are wondering) and not pure Python, so it frequently has problems integrating with third-party libraries. Also, it targets Mono (i.e Linux) and currently AF SDK does not support it. Last but not least, the latest stable release was almost two years ago, so things may be very outdated.

So let's start. For this tutorial you will need:

- A Windows box

- Python 2.7

- AF SDK (Included in the AF Client installer)

- Pythonnet

Proceed with the installation of everything and once it's done, go ahead and create a module called PIthon. Now comes the most important part: libraries import.

import sys
sys.path.append('C:\\Program Files (x86)\\PIPC\\AF\\PublicAssemblies\\4.0\\')
import clr
clr.AddReference('OSIsoft.AFSDK')

And that's all. No kidding. It's so simple I don't dare to explain it. The important thing is that from here you can use a syntax that resembles a lot C# development. Start by importing classes:

from OSIsoft.AF.PI import *
from OSIsoft.AF.Search import *
from OSIsoft.AF.Asset import *
from OSIsoft.AF.Data import *
from OSIsoft.AF.Time import *

Now let's create two important functions.

def connect_to_Server(serverName):
    piServers = PIServers()
    global piServer
    piServer = piServers[serverName]
    piServer.Connect(False)

def get_tag_snapshot(tagname):
    tag = PIPoint.FindPIPoint(piServer, tagname)
    lastData = tag.Snapshot()
    return lastData.Value, lastData.Timestamp

Did you notice how close to C# development this is? Most of the time is pretty much the same, but let me give you a very important tip : PythonNet does not handle well optional parameters from .NET assemblies, so you always need to be explicit. That's why I had to pass False on piServer.Connect. The explicit parameter allows me to dodge the optional parameter problem.

Well, from here you have your module, so let's use it! Go ahead and create a new script and use PIthon:

from PIthon import PIthon

PIConnector.connect_to_Server("Server")
value, timestamp = PIConnector.get_tag_snapshot('sinusoid')
print('Timestamp: {0} Value: {1}'.format(timestamp, value))

And if you run this...

Timestamp: 8/1/2016 5:03:59 PM Value: 73.48035

Voilà! See how simple it was? From here you have the base to create your own AF SDK module for Python! And if you have any questions, feel free to ask them!

Have fun coding with PIthon!

  • Does anyone know how the Python clr module handles references to .NET classes that implement IDisposable? The PI AF SDK has a lot of methods that return IEnumerable objects which must be enclosed by a "using" statement, but I'm not aware of any similar keyword in Python.

  • Hi,

    Please help,

    I have followed the steps. I am not sure if I am missing something. I have created the PIThon script.

     


    pastedImage_3.png_558a6772-d9e9-4d91-a470-58471b483cda.png

     

    I have also following script. 


    pastedImage_2.png_96d5f70e-42a2-4502-99aa-85083259494b.png

    I get the following run time error message.


    pastedImage_1.png_8c152b8a-2270-4449-84c1-296365972dd3.png

  • Very excellent, I used it to retrieve archived data into a predictive ML algorithm and the resulting prediction was entered into future archive.

     

    With this result, I was able to display current and predicted process information on PI Vision.

     

    Thank you for sharing this.

  • Hi, 

     

    I have a problem that we use two servers, but, using piServers.DefaultPIServer it only sends to one of them. 

    I need it to send to both.

     

    IN excel VBA we use:

    Set Collective = pisdk.Servers("PIHAVC") 'collective name

     

    Set cList = Collective.ListMembers

     

    Set cMember1 = cList.Item(1)

     

    Set cMember2 = cList.Item(2)

    ' update primary server

     

    Set srv1 = Collective.MemberOpen(cMember1, "")

     

    srv1.Open

    ' update secondary server

     

    Set srv2 = Collective.MemberOpen(cMember2, "")

     

    srv2.Open

     

    'primary server

     

    srv1.PIPoints(mtag).Data.UpdateValue mval, mdata, dmReplaceDuplicates

    'Ssecondary server

     

    srv2.PIPoints(mtag).Data.UpdateValue mval, mdata, dmReplaceDuplicates

     

     

    srv1.Close

     

    srv2.Close

     

    But I didn't find a way to do it in Python. I was able only to count 2 members.

    Then, I tried to:

    srv1= 'BRSAOW...'

    piServers = PIServers()

     

    piServer = piServers[srv1]

     

    but then I got an error:

     

    PIDuplicateIDException Traceback (most recent call last)PIDuplicateIDException: Confirmed server entry with duplicate Server ID already exists: **** em OSIsoft.PI.Configuration.PISDKRegistryDirectoryProvider.OSIsoft.PI.Configuration.IDirectoryProvider.UpdateService(String configName, ServiceEntity service) em OSIsoft.PI.Net.Connection.ValidateService()

  • Hi,

    thanx for the detailed post and sharing it was very helpful, however i could not find a reference of how to change attribute value (the set function in c#). can someone shade some light?