Delta Tag Calculation in script

Hello everyone.

This is my first experience with ignition and I need some help.
I must calculate the difference between two values of a tag at a distance of a predetermined time.
I created a script that runs once a minute but when I read the value of the tag I don’t get a value but a structure of type “java.util.ArrayList” type list.
The problem is that I can’t just aim for the value in order to make the difference.
I am attaching the script, sorry if it’s a little dirty but I’m doing tests.
I need to do this in a script but if there are other ways please tell me

Thank you

import traceback  # lib -> get error traceback stack


# console output
logger = system.util.logger("====> DeltaTag Logger <====")
d_logger = system.util.logger("====> DelTag Disconnected <====")

# tag paths list
tagList = [
    "[default]ConsumiTag/Print1_AriaCompressa",     # 1
    "[default]ConsumiTag/Print1_Catox_EE",          # 2
    "[default]ConsumiTag/Print1_Catox_Gas",         # 3
    "[default]ConsumiTag/Print1_Compr1_EE",         # 4
    "[default]ConsumiTag/Print1_Compr2_EE",         # 5
    "[default]ConsumiTag/Print1_Compr3_EE",         # 6
    "[default]ConsumiTag/Print1_Compr4_EE",         # 7
    "[default]ConsumiTag/Print1_CT1_Gas",           # 8
    "[default]ConsumiTag/Print1_CT1Caldaia1_EE",    # 9
    "[default]ConsumiTag/Print1_CT1Caldaia2_EE",    # 10
    "[default]ConsumiTag/Print1_CT2_Gas",           # 11
    "[default]ConsumiTag/Print1_Finitura1_EE",      # 12
    "[default]ConsumiTag/Print1_Finitura5_EE",      # 13
    "[default]ConsumiTag/Print1_Pressa3_EE",        # 14
    "[default]ConsumiTag/Print1_Pressa5_EE",        # 15
    "[default]ConsumiTag/Print1_Pressa6-7_EE",      # 16
    "[default]ConsumiTag/Print1_Pressa8_EE",        # 17
    "[default]ConsumiTag/Print1_Torre1_EE",         # 18
    "[default]ConsumiTag/Print1_Torri2-3_EE",       # 19
    "[default]ConsumiTag/Print1_Vit5_Gas",          # 20
    "[default]ConsumiTag/Print1_Vits3_EE",          # 21
    "[default]ConsumiTag/Print1_Vits3_Gas",         # 22
    "[default]ConsumiTag/Print1_Vits5_EE",          # 23
    "[default]ConsumiTag/Print1_Vits11_EE",         # 24
    "[default]ConsumiTag/Print1_Vits11_Gas",        # 25
    "[default]ConsumiTag/Print2_AriaCompressa",     # 26
    "[default]ConsumiTag/Print2_Compr1_EE",         # 27
    "[default]ConsumiTag/Print2_Compr2_EE",         # 28
    "[default]ConsumiTag/Print2_Compr3_EE",         # 29
    "[default]ConsumiTag/Print2_Compr4_EE",         # 30
    "[default]ConsumiTag/Print2_Finitura21_EE",     # 31
    "[default]ConsumiTag/Print2_Fintura22_EE",      # 32
    "[default]ConsumiTag/Print2_Hymmen_EE",         # 33
    "[default]ConsumiTag/Print2_Miltex_EE",         # 34
    "[default]ConsumiTag/Print2_Pressa21_EE",       # 35
    "[default]ConsumiTag/Print2_Pressa21_ET",       # 36
    "[default]ConsumiTag/Print2_Pressa22_EE",       # 37
    "[default]ConsumiTag/Print2_RTO_EE",            # 38
    "[default]ConsumiTag/Print2_RTO_Gas",           # 39
    "[default]ConsumiTag/Print2_T22_EE",            # 40
    "[default]ConsumiTag/Print2_T23_EE",            # 41
    "[default]ConsumiTag/Print2_T23_Gas",           # 42
    "[default]ConsumiTag/Print2_T23Chiller_EE",     # 43
    "[default]ConsumiTag/Print2_T24_EE",            # 44
    "[default]ConsumiTag/Print2_T24_Gas",           # 45
    "[default]ConsumiTag/Print2_Torri_EE",          # 46
    "[default]ConsumiTag/Print2_V21Chiller_EE",     # 47
    "[default]ConsumiTag/Print2_Vits21_EE"          # 48
]

