Historical data on graphic

Hello all SCADA experts.

Is there anyone who has any idea how to make a function that shows historical data on a graphic object?

For example: to view analog data (Apperance -> Display Value; Numeric expression = Tag.HistoricalValue) from a selected point of time.

Parents
  • You'll have to call the TrnGetTable() function. However, there are a couple of limitations: (1) It is a blocking function (takes time to execute) so you can't call it directly from an expression on a page. (2) it doesn't just return a value--it writes one or more values to a Cicode array. So, you need to write a Cicode function to call TrnGetTable with the right arguments and to do something with the value written to the array.

    If you know what variables you will need and what sample times you need to read, then you could write a function that runs periodically off an event. The function would just be hard-coded to call TrnGetTable for each trend tag, get the sample from the Cicode array, then write it to a different disk tag (or persisted memory tag) for each trend.

    Another option is to only read the trend value when it is requested from a page. The code is a little more complicated but should put less load on the trend server, and not need disk tags. What you need is a function that accepts a tag name and time, then returns the current value stored in a page variable.  It calls a helper function as a new task. The helper function calls TrnGetTable(), retrieves the sample from the Cicode array, and writes it to a page string. It also uses Enter/LeaveCriticalSection() to avoid problems with multiple instances running at the same time (like if you try to display 2 different historical values on the same page).

    REAL rHistoricalSample[1];
    
    //Retrieve a sample value from a trend tag
    //This function is non-blocking and is meant to be called from an expression on a page
    //
    //sTag		Trend tag name
    //nSecsAgo	Number of seconds in the past to read the sample from
    //
    //Returns the sample value as a REAL, or 0 if the tag has not been read yet.
    //
    //Note: This function will return a blank value until the sample is returned from the trend
    //server. The value is stored in a local page-based string named  so it does
    //not interfere with other tags or samples being read
    REAL FUNCTION GetHistoricalValue(STRING sTag, INT nSecsAgo)
    	TaskNew("_GetHistoricalValue", "^"" + sTag + "^"," + nSecsAgo:# + "," + WinNumber():#, 1);
    	RETURN PageGetStr(sTag + nSecsAgo:#);
    END
    
    
    //Helper function for GetHistoricalValue runs as a separate task, retrieves the trend sample
    //and stores it in a local page-based string. A critical section is used so only one copy of
    //the function executes at a time since the sample must be stored in a shared module variable.
    FUNCTION _GetHistoricalValue(STRING sTag, INT nSecsAgo, INT nWindow)
    	EnterCriticalSection("GetHistoricalValue");
    	TrnGetTable(sTag, TimeCurrent() - nSecsAgo, 0, 1, rHistoricalSample, 0, 0);
    	PageSetStr(sTag + nSecsAgo:#, rHistoricalSample[0], nWindow);
    	LeaveCriticalSection("GetHistoricalValue");
    END
    

    To use this to read Tag1 from 30sec ago, set your text object's Appearance (Display Value) tab's Numeric expression to GetHistoricalValue("Tag1", 30)

    Note that this is not designed to be called from a background Cicode function, or task running in a server process, since it stores the temporary value in a local page variable.

Reply
  • You'll have to call the TrnGetTable() function. However, there are a couple of limitations: (1) It is a blocking function (takes time to execute) so you can't call it directly from an expression on a page. (2) it doesn't just return a value--it writes one or more values to a Cicode array. So, you need to write a Cicode function to call TrnGetTable with the right arguments and to do something with the value written to the array.

    If you know what variables you will need and what sample times you need to read, then you could write a function that runs periodically off an event. The function would just be hard-coded to call TrnGetTable for each trend tag, get the sample from the Cicode array, then write it to a different disk tag (or persisted memory tag) for each trend.

    Another option is to only read the trend value when it is requested from a page. The code is a little more complicated but should put less load on the trend server, and not need disk tags. What you need is a function that accepts a tag name and time, then returns the current value stored in a page variable.  It calls a helper function as a new task. The helper function calls TrnGetTable(), retrieves the sample from the Cicode array, and writes it to a page string. It also uses Enter/LeaveCriticalSection() to avoid problems with multiple instances running at the same time (like if you try to display 2 different historical values on the same page).

    REAL rHistoricalSample[1];
    
    //Retrieve a sample value from a trend tag
    //This function is non-blocking and is meant to be called from an expression on a page
    //
    //sTag		Trend tag name
    //nSecsAgo	Number of seconds in the past to read the sample from
    //
    //Returns the sample value as a REAL, or 0 if the tag has not been read yet.
    //
    //Note: This function will return a blank value until the sample is returned from the trend
    //server. The value is stored in a local page-based string named  so it does
    //not interfere with other tags or samples being read
    REAL FUNCTION GetHistoricalValue(STRING sTag, INT nSecsAgo)
    	TaskNew("_GetHistoricalValue", "^"" + sTag + "^"," + nSecsAgo:# + "," + WinNumber():#, 1);
    	RETURN PageGetStr(sTag + nSecsAgo:#);
    END
    
    
    //Helper function for GetHistoricalValue runs as a separate task, retrieves the trend sample
    //and stores it in a local page-based string. A critical section is used so only one copy of
    //the function executes at a time since the sample must be stored in a shared module variable.
    FUNCTION _GetHistoricalValue(STRING sTag, INT nSecsAgo, INT nWindow)
    	EnterCriticalSection("GetHistoricalValue");
    	TrnGetTable(sTag, TimeCurrent() - nSecsAgo, 0, 1, rHistoricalSample, 0, 0);
    	PageSetStr(sTag + nSecsAgo:#, rHistoricalSample[0], nWindow);
    	LeaveCriticalSection("GetHistoricalValue");
    END
    

    To use this to read Tag1 from 30sec ago, set your text object's Appearance (Display Value) tab's Numeric expression to GetHistoricalValue("Tag1", 30)

    Note that this is not designed to be called from a background Cicode function, or task running in a server process, since it stores the temporary value in a local page variable.

Children
No Data