Erweiterte Programmierung


Netzwerkfähige Eigenschaften verteilen Ihre Werte bei einer Änderung automatisch im Netzwerk.

Alle Clients, die Netzwerk-Verbindungen zum Server aufgebaut haben und sich per Proxy-Komponente am Server angemeldet haben, erhalten eine automatische Aktualisierung ihrer Werte im Hintergrund (asynchrone Kommunikation).

"Lokale" Eigenschaften dagegen, arbeiten synchron und sind daher nur für gelegentliche Zugriffe per Netzwerk geeignet.


Netzwerkfähige Eigenschaften bieten die Möglichkeit der sogenannten "Eigenschaftsbindung". Das Konzept der Eigenschaftsbindung ermöglicht es Ihnen eine Eigenschaft von Komponte A mit einer anderen Eigenschaft von Komponente B zu "verbinden" um damit eine automatische Weiterleitung von Wertänderungen zu erreichen.

 

Zunächst einige Beispiele für ein besseres Verständnis:

Thermometer

Angenommen Sie haben eine Klasse "Thermometer" die die Eigenschaft "Temperatur" besitzt und als Instanz in einem Anwendungsbaum definiert worden ist (Komponente A). Da das Thermometer tatsächlich als physikalisch reeles Objekt existiert und seine Temperatur zu iPlus übertragen werden muss, benötigt man noch eine weitere Klasse/Instanz welche in der Lage ist, den Wert über Bustechnik, I/O-Karten oder andere Schnittstellen auslesen zu können (Komponente B). Über einen Konfigurationsmechnismus kann man diese Kommunikationskomponente (Treiber) so konfigurieren, dass Sie den Temperaturwert lesen und als Netzwerk-Eigenschaft bereitstellen kann. In der iPlus-Entwicklungsumgebung in der Registerkarte "Bindungen" können Sie nun die beiden Eigenschaften per Drag-And-Drop miteinander verbinden. Genauer betrachtet ist diese Eigenschaft von Komponente B die eigentliche Datenquelle (Source), weil von hier der ursprüngliche Wert herkommt und zur Komponente A and seine Temperatur-Eigenschaft weitergeleitet werden muss. Weil also die Komponente A, den Temperaturwert selbst nicht generieren kann und darauf angewiesen ist, dass dieser Wert von einer anderen Komponente kommt, bezeichnet man die Temperatur-Eigenschaft der Thermometer-Klasse als Datenziel (Target) oder Stellvertreter-Eigenschaft.

Alarmverteiler e-Mail

Angenommen Sie haben eigene Klassen programmiert, die eine Alarmzähleigenschaft besitzen (Komponenten A). Immer wenn der Zähler einer der vielen Instanzen (Komponenten A) hochgezählt wird soll eine e-Mail gesendet werden. Da Ihre Klassen keine Mailfähigkeit besitzen und außerdem Ihre Funktion eine ganz andere ist, benötigen Sie eine weitere Komponente B, in der die Mailfunktionalität implementiert ist und Sie dort z.B. auf Empfängeradressen konfigurieren können. Diese Mail-Komponente besitzt ebenfalls einen Alarmzähleigenschaft. Immer wenn sich Ihr Wert erhöht, dann wird eine e-Mail versendet. Jetzt müssen Sie wieder per iPlus-Entwicklungsumgebung die Alarmzähleigenschaften der vielen Komponenten A-Instanzen mit der Alarmzähleigenschaft der Mail-Klasse verbinden. Aufgrund des vorigen Beispiels werden Sie nun denken, dass die Alarmzähleigenschaft der Mail-Klasse ein Datenziel ist und die anderen die Datenquelle ist. Dem ist nicht so. Sie dürfen hier nicht die Flussrichtung der Daten für die Entscheidung zu Grunde legen sondern ausschlaggebend ist, welche Komponente den "Originalen" Wert halten muss und welche nur "Kopien" haben. Schließlich möchte man ja auch in diesem Beispiel, dass alle Alarmzähleigenschaften der Komponenten A immer den gleichen Wert aufweisen. Somit ist die Alarmzähleigenschaft der Mailklasse (Komponente B) die Datenquelle (Source) und alle anderen von den Komponenten B das Datenziel (Target)

 

Quell- und Zieleigenschaften (Source and Target)

