Step 7 SCL - ANY als Out-Parameter

Löwensenft

Level-1
Beiträge
43
Reaktionspunkte
1
Zuviel Werbung?
-> Hier kostenlos registrieren
Hallo Leute,

ich habe im Moment folgendes Problem: Ich habe einen Baustein gebaut, der anhand verschiedener Angaben einen ANY-Pointer baut:
Code:
FC240 : 

Description
    

Parameter
000.0: ROOTNODE: STRUCT
000.0:     IN: STRUCT
000.0:         InDB: INT := 0 //DB-Nummer
002.0:         InOffset: INT := 0 //Start-Byte
004.0:         InLength: INT := 0 //Anzahl Bytes
006.0:     OUT: STRUCT
006.0:         OutAny: ANY
016.0:     TEMP: STRUCT


AWL-Code
Netzwerk 1 : 

      L     P##OutAny
      LAR1  
      L     W#16#10       //SyntaxID. bei S7 immer 10
      T     B[AR1,P#0.0]
      L     W#16#2        //Typ BYTE
      T     B[AR1,P#1.0]
      L     #InLength     //Anzahl Bytes
      T     W[AR1,P#2.0]
      L     #InDB         //Quell-DB
      T     W[AR1,P#4.0]
      L     #InOffset     //Anfang der Quelle
      SLD   3
      T     D[AR1,P#6.0]
      L     B#16#84       //Speicherbereich (hier DB)
      T     B[AR1,P#6.0]

Soweit so gut. Tut was es soll. Allerdings rufe ich diesen FC aus einem SCL-FC auf. Ich habe eine temporäre Variable tmpAny vom Typ ANY erstellt und rufe den Baustein bspw. folgendermaßen auf:
Code:
FC_GetAny(InDB := 1, InOffset := 4711, InLength := 12, OutAny := tmpAny);

tmpAny soll nun eben auf die entsprechenden Daten im DB1 zeigen. Grund: Ich möchte im Anschluss den tmpAny an BLKMOV übergeben.

Wenn ich mir allerdings den AWL-Code anschaue, der generiert wird, sieht dieser wie folgt aus:
Code:
      L     #InPMFIFONum
      T     LW86
      L     LD72
      T     LD88
      L     LD76
      T     LD92
      L     LW80
      T     LW96
      UC    "FC_GetAny"
            P#V 86.0
            P#V 38.0
            P#V 34.0
            P#V 88.0

Zur Erklärung der Adressen:
V 86.0 = Wert von "InPMFIFONum"
V 38.0 = tmpByteOffset
V 34.0 = tmpLength
V 88.0 = Wert von tmpAny

Das heißt er kopiert erst den (leeren) tmpAny in ungenutzten Lokalspeicher und ruft damit dann FC_GetAny auf. Folglich liegen die Informationen, die GetAny schreibt, im Nirvana. Ein Rückübertragen in den echten tmpAny erfolgt nicht.

Ich hatte auch schon getestet was passiert, wenn im FC_GetAny OutAny in INOUT deklariert ist: Kein Unterschied. *grummel*

Gibt's hier irgendeinen Tipp wie ich die Ausgabe von FC_GetAny in tmpAny bekomme? ;)

Vielen Dank und viele Grüße
Max
 
Noch ein Nachtrag. Der Aufruf von FC_GetAny aus einem in AWL geschriebenen Baustein sieht bspw. so aus:
Code:
      CALL  "FC_GetAny" (
            InDB     := #InFIFONr,
            InOffset := #InOffset,
            InLength := #InLen,
            OutAny   := P#V 16.0)

An V 16.0 liegt tmpAny: ANY.

*noch mehr grummel*

Gruß
Max
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Problem umschiffen:
Im SCL-Baustein eine AT-Sicht auf den Any machen, und die Werte direkt auf die Elemente der Sicht schreiben. Geht in SCL sehr komfortabel.

Oder dein FC_GetAny gibt als Ausgangsparameter eine Struct zurück, die du mit der AT-Sicht aus dem SCL-Baustein verschalten müsstest. Bringt dann aber keinen wirklichen Gewinn an Übersichtlichkeit mehr (und der Baustein ist für den Aufruf aus AWL mehr oder weniger sinnlos.).
 
Code:
      L     P##OutAny
      LAR1  
      L     W#16#10       //SyntaxID. bei S7 immer 10
      T     B[AR1,P#0.0]
      L     W#16#2        //Typ BYTE
      T     B[AR1,P#1.0]
      L     #InLength     //Anzahl Bytes
      T     W[AR1,P#2.0]
      L     #InDB         //Quell-DB
      T     W[AR1,P#4.0]
      L     #InOffset     //Anfang der Quelle
      SLD   3
      T     D[AR1,P#6.0]
      L     B#16#84       //Speicherbereich (hier DB)
      T     B[AR1,P#6.0]

Soweit so gut. Tut was es soll.
Nicht gut. Tut es nicht.
Über den Stack wird nicht der ANY übergeben, sondern ein Pointer (6 Byte, P#V88.0) auf eine ANY-Variable im TEMP des Aufrufers.
"L P##OutAny" lädt nicht die Adresse des übergebenen ANY-Pointers sondern die Adresse des OUT-Parameters #OutAny.

Du mußt Deinen ANY auf die Adresse schreiben, welche Du an der Adresse P##OutAny vorfindest.

Harald
 
@PN
Es wird schon ein 10 Byte Any übergeben.
Nur hat SCL eine andere Interpretation davon wozu ein Any-Pointer als Parameter verwendet werden soll. SCL geht wohl davon aus, dass ich den Any dereferenzieren möchte und nicht den Any an sich modifizieren. Das Dereferenzieren würde auch funktionieren wenn ich in tmpAny vorher einen gültigen Any eintrage.
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Hi,

ich konnte SCL halbwegs austricksen, indem ich eine AT-Sicht (tmpAnyAt) auf tmpAny erstellt habe und tmpAnyAt an GetAny übergeben habe. Dann wurde automatisch die Adresse von tmpAny in die Lokaldaten geschrieben, die dann an GetAny übergeben wurden...

@PN/DP: Ich hatte auch schon überlegt, ob ich tatsächlich die Parameterübergabe und die Verwendung von L P##OutAny missverstanden habe. Aber wie Thomas richtigerweise schreibt interpretieren der AWL-Editor/Syntaxchecker(?) und der SCL-Compiler den ANY-Parameter unterschiedlich.

Ich habe nun allerdings - da ich vermute, dass es hierzu keine einfache und schnelle Lösung gibt - tatsächlich die AT-Sicht verwendet und schreibe halt doch wieder manuell die Daten rein.

Da kommt mir mal wieder die Frage hoch, ob es bei Siemens nicht zum guten Programmierstil dazugehört definierte Funktionen in eben solche zu verpacken. Ich will halt nicht ein und dieselbe Funktion zig mal in meinem Programm haben, wenn ich auch ne Funktion aufrufen kann, die genau das macht. Erhöht die Wartbarkeit minimalst. Naja, ich bin wohl doch ein verwöhnter Hochsprachenprogrammierer...

Wenn dennoch jemand ne Idee hat, immer her damit. Gibt es eigentlich einen Third-Party-SCL-Compiler der gescheit ist? ;) :p

Grüße
Max
 
Für meinen Geschmack ist das Verhalten des SCL Compilers logischer. So wie du das in AWL verwendest, ist eigentlich nicht das wozu ein Any-Pointer vorgesehen ist. Du musst da ja auch schon mit den Vorgänger Lokaldaten tricksen.

Theoretisch könnte jemand deinen Baustein aus AWL heraus mit:

CALL "FC_GetAny" (
InDB := #InFIFONr,
InOffset := #InOffset,
InLength := #InLen,
OutAny := P#DB1.DBX 200.0 BYTE 1000)

aufrufen. Da kommt auch nichts sinnvolles bei heraus. So wird ein Any aber üblicherweise verwendet.
 
Hmhm. Auf der einen Seite gebe ich dir Recht. Auf der anderen Seite wiedersprichst du dir ja selbst. Denn wenn ich bspw. "P#DB1.DBX 200.0 BYTE 1000" übergebe will ich ja genau an die Daten, die dort stehen, bzw. diese verändern. Ebenso geht es mir mit meinem Aufruf von FC_GetAny. So wie es im AWL interpretiert wird, ist es genau das, was der Any-Pointer machen soll. Er zeigt auf den tmpAny, der von FC_GetAny aus mit P#V 16.0 erreichbar ist. Wieso nun SCL hier meint besonders intelligent zu sein, verstehe ich nicht...

Gruß
Max
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Du wirst aber wohl nicht P#V 16.0 geschrieben haben, sondern:
Code:
      CALL  "FC_GetAny"
       InDB    :=1
       InOffset:=4711
       InLength:=12
       OutAny  :=#tmpAny

Wenn du den von dir erzeugten Any nun an deinem BLKMOV verwendest:
Code:
      CALL  "BLKMOV"
       SRCBLK :=P#DB1.DBX 0.0 BYTE 10
       RET_VAL:=MW100
       DSTBLK :=#tmpAny

gehst du auch davon aus, dass dieser den tmpAny dereferenziert, d.h. die 10 Bytes vom DB1 auf die Adresse kopiert auf die tmpAny zeigt, und nicht an der der Any liegt.

Bei der Verwendung des Any-Pointers in SCL hast du nicht alle Möglichkeiten wie in AWL. Ich finde in diesem Beispiel ist das Verhalten zumindest nachvollziehbar wenn man darüber nachdenkt.
 
gehst du auch davon aus, dass dieser den tmpAny dereferenziert, d.h. die 10 Bytes vom DB1 auf die Adresse kopiert auf die tmpAny zeigt, und nicht an der der Any liegt.
Nö, bin ich nicht ganz deiner Meinung.

Wenn der Blockmove seinen Any dereferenziert muss er schließlich auch die Informationen (DB1/10Byte/etc.) aus der Speicherstelle wo der Any liegt auslesen.
Die Kopierfunktion selbst erfolgt danach separat.
Würde also auch nichts dagegen sprechen (sofern es die Sollfunktion wäre) dass Blockmove den Any bearbeitet. Ist bei Blockmove natürlich nicht der Fall.
Ob man den Any an seinen FB/FC-Paramtern jetzt lesend-derefenziert oder schreibend-bearbeitet sollte eigentlich schon Aufgabe des FC/FB-Programmierers sein. Ich gehe eigentlich nicht grundsätzlich davon aus dass ein Any an einem FB/FC-Parameter nur dereferenziert wird.

Generell ist der Unterschied zwischen AWL-Compiler und SCL-Compiler in dem Fall schon ordentlich. Da kann man schön auf die Nase fallen. :sad:
 
Zuletzt bearbeitet:
Wenn der Blockmove seinen Any dereferenziert muss er schließlich auch die Informationen (DB1/10Byte/etc.) aus der Speicherstelle wo der Any liegt auslesen.
Die Kopierfunktion selbst erfolgt danach separat.

Wenn du dem Blockmove den Parameter "P#DB1.DBX 0.0 BYTE 100" übergibst, wird dieser für dich nicht sichtbar im Lokaldatenbereich zusammengebaut, und der Blockmove bekommt auch nur einen Zeiger auf den Anfang dieses Bereichs. D.h. er muss immer den Parameter dereferenzieren, ob du einen Variable vom Typ ANY oder eine Konstante übergibst.

Ein Unterschied ist, dass SCL den Parameter nochmal umkopiert. Das hat aber etwas mit den erweiterten Möglichkeiten von SCL zu tun, z.B. der einen Any Parameter eines FBs an einen unterlagerten durchzureichen.
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Wenn du dem Blockmove den Parameter "P#DB1.DBX 0.0 BYTE 100" übergibst, wird dieser für dich nicht sichtbar im Lokaldatenbereich zusammengebaut, und der Blockmove bekommt auch nur einen Zeiger auf den Anfang dieses Bereichs. D.h. er muss immer den Parameter dereferenzieren, ob du einen Variable vom Typ ANY oder eine Konstante übergibst.
Das ist schon klar, hab mich aber missverständlich ausgedrückt, sorry.
Der Parameter am IN/OUT (Zeiger auf den Anfang des Any-Speicherbereichs) muss natürlich immer derefenziert werden damit man an den Inhalt des Any kommt.

Aber ob ich jetzt den Inhalt/Zusammensetzung des Any über den Zeiger auslese oder ihn modifiziere ist eine andere Sache.

Selbst wenn ich einen Konstante für den Any eintrage, dieser dann im Temp zusammen gebaut wird und der modifizierende FC/FB dann die Werte im Temp-Bereich modifiziert, ist das auch OK. Bringt in dem Fall halt nix.

Wenn man einen Any über eine Schnittstelle übergibt ist klar das man dessen Zusammensetzung lesen will damit man den Zielbereich bearbeiten kann. Die Frage ist halt nur ob man davon ausgehen muss dass man auch daran interessiert sein könnte diese Zusammensetzung zu bearbeiten.

Ein Unterschied ist, dass SCL den Parameter nochmal umkopiert.
Ja, leider ist er dann nicht mehr so gütig ihn am Schluss nochmal zurück zu kopieren... :p
 
Nicht gut. Tut es nicht.
Über den Stack wird nicht der ANY übergeben,
@PN
Es wird schon ein 10 Byte Any übergeben.
Hast' recht, da habe ich auf dem nachhause-Weg was verwechselt... :oops:


Theoretisch könnte jemand deinen Baustein aus AWL heraus mit:

CALL "FC_GetAny" (
InDB := #InFIFONr,
InOffset := #InOffset,
InLength := #InLen,
OutAny := P#DB1.DBX 200.0 BYTE 1000)

aufrufen. Da kommt auch nichts sinnvolles bei heraus. So wird ein Any aber üblicherweise verwendet.
Genau sowas macht z.B. der SFC20 BLKMOV: da wird der DSTBLK-ANY als OUT übergeben, und niemand wundert sich!


So wie du das in AWL verwendest, ist eigentlich nicht das wozu ein Any-Pointer vorgesehen ist.
Richtig, die ANY-Sache ist vielleicht ein großes Missverständnis.

ANY erstellen macht man eigentlich nicht um die Adresse von Variablen zu erhalten im Sinne eines Adress-Operators. ANY heißt nicht "Adresse" sondern "beliebig". ANY wird benutzt um Aktualwerte beliebigen Datentyps übergeben zu können. Der aufgerufene Baustein teilt mit, daß er mit verschiedenen Datentypen an IN/OUT/IN_OUT-Parametern umgehen kann und der Aufrufer übergibt dem Baustein die unterschiedlichsten Variablen per ANY-Pointer. Egal ob bei IN/OUT/IN_OUT deklariert - die Übergabe der ANY-Pointer ist nur in dieser Richtung vorgesehen. Nach dem Aufruf werden ANY-OUT/-IN_OUT-Werte auch NICHT in die Aktual-Variablen kopiert, der Baustein muß selber zu den übergebenen Adressen schreiben!

Eine Rückgabe von ANY-Pointern ist nicht vorgesehen. Daher das vielleicht unverständliche Verhalten von SCL. Weiters kann SCL auch keine Function (FC) aufrufen, deren RETURN als ANY deklariert ist - das ist einfach nicht vorgesehen.

Um von einer FC einen Wert in eine ANY-Variable zurückzugeben, muß:
- der Aufrufer die Adresse der ANY-Variable an die FC übergeben
- die FC zu dieser Adresse schreiben

SCL übergibt aber nicht die Adresse von ANY-Variablen, sondern kopiert den Wert der ANY-Variable in eine temporäre Variable im Lokaldatenstack und übergibt deren Adresse. Die Adresse der original-Variable bleibt der FC verborgen. Es ist nicht möglich, die Adresse der original-Variable zu ermitteln um dahin zu schreiben. Außerhalb der FC im aufrufenden SCL kommt man nicht an die temporär im Stack benutzte Variable ran, SCL verwendet den Wert dieser Variable auch nicht weiter - eine Rückgabe von irgendwas über diese Variable ist nutzlos.

Daß SCL die Adresse einer ANY-Variablen übergibt geht nur über den Trick, die Adresse einer anderen nicht-ANY-Variable oder Struktur zu übergeben, welche per AT auf der Adresse der ANY-Variable liegt.
Auf diese Weise kann man dann doch ANY-Pointer an SCL zurückgeben. :)

Hilfe zu STEP7 schrieb:
Verwenden des Parametertyps ANY

Sie können für einen Baustein Formalparameter definieren, die für Aktualparameter mit beliebigen Datentypen geeignet sind. Dies ist vor allem dann nützlich, wenn der Datentyp des Aktualparameters, der beim Aufrufen des Bausteins bereitgestellt wird, unbekannt ist oder variieren kann (und wenn ein beliebiger Datentyp zulässig ist). In der Variablendeklaration des Bausteins deklarieren Sie den Parameter als Datentyp ANY. In STEP 7 können Sie dann einen Aktualparameter eines beliebigen Datentyps zuordnen.


Da kommt mir mal wieder die Frage hoch, ob es bei Siemens nicht zum guten Programmierstil dazugehört definierte Funktionen in eben solche zu verpacken.
Apropos guten Programmierstil:
Man sollte mal darüber nachdenken, ob man heute noch Bausteine mit solch unsauberem nicht in den Referenzdaten auftauchendem Absolut-Adress-Gebastel im Stile des vorigen Jahrhunderts entwickelt. Ich sehe schon in 2 Jahren die Frage: "Hilfe, das TIA mag meinen Baustein nicht ..."

Harald
 
Hi Leute,

danke für die vielen Erklärungen. Ich hatte gestern abend und gerade noch einmal ein wenig getestet und mir die Ergebnisse nochmal mit der "DotNetSiemensPLCToolBoxLibrary" (ein richtig tolles Tool! :) ) angeschaut. Dabei ist mir aufgefallen, dass das Tool so clever ist und versucht UC-Aufrufe in CALL-Aufrufe zu wandeln. Häkchen raus und schon sehe ich die wahren Innereien meines AWL-Codes.

Dort passiert, wenn ich bspw. BLKMOV mit "P#DB1.DBX0.0 BYTE 100" aufrufe, genau das gleiche. Er kopiert die ANY-Daten in freien Lokaldatenspeicher und übergibt (natürlich) nur 32bit, den Speicherbereich+Byte+Bit-adresse. Wie ihr weiter richtig bemerkt habt, will BLKMOV natürlich wissen, was in dem ANY steht, der an der übergebenen (32bittigen) Adresse steht. AWL spart sich hier einfach das Erstellen einer Kopie der angegebenen temporären ANY-Variable.

Das heißt für mich im Moment weiter, dass mein GetAny-Baustein mehr aus "Zufall" funktioniert. :(

Wegen des Programmierstils: Das ist so eine Sache. Wenn ich aufgrund von Eingangsparametern auf Daten zugreifen muss, die aufgrund der Menge der Daten über mehrere DBs verteilt sind, dann bin ich doch froh, dass ich mir da einen Pointer auf die richtige Stelle bauen kann... Wenn es diese bösen DB-Grenzen nur nicht gäbe, dann könnte man sicherlich wunderbar ohne Auskommen ...

Apropos habe ich das gleiche "Problem" in SCL: Wenn ich dynamisch per WORD_TO_BLOCK_DB auf einen DB zugreifen will/muss, dann habe ich hier noch nichtmal die Möglichkeit dem Compiler zu sagen, dass der DB von einem bestimmten "Typ" ist um dann wieder symbolisch drauf zuzugreifen (wie gesagt, ich komme aus der Hochsprache und bin auf SPS-Seite also absoluter Verfechter von symbolischen Zugriffen!). Typecasting (ohne BLKMOV in eine entsprechende Struktur) wäre was schönes...

Vielen Dank für die Aufklärung über die Interna! :) Wieder was gelernt. :p

Viele Grüße
Max
 
Zurück
Oben