Definition and configuration of application objects
Application objects mean the data types to be defined, such as single points, double points, measured values, short floating point values, etc.
In this example the commands were configured such that the process data for the commands are located in the same memory area as the information data in monitoring direction, although in a different byte and bit offset. If required the commands may have the same byte and bit offset as the information in monitoring direction.
Example:
C_SC_NA_1 with IOA = 10 on the same byte/bit offset as M_SP_NA_1 with IOA = 100 (both byte offset = 100 and bit offset = 0). In this case a change in value of the single point in the substation (M_SP_NA_1 with object address 100) would trigger a data transfer with cause of transmission <3> (spontaneous) to the control station. In the control station the new value is copied to the bit/byte offset address of the command (C_SC_NA_1 with object address 10) and triggers a command transfer to the substation.
As an example we will configure the following application objects as part of the introductory project:
ASDU identifier | Object address | Group configuration parameter | Base time multiplier | PLC process data area | Offset | Offset | Process data width in the TwinCAT PLC |
M_SP_NA_1 | 100 | General query | 0 | Flags | 100 | 0 | 1 bit |
M_SP_NA_1 | 101 | General query | 0 | Flags | 100 | 1 | 1 bit |
M_SP_TB_1 | 102 | General query | 0 | Flags | 100 | 2 | 1 bit |
M_DP_NA_1 | 200 | General query | 0 | Flags | 200 | 0 | 2 bits |
M_DP_NA_1 | 201 | General query | 0 | Flags | 200 | 2 | 2 bits |
M_DP_TB_1 | 202 | General query | 0 | Flags | 200 | 4 | 2 bits |
M_ST_NA_1 | 300 | General query | 0 | Flags | 300 | 0 | 1 byte |
M_ST_NA_1 | 301 | General query | 0 | Flags | 301 | 0 | 1 byte |
M_ST_TB_1 | 302 | General query | 0 | Flags | 302 | 0 | 1 byte |
M_BO_NA_1 | 400 | General query | 0 | Flags | 400 | 0 | 4 byte |
M_BO_NA_1 | 401 | General query | 0 | Flags | 404 | 0 | 4 byte |
M_BO_TB_1 | 402 | General query | 0 | Flags | 408 | 0 | 4 byte |
M_ME_NA_1 | 500 | General query | 0 | Flags | 500 | 0 | 2 byte |
M_ME_NA_1 | 501 | General query | 0 | Flags | 502 | 0 | 2 byte |
M_ME_TD_1 | 502 | General query | 0 | Flags | 504 | 0 | 2 byte |
M_ME_NB_1 | 600 | General query | 0 | Flags | 600 | 0 | 2 byte |
M_ME_NB_1 | 601 | General query | 0 | Flags | 602 | 0 | 2 byte |
M_ME_TE_1 | 602 | General query | 0 | Flags | 604 | 0 | 2 byte |
M_ME_NC_1 | 700 | General query | 0 | Flags | 700 | 0 | 4 byte |
M_ME_NC_1 | 701 | General query | 0 | Flags | 704 | 0 | 4 byte |
M_ME_TF_1 | 702 | General query | 0 | Flags | 708 | 0 | 4 byte |
M_IT_NA_1 | 800 | General counter query | 0 | Flags | 800 | 0 | 4 byte |
M_IT_NA_1 | 801 | General counter query | 0 | Flags | 804 | 0 | 4 byte |
M_IT_TB_1 | 802 | General counter query | 0 | Flags | 808 | 0 | 4 byte |
Commands |
|
|
|
|
|
|
|
C_SC_NA_1 | 10 | - | 0 | Flags | 2100 | 0 | 1 bit |
C_SC_NA_1 | 11 | - | 0 | Flags | 2100 | 1 | 1 bit |
C_SC_TA_1 | 12 | - | 0 | Flags | 2100 | 2 | 1 bit |
C_DC_NA_1 | 20 | - | 0 | Flags | 2200 | 0 | 2 bit |
C_DC_NA_1 | 21 | - | 0 | Flags | 2200 | 2 | 2 bit |
C_DC_TA_1 | 22 | - | 0 | Flags | 2200 | 4 | 2 bit |
C_BO_NA_1 | 40 | - | 0 | Flags | 2400 | 0 | 4 byte |
C_BO_NA_1 | 41 | - | 0 | Flags | 2404 | 0 | 4 byte |
C_BO_TA_1 | 42 | - | 0 | Flags | 2408 | 0 | 4 byte |
C_SE_NA_1 | 50 | - | 0 | Flags | 2500 | 0 | 2 byte |
C_SE_NA_1 | 51 | - | 0 | Flags | 2502 | 0 | 2 byte |
C_SE_TA_1 | 52 | - | 0 | Flags | 2504 | 0 | 2 byte |
C_SE_NB_1 | 60 | - | 0 | Flags | 2600 | 0 | 2 byte |
C_SE_NB_1 | 61 | - | 0 | Flags | 2602 | 0 | 2 byte |
C_SE_TB_1 | 62 | - | 0 | Flags | 2604 | 0 | 2 byte |
C_SE_NC_1 | 70 | - | 0 | Flags | 2700 | 0 | 4 byte |
C_SE_NC_1 | 71 | - | 0 | Flags | 2704 | 0 | 4 byte |
C_SE_TC_1 | 72 | - | 0 | Flags | 2708 | 0 | 4 byte |
Declaring a database variable
The application object database is an array variable of type ST_IEC870_5_101AODBEntry. Each array element corresponds to an application object. The array elements are not manipulated directly but through specially provided functions and a database handle (table handle). The database handle must be initialized via a F_iecCreateTableHnd function call before it can be used. During this process the array elements are linked as a hash table. With a larger number of data points the hash table enables faster access to individual data points.
The maximum number of application objects is freely selectable and is only limited by the available memory. During PLC programming you have to specify a constant maximum number. The maximum number of application objects cannot be changed at runtime. In our example 50 application objects are declared. This number is sufficient for most applications. Please note that many application objects require adequate memory and runtime resources.
Define the following variables in MAIN:
PROGRAM MAIN
VAR
AODB : ARRAY[0..49] OF ST_IEC870_5_101AODBEntry;
hTable : T_HAODBTable;
END_VAR
Configuring application objects
The required application objects are configured during program runtime. Amongst other things, the object type (M_SP_NA_1, M_DP_NA_1, M_ST_NA_1, etc.), the common ASDU address, the object address and further object parameters are defined during the configuration.
After initialization of the database handle is the application object database (database array) is empty and must be filled with the required data (data points). The configuration of the data points in the central station must correspond to the configuration of the data points in the substation! This means that data points configured in the central station must be of the same type, with the same common ASDU address and with the same information object address as in the substation. Other parameters such as the mapping range, byte and bit offset can be configured as desired.
The following functions are available for manipulating the application database:
Function | Description |
---|---|
Initializes the hash table handle | |
Configures and inserts a new hash table entry | |
Removes a hash table entry | |
Checks whether a certain hash table entry exists | |
Determines the linear position of a hash table entry |
The database handle must be transmitted to the function via VAR_IN_OUT. Configuration is usually carried out once during PLC program start-up via an Init routine.
In order to configure the application objects during program start-up, add the following PLC code in MAIN:
PROGRAM MAIN
VAR
AODB : ARRAY[0..49] OF ST_IEC870_5_101AODBEntry;
hTable : T_HAODBTable;
init : BOOL := TRUE;
initError : UDINT;
asduAddr : UDINT := 7;
END_VAR
IF init THEN
init := FALSE;
initError := F_iecCreateTableHnd( ADR( AODB ), SIZEOF( AODB ), hTable );
IF initError <> 0 THEN
ADSLOGSTR( ADSLOG_MSGTYPE_HINT OR ADSLOG_MSGTYPE_LOG,
'F_iecCreateTableHnd() error: %s',
DWORD_TO_HEXSTR( initError, 8, FALSE ) );
RETURN;
END_IF
(* Monitored Single Points *)
initError := F_iecAddTableEntry( M_SP_NA_1, asduAddr, 100, IEC870_GRP_INROGEN, 0, MAP_AREA_MEMORY, 100, 0, 0, hTable );
initError := F_iecAddTableEntry( M_SP_NA_1, asduAddr, 101, IEC870_GRP_INROGEN, 0, MAP_AREA_MEMORY, 100, 1, 0, hTable );
initError := F_iecAddTableEntry( M_SP_TB_1, asduAddr, 102, IEC870_GRP_INROGEN, 0, MAP_AREA_MEMORY, 100, 2, 0, hTable );
(* Double Points*)
initError := F_iecAddTableEntry( M_DP_NA_1, asduAddr, 200, IEC870_GRP_INROGEN, 0, MAP_AREA_MEMORY, 200, 0, 0, hTable );
initError := F_iecAddTableEntry( M_DP_NA_1, asduAddr, 201, IEC870_GRP_INROGEN, 0, MAP_AREA_MEMORY, 200, 2, 0, hTable );
initError := F_iecAddTableEntry( M_DP_TB_1, asduAddr, 202, IEC870_GRP_INROGEN, 0, MAP_AREA_MEMORY, 200, 4, 0, hTable );
(* Regulating step value *)
initError := F_iecAddTableEntry( M_ST_NA_1, asduAddr, 300, IEC870_GRP_INROGEN, 0, MAP_AREA_MEMORY, 300, 0, 0, hTable );
initError := F_iecAddTableEntry( M_ST_NA_1, asduAddr, 301, IEC870_GRP_INROGEN, 0, MAP_AREA_MEMORY, 301, 0, 0, hTable );
initError := F_iecAddTableEntry( M_ST_TB_1, asduAddr, 302, IEC870_GRP_INROGEN, 0, MAP_AREA_MEMORY, 302, 0, 0, hTable );
(* 32 bit string *)
initError := F_iecAddTableEntry( M_BO_NA_1, asduAddr, 400, IEC870_GRP_INROGEN, 0, MAP_AREA_MEMORY, 400, 0, 0, hTable );
initError := F_iecAddTableEntry( M_BO_NA_1, asduAddr, 401, IEC870_GRP_INROGEN, 0, MAP_AREA_MEMORY, 404, 0, 0, hTable );
initError := F_iecAddTableEntry( M_BO_TB_1, asduAddr, 402, IEC870_GRP_INROGEN, 0, MAP_AREA_MEMORY, 408, 0, 0, hTable );
(* Measured value, normalized value *)
initError := F_iecAddTableEntry( M_ME_NA_1, asduAddr, 500, IEC870_GRP_INROGEN, 0, MAP_AREA_MEMORY, 500, 0, 0, hTable );
initError := F_iecAddTableEntry( M_ME_NA_1, asduAddr, 501, IEC870_GRP_INROGEN, 0, MAP_AREA_MEMORY, 502, 0, 0, hTable );
initError := F_iecAddTableEntry( M_ME_TD_1, asduAddr, 502, IEC870_GRP_INROGEN, 0, MAP_AREA_MEMORY, 504, 0, 0, hTable );
(* Mesured value, scaled value *)
initError := F_iecAddTableEntry( M_ME_NB_1, asduAddr, 600, IEC870_GRP_INROGEN, 0, MAP_AREA_MEMORY, 600, 0, 0, hTable );
initError := F_iecAddTableEntry( M_ME_NB_1, asduAddr, 601, IEC870_GRP_INROGEN, 0, MAP_AREA_MEMORY, 602, 0, 0, hTable );
initError := F_iecAddTableEntry( M_ME_TE_1, asduAddr, 602, IEC870_GRP_INROGEN, 0, MAP_AREA_MEMORY, 604, 0, 0, hTable );
(* Measured value , short floating point value *)
initError := F_iecAddTableEntry( M_ME_NC_1, asduAddr, 700, IEC870_GRP_INROGEN, 0, MAP_AREA_MEMORY, 700, 0, 0, hTable );
initError := F_iecAddTableEntry( M_ME_NC_1, asduAddr, 701, IEC870_GRP_INROGEN, 0, MAP_AREA_MEMORY, 704, 0, 0, hTable );
initError := F_iecAddTableEntry( M_ME_TF_1, asduAddr, 702, IEC870_GRP_INROGEN, 0, MAP_AREA_MEMORY, 708, 0, 0, hTable );
(* Integrated totals *)
initError := F_iecAddTableEntry( M_IT_NA_1, asduAddr, 800, IEC870_GRP_REQCOGEN, 0, MAP_AREA_MEMORY, 800, 0, 0, hTable );
initError := F_iecAddTableEntry( M_IT_NA_1, asduAddr, 801, IEC870_GRP_REQCOGEN, 0, MAP_AREA_MEMORY, 804, 0, 0, hTable );
initError := F_iecAddTableEntry( M_IT_TB_1, asduAddr, 802, IEC870_GRP_REQCOGEN, 0, MAP_AREA_MEMORY, 808, 0, 0, hTable );
(* Single commands *)
initError := F_iecAddTableEntry( C_SC_NA_1, asduAddr, 10, 0, 0, MAP_AREA_MEMORY, 2100, 0, 0, hTable );
initError := F_iecAddTableEntry( C_SC_NA_1, asduAddr, 11, 0, 0, MAP_AREA_MEMORY, 2100, 1, 0, hTable );
initError := F_iecAddTableEntry( C_SC_TA_1, asduAddr, 12, 0, 0, MAP_AREA_MEMORY, 2100, 2, 0, hTable );
(* Double commands *)
initError := F_iecAddTableEntry( C_DC_NA_1, asduAddr, 20, 0, 0, MAP_AREA_MEMORY, 2200, 0, 0, hTable );
initError := F_iecAddTableEntry( C_DC_NA_1, asduAddr, 21, 0, 0, MAP_AREA_MEMORY, 2200, 2, 0, hTable );
initError := F_iecAddTableEntry( C_DC_TA_1, asduAddr, 22, 0, 0, MAP_AREA_MEMORY, 2200, 4, 0, hTable );
(* 32 bit string commands *)
initError := F_iecAddTableEntry( C_BO_NA_1, asduAddr, 40, 0, 0, MAP_AREA_MEMORY, 2400, 0, 0, hTable );
initError := F_iecAddTableEntry( C_BO_NA_1, asduAddr, 41, 0, 0, MAP_AREA_MEMORY, 2404, 0, 0, hTable );
initError := F_iecAddTableEntry( C_BO_TA_1, asduAddr, 42, 0, 0, MAP_AREA_MEMORY, 2408, 0, 0, hTable );
(* Set point, normalized values*)
initError := F_iecAddTableEntry( C_SE_NA_1, asduAddr, 50, 0, 0, MAP_AREA_MEMORY, 2500, 0, 0, hTable );
initError := F_iecAddTableEntry( C_SE_NA_1, asduAddr, 51, 0, 0, MAP_AREA_MEMORY, 2502, 0, 0, hTable );
initError := F_iecAddTableEntry( C_SE_TA_1, asduAddr, 52, 0, 0, MAP_AREA_MEMORY, 2504, 0, 0, hTable );
(* Set point, scaled valuess *)
initError := F_iecAddTableEntry( C_SE_NB_1, asduAddr, 60, 0, 0, MAP_AREA_MEMORY, 2600, 0, 0, hTable );
initError := F_iecAddTableEntry( C_SE_NB_1, asduAddr, 61, 0, 0, MAP_AREA_MEMORY, 2602, 0, 0, hTable );
initError := F_iecAddTableEntry( C_SE_TB_1, asduAddr, 62, 0, 0, MAP_AREA_MEMORY, 2604, 0, 0, hTable );
(* Set point, short floating point values *)
initError := F_iecAddTableEntry( C_SE_NC_1, asduAddr, 70, 0, 0, MAP_AREA_MEMORY, 2700, 0, 0, hTable );
initError := F_iecAddTableEntry( C_SE_NC_1, asduAddr, 71, 0, 0, MAP_AREA_MEMORY, 2704, 0, 0, hTable );
initError := F_iecAddTableEntry( C_SE_TC_1, asduAddr, 72, 0, 0, MAP_AREA_MEMORY, 2708, 0, 0, hTable );
END_IF
The associated PLC example tutorial can be downloaded here.