TIA AssignmentAttempt: Zuweisung eines VARIANT auf eine Referenz versuchen [Wozu?]

vollmi

Level-3
Beiträge
5.876
Reaktionspunkte
1.802
Zuviel Werbung?
-> Hier kostenlos registrieren
ich stolpere wiedermal über neue Funktionen, deren Bedeutung in der Programmatischen Zukunft ich offenbar noch nicht so richtig erfasse.

Heute mal [TABLE="class: title, width: 100%"]
[TR]
[TD]AssignmentAttempt: Zuweisung eines VARIANT auf eine Referenz versuchen
oder
?=
oder
REF_TO

Was bringt einem diese Funktion?

Offenbar kann man damit einen Variant Referenzieren. Aber wozu soll das Gut sein?
Das Beispiel aus der Hilfe sieht ja so aus.
Screenshot 2021-03-09 064133.jpg[/TD]
[/TR]
[/TABLE]

Aber was ist jetzt der Vorteil dazu, als wenn man an INOUT direkt den Datentyp übergibt (oder den UDT)?
Man kann ja auf die Referenz auch nur zugreifen wenn der Datentyp am Variant exakt dem entspricht was man am REF_TO angibt. Jetzt mal abgesehen davon dass an INOUT nur UDT als Referenz übergeben werden und keine Basisdatentypen. Aber das kann ja wohl nicht alles sein.
Ich hab mir schon gedacht, dass damit vielleicht eine überlagerung stattfinden kann, aber dem ist ja auch nicht so.
 
Ich könnte mir vorstellen, daß man die "Überlagerung" so hinbekommt, daß Du intern überprüfst, welcher Datentyp übergeben wurde. Intern mußt Du dann jeden Datentypen entsprechend behandeln. Dann hast Du eine Funktion für alle Datentypen, also auch eine Art Überlagerung...

Intern erschließt sich mir der Typ Variant auch nicht wirklich, denn da weiß ich ja in der Regel, mit was ich arbeite und definiere das auch gerne so.
Ich könnte mir aber vorstellen, daß man den Variant gebrauchen kann, wenn man mit externen Systemen kommuniziert, wo man nicht genau weiß, was man bekommt bzw. einen "Treiber" für verschiedene Kommunikationsteilnehmer schreibt.
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Man will hier mMn. die Brücke zu den Hochsprachen schlagen, siehe "Call-by-reference" und "Call-by-value".

Je nach Anwendungsgebiet macht das schon Sinn, genauso wie der Typ Variant. Ein Überladen von Eingangsvariablen ist damit problemlos möglich indem man eine Case-Anweisung und den TypeOf()-Command verwendet (Erst mit TypeOf() den Typ der Aktualvariable prüfen und dann entsprechend dereferenzieren).
Ob man dadurch letztlich irgendwas gewonnen hat sollte jeder für sich selbst entscheiden. Wir stoßen aber auch oftmals darauf, dass inbesondere die S5-Haudegen nicht mehr so gut mit dem neumodischen Krempel klar kommen (das soll keinerlei Kritik an jedweder Person sein!).

Referenzen und auch überladene Funktionen verwenden wir bei manchen Standardfunktionen (meist kleinere FCs die irgendeinen Rückgabewert zurück werfen), da kann man dann je nach Aktualvariable am Eingang unterschiedlich damit umgehen.
 
Ich seh halt noch nicht wie man da z.B. Codekopiererei sparen kann.
Als Beispiel.
Code:
TYPE "DFK_pump"
VERSION : 0.1
   STRUCT
      Sollwert : Int;
      Betriebsart : Byte;
      Status_DFK : Word;
      Status_Com : Word;
      Serial : String[10];
      Last : Real;
   END_STRUCT;


END_TYPE


TYPE "ELO_pump"
VERSION : 0.1
   STRUCT
      Sollwert : Int;
      Betriebsart : Byte;
      Status_ELO : Word;
      Serial : String[10];
      Last : Real;
   END_STRUCT;


END_TYPE


TYPE "ELOP_pump"
VERSION : 0.1
   STRUCT
      Sollwert : Int;
      Betriebsart : Byte;
      Status_ELOP : Word;
      Serial : String[10];
      Last : Real;
   END_STRUCT;


END_TYPE


FUNCTION_BLOCK "FB_Pumpstat"
{ S7_Optimized_Access := 'TRUE' }
VERSION : 0.1
   VAR_INPUT 
      GlobaleBetriebsart : Byte;
      Serial : String;
   END_VAR


   VAR_IN_OUT 
      Pumpstat : Variant;
   END_VAR


   VAR_TEMP 
      DFK_Pump : REF_TO "DFK_pump";
      ELO_Pump : REF_TO "ELO_pump";
      ELOP_Pump : REF_TO "ELOP_pump";
   END_VAR




BEGIN
	#DFK_Pump ?= #Pumpstat;
	#ELO_Pump ?= #Pumpstat;
	#ELOP_Pump ?= #Pumpstat;
	
	IF #DFK_Pump <> null THEN
	    #DFK_Pump^.Betriebsart := #GlobaleBetriebsart;
	    #DFK_Pump^.Serial := LEFT(IN := #Serial, L := 2);
	// Und das ganze Programmm das mit DFK Pump zu tun hat
	END_IF;
	
	IF #ELO_Pump <> null THEN
	    #ELO_Pump^.Betriebsart := #GlobaleBetriebsart;
	    #ELO_Pump^.Serial := LEFT(IN := #Serial, L := 2);
	    // Und das ganze Programmm das mit ELO Pump zu tun hat
	END_IF;
