RSC GDS services

Note: The execution of RSC services can take some time. For this reason, avoid direct calls from ESM tasks that are scheduled for real-time execution.

IDataAccessService

During runtime, the function extensions can use the IDataAccessService interface to have read and write access to the GDS data. The service enables asynchronous reading and writing of one or more ports or even internal variables. For this access, you need the name of the port that is to be read out. The data read can be written to a database, for example.

You need the following header to use the IDataAccessService service. If necessary, include it via the #include command:

  • Arp/Plc/Gds/Services/IDataAccessService.hpp

Service functions for direct data access:

  • Read(): This function is used to read the values of the variable addresses passed on.
  • ReadSingle(): This function is used to read the value of the variable address passed on. Only simple variables are supported (no arrays or structures).
  • Write(): This function is used to write the values passed on to the variables of the variable address passed on.
  • WriteSingle(): This function is used to write the value passed on to the variables of the variable address passed on. Only simple variables are supported (no arrays or structures).

 

These examples show how the above functions can be implemented:

ReadItem         ReadSingle(const RscString<512>& portName)
                 Read(ReadPortNamesDelegate portNamesDelegate, 
                      ReadResultDelegate resultDelegate)
DataAccessError  WriteSingle(const WriteItem& data)
                 Write(WriteDataDelegate dataDelegate, 
                       WriteResultDelegate resultDelegate)

 

Each port has a unique name within the GDS that is made up as described in Global Data Space configuration. You need the complete name (URI) of a port to address it. For example, the following port addresses are valid:

  • ComponentName-1/ProgramName-1.Variable_Name
  • ComponentName-1/Global_Variable_Name
  • ComponentName-1/ProgramName-1.Array_Variable_Name
  • ComponentName-1/ProgramName-1.Array_Variable_Name[index]
  • ComponentName-1/ProgramName-1.Array_Variable_Name[startIndex:endIndex]
  • ComponentName-1/ProgramName-1.Struct_Variable_Name.Element1.Leaf
  • ComponentName-1/ProgramName-1.Struct_Variable_Name.Element1.LeafArray
  • ComponentName-1/ProgramName-1.Struct_Variable_Name.Element1.LeafArray[index]

The following variable types can be read and written:

  • Primitive
  • DateTime
  • StaticString
  • IecString
  • Enum:
    The values of an enum are only transmitted as BaseType.
  • Struct:
    You can address the entire structure or each element of a structure, even if it is not an END element.
    Information on the layout of the structure can be obtained from the data that is transmitted.
    This is an example for a Struct:
    In C#, you receive an array from object[] containing values of a and b:
    In C++, you receive each individual element in an RscVariant.
    The first type Struct RscVariant contains information, e.g., that it is a type Struct variable with two elements. Next, the type Int RscVariant with the associated value and the type Bool RscVariant with the associated value are read.
  • Array:
    You can address each individual element or the entire array. You also have the option to address a specific range of an array. Enter the start index and the end index separated with a colon for this.
    This is an example for an  Array:
    ComponentName-1/ProgramName-1.Array_Variable_Name[20:30].

ISubscriptionService

ISubscriptionService offers an alternative to the read functions of IDataAccessService. The variables of which the values are to be read are only registered once and can then be read continuously. The data can be read more rapidly, and, due to the elimination or variable addressing, also recorded consistently with the task cycle. All variables in the same ESM task are recorded in the same cycle (with the exception of the DirectRead subscription type). In addition, ISubscriptionService provides time stamps that can be used to assign a clear recording time to each value.

To use the ISubscriptionService class, you require the following header file which was declared in the Arp::Plc::Gds::Services namespace; include it via the #include command:

#include "Arp/Plc/Gds/Services/ISubscriptionService.hpp"

 

Here's how to create, start and read a subscription.

Creating a subscription

To create a subscription, use the following function of “ISubscriptionService”:

uint32    CreateSubscription(SubscriptionKind kind)

