Which is Better Gateway Schedule or Gateway Timer script

Hi team,

I just want suggestion on Gateway Events script. The requirement is I have 2 scripts which are running when day starts and other 2 runs throughout the day so which gateway script we need to use for server optimum performance.

The ones that need to run at specific times should use the scheduled event. Because you can specify the time of day for it to run.

The choice for the others depends on how fast you need them to run, and if they should be aligned to the clock. The timer event just runs at a pace. Either with a fixed interval from start-to-start, or a fixed pause from finish-to-start. These can be set to run very often--fractions of a second apart. The scheduled event cannot run so often, but can be aligned to run close to even intervals on the clock, like right after the top of each hour. Or every quarter hour on the quarter hour (minute==0,15,30,45).

1 Like

Thanks for the detail difference.

Why i asked is we are facing major issue with timer script which has delay of 2500 sec but it is still taking 400sec to execute script which in turn make the application slow. so do you have anything for this. Please find image as script is slow.

That is entirely the fault of the code you are trying to run. There's no magic wand to make the code run faster. But if you share your code, we might be able to help you clean it up (optimize it).

Sometimes code is simple but takes a long time for other reasons, like a slow database, or a huge amount of data on which to perform calculations. We might be able to help optimize your database, but we'd need more info.

{ It is hard to tell in your graphic, but are those commas in your rate column? }

it is comma, please suggest me for better time. I have attached script for which we are facing issue.
UpdateOEEtag script which will call function in historiandata function. Please check and let me know
MDT_OEE_App_V2_2023-06-14_histDat.zip (20.6 KB)
UpdateOEETag_Script_2023-06-13.zip (5.6 KB)

Whoa! Those are a bit large to ask this volunteer to review.

Sorry @pturmel what can i do for you to make it shorter

Wait, what exactly are you running in your event scripts ? Not the whole thing I hope ?

I only looked at UpdateOEETag_Script here is what I see.

Don't do this:


You can unpack pyDataSets so your for loop can be written as:

for machineName,zoneName,lineId,facilityName,attachedTo in allShifts:

If you do that, then there is no need for the if statement prior to the loop, if allShifts is empty, it just wont run. So remove:

if(len(allShifts) > 0):

What is the point of the try: except: block in the for loop if you're not going to log an issue? Also, I really don't think that you need it here, but since you're calling external functions perhaps you do?


if (len(result) > 0):

Should be written as

if result:

You've already set count to 0 so there is not need for the else in this if statement, but meh...

I don't know what you're doing in the HistorianData functions but you are calling getOeewithZone() and getUnattendedDT() multiple times, perhaps seeing the code for those functions we could provide some more insight.

I would certainly combine all of your tag writes into a single function call, and remove the unneeded string conversions.

Something like:

tags = ['OEE','Availablity','Downtime','GoodCount','Performance','Quality','Runtime','Scrap','UnattendedDT','IsHoliday','Department']
tagPaths = ['{0}{1}/{2}/{3}'.format(machineOEEPath,facilityName,machineName,tag) for tag in tags]
tagValues = [oee,availability,totalDownTime,goodCount,performance,quality,runTime,scrapCount,count,Holiday,attachedTo]

You're collecting error info to log, but then not actually logging it in you're final except block.

There is also potentially a typo in one of your tag paths. You have 'UnattendentDT' but I think you might mean 'UnattendedDT'?

Thanks @lrose I have worked with script with changes you mentioned. I have getoeewithzone can you help me with that.