Anhand der Beispiele erkennen Sie, dass bereits bei der Design-/ bzw. Programmierphase entschieden werden muss welche Eigenschaft eine Quelle und welche Eigenschaft ein Ziel bzw. Stellvertreter ist. Deswegen müssen Sie bei Netzwerkfähigen Eigenschaften anstatt der Ihnen bereits bekannten "ACPropertyInfo"-Attributklasse zwei andere Attributklassen verwenden:

Oder wenn Sie die Eigenschaften "virtuell" per Entwicklungsumgebung definieren, müssen Sie die Checkbox "Netzwerkfähig" und optional "Daten-Stellvertreter" anwählen wenn die Eigenschaft ein Datenziel sein soll.

Genaueres zum Konzept der Datenbindung können Sie bei Wikipedia oder msdn nachlesen. Im Unterschied zur WPF-Datenbindung erfolgt die Datenverteilung nur innerhalb des Datenmodells und nicht zur Oberfläche. Ausserdem erfolgt die Datenverteilung nicht nur innerhalb der selben Anwendungsdomäne sondern Netzwerktransparent.


Anhand des folgenden Bildes wird erklärt wie die Datenverteilung technisch gelöst ist:

 

Die zwei  abgerundeten Rechtecke  auf rechten Seite sind relle Komponten, die serverseitig instanziiert worden sind. Die Rechtecke mit den  gestrichelten Linien  auf der linken Seite repräsentieren Proxy-Objekte auf Client-Seite. 

Rechts unten befindet sich die Komponente A die eine Quelleigenschaft (S) besitzt und die mit der Zieleigenschaft (T) der Komponente B rechts oben verbunden ist. Die grüne Linie "Binding" symbolisiert, die in der Tabelle ACClassPropertyRelation gespeicherte Eigenschaftsbindung, die per iPlus-Entwicklungsumgebung definiert worden ist.

Clientseitig werden die Eigenschaften an den Proxy-Instanzen mit  S'  und  T'  auf der linken Seite dargestellt.

 

Wertänderung in Quelle S

Ändert Sich der Wert in Quelle S, dann entstehen folgende Datenflüsse:

  • (A) Der Wert wird zum Datenstellvertreter T übertragen
  • (B) Die Wertänderung an T hat zur folge, dass eine Netzwerkübertragung zur Proxy-Eigenschaft T' asynchron erfolgt. Falls mehrere Clients im Netzwerk diese Komponente "abonniert" haben werden natürlich alle anderen auch aktualisiert.
  • (E) Eine Netzwerkübertragung zur Proxy-Eigenschaft S' erfolgt ebenfalls asynchron.

 

Wertänderung in Ziel T

Ändert Sich der Wert in Ziel T, dann entstehen folgende Datenflüsse:

  • (B) Netzwerkübertragung zur Proxy-Eigenschaft T' (auch an andere Clients).
  • (D) Der Wert wird zur Quelle S übertragen
  • (E) Netzwerkübertragung zur Proxy-Eigenschaft S' (auch an andere Clients).

 

Wertänderung in Proxy-Quelle S'

Ändert Sich der Wert clientseitig in Quelle S', dann entstehen folgende Datenflüsse:

  • (F) Der Wert wird per Netzwerk zur reelen Quelleigenschaft S übertragen.
  • (E) Quelleigenschaft S löst eine Netzwerkübertragung zur Proxy-Eigenschaft S' von anderen Clients, die diese Komponente abonniert haben.
  • (A) Der Wert wird zum Datenstellvertreter T übertragen
  • (B) Netzwerkübertragung zur Proxy-Eigenschaft T' (auch an andere Clients).

 

Wertänderung in Proxy-Ziel T'

Ändert Sich der Wert clientseitig in Datenstellvertreter T', dann entstehen folgende Datenflüsse:

  • (C) Der Wert wird per Netzwerk zur reelen Zieleigenschaft T übertragen.
  • (B) Zieleigenschaft T löst eine Netzwerkübertragung zur Proxy-Eigenschaft T' von anderen Clients, die diese Komponente abonniert haben.
  • (D) Der Wert wird zur Quelle S übertragen
  • (E) Netzwerkübertragung zur Proxy-Eigenschaft S'.

 


Das nachfolgende Bild stellt ein komplexeres Szenario dar bei dem es zwei Server gibt:

 

Das erste Beispiel wurde so abgeändert, dass sich die Komponente B in einem anderen Anwendungsbaum (Projekt) befindet (Siehe im Bild Server B). Es wurde eine zweiter Benutzer in der Benutzerverwaltung angelegt und dieser zweite Anwendungsbaum (Projekt) als Server zugeordnet. Das erste Projekt in dem sich Komponente A befindet wurde dem zweiten Benutzer als Client zugeordnet. Der erste Benutzer startet Komponente A nach wie vor als Server (siehe im Bild Server A). Mehr zur Projektzuordnung zu einem Benutzer können Sie im Tutorial nachlesen

