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 | Group-Konfigurationsparameter | Basiszeitmultiplikator | SPS-Prozessdatenbereich | Offset | Offset | 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 |
---|---|
Initialisiert das Hash-Tabellenhandle | |
Konfiguriert und fügt einen neuen Hash-Tabelleneintrag ein | |
Entfernt einen Hash-Tabelleneintrag | |
Prüft ob ein bestimmter Hash-Tabelleneintrag existiert | |
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.