def getOeewithZone(stTime,enTime,Facility,Line,lineValue,IsMonthToDate,zoneName,isExist = 0,decimalValue = 0,shift = 1):
        totalCount = 0
        goodCount = 0
        isFullTimeDown = False
        totalDowntimeMin = 0
        shiftEndTime = enTime
        zoneEndTime = enTime
        if(isExist == 1):
            enTime =  str(system.date.format(system.date.now(),"yyyy-MM-dd HH:mm:00"))
        isQuality = system.tag.read(str(configurationTagPath) + str(Facility) + '/' + str(Line) + '/Is_Quality_Visible').value
        isPerformance = system.tag.read(str(configurationTagPath) + str(Facility) + '/' + str(Line) + '/Is_Performance_Visible').value
        tag = ''
        scrapTagPath = str(configurationTagPath)+str(Facility)+'/'+str(Line)+'/Is_Manual_Scrap'
        isManualScap = system.tag.read(scrapTagPath).value
        startTimeUTC = getUTCfromTimeZone(zoneName, system.date.parse(stTime), -1)
        endTimeUTC = enTime
        if(isExist == 0):
            endTimeUTC = getUTCfromTimeZone(zoneName, system.date.parse(enTime), -1)

        runTime,downTime,downTimeRangeTable,targetUnit,plannedDownTime = getOeeCalculationValues(startTimeUTC , endTimeUTC, Facility ,Line,lineValue,decimalValue)
        totalCount = getOEEProdCount(startTimeUTC, endTimeUTC, Facility ,Line)

        #endTimeUTC1 = getUTCfromTimeZone(zoneName, system.date.parse(enTime), +1)
        hourStartTime = str(system.date.format(system.date.parse(stTime),"yyyy-MM-dd HH:mm:00"))
        hourEndTime = str(system.date.format(system.date.parse(enTime),"yyyy-MM-dd HH:mm:00"))


            paramsScrap = {"LineId":lineValue,"HourStart":hourStartTime,"HourEnd":shiftEndTime,"IsMonthToDate":shift} #changed to include currentHour scrap quantity
            scrapCount = system.db.runNamedQuery("Project_OEE/Scrap/Get_OEE_Total_Scrap_Count",paramsScrap)
            scrapCount = getPlcScrapCount(startTimeUTC,endTimeUTC,Facility ,Line)
            totalCount = totalCount + scrapCount

        #This function is used to fetch total break duration of configured breaks in minutes
        Bktime = getShiftBreaktime(shift ,lineValue, zoneName)

        totalTime = system.date.minutesBetween(system.date.parse(startTimeUTC),system.date.parse(endTimeUTC))


        stTime = system.date.parse(stTime)
        zoneEndTime = system.date.parse(zoneEndTime)
#        system.perspective.print('!!@@@@stTime...'+str(stTime))    
#        system.perspective.print('!!@@@@zoneEndTime...'+str(zoneEndTime))    
        # to get downtime duration data from Holiday Table
        isFullTimeDown, totalDowntimeMin = getDowntimeConfigurationTime(stTime, zoneEndTime, lineValue)

        if (isFullTimeDown):
            totalTime = 0
            totalTime = totalTime - plannedDownTime - Bktime - totalDowntimeMin


        if(int(runTime) == 0):
            Efficiency = 0
            if(int(totalTime) > 0):
                Efficiency = float(runTime) / float(totalTime)
                Efficiency = 0    
        #system.perspective.print("@@runTime : "+ str(runTime))
        #system.perspective.print("@@Efficiency : "+ str(Efficiency))
        if(str(isPerformance) == 'None'):
            isPerformance = 0
        if(int(isPerformance) == 0):
            Performance = 1
            if(int(runTime) == 0):
                Performance = 0    
                if(targetUnit != 0):
                    #Performance = float(goodCount) / float(targetUnit) #Change 18thjuly
                    Performance = float(totalCount) / float(targetUnit)
                    Performance = 0    
        #system.perspective.print("@@Performance : "+ str(Performance))
        if(str(isQuality) == 'None'):
            isQuality = 0
        if(int(isQuality) == 0):        
            Quality = 1
            if((int(totalCount) == 0) and (int(scrapCount) == 0)):    
                    Quality = 0        
    #            Quality = float(goodCount)/float((goodCount + scrapCount))
                goodCount= totalCount-scrapCount    
                Quality = float(goodCount)/float(totalCount) #Change for MI Site

        oee = round((Efficiency*Performance*Quality*100) ,int(decimalValue))
        avilability = round((Efficiency*100) , int(decimalValue))
        performance = round((Performance*100) , int(decimalValue))
        quality =     round((Quality*100) , int(decimalValue))
        totalRunTime = round((float(runTime) / float(60)) , int(decimalValue))    
    #    system.perspective.print('@@@avilability'+str(avilability))
        totalDownTime = round((float(downTime) / float(60)),int(decimalValue))
        goodCount = goodCount
        targetUnit = round(targetUnit,int(decimalValue))

        return oee,avilability,performance,quality,totalRunTime,totalDownTime,goodCount,targetUnit,runTime,downTime,downTimeRangeTable,scrapCount,totalTime,isFullTimeDown
        exc_type, exc_obj,tb = sys.exc_info()
        lineno = tb.tb_lineno
        errorMessage = "Error , Line:" + str(lineno) + ", Err:"+ str(exc_obj)    
        ErrorLogs.addLogDetails('script','HistorianData','getOeewithZone',2,lineno, str(exc_obj))
