ADS-Kommunikation zwischen zwei SPS

Niels Herrmann

Level-2
Beiträge
5
Reaktionspunkte
0
Zuviel Werbung?
-> Hier kostenlos registrieren
Hallo zusammen,

ich habe zwei Fragen zum Thema ADS-Kommunikation.

Ich möchte von einer SPS (Port851) auf eine andere SPS (Port801) Lesend und Schreiben zugreifen.
Bevor ich auf eine Variable im Zielsystem zugreife würde ich gerne prüfen ob die Größe der Zielvariable mit der Quellvariable übereinstimmt. Ich möchte damit verhindern das man ausersehen in Speicherbereiche Schreibt die außerhalb der Zielvariable liegen. Gibt es da eine Möglichkeit auf die Größe und den Datentyp der Zielvariable zu schließen?
Ich habe mal gehört das man über den Variablen-Handle auch Informationen über den Datentyp und dessen Größe bekommt.

Die Zweite Frage Bezieht sich auf den ADS-Systemdienst "GET_SYMHANDLE_BYNAME". In welchem Format übergebe ich den ADSReadWrite den Variablennamen. Kann ich direkt auf eine String Variable verweise oder muss das ein anderer Datentyp sein? Reicht das wenn man nur den Namen übergibt ? oder muss der Pfad übergeben werden? z.B. "Main.myVar" , "GVL.myVar" oder "Main.FBxy.myVar"?
Ich arbeite das erste mal mit dem Thema und habe da noch keine Erfahrung.

Gibt es zu dem Thema ADS ein Dokumentation, Systemhandbuch o.ä. ? Ich finde das die Informationen in der Infosys recht unpräzise und sehr verstreut sind.
 
Über Netzwerkvariablen habe ich mir auch schon gendanken gemacht. Allerdings soll das Zielsystem austauschbar sein. Es gibt 4 nahezu identische Zielsysteme die da eingesetzt werden können. daher wollte ich das Zielsystem unangetastet lassen.

Die Verbindung sowohl per Handle als auch per Adresse habe ich zum laufen bekommen.
Ich habe festgestellt das Latenz recht deutlich schwankt. Zwischen Variable im Zielsystem Schreiben und Lesen Habe ich eine Laufzeit von 40-80ms
Das ist für meinen Anwendungsfall aber im akzeptierbaren Rahmen.

Ich warte noch auf eine Antwort von Beckhoff welche Informationen ich aus dem Variablen-Handle ziehen kann
 
Ich habe jetzt eine Antwort bekommen. Über die IndexGruppe 16#F009 und IndexOffset kann mit einem ADSReadWrite auf die Symbol-Info einer Variable zuggegriffen werden.


FUNCTION_BLOCK FB_GetSymbolInfo
VAR_INPUT
bEnable : BOOL;
netID : T_AmsNetId;
port : T_AmsPort;
SymbolName : STRING(255);
END_VAR
VAR
adsReadWrite : ADSRDWRT;
nstate : INT := 0;
rawSymbolInfo : ARRAY[0..4095] OF BYTE;
nCounter : INT :=0;
nextEnding : INT :=0;
Trig : R_TRIG;
END_VAR

VAR_OUTPUT
symbolInfo : AdsSymbol;
bBusy : BOOL;
END_VAR

CASE nstate OF
0:
Trig(CLK:=bEnable);

IF Trig.Q THEN
nstate := 10;
END_IF

adsReadWrite(
NETID :=netID ,
PORT := port,
IDXGRP :=16#F009 ,
IDXOFFS :=16#0 ,
WRITELEN:= Len(SymbolName),
READLEN := SIZEOF(rawSymbolInfo),
SRCADDR := ADR(SymbolName),
DESTADDR:= ADR(rawSymbolInfo),
WRTRD := TRUE,
TMOUT := ,
BUSY => ,
ERR => ,
ERRID => );

10:
adsReadWrite();
IF NOT adsReadWrite.BUSY AND NOT adsReadWrite.ERR THEN
nstate := 20;
adsReadWrite(WRTRD := FALSE);
END_IF
20:

MEMCPY(ADR(symbolInfo.header.entryLength),ADR(rawSymbolInfo[0]),4);
MEMCPY(ADR(symbolInfo.header.iGroup),ADR(rawSymbolInfo[4]),4);
MEMCPY(ADR(symbolInfo.header.iOffs),ADR(rawSymbolInfo[8]),4);
MEMCPY(ADR(symbolInfo.header.size),ADR(rawSymbolInfo[12]),4);
MEMCPY(ADR(symbolInfo.header.dataType),ADR(rawSymbolInfo[16]),4);
MEMCPY(ADR(symbolInfo.header.flags),ADR(rawSymbolInfo[20]),4);
MEMCPY(ADR(symbolInfo.header.nameLength),ADR(rawSymbolInfo[24]),2);
MEMCPY(ADR(symbolInfo.header.typeLength),ADR(rawSymbolInfo[26]),2);
MEMCPY(ADR(symbolInfo.header.commentLength),ADR(rawSymbolInfo[28]),2);