# Save tag paths list
SavetagList = [
    "[default]DeltaConsumiTag/Print1_Save_AriaCompressa",     # 1
    "[default]DeltaConsumiTag/Print1_Save_Catox_EE",          # 2
    "[default]DeltaConsumiTag/Print1_Save_Catox_Gas",         # 3
    "[default]DeltaConsumiTag/Print1_Save_Compr1_EE",         # 4
    "[default]DeltaConsumiTag/Print1_Save_Compr2_EE",         # 5
    "[default]DeltaConsumiTag/Print1_Save_Compr3_EE",         # 6
    "[default]DeltaConsumiTag/Print1_Save_Compr4_EE",         # 7
    "[default]DeltaConsumiTag/Print1_Save_CT1_Gas",           # 8
    "[default]DeltaConsumiTag/Print1_Save_CT1Caldaia1_EE",    # 9
    "[default]DeltaConsumiTag/Print1_Save_CT1Caldaia2_EE",    # 10
    "[default]DeltaConsumiTag/Print1_Save_CT2_Gas",           # 11
    "[default]DeltaConsumiTag/Print1_Save_Finitura1_EE",      # 12
    "[default]DeltaConsumiTag/Print1_Save_Finitura5_EE",      # 13
    "[default]DeltaConsumiTag/Print1_Save_Pressa3_EE",        # 14
    "[default]DeltaConsumiTag/Print1_Save_Pressa5_EE",        # 15
    "[default]DeltaConsumiTag/Print1_Save_Pressa6-7_EE",      # 16
    "[default]DeltaConsumiTag/Print1_Save_Pressa8_EE",        # 17
    "[default]DeltaConsumiTag/Print1_Save_Torre1_EE",         # 18
    "[default]DeltaConsumiTag/Print1_Save_Torri2-3_EE",       # 19
    "[default]DeltaConsumiTag/Print1_Save_Vit5_Gas",          # 20
    "[default]DeltaConsumiTag/Print1_Save_Vits3_EE",          # 21
    "[default]DeltaConsumiTag/Print1_Save_Vits3_Gas",         # 22
    "[default]DeltaConsumiTag/Print1_Save_Vits5_EE",          # 23
    "[default]DeltaConsumiTag/Print1_Save_Vits11_EE",         # 24
    "[default]DeltaConsumiTag/Print1_Save_Vits11_Gas",        # 25
    "[default]DeltaConsumiTag/Print2_Save_AriaCompressa",     # 26
    "[default]DeltaConsumiTag/Print2_Save_Compr1_EE",         # 27
    "[default]DeltaConsumiTag/Print2_Save_Compr2_EE",         # 28
    "[default]DeltaConsumiTag/Print2_Save_Compr3_EE",         # 29
    "[default]DeltaConsumiTag/Print2_Save_Compr4_EE",         # 30
    "[default]DeltaConsumiTag/Print2_Save_Finitura21_EE",     # 31
    "[default]DeltaConsumiTag/Print2_Save_Finitura22_EE",      # 32
    "[default]DeltaConsumiTag/Print2_Save_Hymmen_EE",         # 33
    "[default]DeltaConsumiTag/Print2_Save_Miltex_EE",         # 34
    "[default]DeltaConsumiTag/Print2_Save_Pressa21_EE",       # 35
    "[default]DeltaConsumiTag/Print2_Save_Pressa21_ET",       # 36
    "[default]DeltaConsumiTag/Print2_Save_Pressa22_EE",       # 37
    "[default]DeltaConsumiTag/Print2_Save_RTO_EE",            # 38
    "[default]DeltaConsumiTag/Print2_Save_RTO_Gas",           # 39
    "[default]DeltaConsumiTag/Print2_Save_T22_EE",            # 40
    "[default]DeltaConsumiTag/Print2_Save_T23_EE",            # 41
    "[default]DeltaConsumiTag/Print2_Save_T23_Gas",           # 42
    "[default]DeltaConsumiTag/Print2_Save_T23Chiller_EE",     # 43
    "[default]DeltaConsumiTag/Print2_Save_T24_EE",            # 44
    "[default]DeltaConsumiTag/Print2_Save_T24_Gas",           # 45
    "[default]DeltaConsumiTag/Print2_Save_Torri_EE",          # 46
    "[default]DeltaConsumiTag/Print2_Save_V21Chiller_EE",     # 47
    "[default]DeltaConsumiTag/Print2_Save_Vits21_EE"          # 48
]

