Objektorientierte Panelreferenzen

Eine Panelreferenz ist ein mehrfach verwendbares Objekt welches mit GEDI Werkzeugen erstellt wurde. Die Referenz wird verwendet durch das Hinzufügen in ein bestehendes Panel um ein grafisches Element darzustellen. Eine objektorientierte Panelreferenz wird wie eine herkömmliche Panelreferenz verwendet, bietet aber durch den Einsatz der Prinzipien objektorientierter Programmierung eine flexiblere Einsatzmöglichkeit. Das Konzept der objektorientierten Panelreferenz ermöglicht es dem Anwender wiederverwendbare Objekte mit erweiterten Eigenschaften und Events zu versehen. Dies ist vergleichbar zu den Qt Widgets welche mit WinCC OA zur Verfügung gestellt werden, z.B. PushButton oder Textfeld.

Wie ein einfaches Objekt mit Dollarparametern kann auch das Objekt der objektorientierten Panelreferenz Dollarparameter verwenden. Die Wiederverwendbarkeit wird durch die erweiterten Funktionen und Ereignisse jedoch maximiert. Kombiniert mit dem Konzept des Objektorientiertes Skripting (CTRL++) können diese neuen Objekte nicht nur Eigenschaften und Ereignisse haben sondern auch Werte mit direktem Zugriff hinterlegen.

  • Panelreferenzen müssen Funktionen zur Verfügung stellen welche von außen (inklusive Parameterübergabe und Rückgabewert) angesprochen werden können.
  • Panelreferenzen können so angelegt werden, dass sie benutzerdefinierte Ereignisse besitzen mit welchen auf die Interaktion durch den Operator reagiert werden kann (wie mit EWOs oder Grafikobjekten)
  • (optional) Panelreferenzen können Eigenschaften anbieten welche mittels GEDI bearbeitet werden können. Dies ermöglicht es die Eigenschaften pro Instanz zu speichern. Diese Eigenschaften sind beliebige Datentypen (Standard CTRL-Datentypen und UI spezifische grafische Datentypen wie color oder filling).
Anmerkung: Die Namen von Objekten in Panelreferenzen dürfen keine Leerzeichen oder Sonderzeichen enthalten, um die ordnungsgemäße Verwendung des "Punkt"-Operators zu gewährleisten. Siehe dazu auch das Kapitel Namenskonventionen / Notationen.

ScopeLib – public- und private Funktionen

Die ScopeLib ist ein spezielles Script-Ereignis für ein Panel. Es kann über das Eigenschaftsfenster des Panels geöffnet werden und beinhaltet Variablen Definitionen und Skriptbibliotheken, welche für das Panel und alle darin befindlichen Objekte verfügbar sind.

Objektorientierte Panelreferenzen verwenden das ScopeLib-Skript um einen Ort für den GEDI zu bieten in welchem die erweiterten Eigenschaften für die benutzerdefinierten Objekte deklariert werden können.

Durch die Einführung des Schlüsselwortes public für die Verwendung innerhalb des Skriptings kann die Verfügbarkeit von Funktionen und Variablen innerhalb der ScopeLib definiert werden. Standardmäßig sind Funktionen und Variablen immer als private spezifiziert. Nur durch das Setzen des Schlüsselwortes public wird eine Schnittstelle nach außen geöffnet.

Beispiel

<public> <return type> <function name>() { your code here; }
public void startTheMachine() { your code here; }
public bool hasPermission(string user) { return (user != “guest“);}
Anmerkung: Eine Deklaration mittels "private" ist nicht vorgesehen! "Private"-Funktionen werden ausschließlich dadurch definiert, dass kein "public"-Schlüsselwort verwendet wird.

Der Benutzer kann public-Funktionen der Panelreferenz mittels der Punktnotation für Objekte innerhalb des gleichen Panels aufrufen, z.B. für ein Objekt mit dem Namen Machine17:

if ( Machine17.hasPermission(user) )
    Machine17.startTheMachine();