// ASCII-String aus Byte-Array erzeugen und name zuweisen
symbolInfo.name := '';
FOR nCounter := 0 TO symbolInfo.header.nameLength - 1 DO
symbolInfo.name := CONCAT(symbolInfo.name, F_TOCHR(rawSymbolInfo[30 + nCounter]));
END_FOR

nextEnding := 30 + symbolInfo.header.nameLength + 1;

symbolInfo.typeName := '';
FOR nCounter := 0 TO symbolInfo.header.typeLength - 1 DO
symbolInfo.typeName := CONCAT(symbolInfo.typeName, F_TOCHR(rawSymbolInfo[nextEnding + nCounter]));
END_FOR

nextEnding := nextEnding + symbolInfo.header.typeLength + 1;

symbolInfo.comment := '';
FOR nCounter := 0 TO symbolInfo.header.commentLength - 1 DO
symbolInfo.comment := CONCAT(symbolInfo.comment, F_TOCHR(rawSymbolInfo[nextEnding + nCounter]));
END_FOR


TYPE AdsSymbolHeader :
STRUCT
entryLength : UDINT := 0; // length of complete symbol entry
iGroup : UDINT := 0; // indexGroup of symbol: input, output etc.
iOffs : UDINT := 0; // indexOffset of symbol
size : UDINT := 0; // size of symbol ( in bytes, 0 = bit )
dataType : UDINT := 0; // adsDataType of symbol
flags : UDINT := 0; // see ADSSYMBOLFLAG_*
nameLength : UINT := 0; // length of symbol name (null terminating character not counted)
typeLength : UINT := 0; // length of type name (null terminating character not counted)
commentLength : UINT := 0; // length of comment (null terminating character not counted)
END_STRUCT
END_TYPE

TYPE AdsSymbol :
STRUCT
header : AdsSymbolHeader;
name : STRING(255) := '';
typeName : STRING(255) := '';
comment : STRING(255) := '';
END_STRUCT
END_TYPE
 
Ich würde mir das Leben einfacher machen und die Tc2_Dataexchange-Bibliothek nutzen.
Inhaltlich wird über den Symbolname der Indexoffset/Indexgroup der Variable geholt.

Klar - du musst sicher sein das die Datenlänge gleich ist (Achtung Spoiler: Bei Strukturen beachten das TC3 ein 8 Byte Alignment per Default fährt, in TC2 unterscheidet es sich je nach SPS-Typ. x68 ist 1 Byte aligned, ARM=4 Byte Aligned).
Man sollte also die relevanten Strukturen entsprechend sauber aufbauen damit nicht irgendwann doch mal ein upsala passiert.

Und die Abfrage mit 0xF009 ist im Baustein PLC_ReadSymInfoByNameEx verbaut.
Ob das aber den Aufwand lohnt?? Das musst du entscheiden.
 
Hab keine direkte Antwort auf die ursprüngliche Fragestellung, aber einen Vorschlag zum Aufbau der ADS-Kommunikation zwischen zwei CX mit Twincat2 bzw. Twincat3:

Ich baue die Kommunikation normalerweise 2 x lesend anstatt 1 x lesend/schreibend auf.
Jede CX hat eine ST_ADS_DATA auf dem Merkerbyte 1000 liegen, auf das die andere lesend zugreift.
Die Struktur wird einseitig erstellt (Byte alignment beachten) und beschrieben, dann auf das andere System kopiert und dort mittels ADSRead gelesen.

So ist die Adresse von vornherein fix, der nötige offset ergibt sich mit SIZEOF().

Das Standardprozedere hat den Vorteil, dass es im Zweifelsfall in 5 Minuten eingebaut ist, und ich nie versehentlich auf falsche Speicherbereiche zugreifen kann.
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Schau doch mal hier vorbei, für mich ist ADS zwischen zwei Beckhoff Systemen Ultra easy.
Um diese Inhalte anzuzeigen, benötigen wir die Zustimmung zum Setzen von Drittanbieter-Cookies.
Für weitere Informationen siehe die Seite Verwendung von Cookies.
 
Hallo Niels,

falls du eine ganz einfache Lösung suchst, hätte ich dir einen FB (den gleichen FB gibt's auch als FB_MultiAdsSymbolWrite).

Key-Features:
  • Kommunikation ist sowohl zu Tc2 wie auch Tc3 Runtimes möglich
  • Link wird (auch ohne R/W-Requests) auf Funktionsfähigkeit überwacht
  • Symbolname von bis zu 256 Variablen via Array aData spezifizierbar
  • R/W periodisch (Intervall kann angegeben werden), oder getriggert
  • Überwachung externe Steuerung auf OnlineChanges und automatische
    Aktualisierung der Symbol-Adressen wenn ein OnlineChange erkannt
    wurde.
Beispiel-Screenshot:
1729059989306.png

Gruss, Peter
 
Zurück
Oben