Diese Konfiguration hat nun zur Folge, dass Server B Komponente A als Proxy instanziiert.

  

Wertänderung in Quelle S

Ändert Sich der Wert in Quelle S, dann entstehen folgende Datenflüsse:

  • (A) Der Wert wird zum Datenstellvertreter S' übertragen auf Server B übertragen.
  • (B) Da die Zieleigenschaft T nun an Datenstellvertreter S' gebunden ist wird T aktualisiert.
  • (C) Die Wertänderung in T hat zu Folge, dass eine Netzwerkübertragung zur Proxy-Eigenschaft T' auf dem Client erfolgt (auch an andere Clients).

 

Wertänderung in Ziel T

Ändert Sich der Wert in Ziel T, dann entstehen folgende Datenflüsse:

  • (C) Netzwerkübertragung zur Proxy-Eigenschaft T' (auch an andere Clients).
  • (E) Der Wert wird zur Proxy-Quelle S' übertragen.
  • (F) Netzwerkübertragung zurück zum Server A an die reele Eigenschaft S.

 

 

Wertänderung in Proxy-Ziel T'

Ändert Sich der Wert clientseitig in Datenstellvertreter T', dann entstehen folgende Datenflüsse:

  • (D) Der Wert wird per Netzwerk zur reelen Zieleigenschaft T übertragen.
  • (E) Der Wert wird zur Proxy-Quelle S' übertragen.
  • (F) Netzwerkübertragung zurück zum Server A an die reele Eigenschaft S.

Deklaration per  IACContainerTNet<T>

Im Gegensatz zur Deklaration von lokalen Eigenschaften, können netzwerkfähige Eigenschaften nicht wie normale c#-Properties deklariert werden. Stattdessen müssen Sie eine Eigenschaft mit dem generischen Typ IACContainerTNet<T> deklarieren.

 

ACPropertyBindingTarget- und ACPropertyBindingSource Attributklasse

Anstatt der Verwendung der ACPropertyInfo-Attribuklasse, wie es bei lokalen Eigenschaften der Fall ist, müssen Sie nun ACPropertyBindingTarget- und ACPropertyBindingSource Attributklassen verwenden. 

 

 


Das folgende Beispiel zeigt wie man mit Netwerk-Eigenschaften arbeitet.

  • Bei den Programmzeilen 1-6 wird angenommen, dass man sich innerhalb der Klasse befindet wo die Eigenschaft "TareWeight" definiert ist (this).
  • Bei den Programmzeilen 8 wird angenommen, dass man sich in einer anderen Instanz befindet, die sich jedoch in der gleichen Anwendungsdomäne befindet.
  • Bei den Programmzeilen 9 wird angenommen, dass man sich in einer anderen Anwendungsdomäne bzw. auf einem anderen Rechner befindet und der Zugriff auf die Eigenschaft "TareWeight" per Netzwerk erfolgt.

 

Persistierungsverhalten

Im oberen Beispiel wurde eine Eigenschaft "StoredTareWeight" definiert. Der letzte Parameter des ACPropertyBindingSource-Konstruktor gibt an, dass der Wert der Eigenschaft in der Datenbank persistiert werden soll. Das bedeutet dass innerhalb des Aufrufs der setter-Methode von ValueT der Wert automatisch gespeichert wird (Zeile 7). Wird diese Instanz bei nächsten Hochfahren wieder erzeugt, dann erhält ValueT bei der Initialisierung den Wert der zuletzt in der Datenbank gespeichert worden ist.

Lesen Sie mehr im Kapitel "Persistierung".


In den vorigen Abschnitten wurde anhand der zwei Szenarien erklärt wie Eigenschaftsänderungen zwischen Client und Server und Target- und Source-Eigenschaften transportiert werden. Als Entwickler möchte man nun wissen

  • wer diese Eigenschaftsänderung ausgelöst hat,
  • wo sie stattgefunden hat,
  • Validierungen durchführen und die Wertänderung noch manipulieren bevor sie endgültig in der Quell-Eigenschaft gespeichert wird,
  • und die Weiterleitung irgendwo auf dem ganzen Transportweg (Target-Proxy->Target->Source-Proxy->Source) an einer bestimmten Stelle abzubrechen.

