Applikationsobjekt-Datenbank der Zentralstation definieren und konfigurieren
Applikationsobjekte = Single Points, Double Points, Measured Values, Short Floating Point Values usw.
In diesem Beispiel wurden die Befehle so konfiguriert, dass die Prozessdaten in Steuerungsrichtung (Befehle) im gleichen Speicherbereich aber auf einem anderen Byte-/Bit-Offset wie die Daten der Information in Überwachungsrichtung liegen. Sie können aber auch die Befehle auf den gleichen Byte- 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). Bei einer Wertänderung von M_SP_NA_1 wird ein neues C_SC_NA_1-Kommando ausgelöst.
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_TA_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_TA_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_TA_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_TA_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_TA_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_TB_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_TC_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 und Mode A (lokal Umspeichern mit Spontanübertragung alle 15s) | 0 | Merker | 800 | 0 | 4 Byte |
M_IT_TA_1 | 801 | Generalzählerabfrage und Mode A (lokal Umspeichern mit Spontanübertragung alle 15s) | 0 | Merker | 804 | 0 | 4 Byte |
M_IT_TB_1 | 802 | Generalzählerabfrage und Mode A (lokal Umspeichern mit Spontanübertragung alle 15s) | 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 ein 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 folgende Variablen in P_MAIN_LowSpeed:
PROGRAM P_MAIN_LowSpeed
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. das Mapping-Bereich, Byte-, Bit-Offset können beliebig konfiguriert werden.
Es stehen folgende Funktionen zur Manipulation der Applikationsdatenbank zur Verfügung:
Function | Description |
---|---|
F_iecCreateTableHnd
| Initialisiert das Hash-Tabellenhandle |
F_iecAddTableEntry
| Konfiguriert und fügt einen neuen Hash-Tabelleneintrag |
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 P_MAIN_LowSpeed folgender SPS-Code hinzugefügt:
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 );
(* Measured 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 values *)
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