Rules for Writing a Communications Driver

  • Communication drivers are tags and must be built as such.
  • The module must contain a variable named "Driver".
    This variable must not have a value set in the module. The VTScada loader sets it before the module starts.
  • The module must include a variable named "Ready".
    The driver tag code must set this to true when the driver tag is ready to be used.
  • Ready must not be set to true before:
    • The variable Root is set to some object value (usually Self() )
    • The variable RPCService is set.
    • Before Driver\Started becomes true.
    • Before Driver\RPCStatus is 2 (server) or 1 (client)
Ready = Valid(Root) && Valid(RPCService) && Driver\Started && 
        (Driver\RPCStatus == 2 || Driver\RPCStatus == 1);
  • If the driver tag being developed has other constraints they must also be added.
  • For tags that do I/O using a communication driver, the driver tag passed in as a parameter must first be converted to an object value. This object value must be used to launch a copy of any read requests required. This is done with the script code like:
If Watch(1, IODevice, Address, ScanRate) &&
           (ValueType(IODevice) == 4 { Text } ||
            Valid(IODevice\Driver) { Driver has started });
[
  IfThen (ValueType(IODevice) == 4 { Text string },
     { Convert text driver name to the object value of }
     { that driver                                  }
    IODevice = Scope(\Code, IODevice) 
  );
    { Save a copy of the driver instance for next change }
  Driver = IODevice\Driver;
    { Delete any previous read requests }
  Driver\DelRead(&RawValue);
    { Start new request for the data }
  Driver\AddRead(Address,
                 1 { # of Elements/Bytes },
                 &RawValue,
                 ScanRate);
]

The Address and ScanRate are typically parameters to the tag template module. The Driver variable must be defined in the module.

  • Writes to the I/O Device are done by executing a call to the Write module in the driver within a script. The code might look like:
 Driver\Write(Address, Length { # of elements/bytes }, Data);
  • A Value variable is common for most tags. For drivers, this value is an error code where 0 indicates no error. Value should be set to the Error from VTSRead/VTSWrite, and must be set on the current primary I/O server.
  • All drivers must have a variable called Drivers with a class of GROUPS. (not to be confused with "Driver" in point 1). This should also be a shared variable to conserve RAM. This makes the tag a part of the Drivers group and is necessary to permit the tag to show up in driver tag selection lists and to allow it to set its "Driver" variable before the tags use it.
  • Any driver tag that should share the same RPC service for all instances, should define a constant named "RPCService" with the default value being the name of the service. This name should typically be the name of the driver tag. This will force all instances of the driver tag to share the same server. It will dramatically improve the startup performance of systems that have a large number of instances of the same driver tag type, such as SCADA systems with a large number of RTU’s. An alternative option is possible if the RPCService variable does not have a default value. The driver tag may elect to set this variable based upon some run-time condition (such as a configuration file setting). If the variable exists, it must be set prior to the Driver\Started variable being set in the VTSDriver code.
  • If the driver tag uses a serial port, it is a good idea to flush the input buffer before any data is sent. The mechanism that VTScada uses to add and delete read/write commands is such that a read or write that is in progress could be removed from the "queue" in the middle of processing. This means that it is possible that a read or write command could send its data but then be removed from the communications loop before the response comes back. The next read or write command would then collect the data, if the serial buffer was not emptied. (Refer to the function, SerRcv, for one mechanism to empty the input buffer).
  • Do not create a separate module to serve as a low level driver
  • Do not declare the variables that are added by diagnostics.
  • Add the driver tag to the groups: Trenders, Loggers and Numeric, Drivers.