Applikationsobjekte definieren und konfigurieren

Mit Applikationsobjekten sind die zu definierenden Datentypen wie Single Points, Double Points, Measured Values, Short Floating Point Values usw. gemeint.

In diesem Beispiel wurden die Befehle so konfiguriert, dass die Prozessdaten der Befehle im gleichen Speicherbereich aber auf einem anderen Byte- und Bit-Offset wie die Daten der Information in Überwachungsrichtung liegen. Sie können aber auch die Befehle auf den gleichen Byte- und Bit-Offset wie die Information in Überwachungsrichtung legen.

Beispiel:
C_SC_NA_1 mit IOA = 10 auf den gleichen Byte- und Bit-Offset wie M_SP_NA_1 mit IOA = 100 (beide Byte-Offset = 100 und Bit-Offset = 0). In diesem Fall würde eine Wertänderung des Single-Points in der Unterstation (M_SP_NA_1 mit der Objektadresse 100) eine Datenübertragung mit der Übertragungsursache <3> (Spontan) zur Leitstation auslösen. In der Leitstation wird der neue Wert auf die Bit- und Byte-Offsetadresse des Befehls (C_SC_NA_1 mit der Objektadresse 10) kopiert und löst eine Befehlsübertragung zur Unterstation aus.

Als Beispiel konfigurieren wir in dem Einführungsprojekt folgende Applikationsobjekte:

ASDU identifier

Objektadresse
IOA

Group-Konfigurationsparameter

Basiszeitmultiplikator

SPS-Prozessdatenbereich

Offset
Byte

Offset
Bit

Prozessdatenbreite

in der TwinCAT SPS

M_SP_NA_1

100

Generalabfrage

0

Merker

100

0

1 Bit

M_SP_NA_1

101

Generalabfrage

0

Merker

100

1

1 Bit

M_SP_TB_1

102

Generalabfrage

0

Merker

100

2

1 Bit

M_DP_NA_1

200

Generalabfrage

0

Merker

200

0

2 Bits

M_DP_NA_1

201

Generalabfrage

0

Merker

200

2

2 Bits

M_DP_TB_1

202

Generalabfrage

0

Merker

200

4

2 Bits

M_ST_NA_1

300

Generalabfrage

0

Merker

300

0

1 Byte

M_ST_NA_1

301

Generalabfrage

0

Merker

301

0

1 Byte

M_ST_TB_1

302

Generalabfrage

0

Merker

302

0

1 Byte

M_BO_NA_1

400

Generalabfrage

0

Merker

400

0

4 Byte

M_BO_NA_1

401

Generalabfrage

0

Merker

404

0

4 Byte

M_BO_TB_1

402

Generalabfrage

0

Merker

408

0

4 Byte

M_ME_NA_1

500

Generalabfrage

0

Merker

500

0

2 Byte

M_ME_NA_1

501

Generalabfrage

0

Merker

502

0

2 Byte

M_ME_TD_1

502

Generalabfrage

0

Merker

504

0

2 Byte

M_ME_NB_1

600

Generalabfrage

0

Merker

600

0

2 Byte

M_ME_NB_1

601

Generalabfrage

0

Merker

602

0

2 Byte

M_ME_TE_1

602

Generalabfrage

0

Merker

604

0

2 Byte

M_ME_NC_1

700

Generalabfrage

0

Merker

700

0

4 Byte

M_ME_NC_1

701

Generalabfrage

0

Merker

704

0

4 Byte

M_ME_TF_1

702

Generalabfrage

0

Merker

708

0

4 Byte

M_IT_NA_1

800

Generalzählerabfrage

0

Merker

800

0

4 Byte

M_IT_NA_1

801

Generalzählerabfrage

0

Merker

804

0

4 Byte

M_IT_TB_1

802

Generalzählerabfrage

0

Merker

808

0

4 Byte

Commands

 

 

 

 

 

 

 

C_SC_NA_1

10

-

0

Merker

2100

0

1 Bit

C_SC_NA_1

11

-

0

Merker

2100

1

1 Bit

C_SC_TA_1

12

-

0

Merker

2100

2

1 Bit

C_DC_NA_1

20

-

0

Merker

2200

0

2 Bit

C_DC_NA_1

21

-

0

Merker

2200

2

2 Bit

C_DC_TA_1

22

-

0

Merker

2200

4

2 Bit

C_BO_NA_1

40

-

0

Merker

2400

0

4 Byte

C_BO_NA_1

41

-

0

Merker

2404

0

4 Byte

C_BO_TA_1

42

-

0

Merker

2408

0

