Defining and configuring an application object database of controling station
Here you can unpack the complete PLC sources: TutorialSample.zip
Application objects = single points, double points, measured values, short floating point values, etc.
In this example the process data in monitoring direction (commands) are placed in the same memory area but on another byte/bit offset than the data of information in control direction. But you can also place the commands to the same byte/bit offset like information in control direction.
Example:
C_SC_NA_1 with IOA = 10 on the same byte/bit offset like M_SP_NA_1 with IOA = 100 (both byte ofset = 100 and bit offset = 0). In this case a change of M_SP_NA_1 value will cause a activation of the C_SC_NA_1 command.
As an example, we will configure the following application objects as part of the introductory project:
ASDU | Object address | Group | Base time | PLC | Byte | Bit | Process data width in the TwinCAT PLC |
---|---|---|---|---|---|---|---|
M_SP_NA_1 | 100 | General interrogation group global | 0 | Memory | 100 | 0 | 1 bit |
M_SP_TA_1 | 101 | General interrogation group global | 0 | Memory | 100 | 1 | 1 Bit |
M_SP_TB_1 | 102 | General interrogation group global | 0 | Memory | 100 | 2 | 1 Bit |
M_DP_NA_1 | 200 | General interrogation group global | 0 | Memory | 200 | 0 | 2 Bits |
M_DP_TA_1 | 201 | General interrogation group global | 0 | Memory | 200 | 2 | 2 Bits |
M_DP_TB_1 | 202 | General interrogation group global | 0 | Memory | 200 | 4 | 2 Bits |
M_ST_NA_1 | 300 | General interrogation group global | 0 | Memory | 300 | 0 | 1 Byte |
M_ST_TA_1 | 301 | General interrogation group global | 0 | Memory | 301 | 0 | 1 Byte |
M_ST_TB_1 | 302 | General interrogation group global | 0 | Memory | 302 | 0 | 1 Byte |
M_BO_NA_1 | 400 | General interrogation group global | 0 | Memory | 400 | 0 | 4 Byte |
M_BO_TA_1 | 401 | General interrogation group global | 0 | Memory | 404 | 0 | 4 Byte |
M_BO_TB_1 | 402 | General interrogation group global | 0 | Memory | 408 | 0 | 4 Byte |
M_ME_NA_1 | 500 | General interrogation group global | 0 | Memory | 500 | 0 | 2 Byte |
M_ME_TA_1 | 501 | General interrogation group global | 0 | Memory | 502 | 0 | 2 Byte |
M_ME_TD_1 | 502 | General interrogation group global | 0 | Memory | 504 | 0 | 2 Byte |
M_ME_NB_1 | 600 | General interrogation group global | 0 | Memory | 600 | 0 | 2 Byte |
M_ME_TB_1 | 601 | General interrogation group global | 0 | Memory | 602 | 0 | 2 Byte |
M_ME_TE_1 | 602 | General interrogation group global | 0 | Memory | 604 | 0 | 2 Byte |
M_ME_NC_1 | 700 | General interrogation group global | 0 | Memory | 700 | 0 | 4 Byte |
M_ME_TC_1 | 701 | General interrogation group global | 0 | Memory | 704 | 0 | 4 Byte |
M_ME_TF_1 | 702 | General interrogation group global | 0 | Memory | 708 | 0 | 4 Byte |
M_IT_NA_1 | 800 | General counter interrogation and mode A (local freeze with spontaneous transfer every 15s) | 0 | Memory | 800 | 0 | 4 Byte |
M_IT_TA_1 | 801 | General counter interrogation and mode A (local freeze with spontaneous transfer every 15s) | 0 | Memory | 804 | 0 | 4 Byte |
M_IT_TB_1 | 802 | General counter interrogation and mode A (local freeze with spontaneous transfer every 15s) | 0 | Memory | 808 | 0 | 4 Byte |
Commands |
|
|
|
|
|
|
|
C_SC_NA_1 | 10 | - | 0 | Memory | 2100 | 0 | 1 Bit |
C_SC_NA_1 | 11 | - | 0 | Memory | 2100 | 1 | 1 Bit |
C_SC_TA_1 | 12 | - | 0 | Memory | 2100 | 2 | 1 Bit |
C_DC_NA_1 | 20 | - | 0 | Memory | 2200 | 0 | 2 Bit |
C_DC_NA_1 | 21 | - | 0 | Memory | 2200 | 2 | 2 Bit |
C_DC_TA_1 | 22 | - | 0 | Memory | 2200 | 4 | 2 Bit |
C_BO_NA_1 | 40 | - | 0 | Memory | 2400 | 0 | 4 Byte |
C_BO_NA_1 | 41 | - | 0 | Memory | 2404 | 0 | 4 Byte |
C_BO_TA_1 | 42 | - | 0 | Memory | 2408 | 0 | 4 Byte |
C_SE_NA_1 | 50 | - | 0 | Memory | 2500 | 0 | 2 Byte |
C_SE_NA_1 | 51 | - | 0 | Memory | 2502 | 0 | 2 Byte |
C_SE_TA_1 | 52 | - | 0 | Memory | 2504 | 0 | 2 Byte |
C_SE_NB_1 | 60 | - | 0 | Memory | 2600 | 0 | 2 Byte |
C_SE_NB_1 | 61 | - | 0 | Memory | 2602 | 0 | 2 Byte |
C_SE_TB_1 | 62 | - | 0 | Memory | 2604 | 0 | 2 Byte |
C_SE_NC_1 | 70 | - | 0 | Memory | 2700 | 0 | 4 Byte |
C_SE_NC_1 | 71 | - | 0 | Memory | 2704 | 0 | 4 Byte |
C_SE_TC_1 | 72 | - | 0 | Memory | 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 variable in P_MAIN_LowSpeed:
PROGRAM P_MAIN_LowSpeed
VAR
AODB : ARRAY[0..49] OF ST_IEC870_5_101AODBEntry;
hTable : T_HAODBTable;
END_VAR
Configuring application objects
The object type (M_SP_NA_1, M_DP_NA_1, M_ST_NA_1 etc.), the object address and further object parameters are specified during configuration of the individual application objects.
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 data point configuration for the central station must match the data point configuration for the substation, i.e. in the central station data points of the same type and with the same common ASDU address and the same information object address must be configured as in the substation. Other parameters such as the mapping range and byte/bit offset can be configured as required.
The following functions are available for manipulating the application database:
Function | Description |
---|---|
Initializes the hash table handle | |
Configures and adds a new hash table entry | |
Removes a hash table entry | |
Checks if a special hash table entry exists | |
Determines the linear position of a hash table entry |
The required application objects are configured during program runtime. The data base handle is transferred to the function via VAR_IN_OUT. Configuration is usually carried out once during PLC program start-up via an Init routine.
To configure the application objects during program start-up, add the following PLC code in P_MAIN_LowSpeed:
PROGRAM P_MAIN_LowSpeed
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