def getUnattendedDT(facility,lineValue,line,currentTime,startTime,endTime,shift,startHour,endHour,day,zoneName,batchId):
        parentStartHour = startHour  #Considering this is start hour of shift selected in HH format
        parentEndHour = endHour  #Considering this is end hour of shift selected in HH format
        parentStartTime = system.date.format(startTime , "yyyy-MM-dd")
        parentEndTime = system.date.format(endTime , "yyyy-MM-dd")

        #now find the number of hours of shift by sustracting start time from end time.
        totalHours = system.date.hoursBetween(startTime,endTime)
        diffMinutes = int(system.date.format(startTime,'mm')) - int(system.date.format(endTime,'mm'))
        if(diffMinutes != 0):
            totalHours = totalHours + 1
        data = []
        partNumber = ''
        isCurrentHour = 0
        lastPartNumber = '-1'
        startTimeUTC = ShiftTimeZone.getUTCfromTimeZone(zoneName, startTime, -1)
        endTimeUTC = ShiftTimeZone.getUTCfromTimeZone(zoneName, endTime, -1)

        partNumberDetails = getPartNumberwithZone(startTimeUTC , endTimeUTC, facility ,line, zoneName)

        hourCount = 1
        runTimetable,downTimetable,plannedDowntimeTable,finaldownTimeLapsed = getTotalEquipCalculation(system.date.format(startTimeUTC, 'yyyy-MM-dd HH:mm:ss'),system.date.format(currentTime, 'yyyy-MM-dd HH:mm:ss'),facility,line,int(1))
        downTimetable = system.dataset.toPyDataSet(downTimetable)
        plannedDowntimeTable = system.dataset.toPyDataSet(plannedDowntimeTable)
#        system.perspective.print("LineNo 2334")
#        params = {'LineId':lineValue, 'Day':day, 'HourStart':startTime, 'HourEnd':endTime}
#        plannedDowntimeTable = system.db.runNamedQuery('Project_OEE/Hourly/DownTime/Get_PlannedDownTimeDetails_ByRange', params)
#        plannedDowntimeTable = plannedDownTimetable  plannedDowntimeTable
#        system.perspective.print("LineNo 2339")
        params = {"LineId":int(lineValue)}
        downtimesDt = system.db.runNamedQuery("Project_OEE/Hourly/DownTime/Get_AllDownTimeCode")
        dtDescdataset = system.dataset.toPyDataSet(downtimesDt)
#        system.perspective.print("LineNo 2343")
        dtTagPath = str(configurationTagPath)+str(facility)+'/'+str(facility)+'/DT_sec'    
