TIA 1500 Zeiger wie Variant zerlegen und bearbeiten

vollmi

Level-3
Beiträge
5.423
Reaktionspunkte
1.403
Zuviel Werbung?
-> Hier kostenlos registrieren
Ich versuche gerade mich etwas in die neuen Zeigerdatentypen der 1500/1200 einzuarbeiten.

Variant müsste doch eigentlich auch ein normaler Zeiter wie ein Anypointer sein. Nur halt mit mehr funktionen.
Aber kann ich den auch verändern bzw anpassen?

Wenn ich einen Baustein (klassisches Beispiel: PUT) habe der sich mit Variant beschalten lässt. Dann kann ich am Variant Eingang ja einfach einen Pointer anlegen P#DB20.dbx0.0 byte 2 oder die Struktur "DatenBaustein2".Bytes2

Aber kann ich in der Laufzeit z.B. den Verweis auf DB2 nach DB3 verändern? Kann ich eine Variant Deklaration am INOUT eines eigenen FBs verändern? Kann ich darin auslesen welcher DB er enthält, welche Länge etc?

Kann man da eine Sicht drauflegen wie beim ANY Pointer?

mfg René
 
Hallo René,

ich habe den Variant gestern erst benutzt um ein paar "Standard"-Bausteine für die 15xx-Steuerungen zusammenzufassen.

