Log Tag Data
Tag data can be logged either by attaching a Logger tag, or by including code in the tag to write history. In both cases the tag must define a HistorianSchemaString, identifying the data to be logged.
Custom Logging for Tags
One reason to add logging code to your tag modules is to free the user from doing any configuration work to enable data logging. It also provides you with control over what is logged and when.
Three things are required in order for your tag to do its own logging:
HistorianName
HistorianName will hold the name of the Historian tag and must be declared as a parameter or variable in your tag. In general, it is better to use a parameter than a variable for HistorianName. This will provide greater flexibility in configuration. For example, in the parameters declaration of the Analog Status tag, HistorianName is declared as follows. Note that the System Historian is provided as the default value.
HistorianName <:TagField("SQL_VARCHAR(255)" ):> = #SYSTEM_HISTORIAN { Historian Tag name };
HistorianSchemaString
This variable within your tag defines what information to write for each entry in the log and how to format that information. In most cases there should be four fields, as described in the following notes. Each field within the schema is separated from the next by a pipe character (vertical bar: |).
The first two fields must always specify the primary time stamp and the generation timestamp. Timestamp is usually provided by the PLC or data source and GenTimeStamp is automatically generated by the Historian. Timestamp should be stored as type TSTAMP (data and time timestamp). GenTimeStamp should be stored as UI4, a four-byte integer suitable for UTC values.
The third field is the tag data to be logged. "Value" is recommended in nearly all cases. The number (as in UI4 or R8) is the data type and the number of bytes required to store the data.
The fourth field, Value!, is used to log tag attribute data. Refer to Table Structure and Notes
While this may take any of the forms described in the following table, only three forms are used for most VTScada tags:
Analog values:
{ Historian schema } Constant HistorianSchemaString = "Timestamp-TSTAMP-T|GenTimestamp-UI4-A|Value-R8|Value!-UI4";
Digital values:
{ Historian schema } Constant HistorianSchemaString = "Timestamp-TSTAMP-T|GenTimestamp-UI4-A|Value-UI1|Value!-UI4";
String values:
{ Historian schema } Constant HistorianSchemaString = "Timestamp-TSTAMP-T|GenTimestamp-UI4-A|Value-STR|Value!-UI4";
The type is defined by a short text string:
TSTAMP I Signed integer. UI Unsigned integer.
UI4 is typically used for UTC timestamps.R Real number STR String of bytes BYTES Synonym for STR BOOL Boolean value
Any field that you wish to hide from the HDV must be marked with an exclamation mark, such as Value!. Although any field marked with a ! will be hidden from the HDV, the HDV treats the Value! field specifically as a value attributes field. Other fields with ! will be hidden entirely.
WriteHistory
You must call \HistorianManager\WriteHistory(Root) each time you want to write log data. (WriteHistory)
\HistorianManager\WriteHistory(Root, TimeStamp, Value);
The Value parameter must be an array of values (one for Value and one for Value!) rather than a single value.
The call to write to the historian should be placed such that it is called whenever the value changes sufficiently to merit a new record (a deadband parameter is commonly used to avoid logging of very small changes). The call may also be placed in the tag's Refresh module (or a submodule called from Refresh) for the case where the HistorianName parameter may change, to log invalid to the old Historian and the current value to the new one.
If your tag supports the retrieval and logging of historical data that has been stored on the PLC, you will need a control structure to loop through the arrays. Typically, this is handled in a separate state.
You may choose to record any calculated values you like, and may impose conditions on whether the write occurs with each refresh. For example, the Analog Status tag will not perform a write until the tag's value has changed from the previously written value by at least a threshold amount (controlled by the parameter, Deadband, if set).
IfThen(PickValid(Abs(Value - LastLoggedValue) >= Threshold, 1), \HistorianManager\WriteHistory(Root, TimeStamp, Value); LastLoggedValue = Value; );
Note that any tag with an HistorianName parameter or variable will be added to both the "Loggers" and the "Trenders" tag groups, automatically.
Attach a Logger to a custom tag
In order to be used as the data source for a Logger tag, a tag must be a member of the Trenders group and must expose one or more variables by defining an HistorianSchemaString.
The HistorianSchemaString is similar to that just described for built-in logging except that the name of the loggable fields must match the name of the loggable variables. In addition, your tag needs to add a Value! variable programmatically, using the function AddVariable(). It must also set Value! using Variable() in the same block of script code that sets Value.
It is also common practice to include a Logger tab in a tag's configuration dialog.
To do this, include the variable "LogContributors" as a plugin in your tag definition module.
[ (PLUGINS) LogContributors { This is the "handle" used by the VTS code to maintain a list of the logger contributors};
And, add the following to your tag's configuration panel code:
Variable declarations:
LogContribs { List of log contributors }; NumLogs { Number of log contributors };
Logger panel:
Logger [ If Current != x Switch; {***** Logger contributor list *****} GUITransform(80, 203, 420, 96, 1, 1, 1, 1, 1 { No scaling }, 0, 0, 1, 0 { No movement; visible; reserved }, 0, 0, 0 { Not selectable }, \DialogLibrary.PContributor("LogContributors" { holder }, \Root, Parms[\#ContributionType] { contrib type }, "LogPoint" { point type }, \GetPhrase("LoggerContribLabel"), 1 { & 2 - ID }, NumLogs ? 1 : 0 { no add if already 1 })); {***** Keep track of the number of log contributors *****} LogContribs = Scope(\Root, "LogContributors"); NumLogs = PickValid(ArraySize(*LogContribs, 1), 0); ]
Prior to the release of VTS 10, logging was done using the LogManager module. This is now obsolete, having been replaced by the HistorianManager. Any references to LogManager or Logger in tags from legacy applications must be updated to use the HistorianManager API instead.