# Delta tag paths list
DeltatagList = [
    "[default]DeltaConsumiTag/Print1_Delta_AriaCompressa",     # 1
    "[default]DeltaConsumiTag/Print1_Delta_Catox_EE",          # 2
    "[default]DeltaConsumiTag/Print1_Delta_Catox_Gas",         # 3
    "[default]DeltaConsumiTag/Print1_Delta_Compr1_EE",         # 4
    "[default]DeltaConsumiTag/Print1_Delta_Compr2_EE",         # 5
    "[default]DeltaConsumiTag/Print1_Delta_Compr3_EE",         # 6
    "[default]DeltaConsumiTag/Print1_Delta_Compr4_EE",         # 7
    "[default]DeltaConsumiTag/Print1_Delta_CT1_Gas",           # 8
    "[default]DeltaConsumiTag/Print1_Delta_CT1Caldaia1_EE",    # 9
    "[default]DeltaConsumiTag/Print1_Delta_CT1Caldaia2_EE",    # 10
    "[default]DeltaConsumiTag/Print1_Delta_CT2_Gas",           # 11
    "[default]DeltaConsumiTag/Print1_Delta_Finitura1_EE",      # 12
    "[default]DeltaConsumiTag/Print1_Delta_Finitura5_EE",      # 13
    "[default]DeltaConsumiTag/Print1_Delta_Pressa3_EE",        # 14
    "[default]DeltaConsumiTag/Print1_Delta_Pressa5_EE",        # 15
    "[default]DeltaConsumiTag/Print1_Delta_Pressa6-7_EE",      # 16
    "[default]DeltaConsumiTag/Print1_Delta_Pressa8_EE",        # 17
    "[default]DeltaConsumiTag/Print1_Delta_Torre1_EE",         # 18
    "[default]DeltaConsumiTag/Print1_Delta_Torri2-3_EE",       # 19
    "[default]DeltaConsumiTag/Print1_Delta_Vit5_Gas",          # 20
    "[default]DeltaConsumiTag/Print1_Delta_Vits3_EE",          # 21
    "[default]DeltaConsumiTag/Print1_Delta_Vits3_Gas",         # 22
    "[default]DeltaConsumiTag/Print1_Delta_Vits5_EE",          # 23
    "[default]DeltaConsumiTag/Print1_Delta_Vits11_EE",         # 24
    "[default]DeltaConsumiTag/Print1_Delta_Vits11_Gas",        # 25
    "[default]DeltaConsumiTag/Print2_Delta_AriaCompressa",     # 26
    "[default]DeltaConsumiTag/Print2_Delta_Compr1_EE",         # 27
    "[default]DeltaConsumiTag/Print2_Delta_Compr2_EE",         # 28
    "[default]DeltaConsumiTag/Print2_Delta_Compr3_EE",         # 29
    "[default]DeltaConsumiTag/Print2_Delta_Compr4_EE",         # 30
    "[default]DeltaConsumiTag/Print2_Delta_Finitura21_EE",     # 31
    "[default]DeltaConsumiTag/Print2_Delta_Finitura22_EE",      # 32
    "[default]DeltaConsumiTag/Print2_Delta_Hymmen_EE",         # 33
    "[default]DeltaConsumiTag/Print2_Delta_Miltex_EE",         # 34
    "[default]DeltaConsumiTag/Print2_Delta_Pressa21_EE",       # 35
    "[default]DeltaConsumiTag/Print2_Delta_Pressa21_ET",       # 36
    "[default]DeltaConsumiTag/Print2_Delta_Pressa22_EE",       # 37
    "[default]DeltaConsumiTag/Print2_Delta_RTO_EE",            # 38
    "[default]DeltaConsumiTag/Print2_Delta_RTO_Gas",           # 39
    "[default]DeltaConsumiTag/Print2_Delta_T22_EE",            # 40
    "[default]DeltaConsumiTag/Print2_Delta_T23_EE",            # 41
    "[default]DeltaConsumiTag/Print2_Delta_T23_Gas",           # 42
    "[default]DeltaConsumiTag/Print2_Delta_T23Chiller_EE",     # 43
    "[default]DeltaConsumiTag/Print2_Delta_T24_EE",            # 44
    "[default]DeltaConsumiTag/Print2_Delta_T24_Gas",           # 45
    "[default]DeltaConsumiTag/Print2_Delta_Torri_EE",          # 46
    "[default]DeltaConsumiTag/Print2_Delta_V21Chiller_EE",     # 47
    "[default]DeltaConsumiTag/Print2_Delta_Vits21_EE"          # 48
]



