Make a Custom Tag Visible to OPC Clients

When running VTScada as an OPC server, the values of standard tags are available to clients. This will not be true of your custom tags unless you add modules that specify what will be made available.

The following modules may be added. Examples of these follow.

OPCGetTagAttributes This tells the VTScada OPC server whether this tag can be read from or written to, as well as the data type it is to return. Required for the tag to be visible to an OPC client.
OPCGetTagProperties Optional. Returns a dictionary of all the items that will be available from the tag.
OPCReadTagValue Must be included if the tag is to be readable.
OPCWriteTagValue Only required if the tag is writeable.

For any OPC constants that are not defined locally, you should be able to add a backslash to obtain it from VTScada.

OPCServerEnabled

This is a public variable that will be set to TRUE when there are OPC Servers to which a tag should be publishing. It will be either Invalid or FALSE if no server is present.

UpdateOPCData

UpdateOPCData() is a VTScada module. As with the constants, if it is not in the immediate scope, you can add the backslash operator.

Example:

    IfThen(Valid(\OPCServerHandle),
           \UpdateOPCData(Self(), \OPC_PROP_RAW_VALUE, RawValue); 
    );
    IfThen(True(\Code.OPCServerEnabled),
           \UpdateOPCData(Self(), \OPC_PROP_RAW_VALUE, RawValue);
    );

This will tell the server code to update the RawValue attribute, since it has changed. OPCServerEnabled is set higher up the scope tree when the OPC server is enabled - this prevents that code from being called if it is not present.

Make the tag's value available to OPC clients. If included, your tag's main state should call \UpdateOPCData(Self()); whenever the value changes.

<
{======================== OPCReadTagValue =============================}
{ Subroutine that returns the current value, quality, and timestamp of }
{ this tag for OPC purposes.                                           }
{======================================================================}
OPCReadTagValue
(
  pValue          { (Output) The value of the tag, must be a Valid value };
  pQuality        { (Output) The quality of the value                    };
  pTimestamp      { (Output) The timestamp of the value                  };
  pTimestampIsUTC { (Output) Indicates whether the timestamp is in UTC   };
)

Main [
  If 1;
  [
    *pValue     = PickValid(Value, Valid(Cast(Value, \#VTypeText)) ? "" : 0);
    *pTimestamp = Timestamp - TimeZone(0);
    *pQuality = Operational
                ? (Operable && Valid(ManValue))
                  ? \OPC_QUALITY_GOOD_LOCAL_OVERRIDE
                  : Valid(Value)
                    ? \OPC_QUALITY_GOOD
                    : \OPC_QUALITY_BAD
                : \OPC_QUALITY_UNCERTAIN;
    Return(Invalid);
  ]
]

{ End of OPCReadTagValue }
>

This will be called when an OPC client is requesting the values. The parameter, pValue, is what you want it to show, normally gained from Value

The following module example creates a dictionary of properties that will be available to OPC clients. Remove any properties that you do not intent to make available. For example, if you don’t need OPC_PROP_DESCRIPTION declared then remove the line within this module. A complete list of OPC values can be found in Properties of OPC Classic Item Tags.

<
{======================== OPCGetTagProperties ========================}
{ Subroutine that returns a dictionary of OPC properties and values   }
{ supported by this tag. The dictionary is keyed by property ID.      }
{=====================================================================}
OPCGetTagProperties
 [
  PropDict; 
] 
Main [
  If 1; 
  [
    PropDict = Dictionary();
    PropDict[OPC_PROP_EU_UNITS]                = Units; 
    PropDict[OPC_PROP_DESCRIPTION]             = Description; 
    PropDict[OPC_PROP_HIGH_EU]                 = ScaledMax; 
    PropDict[OPC_PROP_LOW_EU]                  = ScaledMin; 
    PropDict[OPC_PROP_HIGH_INSTRUMENT]         = UnscaledMax; 
    PropDict[OPC_PROP_LOW_INSTRUMENT]          = UnscaledMin; 
    PropDict[OPC_PROP_TIMEZONE_MINUTE_OFFSET]  = TimeZone(0)/60; 
    PropDict[OPC_PROP_AREA]                    = Area; 
    PropDict[OPC_PROP_DEVICE_TAG]              = DeviceTag; 
    PropDict[OPC_PROP_ADDRESS]                 = Address; 
    PropDict[OPC_PROP_RAW_VALUE]               = RawValue; 
    PropDict[OPC_PROP_LOW_ALARM]               = Cast(AlarmLo,3); 
         { In case AlarmLo is a tag or expression }
    PropDict[OPC_PROP_HIGH_ALARM]              = Cast(AlarmHi,3); 
         { In case AlarmHi is a tag or expression }
    Return(PropDict); 
  ] 
] 

{ End of OPCGetTagProperties }
>

Example to define whether the tag can be read from or written to by the OPC client:

<
{======================= OPCGetTagAttributes ==========================}
{ Subroutine that returns the OPC attributes of this tag.              }
{======================================================================}
OPCGetTagAttributes
 (
  pAccessRights  { (Output) The read/writeability of the tag           };
  pDataType      { (Output) The COM datatype of the value of the tag   };
) 

Main [
  If 1; 
  [
    *pAccessRights = Output ? \OPC_ACCESS_READWRITEABLE : \OPC_ACCESS_READABLE; 
    *pDataType = Valid(Value) 
                 ? Valid(Cast(Value, \#VTypeText)) 
                   ? \VT_BSTR
                   : \VT_R8
                 : \VT_EMPTY; 
    Return(Invalid); 
  ] 
] 

{ End of OPCGetTagAttributes }
>

pAccessRights defines whether the tag's value can be read or written to according to the assignment of one of the following constants:

  CONSTANT OPC_ACCESS_READABLE               = 1; 
CONSTANT OPC_ACCESS_WRITEABLE              = 2; 
CONSTANT OPC_ACCESS_READWRITEABLE   = 3;

pDataType sets the data type of the tag to one of the following for compatibility with component object model standards:

 CONSTANT VT_EMPTY= 0; 
 CONSTANT VT_NULL= 1; 
 CONSTANT VT_I2= 2; 
 CONSTANT VT_I4= 3; 
 CONSTANT VT_R4= 4; 
 CONSTANT VT_R8= 5; 
 CONSTANT VT_CY= 6; 
 CONSTANT VT_DATE= 7; 
 CONSTANT VT_BSTR= 8; 
 CONSTANT VT_BOOL= 11; 
 CONSTANT VT_VARIANT= 12

If your tag will allow OPC Write access, you will need a module similar to the following example. Note that this example does not provide for any checking of the request - something may wish to add.

<
{=========================== OPCWriteTagValue =============================}
{ Subroutine called when an OPC client wants to write a value to this tag. }
{==========================================================================}
OPCWriteTagValue
 (
  NewValue      { (Input) The value to be written, must be a Valid value };
) 

Main [
  If 1; 
  [
    Set(NewValue); 
    Return(0); 
  ] 
] 

{ End of AnalogOutput\OPCWriteTagValue }
>

For a description of the Set() module, see: Write Data: The Set Module.