VTScada Plug-In API
The plug-in must be named ServiceCtrlLogic and is automatically launched (on the application thread) and attached to the ServiceCtrl service when the VTScada application starts.
ServiceCtrl provides an interface that the ServiceCtrlLogic may use. The interface consists of a number of useful variable and constants defining a structure that holds the current server states:
ServerStates { Structure holding server states };
Trigger = 0 { Boolean - TRUE on ServerStates change };
RPCStatus { Current RPC status of ServiceCtrl };
ServiceName = "ServiceCtrl" { Service name };
RPCScopeName = "ServiceCtrl" { RPC scope name (must match AppRoot decl) };
LogicObj { Instance of the ServiceCtrlLogic module };
{***** Indices into the ServiceState structure.
The structure is held as an array, with row #SST_NAME being the
service names and row #SST_STATES being arrays of a sub-structure.
The sub-structure is an array with row #SST_SS_SERVER being the
server name and row #SST_SS_STATE being the server state. *****}
Constant #SST_NAME = 0 { Service name };
Constant #SST_STATES = 1 { Array of server/states };
Constant #SST_NUMELEMENTS = 2 { Keep one bigger than the max above };
Constant #SST_SS_SERVER = 0 { Server names };
Constant #SST_SS_STATE = 1 { Server states };
Constant #SST_SS_NUMELEMENTS = 2 { Keep one bigger than the max above };
Fundamental to the plug-in logic is an understanding of the ServerStates structure. Essentially, ServerStates is a 2D array with each element of row [#SST_NAME] holding the service names and each element of row [#SST_STATES] holding an array. ServerStates is initialized from the inhibited service list, from RPCManager. There is therefore one column for each service under application control.
Each element of row [#SST_STATES] is also a 2D array, with each element of row [#SST_SS_SERVER] holding the ordered list of servers, highest listed first. Each element of row [#SST_SS_STATE] is initially Invalid. It is the responsibility of the plug-in to maintain this row with the servership state of the service on the corresponding server.
A diagram may help here:
The plug-in must provide an InitializeState subroutine that gets called, in a CriticalSection, when the ServiceCtrl service instance becomes server. It must populate the ServerStates structure's #SST_SS_STATE elements to reflect the servership states for the service. Only one element for each service may be set to \#RPCServer. All others must be set to \#RPCClient. Failure to observe this precaution will result in unpredictable behavior in the services being controlled.
The plug-in is at liberty to change these element states at any time, but has to do so in a CriticalSection, to avoid race conditions with RPCManager. After setting them it must set ServiceCtrl’s Trigger variable to 1.
If the plug-in wishes to make an RPC call to other service instances of itself (inside the ServiceCtrl service), it can use the expression Concat(\RPCScopeName, "\LogicObj") as the RPC call scope and \ServiceName as the name of the service in which to make the call.