def processTagWrites(system, readingResult,Savetag,DeltaTag):
    """Reads and publishes data after creating payload; func tree:

            | processTagWrites()
                    readBlocking
                    > publish()
                            >makePayload()
                                    >extractID()
    """

    index = 0
    for tag in readingResult:
        tagID = system.tag.readBlocking(tagList[index] + ".Documentation")[0]
        tagQ = system.tag.readBlocking(tagList[index] + ".Quality")
        tagValue = system.tag.readBlocking(tagList[index] + ".Value")
        tagSave = system.tag.readBlocking(SavetagList[index] + ".Value")
        #tagDelta = int(tagSave[0])-int(tagValue[0])
        
        if str(tagQ) != None and ("Bad" in str(tagQ) or "Uncertain" in str(tagQ) or "Error" in str(tagQ)):
           d_logger.warn("" + dataScript.extractAttr(str(tagID)) + " - Quality: " + dataScript.extractAttr(str(tagQ)))
           system.tag.writeBlocking(DeltaTag[index], 0)
        else:
            # get tag external attributes (-> Documentation (Name) / EngineeringUnit)
            # Calcolo differenza            
            logger.info(str(tagSave) + "-" + str(type(tagSave)))
            # in tagSave invece del valore arriva 1374806, Good, Wed Jul 27 15:13:19 CEST 2022 (1658927599277)
            # quindi non posso fare la differenza e pur sembrando una lista non riesco a legerne il valore
            # il tipo di dato è un type 'java.util.ArrayLis
            #system.tag.writeBlocking(DeltatagList[index] + ".Value", (tagSave-tagValue))
            
            # Aggiornamento valori salvati per successivo calcolo
            system.tag.writeBlocking(SavetagList[index] + ".Value", (tagValue))            
            #system.tag.writeBlocking(SavetagList[index] + ".Value", 100)
        index += 1
		
def exe():
    """Script evoker function"""
    
    
    # Se i minuti dell ora corrente diviso quindici danno resto 0
    # check wether the time is XX:00/XX:15/XX:30/XX:45 #* debug:: 1 minute dilay
    
    if system.date.getMinute(system.date.now()) % 1 == 0:
        logger.info("====> entered script: DeltaConsumiScript <====")
        try:
            qc_tags = system.tag.readBlocking(tagList)
            Sv_tags = system.tag.readBlocking(SavetagList)
            Dt_tags = system.tag.readBlocking(DeltatagList)
            # ? consider wether using asynchronous or synchronous reading ?
            processTagWrites(system, qc_tags, Sv_tags, Dt_tags)
        except:
            # ! log error traceback stack
            d_logger.error("[Bad] " + traceback.format_exc())
        else:
            logger.info("[Good]")

To simply solve your problem, try this:

tagID = system.tag.readBlocking(tagList[index] + ".Documentation")[0].value
tagQ = system.tag.readBlocking(tagList[index] + ".Quality")[0].value
tagValue = system.tag.readBlocking(tagList[index])[0].value
tagSave = system.tag.readBlocking(SavetagList[index])[0].value

system.tag.readBlocking always returns a list, so if you want to do anything useful with it you have to pick a member of the list (which is where [0] comes from). Each member is a QualifiedValue object. you get the value of that object (as opposed to the timestamp or quality) by selecting the value property of the object (.value in the code above).

Some other thoughts that aren’t strictly related to your question. Feel free to take or disregard these as you see fit:

  1. Every call to readBlocking is a gateway round trip which is expensive in terms of performance and time. I would make the list of every tag value you need and read them all at once. The same logic applies to writeBlocking.
  2. It seems odd to me that you’re passing the system namespace as an argument. system.* should be available at all levels of your project script, you don’t need to pass it as an argument. If you’re actually passing something else I would rename it so you don’t overwrite the system.* namespace.
2 Likes

I don’t understand exactly what you’re trying to do here, particuarly since there is a call to dataScript which you haven’t provided code for.

What I can tell you, is that you are doing a lot of tag reading (195 if my math is correct). I’m guessing this script takes a long time.

A few things:

  1. As @zacht pointed out you should read all tags (that includes the read to get the .documentation) at once. This will return a list of Qualified Values.
  2. A Qualified Value contains the value, quality and timestamp. So there is no need for an additional read where you add the .value and .Quality to the tag path those are already included.
  3. I would compile the values that you want to write into a list and then write them all at once at the end , as opposed to potentially making 48 individual calls to write, which also blocks the thread slowing down the execution.
  4. Since you’re never using the current value of the delta tags, don’t waste execution reading them.

Since lists are ordered the system.tag.readBlocking() function is guaranteed to return the QualifiedValues in the same order as the list of tag paths provided in the call. You can use this to your advantage here. We can combine the tagList and SavetagList into a single read call. Then we can split them out again so that they become useable to us in the loop. Something like this

qValues = system.tag.readBlocking(tagList + SavetagList)
deltaValues = []
saveValues =  []
for currentValue,currentQuality,saveValue in [qv1.value,qv1.quality,qv2 for qv1,qv2 in zip(qValues[:len(SavetagList)],qValues[len(SavetagList):]):
    #do something with those values
    if not currentQuality or any(substring in currentQuality for substring in ('Bad','Uncertain','Error')):
        deltaValues.append(0)
    else:
        saveValues.append(saveValue - currentValue)

system.tag.writeBlocking(DeltatagList + SavetagList, deltaValues + saveValues)
2 Likes