Es ist möglich public-Funktionen von Panelreferenzen aufzurufen, welche nicht sich nicht innerhalb des Panels befinden. Für diese Aufrufe kann die Punktnotation nicht verwendet werden, da das Objekt außerhalb des Einflussbereiches ist. Um die Funktion in einem anderen geöffneten Panel aufzurufen, muss folgender Aufruf verwendet werden:

invokeMethod(“Module.Panel:PanelRef“, “functionName“, …)
ACHTUNG: Die public Objektorientierten Panelfunktionen können nur mittels der Punkt-Notation oder der Funktion invokeMethod() aufgerufen werden.

Der Objektorientierung entsprechend können keine public Funktionen von untergeordneten Panelreferenzen direkt aufgerufen werden (Direkter Aufruf eines Childs über den eigentlichen Parent hinweg). Eine Panelreferenz definiert nur direkt ihr eigenes Interface. Aber mittels einer public Funktion der Panelreferenz kann eine public Funktion ihrer child-Panelreferenz aufgerufen werden.

Auf Funktionen einer Panelreferenz kann nur innerhalb des Einflussbereichs des Panels zugegriffen werden. Zum Beispiel kann ein dpConnect, welches in der public Funktion einer Panelreferenz aufgerufen wurde nur mittels eines dpDisconnect() aus einer public Funktion des gleichen Skripts wieder aufgelöst werden. Unterschiedliche Aufrufe können das dpConnect bzw. dpDisconnect nur indirekt über eine entsprechende public Funktion aufrufen.

Anmerkung: Eine public Funktion einer Panelreferenz verwendet optionale Parameter nur, wenn diese Konstanten sind. Variablen, auch konstante Variablen werden nicht bearbeitet.

Wenn eine laufende Funktion einer Panelreferenz terminiert werden soll (z.B. wenn das Panel geschlossen oder die Panelreferenz mittels removeSymbol entfernt wurde und damit alle Skripts stoppt), wird eine Exception für den aufrufenden Thread ausgelöst.

Public Funktionen sind automatisch nach ihrer Definition für den Editor verfügbar. Diese scheinen auch in der automatischen Vervollständigung der Auswahlliste auf (mittels Tabulator Taste beim Schreiben eines Namens einer Funktion).

Anmerkung:

Eine public Funktion einer Panelreferenz kann bei mehreren Skripten zur gleichen Zeit aufgerufen werden. Wenn die Implementierung das nicht erlaubt (d.h. die Funktion ist nicht Thread-Safe) muss die public Funktion als "synchronized" deklariert werden.

public synchronized void startTheMachine() { … }

Die ScopeLib wird aber nicht nur für die Definition von public Funktionen sondern auch für alle Interfaces einer Panelreferenz, wie z.B. benutzerdefinierte Ereignisse oder Eigenschaften, verwendet.

this vs. self

Das Skripten eines Objektes wie einen PushButton erlaubt es dem Entwickler die Syntax this.name für das Referenzieren von Eigenschaften und Funktionen des Objektes einzusetzen. Das Schlüsselwort "this" gibt dem Entwickler einen generischen Weg um das momentane Objekt direkt zu refernzieren ohne den vollständigen Namen zu verwenden.

Referenzpanels können jedoch viele Objekte und auch andere Referenzen beinhalten. Um nun auf die eigentliche Panelreferenz zu referenzieren, kann ein Entwickler das Schlüselwort "self" einsetzen. Dieses bietet die Möglichkeit auf die Wurzel des Objektbaums innerhalb der Referenz zuzugreifen, z.B. Funktionen innerhalb der ScopeLib oder entsprechende Element Eigenschaften und Funktionen.

this.backCol = “blue”; // within script on a circle
self.setAlarmColor(“red”); // public function in ScopeLib

ScopeLib Eigenschaften

Grafische EWOs besitzen zusätzliche erweiterte Eigenschaften innerhalb des Eigenschaftsfenster, wie z.B. das DialGauge Objekt. Diese erweiterten Eigenschaften können in Panelreferenzen mittels Eigenschaften, welche innerhalb der ScopeLib definiert werden, repliziert werden. Hierfür muss das Schlüsselwort "#property" verwendet werden.

