Writing key values continuously in openHistorian using Python openhistorian api

I am using the Python openhistorian api to write values into certain tags continuously in real time on the basis of their key PointID. However I found that it is editing that tag only once and on further execution the value is not getting updated. Is there a way around to stream calculated data into those tags in real time using the openhistorian api?

Hello Sir,

I wanted to install the historian python API using “pip install openHistorian”. But, I got the following error.

Did you face the same error during installing the package? Can you please describe how you overcame this error. I will be thankful for the help.

Thanks and Regards,
Adnan Iqbal

Hi Adnan,
Thanks for your query. The installation command is

pip install openhistorian

Everything is in lower case.
Thanks,
Datta

Dear Sir,

I tried the lower case as well “pip install openhistorian”. I still get the same error. Thankyou for the information.

P.S - The documentation said its for python 3.9. I will try to update my python and try again.

Thanks and regards,
Adnan Iqbal

Not sure I understand your question, here is an example that will write multiple values for the same point ID:

import sys
import os
sys.path.append(os.path.dirname(os.path.realpath(__file__)) + "/../src")

from openHistorian.historianConnection import historianConnection
from openHistorian.historianInstance import historianInstance
from openHistorian.historianKey import historianKey
from openHistorian.historianValue import historianValue
from snapDB.enumerations import QualityFlags
from gsf import Ticks
from typing import Optional
from datetime import datetime
from random import random
import numpy as np

def writeTest():
    # Create historian connection (the root API object)
    historian = historianConnection("localhost")
    instance: Optional[historianInstance] = None

    try:
        print("Connecting to openHistorian...")
        historian.Connect()    

        if not historian.IsConnected or len(historian.InstanceNames) == 0:
            print("No openHistorian instances detected!")
        else:
            # Get first historian instance
            initialInstance = historian.InstanceNames[0]
        
            print(f"Opening \"{initialInstance}\" database instance...")
            instance = historian.OpenInstance(initialInstance)
                
            key = historianKey()
            value = historianValue()

            for i in range(10):
                key.PointID = 1
                key.Timestamp = Ticks.FromDateTime(datetime.utcnow())

                value.AsSingle = np.float32(1000.98 * random())
                value.AsQuality = QualityFlags.WARNINGHIGH

                print(f"Writing test point {i}...")
                instance.Write(key, value)
    except Exception as ex:
        print(f"Failed to connect: {ex}")
    finally:
        if instance is not None:
            instance.Dispose()

        if historian.IsConnected:
            print("Disconnecting from openHistorian")
        
        historian.Disconnect()

if __name__ == "__main__":
    writeTest()

Hi Ritchie,
Thanks for your reply. Sorry if I have not been clear with our objective. Actually we want to populate multiple PointIDs at the same time where the PointIDs are stored in an array and we want to assign them values by iterating over a loop.
Thanks,
Dattatreya

That’s true. It is for Python 3.9. Or you might have to upgrade your pip.

Change example above to set key.PointID = i, such as:

            for i in range(10):
                key.PointID = i
                key.Timestamp = Ticks.FromDateTime(datetime.utcnow())

                value.AsSingle = np.float32(1000.98 * random())
                value.AsQuality = QualityFlags.WARNINGHIGH

                print(f"Writing test point {i}...")
                instance.Write(key, value)

Does that help?

If not, can share a code snippet? Maybe then I can better help.

Thanks,
Ritchie

Hi Ritchie,
Thanks for your response. I have also tried out the above approach to assign values to different key point IDs. The assignment is getting executed fine. But when I am testing once again to see the values of those key point IDs at the same instance the values are not getting updated.Is it something got to do with the timestamp? I am providing below the code snippet which I am trying to execute. One thing I would also like to point out is the values that I am trying to assign are calculated values:

def readTest():

    # Create historian connection (the root API object)

    historian = historianConnection("localhost")

    instance: Optional[historianInstance] = None

    key = historianKey()

    value = historianValue()
    comb_ptids = list(itertools.chain.from_iterable(volt_alogs))
    comb_alvals = list(itertools.chain.from_iterable(dev_vseq_comps))
    for i in range(0,len(comb_ptids)):
                  

        key.PointID = comb_ptids[i]

        key.Timestamp = Ticks.FromDateTime(datetime.utcnow())

                        

        value.AsSingle = np.float32(comb_alvals[i])

        value.AsQuality = QualityFlags.NORMAL

        instance.Write(key, value)

Sorry I have forgot add the two lines for the instance creation before assigning values to the PointID in the above code snippet but I had implemented them in the code.

initialInstance = historian.InstanceNames[0]
instance = historian.OpenInstance(initialInstance)

However there is data getting stored in spite of this instance initiation.

I don’t see anything wrong with this code, are you saying that you can’t read the values back out?

Yes when I am reading them they are coming as 0s which are their initial values.

So a few things:

(1) You are using datetime.utcnow() for each point to be saved in the group, each value in the group will thus have a slightly different timestamp, each one greater than the other.
(2) It may take a moment for values to be saved into the archive and be ready for read - this is usually sub-second, but I would at least wait half a second, e.g., sleep(0.5), before trying to read the values back out.
(3) Because the values will have a “range” of timestamps to cover when you are reading, I would suggest adding some “slack” to the time range, openHistorian is looking for an exact range for time during reads. If your write code is not very specific on timestamps, perhaps add a millisecond per value on either end of the read times, e.g.:

startTime = startTime - timedelta(milliseconds = len(comb_ptids))
endTime = endTime + timedelta(milliseconds = len(comb_ptids))

(4) Be sure to keep track of the timestamps used when saving the data, this will be important during the read.
(5) As a suggestion for simplification, always use the same timestamp for a given set of points that were measured at about the same time - this way any read slack can be just one millisecond:

startTime = startTime - timedelta(milliseconds = 1)
endTime = endTime + timedelta(milliseconds = 1)

Hope that helps!

Thanks,
Ritchie

Did you ever get your read to work?

One colleague was having trouble with reads from the package install, but the raw source code is working fine.

This could be a gap in my Python knowledge as this is my first PIP package.

FYI - package seems to be working fine.

Ritchie

BTW - I have found one issue when using PostgeSQL, since all of its field names are lowercase, the Python code parsing the metadata fails to properly load since its field name lookups are case sensitive.

I’m having to noodle on this for a solution that doesn’t make all other metadata parsing implementations slower.

Hi Ritchie,
Hope you had a good Easter break. Regarding reading the data from openHistorian I have been able to implement it. If you want I can share the code with you.
Thanks,
Dattatreya

Hi Ritchie,
Regarding parsing the metadata, the only signal I was having issues to read was FLAG. Don’t know exactly why. But I have been able to access the data via the key point IDs.
Thanks,
Dattatreya