4 Byte

C_SE_NA_1

50

-

0

Merker

2500

0

2 Byte

C_SE_NA_1

51

-

0

Merker

2502

0

2 Byte

C_SE_TA_1

52

-

0

Merker

2504

0

2 Byte

C_SE_NB_1

60

-

0

Merker

2600

0

2 Byte

C_SE_NB_1

61

-

0

Merker

2602

0

2 Byte

C_SE_TB_1

62

-

0

Merker

2604

0

2 Byte

C_SE_NC_1

70

-

0

Merker

2700

0

4 Byte

C_SE_NC_1

71

-

0

Merker

2704

0

4 Byte

C_SE_TC_1

72

-

0

Merker

2708

0

4 Byte

Datenbankvariable deklarieren

Die Applikationsobjekt-Datenbank ist eine Array-Variable vom Typ ST_IEC870_5_101AODBEntry. Jedes Array-Element entspricht einem Applikationsobjekt. Die Array-Elemente werden aber nicht direkt, sondern nur mit Hilfe der speziell dafür zur Verfügung gestellten Funktionen und einem Datenbank-Handle (Tabellen-Handle) manipuliert. Das Datenbank-Handle muss vor der Benutzung durch einen einmaligen F_iecCreateTableHnd-Funktionsaufruf initialisiert werden. Dabei werden auch die Array-Elemente miteinander als Hash-Tabelle verknüpft. Bei einer größeren Anzahl der Datenpunkte ermöglicht die Hash-Tabelle einen schnelleren Zugriff auf einen einzelnen Datenpunkt.

Die maximale Anzahl der Applikationsobjekte ist frei wählbar und nur durch den verfügbaren Speicher begrenzt. Sie müssen sich auf eine konstante maximale Anzahl während der SPS-Programmierung festlegen. Zur Laufzeit kann die maximale Anzahl der Applikationsobjekte nicht mehr verändert werden. In unserem Beispiel werden 50 Applikationsobjekte deklariert. Diese Anzahl reicht für die meisten Anwendungen aus. Beachten Sie, dass sehr viele Applikationsobjekte auch entsprechend viel Speicher und Laufzeit benötigen.

Definieren Sie die folgenden Variablen in MAIN:

PROGRAM MAIN
VAR
AODB : ARRAY[0..49] OF ST_IEC870_5_101AODBEntry; 
hTable : T_HAODBTable;
END_VAR

Applikationsobjekte konfigurieren

Die Konfiguration der gewünschten Applikationsobjekte wird zur Programmlaufzeit durchgeführt. Während der Konfiguration werden unter anderem der Objekt-Typ (M_SP_NA_1, M_DP_NA_1, M_ST_NA_1 usw.), die gemeinsame ASDU-Adresse, die Objekt-Adresse und weitere Objekt-Parameter festgelegt.

Nach der Initialisierung des Datenbank-Handles ist die Applikationsobjekt-Datenbank (Datenbank-Array) leer und muss mit den gewünschten Daten (Datenpunkten) gefüllt werden. Die Konfiguration der Datenpunkte der Zentralstation muss der Konfiguration der Datenpunkte in der Unterstation entsprechen! D.h. in der Zentralstation müssen Datenpunkte vom gleichen Typ, gleicher gemeinsamen ASDU-Adresse und mit der gleichen Informationsobjekt-Adresse wie in der Unterstation konfiguriert werden. Andere Parameter wie z.B. der Mapping-Bereich, Byte-, Bit-Offset können beliebig konfiguriert werden.

Es stehen folgende Funktionen zur Manipulation der Applikationsdatenbank zur Verfügung:

Funktion

Beschreibung

F_iecCreateTableHnd

Initialisiert das Hash-Tabellenhandle

F_iecAddTableEntry

Konfiguriert und fügt einen neuen Hash-Tabelleneintrag ein

F_iecRemoveTableEntry

Entfernt einen Hash-Tabelleneintrag

F_iecLookupTableEntry

Prüft ob ein bestimmter Hash-Tabelleneintrag existiert

F_iecGetPosOfTableEntry

Ermittelt die lineare Position eines Hash-Tabelleneintrags

Das Datenbank-Handle muss an die Funktion per VAR_IN_OUT übergeben werden. Im Regelfall wird die Konfiguration beim SPS-Programmstart einmalig in einer Init-Routine durchgeführt.

Um die Applikationsobjekte beim Programmstart zu konfigurieren wird in MAIN folgender SPS-Code hinzugefügt:

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

Das zugehörige Tutorial SPS-Beispiel kann hier heruntergeladen werden.