With this function, the type of the desired subscription is passed on.

Subscription types

Select one of the following four subscription types:

Type Description
DirectRead The DirectRead subscription records the values directly when the ReadValues() function is called within the context of the thread to be called. The HighPerformance, RealTime, and Recording subscription types collect the values within the context of the assigned ESM task. The data is read directly from the respective variable. A copy process is only implemented if data is also queried. The read data can be from different task cycles.
This subscription can deliver the best performance with the lowest impact on the real time.
Possible use: Asynchronous data acquisition of non-time-critical data.
HighPerformance The HighPerformance subscription uses a double buffer which contains the last written data of a variable. The buffer enables almost simultaneous writing and reading of data.
The HighPerformance type is consistent with the task cycle. It uses the least memory and shows the least impact on the real time (compared to the RealTime and Recording subscription types).
Possible use: Standard type for acquiring data.
RealTime The RealTime subscription uses a quad buffer which contains the last written data of a variable. The buffer minimizes access times during reading and writing.
The RealTime type is consistent with the task cycle. It guarantees fast data access, but requires four times the amount of memory.
Possible use: The subscription is suitable for variables running in very fast tasks and if fast access to read files is required.
Recording The Recording subscription uses a ring buffer that can store several data items of a variable. This type is consistent with the task cycle and has low impact on the real time. However, depending on the ring size, it requires a lot of memory. By default, the ring size is set to 10. It can be configured if the CreateRecordingSubsciption() function is used instead of CreateSubscription().
Possible use: The subscription is suitable for variables running in tasks that are faster than the task of the user but the user still needs all values.

Adding variables

Add the desired variables to the subscription by calling one of the following functions:

DataAccessError     AddVariable(uint32 subscriptionId, 
                    const RscString<512>& variableName)

or

void                AddVariables(uint32 subscriptionId, 
                    AddVariablesVariableNamesDelegate variableNamesDelegate, 
                    AddVariablesResultDelegate resultDelegate);

 

Both functions can be called repeatedly for the same subscription. As with IDataAccessService, the variables are addressed via their complete name.

Examples for variable addressing

Variable addressing Description
ComponentName-1/ProgramName-1.Variable_Name Program variable
ComponentName-1/Global_Variable_Name Component variable (global variable)
ComponentName-1/ProgramName-1:Array_Variable_Name[index] Array element
ComponentName-1/ProgramName-1:Struct_Variable_Name.Element1.Leaf Structure element (leaf)
ComponentName-1/ProgramName-1:Struct_Variable_Name.Element1.LeafArray[index] Array element from a structure
ComponentName-1/ProgramName-1:Array_Variable_Name[startIndex:endIndex] Extract of array elements

 

Return values in case of an error

Once a variable was added successfully to the subscription, the DataAccessError::None value is returned.

In case of an error, the following return values might be returned:

Return Value Description
None No error
NotExists The variable does not exist in the system.
NotAuthorized The user does not have sufficient authorization.
TypeMismatch During writing, the value type is not suitable for the respective port.
PortNameSyntaxError The port address is syntactically incorrect.
PortNameSemanticError The port address is semantically incorrect.
IndexOutOfRange The address contains an array index that is outside the array.
NotImplemented The variable or service function has not yet been implemented.
NotSupported The variable is not supported.
CurrentlyUnavailable The service is currently unavailable.
UnvalidSubscription The specified subscription was not found or is invalid.

 

Supported data types 

The following types are currently supported:

  • Primitive
  • DateTime
  • String (currently, only StaticString and IecString are supported)
  • StaticString
  • IecString
  • Enum
  • Struct
  • Array

Subscribe/unsubscribe

Once you created a subscription and configured it with variables, you can activate it with Subscribe.

To activate the subscription, call the following service function:

DataAccessError     Subscribe(uint32 subscriptionId, uint64 sampleRate);