Dafür stellt das Interface IACPropertyNetServer das Event ValueUpdatedOnReceival zur Verfügung. Es ist vergleichbar mit der Kombination aus

jedoch enthält es weit mehr Informationen aufgrund der Netzwerkfähigkeit des Systems.

Zunächst folgendes Beispiel:

 

Der Eventhandler ACPropertyChangedEventHandler hat folgende Signatur:

public delegate void ACPropertyChangedEventHandler(object sender, ACPropertyChangedEventArgs e, ACPropertyChangedPhase phase)

 

ParameterBeschreibung
object senderReferenz auf die Eigenschaft, die diesen Event-Handler aufgerufen hat.

ACPropertyChangedEventArgs e

Ableitung von PropertyChangedEventArgs.
Besitzt die Eigenschaft public IACPropertyNetValueEvent ValueEvent das folgende Eigenschaften besitzt:

  • Guid RequestID: Eindeutige ID, die vom Aufrufer/Urheber (erste Eigenschaft, die die Wertänderung ausführen will) generiert wird. Das ValueEvent, wird dann entlang des gesamten Transportwegs/Route bis zum Endpunkt weitergereicht mit der selben RequestID. Bei erfolgreicher Wertänderung am Endpunkt wird ein neues Event generiert und im Netzwerk verteilt (Broadcast). Durch die Verteilung kann dann der Aufrufer in seinem ACPropertyChangedEventHandler auf seine RequestID warten, um auszuwerten was mit dem Änderungsrequest passiert ist.
  • bool Handled: Wenn Handled auf true gesetzt wird, dann wird dieses Event nicht mehr weitergeleitet zur nächsten Eigenschaft (auf der Route bis zum Endpunkt). In diesem Fall wird ein neues IACPropertyNetValueEvent generiert, die EventType-Eigenschaft auf RefusedRequest gesetzt und im Netzwerk verteilt bzw. an den Aufrufer zurückgesendet.
  • string Message: Nachricht, die von der ablehnenden Eigenschaft festgelegt werden kann, um den Absender über den Grund zu informieren.
  • string ACUrl: Adresse der ACComponent
  • string ACIdentifier: Name/ID der Eigenschaft, die das Event generiert hat.
  • bool IsRequestedValueStillValid: Ist "true", wenn die angeforderte Wertänderung weiterhin gültig ist. Ist "false", wenn ein anderer Proxy gleichzeitig eine andere Anforderung erstellt hat oder eine interne Änderung der Quelleigenschaft stattgefunden hat. In diesem Fall ist der Wert veraltet.
  • EventTypes EventType: Grund warum dieses Ereignis generiert worden ist.
    • Request,
    • RefusedRequest,
    • Response,
    • ValueChangedInSource,
    • FetchValue
  • EventRaiser Sender: Beschreibt, wer der Aufrufer/Urheber war, der dieses Event generiert hat
    • Source, 
    • Target,
    • Proxy
  • object InvokerInfo: Enthält zusätzliche Informationen über treiberspezifischen Objekte (z. B. ValueQT von OPC oder S7TCPItem...), die den Wert geändert haben
  • object ChangedValue: Neuer Wert auf den die Eigenschaft gesetzt werden soll bzw. gesetzt wurde.
  • bool ForceBroadcast: Ist "true", wenn der Eigenschaftswert nicht geändert worden ist, jedoch soll das Event bis zum Endpunkt transportiert werden.

ACPropertyChangedPhase phase

Falls "BeforeBroadcast", dann wurde der neue Wert "ChangedValue" noch nicht übernommen. Durch setzen der Handled-Eigenschaft auf true, erfolgt keine Änderung.

Falls "AfterBroadcast", dann wurde der neue Wert "ChangedValue" übernommen (ValueT) und zum Kommunikationsdienst übergeben, damit die Clients im Netzwerk aktualisiert werden.

Wichtig: Der EventHandler wird immer zwei Mal nacheinander aufgerufen!

  • Beim ersten Mal bevor der neue Wert in die Eigenschaft übernommen wird (phase = BeforeBroadcast).
  • Beim zweiten Mal nachdem der neue Wert übernommen wurde (phase = AfterBroadcast). 

Eigenschaftsbindungen, die mittels iPlus-Entwicklungsumgebung definiert wurden, sind in der Tabelle ACClassPropertyRelation mit dem ConnectionTypeIndex = 11 (Global.ConnectionTypes.ConnectionPhysical) gespeichert. Während der Initialisierungsphasen (ACInit und ACPostInit) werden diese Tabelleneinträge gelesen und die Eigenschaften miteinander gebunden.

