The Template.src file is included with your VTS installation, and is stored in your VTS installation directory within a subdirectory labeled, "Template" (e.g. "C:\VTS\Template\").
Note: VTS 8.0 introduced a new format for declaring tag parameters. Legacy tags will still compile in VTS 8.0, but tags created using the new format will not compile in versions of VTS prior to 8.0. The new format permits new fields to be added to existing tags.
Template.src is a template for a tag module. It is provided to form the basis for a custom tag type, and includes all basic features of tags. The Template.src file may be used as the outline for a custom tag, and you may discard any sections that are not required for your particular needs.
Tag drawing methods only run when the page or screen upon which they are drawn is being displayed; therefore, any logic for a custom tag must go either in the tag's source code, or in a custom service, as those entities are always running. In the case of a custom service, you should note that because it is hard-coded, a new instance cannot simply be added as it can for a custom tag type. For this reason, it may be prudent to place logic within your tag template.
Highlights from the Template.src file are included below for your reference. Please refer to the actual Template.src file in the \vts\templates directory for the complete and fully commented version.
{======================= Template ==============================}
{ This is a template for a point module. This can form the }
{ basis for a generic point. Every basic feature of a point }
{ is presented here. You can use this as a template and remove }
{ the sections that are not required for your particular }
{ requirements. }
{==============================================================}
(
Name <:TagField("SQL_VARCHAR(64)", "Name", 0 ):>;
{ Name of this instance of the point. Its value must be a }
{ legal WEB variable name & is assumed to be no more than 256 }
{ characters }
{ This parameter must exist and its name must be "Name". The }
{ value of this name string cannot be the name of a reserved }
{ SQL keyword or the name of a module in the library. }
Area <:TagField("SQL_VARCHAR(255)", "Area", 1 ):>;
{ This is the name of the "area" to which this instance }
{ belongs. It may be up to 256 characters long and may be }
{ invalid or a null string. It is used by the browser to }
{ filter the point list but may be used by the point for any }
{ other purpose such as the name of the graphics page which is }
{ responsible for this point. It must not be the same name as }
{ a point or other module in the library. This parameter must }
{ exist and must have the name "Area". }
Description <:TagField("SQL_VARCHAR(255)", "Description",2 ):>;
{ This is the text description of this instance of the point. }
{ It may be any character string up to 64k bytes long. It }
{ is treated as a single line of text. This parameter must }
{ exist and must have the name "Description". }
{ Other parameters found in this section of TEMPLATE.SRC }
{ include: }
{ IODevice }
{ Address }
{ ScanInterval }
{ ManualValue }
{ Owner }
{ ContributionType }
{ Questionable }
{ Quality }
{ Container }
{ Priority }
{ AlarmInhibit }
{ LogRate }
{ HelpKey }
)
[
{ The next block of constant definitions defines the offset }
{ into the parameter list, starting from 0, for each of the }
{ point specific parameters. }
Constant #IODevice = 3;
Constant #Address = 4;
...etc...
Constant #AlarmInhibit = 13;
Constant #LogRate = 14;
{************************************************************}
{ The DrawLabel variable specifies the name of the variable }
{ defined in the Config.INI file which will be used to label }
{ the previews of this point during configuration. This is }
{ done so that text can easily translated. }
{************************************************************}
Constant DrawLabel = "TemplatePointLabel";
Root;
Refresh Module;
{ The following variables may or may not be required for a }
{ specific point }
RawValue { Data value read from the IO };
{************************************************************}
{ D A T A L O G G I N G }
{ }
{ A maximum of 100 variables can be logged and all variables }
{ get logged in the same file for this point. }
{ }
{ The variables are recorded in the .DAT file in increasing }
{ order of variable class and alphabetically within the }
{ class. }
{ You must not add or delete these variables unless the file }
{ is deleted or else the trend manager will misninterpret }
{ the file contents for previous data. }
{ FULL DESCRIPTIONS OF EACH CAN BE FOUND IN TEMPLATE.SRC }
{************************************************************}
Value (5);
LogFileName Module;
ExternalValue = 0;
LogObject;
Alarms Module;
AlarmObject;
{************************************************************}
{ The PLUGINS section lists modules which are defined }
{ outside of this module and are attached dynamically to }
{ this point. This is done so that the user can override }
{ these modules and insert their own versions without having }
{ to modify this code.
{************************************************************}
[ (PLUGINS)
{**********************************************************}
Shared ConfigFolder = "TemplateConfig";
{**********************************************************}
Shared Common = "TemplateCommon";
]
[ (GRAPHICS)
Draw Module;
Shared BarGraph;
]
{===== Generic container point types need the following block.
The Contributors variable is not used for the standard
I/O point types. ====}
Contributors;
ContributorAdded Module;
ContributorDeleted Module;
[ (GROUPS)
Shared Container;
]
{===== End of container point type block =====}
{===== Contributor point types need the following block =====}
ContainerIndex;
{ The following variables are similar in nature to the
ContainerIndex. They hold the array indicies for the
contributions to the Owner object and are typical of
such varaibles found in standard I/O points. }
Data0Index { Index in the Owner\Data[0] array };
...etc...
{===== End of contributor point type block =====}
{************************************************************}
{ The following section would be necessary if this point is }
{ a container for the basic point types: DI, DO, AI and AO. }
{ This would usually not coexist with the previous block of }
{ contributor variable declartions. }
{ The ContributorAdded and ContributorDeleted subroutines }
{ are required for use with these variables. }
{************************************************************}
Data0;
...etc...
{===== End of standard container variables =====}
{===== Point specific variables =====}
Average;
AndData;
Values;
Shared TempData;
{************************************************************}
{ Custom object placement code }
{************************************************************}
PlaceObject Module { The presence of this module declaration
informs the system that this module will
handle placement of the drawing object
on the screen.};
]
TemplateInit [
If 1 Template;
[
Root = Self { This value must be set to self() for all
points. It is used by the parameter editing
code. };
{ Set up initial values }
Refresh();
]
]
{ Name of the state is something fairly unique so that when }
{ it shows up in the profiler the user can see something }
{ other than System, Main }
Template [
{************************************************************}
{ Setting data value }
{************************************************************}
If Watch(1, RawValue, ExternalValue);
[
IfThen (!ExternalValue || Valid(ManualValue),
Value = PickValid(ManualValue, RawValue);
);
]
If Watch(1, Value);
[
{ This block of code handles the contributors to the }
{ owner Test is there is any contributions at all before }
{ we spend the time to test each of the bits. }
...etc...
]
{ Contribute data to our container }
{************************************************************}
Container\Values[ContainerIndex] = Value;
Owner\GoodQuality[QualityIndex] = PickValid(Quality\Value != 0, 1);
{************************************************************}
{ Container calculations using contributor's data }
Average = Mean(Values[0], ArraySize(Values, 0));
If WatchArray(1, Values[0], ArraySize(Values, 0));
[
{ First make a copy of the data }
TempData = New(ArraySize(Values, 0));
... etc...
TempData = Invalid;
]
]
<
{========================= Refresh=============================}
{ This subroutine will be called whenever the parameters for }
{ the module are changed and allow the point to perform any }
{ necessary actions to undo any previous effects necessary }
{ such as changing drivers. }
{==============================================================}
Refresh
(
Parm { This is a pointer to an array which contains
the values of all of the parameters for the
point prior to their change. }
)
Refresh [
If watch(1);
[
{**********************************************************}
{ IODevice }
IfThen (ValueType(Parm[#IODevice]) == 4 { Text },
Parm[#IODevice] = Scope(\Code, Parm[#IODevice])
);
...etc...
{ ManualValue }
{**********************************************************}
ManualValue = Cast(ManualValue, 3 { Floating Point });
...etc...
{ Quality }
{**********************************************************}
IfThen (ValueType(Quality) == 4 { Text },
Quality = Scope(\Code, Quality);
);
{ Questionable }
{**********************************************************}
Questionable = PickValid(Cast(Questionable, 3 { Floating point }) != 0, 1);
{ Owner }
{**********************************************************}
IfThen (ValueType(Parm[#Owner]) == 4 { Text },
Parm[#Owner] = Scope(\Code, Parm[#Owner])
);
...etc...
{ Container }
{**********************************************************}
IfThen (ValueType(Parm[#Container]) == 4 { Text },
Parm[#Container] = Scope(\Code, Parm[#Container])
);
...etc...
{ Data logging }
{**********************************************************}
IfThen (PickValid(Parm[#LogRate] != LogRate, 1),
Slay(LogObject, 0);
IfThen (LogRate > 0,
LogObject = \Logger(Root,
LogRate,
Invalid,
100000,
Int(60 / LogRate));
);
);
{ Alarming }
{**********************************************************}
IfThen (!Valid(AlarmObject) && Priority > 0 &&
ValueType(\AlarmManager) == 7 { Object value },
AlarmObject = Alarms();
);
Return(0);
]
]
{ End of Refresh }
>
<
{===================== ContributorAdded =======================}
{ This subroutine is called whenever a contributor is added to }
{ this point. }
{ This code DOES NOT maintain the list. }
{ This subroutine need not exist if there are no special }
{ actions to take or if this point is not a container. }
{ This code is called by the base VTS system. }
{==============================================================}
ContributorAdded
(
VariableName;
ArrayName;
CountName;
NewPoint;
N;
Index;
)
[
NewArray { Temporary array for the unacknowledged list };
]
ContributorAdded [
If watch(1);
[
... etc ...
]
]
{ End of ContributorAdded }
>
<
{==================== ContributorDeleted ======================}
{ This subroutine is called whenever a contributor is deleted }
{ from this point. }
{ This code DOES NOT maintain the list. }
{ This subroutine need not exist if there are no special }
{ actions to take or if this point is not a container. }
{ This code is called by the base VTS system. }
{==============================================================}
ContributorDeleted
(
VariableName;
ArrayName;
CountName;
OldPoint;
N;
Index;
)
[
NewArray { Temporary array for the unacknowledged list };
]
ContributorDeleted [
If watch(1);
[
...etc...
]
]
{ End of ContributorDeleted }
>
<
{====================== Alarms ================================}
{ This module is launched if there are any alarms to handle. }
{ This module allows handling of multiple alarms easily. For }
{ a single alarm, this module may be too expensive memory-wise }
{ and the code could be placed in the base point state. }
{==============================================================}
Alarms
[
...etc...
]
AlarmsInit [
If \AlarmManager\Started AlarmsMain;
[
...etc...
]
]
AlarmsMain [
{ Handle alarm triggering and returning to normal }
{************************************************************}
...etc...
{ Handle alarm inhibits }
{************************************************************}
If Edge(AlarmInhibit, 1);
...etc...
{ If alarming is turned off for this point, stop running }
{ this module }
{************************************************************}
If PickValid(Priority == 0, 1);
[
\AlarmManager\UnRegister(Self);
{ Killing this module only makes sense here if there ALL }
{ of the alarms are off }
Slay(Self, 0);
]
...etc...
]
{ End of Alarms }
>
<
{==================== LogFileName =============================}
{ This optional launched module is called by the data logger }
{ to set the name of the current log file for this point. }
LogFileName
(
Object;
NamePtr;
)
LogFileNameInit [
...etc...
]
{ End of LogFileName }
>
<
{======================= Draw =================================}
{ This module draws a graphic representation of the point on a }
{ page. }
Draw
(
OnColor;
OffColor;
)
[
Constant DrawLabel = "TemplateDrawLabel";
Persistent LastOnColor;
Persistent LastOffColor;
Panel Module;
...etc...
]
DrawInit [
If 1 Draw;
[
IfThen (NParm(Self) == 0,
OnColor = PickValid(LastOnColor, \DefaultOnColor);
OffColor = PickValid(LastOffColor, \DefaultOffColor);
);
]
]
Draw [
{ Actual drawing section }
...etc...
]
<
{===================== Draw\Panel =============================}
Panel
(
Parm;
)
[
{ AutoConfig }
]
PanelInit [
If 1 PanelMain;
[
{ Set up the parameter values if they have not been set yet }
Parm[0] = PickValid(Parm[0], LastOnColor, \DefaultOnColor);
Parm[1] = PickValid(Parm[1], LastOffColor, \DefaultOffColor);
]
]
PanelMain [
{ Saving of drawing object parameters for next time }
{************************************************************}
LastOnColor = Parm[0];
LastOffColor = Parm[1];
...etc...
]
{ End of Draw\Panel }
>
{ End of Draw }
>
When you create a custom tag type, you must reference its module and the file in which the module exists in your application's AppMod.src file. This should be done in the (POINTS) section, as displayed in the example below:
[
Constant POINTS = 0x0002 { Point template class };
Constant GROUPS = 0xFF00 { Collections of point types };
Constant LIBRARIES = 0xFF01 { Library class module };
Constant GRAPHICS = 0xFF02 { Shared drawing methods for points };
Constant PAGES = 0xFF03 { Graphic pages & dialogs };
Constant SERVICES = 0xFF04 { Service class };
Constant PLUGINS = 0xFF05 { Plug-in class };
Constant PRIORITYSTART = 0xFF06 { Items that are pre-started };
Constant TSERVICES = 0xFF07 { Threaded service class };
Constant CHILDREN = 0xFF08 { Child tags class };
[ (POINTS) {====== Modules that are tag templates =======}
MyCustomTag Module "MyCustomTag.src" { Custom Tag Reference };
]
[ (GROUPS) {== Modules that are collections of point types ===}
]
[ (LIBRARIES) {==== Modules that contain library objects =====}
]
[ (GRAPHICS) {= Modules that are shared drawing methods for points =}
]
[ (PAGES) {=== Modules that are graphic pages and dialogs ==}
MyPage Module "Pages\MyPage.SRC";
]
[ (SERVICES) {=== Modules that are services that are started ===}
]
[ (PLUGINS) {== Modules added to other base system modules ==}
]
[ (PRIORITYSTART) {= Modules/variables that are to be prestarted ==}
]
[ (TSERVICES) {=== Modules that are threaded services to run ===}
]
]