With call of the function, copying of the variable starts (exception: for the DirectRead type, no data is automatically recorded).

Use the sampleRate parameter to indicate in which time grid the values are to be recorded. The sampleRate can only be a multiple of the interval time of a cyclic ESM task. If a sampleRate that does not correspond to this interval time is passed on, the SubscriptionService rounds the value to the next faster value.

Example:
Variables from task A and task B are to be recorded:

  • Interval time for task A: 10 ms
  • Interval time for task B: 8 ms

If you indicate 50 ms for sampleRate, the following is actually recorded:

  • Variables from task A at 50 ms (each fifth cycle)
  • Variables from task B at 48 ms (each sixth cycle)

If you indicate value 0 for sampleRate, all data is recorded in the interval of the respective task.

  • Variables from task A at 10 ms
  • Variables from task B at 8 ms

 

When a subscription was started, you can pause it via Unsubscribe.

For this, call the following service function:

DataAccessError     Unsubscribe(uint subscriptionId);

 

If a subscription pauses, no new data is recorded. Existing data is available in the subscription.

To restart recording, call Subscribe again.

GetVariableInfos

This service function shows which variables are currently recorded. Therefore, the function only returns information once Subscribe is called.

The function only returns information on variable sorting. This information is decisive for reading the data. Each subscription internally sorts the variables, e.g., by assignment to the ESM task. The data of added variables is therefore not read in the order in which is was added. The Read functions only provide the raw values of the variables but do not give information on which variable the value is assigned to. At this point, service function information is the only option to assign the values to the respective variables. Variable information is returned in the same order as data for the Read functions. Therefore, variable information has to be read before the Read functions of a subscription are called for the first time.

A matching Info function is available for each Read function.

To query all currently recorded variables, call the following function:

DataAccessError     GetVariableInfos(uint32 subscriptionId, 
                    GetVariableInfosVariableInfoDelegate variableInfoDelegate);

To query the data, call the associated Read function:

DataAccessError     ReadValues(uint32 subscriptionId, 
                    ReadTimeStampedValuesValuesDelegate valuesDelegate)

If, in addition to the variable values, you also require the time stamps of the value acquisition, call the following function:

DataAccessError     GetTimeStampedVariableInfos(uint32 subscriptionId, 
                    GetTimeStampedVariableInfosVariableInfoDelegate variableInfoDelegate)

Information on time stamps is returned in addition to information on the variables. A variable with the name timestamp and of the Arp.Plc:DataType.Int64 data type is always returned as the first element of an ESM task. This is followed by all information of the variable that is associated with the ESM task and can be assigned to the time stamp. If there are variables of several ESM tasks in the subscription, an additional time stamp is returned for each task, which is followed by the associated information. Here, information is also returned in the same order as the data that is returned with the following Read function:

DataAccessError     GetRecordInfos(uint32 subscriptionId, 
                    GetRecordInfosRecordInfosDelegate recordInfosDelegate);

ReadValues

Once you have started the recording of a subscription, you can query the acquired data. Different Read functions are available for this. The Read functions only return the variable values. The values are not assigned to the respective variable. For assigning the values to the respective variables, you have to call the corresponding Info function once (see GetVariableInfos).

The following Read service functions are available:

DataAccessError     ReadValues(uint32 subscriptionId, 
                    ReadTimeStampedValuesValuesDelegate valuesDelegate)

 

This function returns all values in a static order. The GetVariableInfos() function is used for assigning the variables.

Example:

  • Added variables from task A: a1, a2
  • Added variables from task B: b1

ReadValues:

Object[]

a2
a1
b1

The following function returns all values in a static order, including the associated time stamps:

DataAccessError     ReadTimeStampedValues(uint32 subscriptionId, 
                    ReadTimeStampedValuesValuesDelegate valuesDelegate);

 