END_FUNCTION_BLOCK

Ich hab hier verschiedene typen. die will ich in dem Baustein exakt gleich behandeln. z.B. die Serienummer zuweisen und die Betriebsart überschreiben oder lesen.
Und zusätzlich noch 200 Zeilen zusätzlichen Code ausführen mit den enthaltenen Datenpunkten.

Wenn ich durch die Dereferenzierung irgendwie direkt auf den Variant zugreifen könnte. z.B. mit
IF Pumpstat^.Betriebsart = GlobaleBetriebsart then
Dann müsste ich diese Zeile nur einmal schreiben. Aber so wie hier jetzt aufgebaut. Muss ich erstmal die Referenz zuweisen.
Und dann mit dieser Weiterarbeiten. Das heisst für jeden möglichen zugewiesenen Typ muss ich den exakt gleichen Code nochmal schreiben, einfach mit anderem Variablennamen aus dem Temp.
Früher hätte ich es ja so gemacht, dass die Typen alle gleich aufgebaut sind. Zusätzliche Individuelle Datenpunkte ans Ende. Und dann eine Überlagerung mit einem Standarddatentyp der einfach draufpasst (halt bis dahin wo sich die UDTs dann unterscheiden). Dann muss man den wiederholenden Code nur einmal schreiben.

Ich möchte in dem Baustein also die Typen alle exakt gleich behandeln. Die Datenpunkte die sich unterscheiden behandle ich irgendwoanderst oder individuell in dem Baustein. Aber hier möchte ich halt nur einen 200 Zeilen Code verwenden. Statt diese 200 Zeilen in n
IF xx <> NULL Zweigen aktuell zu halten.

 
Dann mußt Du Dir aus den Referenzen einen gemeinsamen Nenner bauen und den dann im weiteren Code bearbeiten.
Also wenn Du "Betriebsart" in verschiedenen Datenformaten vorliegen hast, dann mach Dir einen "interneBetriebsart" und setze die je nach Variant.
Dann brauchst Du in Deinen 200 Zeilen Code nur noch mit "interneBetriebsart" arbeiten...
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Dann mußt Du Dir aus den Referenzen einen gemeinsamen Nenner bauen und den dann im weiteren Code bearbeiten.
Also wenn Du "Betriebsart" in verschiedenen Datenformaten vorliegen hast, dann mach Dir einen "interneBetriebsart" und setze die je nach Variant.
Dann brauchst Du in Deinen 200 Zeilen Code nur noch mit "interneBetriebsart" arbeiten...

Klar. Man hat ja halt normalerweise nicht nur mit einem Datenpunkt zu tun. Hier möchte man ja eben einen Anwenderdatentyp übergeben wo man vielleicht mit 20 Variablen arbeitet.
Aber ja, ich könnte jetzt die Variablen des UDT einzeln als Variant an die Bausteinschnittstelle übergeben und referenzieren. Macht halt einfach die Schnittstelle wieder enorm gross, das wollte man ja vermeiden indem man einen UDT direkt übergeben kann.
 
ich denke da wird wieder ein Wunsch von Menschen erfüllt, die aus der Hochsprachenecke kommen und nicht wirklich verinnerlicht oder verstanden haben was eigentlich in der SPS-Welt schon immer geht. Da wird nach Vererbung usw. gefragt wo es vermutlich eine Multiinstanz von FBs oder FB-Aufruf im FB genauso tut. Den einzigen Vorteil den ich bei "Void" und Co sehe: die Schnittstelle eines Bausteins kann scheinbar unverändert bleiben obwohl sie sich eigentlich ändert. Das mag das eine oder andere Mal von Vorteil sein. Wenn die Schnittstelle geändert wird, muss ich aber nach wie vor die aufrufende Stelle zeitgleich mit der aufgerufenen Funktion bzw. dem aufgerufenen FB ändern womit der Vorteil meist wieder dahin ist.

Die Gefahr die ich sehe: Man packt den Code mehrerer Bausteine unübersichtlich in einen weil es ja jetzt möglich ist, anhand des abgefragten Datentyps unterschiedliche Dinge da drin zu machen.
 
Dieser Variant-Unfug kommt aus keiner Hochsprachenecke, das scheint mir eine Siemens-Krücke zu sein um irgendwas mit objektorientiert vorzutäuschen, damit man sich nicht eingestehen muss dass Codesys V3 denen mehrere Jahre voraus ist. In anderen Sprachen oder bei Codesys würde man vermutlich Interfaces verwenden.

Das Beispiel von vollmi ließe sich doch auch anders lösen. Für die Schnittstelle wird eine neue UDT z.B. "PublicIf" erstellt, welche alle anderen spezifischen UDTs beinhalten. Und dem FB übergibt man dann als Parameter alleine diese UDT und kann dadurch all deine Antriebstypen verarbeiten. War so auch schon bei Step7 möglich.
 
Zurück
Oben