Die Panelreferenz muss entsprechend auf Änderungen dieser Eigenschaft reagieren können (d.h. für eine Hintergrundfarbe muss eine set-Funktion definiert sein, welche aufgerufen wird, wenn ein Entwickler die Eigenschaft innerhalb des GEDIs anpasst.

Es muss ebenfalls möglich sein diese Eigenschaften mittels CTRL-Skript abzufragen oder zu setzen. Das bedeutet, dass eine Eigenschaft eine Kombination aus der Deklaration (legt den Datentyp fest) und get- und set- Funktionen sind.

Anmerkung: Es wird empfohlen Eigenschaftsnamen mit einem Kleinbuchstaben zu beginnen. Die get- bzw. set- Funktionen verwenden anschließend eine gemischte Groß- und Kleinschreibung unter Verwendung des originalen Namens der Eigenschaft mit einem beginnenden Großbuchstaben als Suffix des Funktionsaufrufes.

Jede Eigenschaft muss eine get und set Funktion besitzen. Herkömmlich sind diese Funktionen public aber können auch durch nicht setzen des Schlüsselwortes public als private deklariert werden, um den Zugang durch externe Skripts einzuschränken.

Beispiel

Folgendes ist in der ScopeLib definiert:

#property color alarmColor
public setAlarmColor(string c)
{
  myBackgroundShape.backCol = c;
}
public string getAlarmColor()
{
  return myBackgroundShape.backCol;
}

Wird nun diese Panelreferenz in ein Panel eingefügt, stellt das Eigenschaftsfenster folgende erweiterten Eigenschaften zur Verfügung:

Die allgemeine Deklaration sieht folgendermaßen aus:

#property <type> <name>

Der Typ der Eigenschaften kann auch ein spezielles Keyword nur für den GEDI sein um einen entsprechenden Auswahldialog innerhalb des Eigenschaftsfensters zu ermöglichen. Zum Beispiel wird für den Typ "color" ein entsprechender Farbendialog angezeigt (siehe oben). Folgende Typen stehen zur Verfügung:

  • float
  • int
  • uint
  • bool
  • string
  • color
  • fill
  • font
  • pen
  • benutzerdefiniertes enum (Wird innerhalb des GEDI als Auswahlliste angezeigt aber intern als Liste von int Werten gehandhabt)

Der GEDI speichert die Werte der benutzerdefinierten Eigenschaften innerhalb des Panels, vergleichbar mit EWOs

Es kann der Wert der benutzerdefinierten Eigenschaft auch über das Referenzpanel gesetzt werden. Der GEDI stellt dann sicher, dass die aufgerufenen public set-Funktionen den gegebenen Wert auch in der Paneldatei speichern.

Hinweise bezüglich Eigenschaften

  • Einbußen in der Performance können bei der Definition von Eigenschaften innerhalb der ScopeLib für GEDI und Eigenschaftsfenster auftreten:
    • Eigenschaften werden mittels CTRL-Skriptfunktionen geschrieben und gelesen - Kann langsam sein.
    • Das UI muss alle Eigenschaften nach dem Öffnen des Panels setzen und das Objekt rendern. Das kann heißen, dass beim Öffnen des Panels eine Vielzahl von Skriptfunktionen gestartet werden müssen.
  • ScopeLib-Eigenschaften können nicht mittels Punktnotation, sondern nur mit den public get bzw. set Funktionen angesprochen werden.
  • Die ScopeLib-Eigenschaften werden von innen nach außen geladen innerhalb des GEDIs, d.h. Child- vor Parent- Referenzen. Sie sind ebenfalls serialisiert, d.h. alle set-Eigenschaften werden vor den get-Eigenschaften geladen. Das trifft ebenfalls auf VISION zu, aber in diesem Fall werden die get Funktionen nicht ausgeführt und nach Abschluss der set Funktionen wird das Initialize der Panelreferenz ausgelöst.

    Das bedeutet auch, dass jedes UI-Ereignis, welches vor dem Abschluss der Initialize Phase ausgelöst wird, nicht erkannt wird und verloren geht.

  • Die Reihenfolge, in welcher Eigenschaften gesetzt werden, ist nicht definiert! Wenn eine set Eigenschaft versucht auf eine andere Eigenschaft zuzugreifen, welche noch nicht gesetzt wurde, wird der default Wert anstelle des eigentlichen Wertes gesetzt.

ScopeLib Ereignisse

Ein benutzerdefiniertes Ereignis kann nur innerhalb der ScopeLib einer PanelReferenz deklariert werden unter Verwendung des Schlüsselwortes "#event". Es ist keine bestimmte Groß- bzw. Kleinschreibung erforderlich und der Entwickler kann festlegen welcher Wert an das Ereignis übergeben wird.

Die Ereignisdeklaration verwendet folgende Syntax:

#event <name>(<parameter>)

Benutzerdefinierte Ereignisse sind immer als void deklariert, d.h. sie haben nur call-by-value Parameter und keinen Rückgabewert (keine in/out Referenz Parameter).

Beispiel

Folgendes wird in der ScopeLib definiert:

#event valueChanged(int value)
#event buttonClicked()

Ein Skript, welches sich irgendwo innerhalb der Panelreferenz befindet kann nun das Ereignis zur Laufzeit auslösen.

Das Clicked-Skript eines PushButtons.

main()
{
  triggerEvent(„buttonClicked“);
}

Das valueChanged Skript eines Objektes:

main()
{
  triggerEvent(„valueChanged“, this.text);
}

Wenn die Panelreferenz innerhalb eines Panels hinzugefügt wird, stehen die Ereignisse innerhalb der erweiterten Eigenschaften des Eigenschaftsfensters zur Verfügung (siehe unten).

Beispiel

Für das vorangegangene Beispiel zeigt das Eigenschaftsfenster dem Benutzer genau die beiden benutzerdefinierten Ereignisse (valueChanged, buttonClicked) an und erlaubt es einen entsprechenden Code für die Ereignisse zu hinterlegen, z.B. kann folgendes benutzerdefiniertes Skript hinterlegt werden:

buttonClicked()
{
  dpSet(“Machine.command”, true);
}

Hinweise bezüglich Objektorientierter Panelreferenzen

  • Das Konzept der Eigenschaften und Ereignisse für Panelreferenzen erlaubt es dem Anwender umfangreiche Objekte zu erstellen, welche portabler sind als beim Einsatz vor herkömmlichen Dollarparametern. Der Einsatz innerhalb eines Projektes ist dadurch auch vereinfacht. Das heißt jedoch nicht, dass diese Panelreferenzen auch einfacher zu erstellen oder zu designen sind als herkömmliche Referenzen mit Dollarparametern. Es muss darauf geachtet werden, dass alle erforderlichen Eigenschaften und Ereignisse auch verfügbar sind, um die Objekte einsetzen zu können.
  • Entwickler sollten es vermeiden Dollarparameter innerhalb von Ereignissen oder Eigenschaften von Panelreferenzen einzusetzen. Die Verwendung von Dollarparametern führt zu einem Hybridobjekt, welches spezifisch für bestimmte DP-Typen ist. Es sollte versucht werden die benutzerdefinierten Panelreferenzen mit möglichst einfachen Eigenschaften und Events zu erstellen, vergleichbar mit den EWOs, welche mit WinCC OA geliefert werden. Dies ermöglicht es dem Entwickler anschließend seine Dollarparameter mit den zur Verfügung stehenden Eigenschaften und Ereignissen zu verbinden.
  • Die Verwendung von objektorientierten Panelreferenzen verhindert eine direkte Adressierung von Objekten. Zugrif kann nur mittels entsprechender public-Schnittstelle ermöglicht werden. Dies gilt ebenfalls für die Adressierungen von Objekten außerhalb der objektorientierten Panelreferenz.