The time stamps are always located before the associated variable values. The number of time stamps always corresponds to the number of ESM tasks the variables originate from. By means of the DateTime class, which is defined in the Arp namespace and in the Arp/System/Core/DateTime.hpp header file, the value of the timestamp variable can be converted into a time stamp. The GetTimeStampedVariableInfos() function is used for assigning the variables.

Example:

  • Added variables from task A: a1, a2
  • Added variables from task B: b1

ReadValues:

Object[]

Timestamp task A
a2
a1
Timestamp task B
b1

The following function returns all values packed in records:

DataAccessError     ReadRecords(uint32 subscriptionId, uint16 count, 
                    ReadRecordsRecordsDelegate recordsDelegate);

A record, also called data record, contains only the variable data from an ESM task and the associated time stamp. The time stamps are always located before the associated variable values. The variable order is always static and does not change during operation. An ESM task record is created for each ESM task. It contains all corresponding data records of the respective ESM task. Depending on the subscription configuration, an ESM task record can contain several data records.

E.g.: A subscription of the Recording type with a task interval of 100 ms and a capacity of 10 returns 10 data records after one second. The time stamps are 100 ms apart. However, a subscription of the HighPerformance, RealTime, or DirectRead type only always returns one data record. By means of the Read function, you can read all subscription data of the Recording type at once.

In addition to the static order of variables in the data records, the order of the ESM task records is static, too. By means of the GetTimeStampedVariableInfos() function, each value can be assigned a variable. The variable information describes exactly the first ESM task record and all data records contained therein, from the first time stamp to the final variable information associated with this time stamp.

By means of the DateTime class, which is defined in the Arp namespace and in the Arp/System/Core/DateTime.hpp header file, the value of the timestamp variable in the respective data records can be converted into a time stamp.

 

Example:

  • Added variables from task A: a1, a2
  • Added variables from task B: b1

 

  • Task A recorded 2 records.
  • Task B recorded 1 record.

ReadValues:

Object[]

Timestamp task A
a2
a1
Timestamp task B
b1

 

ReadRecords:

Object[] (ESM task records)

Objects[] (ESM task record A)

Objects[](data record cycle 1)

Timestamp
a2
a1

Objects[](data record cycle 2)

Timestamp
a2
a1

Objects[] (ESM task record B)

Objects[](data record cycle 1)

Timestamp
b1

Changing a subscription

You can modify two things of an existing subscription:

  • The sampling interval used by the subscription to record data.
  • The variables that are to be recorded.

The subscription type can only be changed if you delete the subscription and create it again.

To change the sample rate, proceed as follows:

  • Stop the subscription by calling Unsubscribe.
  • Execute the subscription by calling Subscribe and entering the new sample rate as the parameter.

Several functions are available for changing the variables to be recorded:

  • To delete a variable from a subscription, call the following function:
    DataAccessError     RemoveVariable(uint32 subscriptionId, 
                        const RscString<512>& variableName),
  • To add a variable to a subscription, call the following function:
    DataAccessError     AddVariable(uint32 subscriptionId, 
                        const RscString<512>& variableName)
  • To add several variables to a subscription, call the following function:
    void                AddVariables(uint32 subscriptionId, 
                        AddVariablesVariableNamesDelegate variableNamesDelegate, 
                        AddVariablesResultDelegate resultDelegate);

The changes can be made during operation and also to a subscription that is currently recording. To apply the changes, execute Resubscribe.

  • To execute Resubscribe, call the following service function:
    DataAccessError     Resubscribe(uint32 subscriptionId, uint64 sampleRate);

Once Resubscribe was called, the changes are applied to the subscription. As the internal memories are rebuilt, data loss might occur.

Deleting a subscription

If a subscription is no longer required, you have to delete it. If you do not delete the subscription, is will be retained until the next restart of the PLC application (warm or cold restart).

  • To delete a subscription, call the following function:
    DataAccessError     DeleteSubscription(uint32 subscriptionId)

The internally reserved memory is released and the subscription ID becomes invalid.