Das Binden von Eigenschaften über die iPlus-Entwicklungsumgebung kann sehr aufwändig werden, wenn man viele Eigenschaften binden muss. Dafür gibt es zwei Lösungsmöglichkeiten:

  1. Man programmiert sich ein eigenes Businessobjekt oder einen Plugin für die iPlus-Enticklungsumgebung, das automatisiert per Datenbankzugriff Einträge in die ACClassPropertyRelation-Tabelle macht
  2. oder man bindet die Quell- und Zieleigenschaft dynamisch während der Initialisierungsphase Ihrer ACComponent-Klasse. Dies wird im folgenden Code-Beispiel näher erklärt:

 

Immer nach ACPostInit()!

In Zeile 2. wird BindMyProperties() nach dem Aufruf von base.ACPostInit() gemacht. Nur dann ist sichergestellt, dass auch alle Instanzen verfügbar sind und ein dynamisches Binding ist ert dann möglich.

 

Interface IACPropertyNetTarget

Im UML-Diagramm des Eigenschaften-Abschnitts ist erkennbar, dass für Eigenschaften, die mit dem [ACPropertyBindingTarget] versehen sind eine andere Property-Klasse vom Framework verwendet wird, die das Interface IACPropertyTarget implementiert. In der Regel wir hier eine Instanz von der Klasse ACPropertyNetTarget<T> generiert. Das IACPropertyTarget stellt eine Eigenschaft "Source" bereit mit der abgefragt werden kann, ob bereits eine Quell-Eigeschaft gebunden ist (siehe Zeile 3).

 

Interface IACPropertyNetSource

Eigenschaften, die mit dem [ACPropertyBindingSource] versehen sind, werden aus der Klasse ACPropertyNetSource<T> instanziiert, die das Interface IACPropertySource implementiert. Das IACPropertySource stellt eine Eigenschaft "Targets" bereit mit der abgefragt werden kann, ob welche Ziel-Eigeschaften gebunden sind (siehe Zeile 5).

 

Dynamische Bindung

Um eine Ziel-Eigenschaft and eine Quell-Eigenschaft zu binden verwenden Sie bitte die Methode BindPropertyToSource(), die es mit zwei Überladungen gibt:

public PropBindingBindingResult BindPropertyToSource(IACPropertyNetSource acPropertySource, PropBindingMode bindingMode = PropBindingMode.BindAndBroadcast)

Diese Methode bindet nur Eigenschaften, die vom selben Datentypen sind (Typ von ValueT). Sind die Datentypen unterscheidlich wird PropBindingBindingResult.NotCompatibleTypes zurückgegeben.

 

public PropBindingBindingResult BindPropertyToSource(IACPropertyNetSource acPropertySource, 
out IACPropertyNetTarget newTarget, out string message,
bool bindInDBIfConverterNeeded = true, PropBindingMode bindingMode = PropBindingMode.BindAndBroadcast)

Diese Methode ruft zunächst Methode (a) auf. Falls die Datentypen inkompatibel sind generiert die Methode entweder

  • eine neue Zieleigenschaft von der Klasse ACPropertyNetTargetConverter<T,S> und ersetzt die aktuelle Referenz (die Methode gibt PropBindingBindingResult.TargetPropReplaced zurück)
    • oder sie verändert/repariert den Datentypen der Quelleigenschaft. In diesem Falls muss der iPlus-Service neu gestartet werden, damit diese Äderung aktiv wird. Das erkennen Sie daran, dass sie als Rückgabewert  PropBindingBindingResult.TypeOfSourceWasChanged erhalten.

Das Ändern der ValueT-Eigenschaft hat zur Folge dass das ValueUpdatedOnReceival-Event ausgelöst wird. Über die Die ACPropertyChangedEventArgs-Klasse enthält zwar  einiges an Informationen über den Urheber jedoch reichen diese Informationen manchmal nicht aus aus. Dafür ist die Eigenschaft object InvokerInfo vorgesehen in der weitere individuelle Informationen übermittelt werden. Dies macht besonders dann Sinn wenn man z.B. Treiber (OPC, OPC-UA, Modbus...) entwickelt und man treiberspezifische Informationen noch übertragen will.

In diesem Fall sollte man nicht die ValueT-Eigenschaft einfach so setzen sondern man sollte stattdessen die überladenene Methoden

für eine Wertänderung nutzen.