Defining and configuring an application object database of controlling 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 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/bit offset. If required the commands may have the same byte/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
IOA

Group
configuration parameter

Base time
multiplier

PLC
process data area

Byte
offset

Bit
offset

Process data width

in the TwinCAT PLC

M_SP_NA_1

100

General interrogation group global

0

Memory

100

0

1 bit

M_SP_NA_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_NA_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_NA_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_NA_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_NA_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_NB_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_NC_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

0

Memory

800

0

4 Byte

M_IT_NA_1

801

General counter interrogation

0

Memory

804

0

4 Byte

M_IT_TB_1

802

General counter interrogation

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 MAIN:

PROGRAM MAIN
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

F_iecCreateTableHnd

Initializes the hash table handle

F_iecAddTableEntry

Configures and adds a new hash table entry

F_iecRemoveTableEntry

Removes a hash table entry

F_iecLookupTableEntry

Checks if a special hash table entry exists

F_iecGetPosOfTableEntry

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 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