Am Variant kann eigendlich jeder Elementare oder Zusammengesetzte Datentyp angelegt werden. In meinem Fall ware es Berechnungsfunktionen und Grenzwertprüfungen, wo vorher je ein 1 Baustein für Int, DInt, Real, und "udt-Long" (2xDInt) existierte.
Nun habe ich nur noch jeweils einen Baustein mit Variants für die Eingangswerte.
Mit einem TypeOf(#VariantXY) kann im Bastein nun ermittelt werden, welcher Datentyp von am Eingang angelegt wurde.
Danach kann mit VariantGet(#VariantXY) auf den konkreten Datentyp gecastet werden. (Vielleicht war es auch GetVariant()...)

Das sieht dann in etwaso aus:
Code:
Case TypeOf(#VG1) of
   Int:
     #tempWert := INT_TO_REAL(VariantGet(#VG1));

   DInt:
     #tempWert := DINT_TO_REAL(VariantGet(#VG1));

   Real:
     #tempWert := VariantGet(#VG1);

   LReal:
     ...
     ...
     ...


Das selbe funktioniert für Outputs mit VariantPut().



Ich hoffe das hilft dir...



MfG Semo
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Danke dir das ist schon viel. Jetzt kapier ich so langsam wie das angedacht ist.

Wenn ich das jetzt aber so mache:
Code:
CASE TypeOf(#PUT_SD) OF
    Any:
        VariantGet(SRC := #PUT_SD,
                   DST => #test);
    Pointer:
        VariantGet(SRC := #PUT_SD,
                   DST => #test);
       
END_CASE;

wobei aussen am Baustein an der Schnittstelle das so angehängt ist.
Code:
#baustein.PUT_SD := P#DB10.dbx0.0 byte 10

Dann müsste das ja einen any erkennen oder zumindest einen Pointer. Aber keine dieser Bedingungen des Case ist erfüllt.
Gibt es ein Verzeichnis welche Typen da zurückgegeben werden können?

mfg René
 
Danke dir

Komisch. Wenn ich einfach nur ein Byte anhänge (z.B. %MB100) dann bekomme ich über typeof() auch Byte.
hänge ich aber einen ANY an (z.B. P#DB5.dbx0.0 byte 4) dann kriege ich über typeOf weder pointer, any, DB_any oder dergleichen.

mfG René
 
Code:
Der Operand muss den Datentyp VARIANT haben. Der Vergleichsoperand kann ein elementarer Datentyp oder ein PLC-Datentyp sein.
 
Irgendwie seht nirgends, dass Variant für Pointer und Any diesen Typ zurückgibt. Vielleicht gibt er nur einfacht Datentypen zurück?

Hmpf da steht aber auch
Datenbausteinnummer.Operand Typ Länge
(gilt nur für Bausteine mit Standardzugriff)

also
P#DB10.DBX10.0 INT 12
Also muss man doch irgendwie an Nummer operand typ und länge einzeln drankommen.

Meine Fresse 11000 Seitiges Handbuch ist echt übel zu durchforsten.

mfG René
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Code:
Der Operand muss den Datentyp VARIANT haben. Der Vergleichsoperand kann ein elementarer Datentyp oder ein PLC-Datentyp sein.

Somit kann die Funktion nicht den Typ Any, Pointer, usw. zurückgeben.

Unter dem Text steht auch noch in der Beschreibung zur Funktion:

Die Anweisung "Datentyp einer VARIANT-Variable abfragen" können Sie nur innerhalb einer IF-Anweisung verwenden.
 
Die Anweisung "Datentyp einer VARIANT-Variable abfragen" können Sie nur innerhalb einer IF-Anweisung verwenden.

Case funktioniert zumindest bei elementaren Datentypen auch.
Im Handbuch hats ja auch eine Tabelle der Hexcodierung der Datentypen. Allerdings lässt sich die TypeOF Rückgabe nicht auf ein Byte legen. Wäre ja zu einfach wenn man direkt den Rückgabewert mal sehen könnte.

mfG René
 
Hab auch grad im Handbuch nachgelesen und verstehe den Hinweis aus der Hilfe mit der If-Anweisung jetzt überhaupt nicht mehr.
 
hänge ich aber einen ANY an (z.B. P#DB5.dbx0.0 byte 4) dann kriege ich über typeOf weder pointer, any, DB_any oder dergleichen.
mfG René

Ich hab's so zwar noch nicht programmiert, aber aus der Beschreibung und deinem Test (.. weder pointer ...) könnte man vermuten, dass in dem Fall im Variant ein Byte oder eine Array von 4 Bytes ankommt. Hast Du das mal probiert?
 
Ich hab's so zwar noch nicht programmiert, aber aus der Beschreibung und deinem Test (.. weder pointer ...) könnte man vermuten, dass in dem Fall im Variant ein Byte oder eine Array von 4 Bytes ankommt. Hast Du das mal probiert?

Byte und Arrays in verschiedenen Grössen habe ich schon probiert. Bisher ohne erfolg. Aber dafür habe ich den Variant sonst ein bisschen ausprobiert. Ist ansich schon ne Tolle Sache.

Ich hab mir jetzt n kleinen Skalierungsbaustein geschrieben der selbstständig erkennen soll ob ich ein Wort einer AI Karte, ein INT oder ein REAL angehängt habe. Ausgeben soll er je nachdem was er angehängt kriegt ein INT oder ein REAL wobei der INT noch mit einem einzugebenden Faktor verrechnet werden soll. Hat tadellos geklappt. In der 300/400er Welt hab ich dafür 4 Verschiedene Bausteine gehabt. Jetzt kann einer alles.

Code:
FUNCTION "SKAL" : Void
TITLE = 'scaling Peripherical Input words'
{ S7_Optimized_Access := 'TRUE' }
AUTHOR : VoR
FAMILY : Peripher
NAME : W_skal_R
VERSION : 1.8
// Input will be converted directly from Eing to AUSG
// the Linearity is calculatet from UGR_IN to UGR_Out and OGR_IN to OGR_OUT
   VAR_INPUT 
      EING : Variant;   // Input. IF Word then PEW scal unipolar
      OGR_IN : Real;   // Upper limit input in format REAL
      UGR_IN : Real;   // Lower limit input in format REAL
      UGR_OUT : Real;   // Lower Output = Lower Input
      OGR_OUT : Real;   // Upper Output = Upper Input
      INT_FACTOR : Real;   // Faktor for the Integer Input (Normaly 10.0) means 13.7 REAL out is 137 for INT
      OBJ_AH : Bool;   // Manualoverride aktiv inaktiv
   END_VAR


   VAR_IN_OUT 
      AUSG : Variant;   // Output scaled INT or REAL
   END_VAR


   VAR_TEMP 
      OGR_IN_MAX : Bool;   // Input >= Upper limit input
      UGR_IN_MAX : Bool;   // Input <= Upper limit input
      EING_REAL : Real;   // Input im format REAL
      EING_WORD : Word;
      EING_INT : Int;
      LIMIT_R : Real;   // limited input (for calculation)
      LIM_X_UG : Real;   // temporary VAR for limiter
      LIM_X_OG : Real;   // Dito
      Status : Word;
      Ausg_REAL : Real;
      EING_REAL_PEW : Real;
   END_VAR




BEGIN

CASE TypeOf(#EING) OF
    Word: // Wenn Wort, dann behandeln wie einen Analogeingang
        VariantGet(SRC := #EING,
                   DST => #EING_WORD);
        #Status := SCALE(IN := WORD_TO_INT(#EING_WORD), HI_LIM := #OGR_OUT, LO_LIM := #UGR_OUT, BIPOLAR := 0, OUT => #EING_REAL_PEW);
    Int: // Wenn Int dann in REAL wandeln
        VariantGet(SRC := #EING,
                   DST => #EING_INT);
        #EING_REAL := INT_TO_REAL(#EING_INT);
    Real: // Wenn Real dann So übernehmen
        VariantGet(SRC := #EING,
                   DST => #EING_REAL);
END_CASE;


(*sorting limits for calculation*) 
IF #UGR_IN < #OGR_IN THEN
  #LIM_X_UG := #UGR_IN;
  #LIM_X_OG := #OGR_IN;
  
ELSIF #OGR_IN <= #UGR_IN THEN
  #LIM_X_UG := #OGR_IN;
  #LIM_X_OG := #UGR_IN;
END_IF;


(*input should be limited*)
#LIMIT_R := LIMIT(MN := #LIM_X_UG, IN := #EING_REAL, MX := #LIM_X_OG);


(*calculating limited data*)
IF NOT #OBJ_AH  THEN
    IF NOT (TypeOf(#EING) = Word) THEN
        #Ausg_REAL := ((#OGR_OUT - #UGR_OUT) / (#OGR_IN - #UGR_IN)
        * #LIMIT_R
        + (#OGR_OUT - (#OGR_OUT - #UGR_OUT)
        / (#OGR_IN - #UGR_IN) * #OGR_IN));
    ELSE
        #Ausg_REAL := #EING_REAL_PEW;
    END_IF;
    
    CASE TypeOf(#AUSG) OF
        Real:
            VariantPut(SRC := #Ausg_REAL,
                       DST := #AUSG);
        Int:
            VariantPut(SRC := REAL_TO_INT(#INT_FACTOR * #Ausg_REAL),
                       DST := #AUSG);
    END_CASE;
END_IF;


END_FUNCTION

Man muss ja auch mal Zeit für Spielereien haben. Trotzdem Will ich die tieferen Funktionen von VARIANT noch begreifen.
Aber ich bezweifle dass da ein Siemenskurs hilfreich wäre. Es gibt zwar einen 1500er Umsteigerkurs. Ich denke aber der reisst die Funktionen wirklich nur oberflächlich an.

mfg René

edit: letztes End_if muss VariantPUT einschliessen. Sonst lässt sich der Datenpunkt am Ausgang nicht von aussen überschreiben.
edit_2: Fehler in Word Eingang darf nicht zweimal skaliert werden.
 
Zuletzt bearbeitet:
Zuviel Werbung?
-> Hier kostenlos registrieren
Ich habe erst vor 2 Wochen den Umsteigerkurs besucht. Der Typ wurde nur mal aufgelistet. Ich glaube der Dozent meinte das man die fortgeschrittenen Funktionen erst im Tia-Pro3 Kurs macht.
Edit: Bzw. in TIA-SCL
 
Zuletzt bearbeitet:
Man muss ja auch mal Zeit für Spielereien haben. Trotzdem Will ich die tieferen Funktionen von VARIANT noch begreifen.
Aber ich bezweifle dass da ein Siemenskurs hilfreich wäre. Es gibt zwar einen 1500er Umsteigerkurs. Ich denke aber der reisst die Funktionen wirklich nur oberflächlich an.

mfg René

Ich finde solche "Spielereien" unverzichtbar, wenn man sich weiter entwickeln will ;)

Als Unterstützung ware mir ein gut gemachtes Buch lieber, das sinnvolle Anwendungen von VARIANT anhand von Beispielen aufzeigt. Falls Du mal eines findest, ware es super, wenn Du hier eine Info bringen könntest.

Was mich interessieren würde: Funktioniert Deine Funktion SKAL, wenn Du sie z.B. mit EING P#DB5.dbx0.0 INT 2 aufrufst?
 
Was mich interessieren würde: Funktioniert Deine Funktion SKAL, wenn Du sie z.B. mit EING P#DB5.dbx0.0 INT 2 aufrufst?

Nein das funktioniert eben nicht. Ich hab noch nicht herausgefunden was beim anlegen eines Pointers P# für eine TypeOF rückgabe kommt.
Beim Skal werde ich sicher noch eine Fehlerbehandlung reinmachen. erstens fürs fehlerhaften Typ anlegen und zweitens für Bereichsgrenze überschreiten von Analogwerten.

mfG René
 
Ich würde von so einer Verwendung von Variant Abstand nehmen.
Du gibst damit jedwede statische Typüberprüfung auf. Außerdem müsstest du in deinen Baustein eine Fehlerbehandlung einbauen, um ungültige Typen abzufangen, wenn jemand deinen Bausteinparameter z.B. mit einer Bool-Variable beschaltet, was durchaus möglich ist um beim Bausteinaufruf nicht einmal angemeckert wird. Fehler werden somit immer erst zur Laufzeit gemeldet, und das möchte ich zumindest bei der SPS-Programmierung soweit es geht vermeiden.

Wird der Typ eigentlich auch erkannt, wenn du als Parameter eine Konstante verwendest?
 
Ich würde von so einer Verwendung von Variant Abstand nehmen.
Du gibst damit jedwede statische Typüberprüfung auf. Außerdem müsstest du in deinen Baustein eine Fehlerbehandlung einbauen, um ungültige Typen abzufangen, wenn jemand deinen Bausteinparameter z.B. mit einer Bool-Variable beschaltet, was durchaus möglich ist um beim Bausteinaufruf nicht einmal angemeckert wird. Fehler werden somit immer erst zur Laufzeit gemeldet, und das möchte ich zumindest bei der SPS-Programmierung soweit es geht vermeiden.

Wird der Typ eigentlich auch erkannt, wenn du als Parameter eine Konstante verwendest?

Dem kann ich nur zustimmen.
Ich finde das zwar auch durchaus interessant, halte aber eine eher konservative Programmierung (Pointer, Any etc., nur, wenn es nicht anders geht). für wesentlich sicherer, in Bezug auf die Funktion meiner Maschinen und auch in Bezug auf die Lesbarkeit für andere Programmierer/Instandhalter. Dann sind es eben 4 Bausteine, aber man weiß und sieht sofort, was da passiert und das schon beim Programmieren.

Trotzdem ist es natürlich vollkommen richtig von dir, sich zumindest damit zu beschäftigen und auseinanderzusetzen, da hat @Mediator recht.
 
Zurück
Oben