#        system.perspective.print("LineNo 2353 "+str(dtTagPath))
        DT_Sec = system.tag.read(dtTagPath).value

        for rowIndex in range(totalHours):
            sTime = system.date.addHours(startTime,rowIndex)
            sTime = system.date.setTime(sTime,system.date.getHour24(sTime),system.date.getMinute(sTime),00)
            sTime = system.date.format(sTime , "yyyy-MM-dd HH:mm:ss")
            enTime = system.date.addHours(startTime,rowIndex+1)
            enTime = system.date.setTime(enTime,system.date.getHour24(enTime),system.date.getMinute(enTime),00)
            enTime= system.date.format(enTime , "yyyy-MM-dd HH:mm:ss")
            sTimeHour = system.date.format(system.date.parse(sTime),"HH")    
            enTimeHour = system.date.format(system.date.parse(enTime),"HH")


            if (rowIndex == (totalHours - 1)):
                if (system.date.parse(enTime) > endTime):
                    enTime= system.date.format(endTime , "yyyy-MM-dd HH:mm:ss")


            if(isCurrentHour == 0):

                #isCurrentShift = system.date.isBetween(system.date.now(), system.date.parse(startTime), system.date.parse(endTime))
                isCurrentShift = system.date.isBetween(currentTime, system.date.parse(startTimeUTC), system.date.parse(endTimeUTC))
                isCurrentShiftHour = fullTimeComparewithZone(system.date.format(system.date.parse(sTime),'HH:mm'), system.date.format(system.date.parse(enTime), 'HH:mm'),zoneName)
                if(int(isCurrentShiftHour) == 1 and int(isCurrentShift) == 1):
                    isCurrentHour = 1
                partNumberData = getHourPartNumber(sTime,enTime,partNumberDetails)

                for rowPartNumber in range(len(partNumberData)):
                    partStartTime  = partNumberData[rowPartNumber][0]
                    partEndTime = partNumberData[rowPartNumber][1]
                    partNumber = partNumberData[rowPartNumber][2]

                #--------------Added to get correct hour for hours with partnumber changed (splitted hours)
                    if lastPartNumber != '-1':
                        if lastPartNumber != partNumber:
                            hourCount = hourCount + 1
                    lastPartNumber = partNumber
                #-------------- change end (by Abhijit)

                    partStartTimeUTC = ShiftTimeZone.getUTCfromTimeZone(zoneName, system.date.parse(partStartTime), -1)
                    partEndTimeUTC = ShiftTimeZone.getUTCfromTimeZone(zoneName, system.date.parse(partEndTime), -1)
#                    system.perspective.print("LineNo 2373")
                    runTime, downTime, downTimeRangeTable, plannedTotalTime,plannedDownTimeRangeTable = getHourDowntime(partStartTimeUTC,partEndTimeUTC,downTimetable,plannedDowntimeTable,currentTime,isCurrentHour,int(1))
#                    system.perspective.print("LineNo 2374")
                    for downDT in range(len(downTimeRangeTable)):
                        startT = downTimeRangeTable[downDT][0]
                        endT = downTimeRangeTable[downDT][1]
                        descDT = downTimeRangeTable[downDT][2]
                        levelDT = 'Level1'
                        if(levelDT == 'Level1'):
                            diffSec = system.date.secondsBetween(system.date.parse(startT), system.date.parse(endT))
                            if(diffSec >= int(DT_Sec)):
#                            if(diffSec >= 120):
                                if(zoneName != '' and startT != ''):
                                    startT = ShiftTimeZone.getUTCfromTimeZone(zoneName, system.date.parse(startT), 1)
                                    startT = system.date.parse(startT)
                                if(zoneName != '' and endT != ''):
                                    endT = ShiftTimeZone.getUTCfromTimeZone(zoneName, system.date.parse(endT), 1)
                                    endT = system.date.parse(endT)
                                #add logic here to check if the Downtime is attended
                                params = {"BatchId": batchId,"DownTimeStart":startT,"DownTimeEnd":endT}
#                                system.perspective.print("!!!downtimeData: "+str(params))    
                                DowntimeData = system.db.runNamedQuery("Project_OEE/Hourly/DownTime/Get_DowntimeByBatchIdStartDate", params)
                                DowntimeData = system.dataset.toPyDataSet(DowntimeData)
                                if(len(DowntimeData) < 1):
                                        for f in range(len(dtDescdataset)):
                                            if(int(descDT) == int(dtDescdataset[f][0])):
                                                descDT = dtDescdataset[f][1]
                                    data.append([round(diffSec/60.0,1),descDT,system.date.format(startT,'yyyy-MM-dd HH:mm:ss'),system.date.format(endT,'yyyy-MM-dd HH:mm:ss'),hourCount])
                hourCount = hourCount + 1
        header = ['Actual','Description','Start time','End time','HourCount']
        dataFinal = system.dataset.toDataSet(header, data)
        dataFinal = system.dataset.toPyDataSet(dataFinal)
        return dataFinal
        exc_type, exc_obj,tb = sys.exc_info()
        f = tb.tb_frame
        lineno = tb.tb_lineno
        errorMessage = "Line Number:" + str(lineno) + ", Err:"+ str(exc_obj)+' '+str(line)
        #system.perspective.print("!!Error getUnattendedDT: "+str(errorMessage))

If you're going to use boolean flags you should use the boolean built ins, so the function signature should be:

def getOeewithZone(stTime,enTime,Facility,Line,lineValue,IsMonthToDate,zoneName,isExist  = False, decimalValue = False, shift = True):


if isExist == 1:

Should be

if isExist:

The same for the opposite:

if isExist == 0:

should be:

if not isExist:

Avoid making multiple read requests. These are blocking calls (the way you're calling them). I'm not sure how many items will be in your loop but say you have 30 iterations through the loop, that means at a minimum you would have 90 calls that are blocking your script from continuing until they return. This can quickly balloon out of control. Much like with the tag writes earlier you should (at a minimum) combine as many tag reads as you can into one call.

tags = ['Is_Quality_Visible','Is_Performance_Visible','Is_Manual_Scrap']
tagPaths = ['{0}{1}/{2}/{3}'.format(configurationTagPath,Facility,Line,tag) for tag in tags]

isQuality,isPerformance,isManualScap  = [tagValue.value for tagValue in system.tag.readBlocking(tagPaths)]

Throughout the script you consistently call str() on things that should already be strings. Might not seem like much but it will add time, and it all adds up. Definitely functions like system.date.format() should not be wrapped in a str() as they already return a string.

Speaking of system.date.fomrat() you should work with pure Date objects not strings. This would prevent you from needing to constantly use system.date.parse() through the script.

if str(isPerformance) == 'None':

should be

if isPerformance is None:

This net result of this

if(str(isPerformance) == 'None':
    isPerformance = 0
if(int(isPerfomrance) == 0:
   Performance = 1
    #do something

Is that Performance will be 1 if isPerfomrance is None or 0, it should be:

if not isPerformance:
    Performance = 1
    #do something

Honestly, this whole nested if structure can be refactored

if not isPerformance:
    Performance = 1
elif not targetUnit:
    Performance = float(totalCount) / float(targetUnit)
    Performance = 0

Same thing for the quality

if not isQuality:
    Quality = 1
elif not totalCount and not scrapCount:
    Quality = 0
    goodCount = toatlCount - scrapCount
    Quality = float(goodCount)/float(toatlCount)

There are similar issues in getUnattededDT() two notable things stick out.

For this:

diffMinutes = int(system.date.format(startTime,'mm')) - int(system.date.format(endTime,'mm'))

Just do

diffMinutes = system.date.minutesBetween(startTime,endTime)

pyDataSets give you the ability to use them in a more pythonic way and so things like this:

for f in range(len(dtDescdataset)):

should be something like:

for row in dtDescdataset:

And for this, all I can say is...Why?


Thanks @lrose for your input. We have worked on the script which you provided which has further enhanced the performance.