Erweiterte Programmierung


Im Code-Beispiel des Kapitels "Starten einer ACComponent" wurde die Methode ACUrlCommand verwendet um die Existenz einer Instanz abzufragen. Die ACUrlCommand ist eine universelle Methode mit der man über einen String (ACUrl)

  1. Referenzen zu Komponenten erhalten,
  2. Eigenschaftswerte abfragen,
  3. Methodenaufrufe ausführen,
  4. Komponenten Starten und Stoppen,
  5. Nachrichten an andere Komponenten senden kann.

Um unterscheiden zu können, welche der zuvor aufgelisteten Funktionen (a-e) man über ein ACUrlCommand ausführen will, müssen Sie im ACUrl-String spezielle Befehlszeichen eingefügen - ähnlich den URL-Parametern z.B. bei http oder REST. 

Diese Methode hat folgende Signatur:

object ACUrlCommand(string acUrl, params Object[] acParameter)

Im ersten Parameter erfolgt die Übergabe des Befehlsstrings (ACUrl). Um eine Methode aufzurufen welche mehrere Parameter besitzt, über geben Sie die Parameter in der anschließenden Argumentliste. Mehr dazu können Sie im Kapitel "Synchrone Methoden" nachlesen.

Der nachfolgende Beispielcode bezieht sich auf grafische Beispiel eines Anwendungsbaumes aus dem übergeordneten Kapitel. Es enthält Beispiele zu den Befehlstypen a und d.

 

Spezielle Befehlszeichen, zur Verwendung im ACUrl-String:

ZeichenBeschreibung
\Pfadtrennzeichen. Ein Trennzeichen am Anfang signalisiert den Beginn einer absoluten Adressierung. Danach muss ein Identifier folgen. Beispiel: \Mixery\Scale1
.\Relative Adressierung: Es wird ein Objekt adressiert das sich im Verzeichnis (Kind) unterhalb befindet. Danach muss ein Identifier folgen. Beispiel: .\Flap1
..\Relative Adressierung: Es wird ein Objekt addressiert das sich im übergeordneten Verzeichnis (Eltern) befindet. Danach kann ein Identifier folgen. Beispiel: ..\  oder ..\Mixer1
?Prüfung auf Existenz einer ACComponent-Instanz. Das Fragezeichen muss immer am Anfang der Zeichenkette stehen! Danach folgt die Url. Beispiel: Beispiel: ?.\Flap1 oder ?\Mixery\Scale1
#Starten einer neuen ACComponent-Instanz (Erzwingen einer Instanziierung). Danach muss ein Identifier folgen.
Beispiel: #Flap1 oder .\#Flap1 oder absolut \Mixery\Scale1\#Flap1
~Stoppen einer ACComponent-Instanz. Danach muss ein Identifier folgen.
Beispiel: ~Flap1 oder .\~Flap1 oder absolut \Mixery\Scale1\~Flap1
!Aufrufen einer Methode. Danach muss ein Methodenname (Identifier) folgen.
Beispiel: !Open oder .\Flap1!Open oder absolut \Mixery\Scale1\Flap1!Open
§Zugriff auf einen übersetzten Text.  Danach muss ein Identifier des Übersetzungstextes folgen.
Beispiel: §Warning50001 oder .\Flap1§Warning50001 oder absolut \Mixery\Scale1\Flap1§Warning50001
^Senden einer benutzerdefinierten Nachricht/Befehl.

 


In der Benutzerverwaltung können Sie festlegen welche Anwendung unter welchen Benutzerkonto verfügbar sein soll. ist das Server-Kennzeichen bei der zugeordneten Anwendung gesetzt, dann werden beim Hochfahren die im Anwendungsbaum definierten Instanzen als echte .NET-Klassen instanziiert ("Real"-Components). Wird dagegen das Client-Kennzeichen gesetzt, dann werden stattdessen "Proxy"-Instanzen erzeugt. Sämtliche Eigenschaftszugriffe und Methodenaufrufe auf solche Instanzen erfolgen im Hintergrund. Das bedeutet, dass diese Zugriffe per Netzwerk zu einer anderen iPlus-Instanz delegiert werden, bei der diese Instanzen wiederum als reele Klassen existieren. (Quellen zum Proxy-Entwurfsmuster und der prinzipiellen Funktionsweise: 

Wenn Sie also Programmcode schreiben und auf Instanzen referenzieren die möglicherweise auf anderen Netzwerkknoten (oder auch in einer anderen .NET-Application-Domain innerhalb des selben Rechners) als reele Komponenten instanziiert sind, dürfen Sie niemals in konkrete .NET-Klassen casten. Methoden-Aufrufe müssen dann z.B. immer per ACUrlCommand oder ExecuteMethod-Aufrufe durchführt werden.

In einem solchen Fall können Sie dann nur mit folgenden Klassen arbeiten bzw. als Referenz arbeiten:

  • ACComponent (Basisklasse aller Komponenten. Verwenden Sie diese Klasse, damit Sie keine Fallunterscheidung machen müssen ob es sich um eine konkrete Klasse oder Proxy-Klasse handelt. Es macht den Code einfach leserlicher.)
  • ACComponentProxy (Basisklasse für alle Proxies)
  • PWNodeProxy (Spezielle Ableitung von ACComponentProxy, wenn es sich um Workflowknoten handelt. D.h. Serverseitig sind das Ableitungen von der PWBase-Klasse)
  • ApplicationManagerProxy (Spezielle Ableitung für den Root-Knoten. D.h. Serverseitig sind das Ableitungen von der ApplicationManager-Klasse)

 


Folgende Regeln sind zu beachten um eine Klasse in der iPlus-Runtime zu instanziieren:

  1. Ihre Klasse muss eine indirekte Ableitung von ACComponent sein.
  2. Sie müssen vor der Klassendeklaration eine ACClassInfo-Attribut-Klasse einfügen.
  3. Der Konstruktor ihrer Klasse darf nicht parameterlos sein, dieser muss öffnentlich sein und muss eine genau vordefinerte Signatur aufweisen.
  4. OPTIONAL: ACClassConstructorInfo-Attribut-Klasse vor der Klassendeklaration einfügen.

Beispielcode:

 

 

1. Indirekte Ableitung von ACComponent

Es gibt eine Reihe von Basis-Klassen von denen Sie ableiten können, um Ihre eigene ACComponent zu programmieren.Im folgenden UML Diagramm finden Sie eine Übersicht der wichtigsten Klassen im iPlus-Framework, von denen aus Sie beginnen sollten Ableitungen zu erstellen (abstrakte Klassen sind kursiv geschrieben):

 

  • ACBSO: Abstrakte Basisklasse für Businessobjekte (dynamische Instanzierung während der Laufzeit. Diese sind vorwiegend Kindelemente der Businessobjects-Instanz). Verwenden Sie diese Art für Programme die vom Anwender über das Menü geöffnet werden.
    • ACBSOvb: Ableitung von ACBSO, diese ermöglicht es mit dem iPlus-MES-Datenbankkontext (MES-Daten) zu arbeiten.
    • ACBSONav: Abstrakte Basisklasse für Navigierbare Businessobjekte welche Zugriff auf Datenbanktabellen benötigen.
      • ACBSOvbNav: Ableitung von ACBSONav, welche es ermöglicht mit dem iPlus-MES-Datenbankkontext (MES-Daten) zu arbeiten und in Anwendungstabellen zu navigieren und zu suchen.
  • PAClassAlarmingBase: Abstrakte Basisklasse für jegliche Instanzen, das iPlus-Framework serverseitig als reele Instanzen in den Anwendungsbäumen instanziiert. Diese Basisklasse erleichtert das Handling mit hierarchischen Alarmen und deren Langzeitspeicherung.
  • ACComponentManager: Pro projektierte Anwendung muss eine Root-Instanz generiert werden. Dies ist die Basisklasse dafür.
    • ApplicationManager: Default-Klasse projektierte Anwendungen - kann projektspezifisch auch abgeleitet werden.
    • Businessobjects: Vom iPlus-Framework vordefinierte Wurzel-Anwendung, die nur Businessobjekte (dynamische Instanzen) verwaltet.
  • PAClassPhysicalBase: Basisklasse für alle Objekte im Anwendungsbaum, die ein physikalisches Objekt aus der Realtität repräsentieren.
  • PABase: Basisklasse für zustandsorientierte Instanzen, die einen Zustandsautomat besitzen und Aufgaben über einen längeren Zeitraum asynchron ausführen können.
    • PAProcessFunction: Basisklasse für Funktionen, die Kindobjekte von PAProcessModule sind und beschreiben welche Funktionalitäten/Schnittstellen eine Baugruppe besitzt und ausführen kann.
    • PWBase: Basisklasse für dynamische während der Laufzeit erzeugte und wieder entladene Instanzen. Im Vorfeld legt der Endanwender bzw. Projekteur mittels Workflows fest welche Instanzen konkret erzeugt werden sollen.

 

2. ACClassInfo-Attributklasse verwenden

Damit Sie mit

  • ihren selbst definierten Klassen in der Entwicklungsumgebung arbeiten können
  • Anwendungsbaum konfigurieren,
  • Designs für Ihre Klasse erstellen,
  • Konfigurationsparameter hinterlegen,

müssen Sie diese zuerst der iPlus-Runtime bekannt machen. Dies geschieht dadurch, dass Sie beim iPlus-Login die linke Steuerungstaste gedrückt halten und mit der linken Maustaste die Logintaste drücken.

Die iPlus-Runtime beginnt danach an alle Assemblies, die sich im Programmverzeichnis befinden, zu untersuchen. Dabei sucht diese nach Klassen, die mit einem ACClassInfo-Attribut versehen sind und registriert diese in der iPlus-Datenbank als bekannten Typ. Sie können aber auch Klassen registrieren die keine indirekten Ableitungen von ACComponent sind. Jedoch müssen Sie diese Instanzen dann auch manuell instanziieren.

Achtung: Ist kein ACClassInfo-Attribut vergeben, kann .NET-Reflection auch keine Eigenschaften und Methoden analysieren! Das ACClassInfo-Attribut gibt es mit verschiedenen Konstruktoren. Die folgenden Parameter stehen Ihnen zur Verfügung:

ParameterBeschreibung
string acPackageName[Pflicht] Zuordnung zu einem Paket-Namen. Über den Paket-Namen erfolgt die Software-Lizensierung mit dem Endkunden.

string
acCaptionTranslation

[Pflicht] Übersetzungstupel für die Übersetzung der Klasse. Syntax für Übersetzungstupel:

  • I18N-Präfix: z.B. en, de, fr...
  • Geschweifte Klammer auf und einfaches Hochkomma: {'
  • Text in der entsprechenden Sprache
  • Einfaches Hochkomma und geschweifte Klammer zu: '}

Beispiel: en{'My english text'}de{'My german text'}

Global.ACKinds
acType

[Pflicht] Zuordnung zu einer Kategorie. Die Kategorie dient zum einen dazu, die Nutzung der Klasse in der Entwicklungsumgebung zu vereinfachen. Indem bestimmte Klassen abhängig vom Kontext den die Klasse benötigt ein oder ausgeblendet werden. Zum anderen, um für projektspezifische Logiken zu programmieren um mit Klassen umgehen zu können, welche zu dem Zeitpunkt noch nicht instanziiert sind.
Global.ACStorableTypes
acStorableType
[Pflicht] Angabe ob der Zustand dieser Instanz (perisitierbare Eigenschaften) gespeichert werden darf oder nicht. Dies funktioniert nur für im Anwendungsbaum instanziierte Klassen. Im iPlus-Projekt instanziierte Klassen sind in der Regel nicht perisitierbar. Mehrfach instanziierbare Klassen sind in der Regeln Ableitungen von ACBSO (Businessobjekte). In diesem Fall muss isMultiInstance den Wert True aufweisen:
bool
isMultiInstance
[Pflicht] Kennzeichen ob die Klasse mehrfach als Kindobjekt instanziert werden darf. Da der ACIdentifier in der Memberliste/Childliste eindeutig sein muss, vergibt die iPlus-Runtime automatisch eine fortlaufende Instanznummer bei einer neuen Instanziierung. Diese Instanznummer wird mittels Umklammerung dem ACIdentifier-String angehängt. Beispiel "BSOMaterial(1), BSOMaterial(2)".
bool
isRightmanagement
[Optional] Kennzeichen ob diese Klasse der Rechteverwaltung obliegt. Rechte für Eigenschaften, Methoden und Designs können Sie in der Gruppenverwaltung erteilen.
string
pwInfoACClass
[Optional] Nur für Klassen relevant, die Ableitungen von PAProcessFunction sind. pwInfoACClass gibt den Klassennamen der Workflowklasse an (Ableitung von PWNodeProcessMethod) die kompatibel mit dieser PAProcessFunction ist. Anderst formuliert: Angabe der Workflowklasse, welche diese Funktion aufrufen kann und ihre Parameterliste kennt.
string
qryConfig
[Optional] Nur für Klassen relevant, die Ableitungen von ACBSONav sind (Navigierbare Businessobjekte). qryConfig gibt den Namen der primären ACQueryDefinition an, welche die standardmäßig im Explorer angezeigten Datensätze filtert. Welche Queries es gibt können Sie mittels iPlus-Entwicklungsumgebung im Pfad \Root\Queries prüfen. Neue Queries tragen Sie ein indem Sie das ACQueryInfoPrimary-Attribut über eine Entity-Klasse deklarieren und anschließend iPlus mit Strg-Login starten.
string bsoConfig[Optional] Nur für Klassen relevant, die Entity-Klassen sind. bsoConfig gibt den Namen der Businessobjekt-Klasse an, welche für die Verwaltung dieser Entity-Klasse zuständig ist. Wird diese Entity-Klasse in einem "Items-Control" wie z.B. einer Combobox angezeigt, dann können Sie per Kontextmenü in das zuständige Businessobjekt navigieren und die entsprechende Entität anzeigen bzw. verwalten.
Int16
sortIndex
[Optional] Sortierreihenfolge für die Präsentation der Klasse in der iPlus-Entwicklungsumgebung.
object[]
acClassChilds

[Optional] Array um Beziehungen zu anderen Entity-Objekten zu definieren. Beispiel:

[ACQueryInfoPrimary(Const.PackName_VarioMaterial, Const.QueryPrefix + Partslist.ClassName, "en{'Bill of Materials'}de{'Stückliste'}", typeof(Partslist), Partslist.ClassName, "PartslistNo,PartslistName", "PartslistName",
new object[]
{
new object[] {Const.QueryPrefix + PartslistPos.ClassName, "en{'Bill of Materials Position'}de{'Stücklistenposition'}", typeof(PartslistPos), PartslistPos.ClassName + "_" + Partslist.ClassName, "Sequence", "Sequence"}
}
)]

 

3. Signatur des Konstruktors

Jeder Konstruktor einer Klasse, die von ACComponent abgeleitet ist, muss die selbe Signatur aufweisen:

ParameterBeschreibung
ACClass
acType

In der Tabelle ACClass sind Metainformationen zu einer Klasse gespeichert, die notwendig sind, damit die iPlus-Runtime diese Klasse instanziieren kann. Dazu gehören folgende Eigenschaften/Tabellenfelder:

  1. ParentACClass (ParentACClassID): Relation zur Eltern-Klasse. In dieser Beziehung erfolgt die Definition der Baumstruktur einer Anwendung (Composition).
  2. BasedOnACClass (BasedOnACClassID): Relation zur Basisklasse, von der diese Klasse abgeleitet ist. Mittels dieser Beziehung, wird die Vererbung einer Klasse definiert. 
  3. AssemblyQualifiedName: Gibt an welcher .NET-Typ per Reflection instanziiert werden soll. 

ACClass-Vererbungsbeziehungen sind oft virtuell. Dies bedeutet, dass zwar der gleiche .NET-Typ erzeugt wird, jedoch es sich aus Sicht des iPlus-Frameworks um zwei verschiedene Typen oder Instanzen handelt. Jede im Anwendungsprojekt definierte Instanz ist automatisch eine neue ACClass die virtuell vererbt wird. Aus .NET-Sicht wird jedoch der gleiche .NET-Typ instanziiert, sobald der AssemblyQualifiedName bei zwei unterschiedlichen Instanzen ebenfalls der gleiche ist.

IACObject
content

Instanzen, die keine Ableitungen von ACBSO sind (in der Regel persistierbare Instanzen, deren Instanziierung in Anwendungsbäumen erfolgt), bekommen ein ACClassTask-Objekt als "Inhalt" übergeben. Wiederherstellung der Zustände persistierbarer Instanzen in den Anwendungsbäumen erfolgt über die ACClassTask-Tabelleneinträge, nach dem Neustart des iPlus-Service (Alle Objektzustände bleiben nach dem Neustart erhalten).

Bei dynamischen Instanzen (Ableitungen von ACBSO, Ableitungen von PARole...) die nicht persistierbar sind wird in der Regel NULL übergeben.

IACObject
parentACObject

Eltern-ACComponent unter der diese Instanz als Kindobjekt angelegt wird.
ACValueList
parameter

Individuelle Konstruktionsparameter, werden mittels ACValueList übergeben. Eine ACValueList ist eine Liste, die Einträge von ACValue enthält. ACValue ist eine Klasse, die im Wesentlichen drei Eigenschaften hat:

  1. string ACIdentifier: Eindeutige ID/Name des Parameters
  2. object Value: Parameterwert
  3. Type ObjectFullType: Datentyp des Parameterwertes
Wenn Sie eine Komponente selbst starten möchten (siehe folgender Abschnitt) müssen Sie ACValueList instanzieren. Damit Sie die Parametereinträge nicht selbst eintragen müssen, rufen Sie die Methode ACClass.TypeACSignature() des entsprechenden iPlus-Typs, den Sie instanzieren möchten auf (= Parameter acType). Welche Parameter notwendig sind, weiß das iPlus-Framework durch die ACClassConstructorInfo-Attributklasse, die man zusätzlich zur ACClassInfo-Attributklasse vor der Klassendeklaration einfügen kann (siehe Punkt 4).
string
acIdentifier = ""
Eindeutige ID, innerhalb der Kind-Instanzen der Eltern-ACComponent, den diese Instanz erhalten wird. Falls string leer ist, vergibt die Runtime die ID selbst über den ACIdentifier des acType-Parameters.

 

4. Optional: ACClassConstructorInfo

Die Metainformationen über die Konstruktionsparameter welche eine Klasse bei der Konstruktion im Parameter "ACValueList" erwartet, sollten Sie über die ACClassConstructorInfo-Attributklasse definieren. Die ACClassConstructorInfo-Attributklasse ist nur ein object-Array, das wiederum Einträge mit object-Arrays enthält (Siehe Code-Beispiel oben). Die inneren object[]-Einträge dürfen nur drei Einträge in folgender Reihenfolge enthalten:

Index 0:
string
Name des Parameters, der später im ACIdentifier des ACValue-Eintrag steht
Index 1:
Global.ParamOption
Angabe ob der Parameter Obligatorisch oder Optional ist

Index 2:
System.Type

.NET-Typ, des Parameterwertes

 


Immer wenn Sie Änderungen an Ihren Klassen, Eigenschaften oder Methoden durchgeführt haben und diese mit den Attributklassen ACClassInfo, ACPropertyInfo, ACMethodInfo.... versehen sind, damit diese im iPlus-Typsystem bekannt gegeben werden, müssen Sie 

beim iPlus-Login die linke Steuerungstaste gedrückt halten und mit der linken Maustaste die Logintaste drücken!


Im vorherigen Abschnitt haben Sie erfahren, wie man ACComponents per ACUrlCommand starten und stoppen kann. Dasselbe können Sie auch direkt ohne ACUrlCommand durchführen.

 

1. Starten bzw. Instanziieren von Komponenten

Dafür sind folgende Methoden (unterschiedliche Signaturen) in der ACComponent-Klasse definiert:

public virtual IACComponent StartACComponent(
string acIdentifier,
object content,
object[] acParameter,
ACStartCompOptions startOptions = ACStartCompOptions.Default)
public virtual IACComponent StartACComponent(
ACClass acClass,
object content,
ACValueList parameterList,
Global.ACStartTypes startChildMode = Global.ACStartTypes.Automatic,
bool asProxy = false,
string acIdentifier = "")
public virtual IACComponent StartACComponent(
ACChildInstanceInfo instanceInfo,
Global.ACStartTypes startChildMode = Global.ACStartTypes.Automatic,
bool asProxy = false,
string acNameInstance = "") 

Im Parameter "acIdentifer" übergeben Sie den Namen der Kind-Klasse/Instanz, welche in der Entwicklungsumgebung im Anwendungsbaum unterhalb der Klasse definiert wurde (In dieser Klasse befinden Sie sich gerade (this-Pointer). Falls keine Klasse mit dem angegebenen ACIdentifier im Anwendungsbaum vorhanden ist, wird geprüft ob es sich im eine Businessobject-Klasse handelt. (Ableitung von ACBSO). Ist die angegebene Klasse vorhanden erzeugt iPlus eine neue dynamische Kind-Instanz unterhalb der Instanz in der Sie sich gerade befinden.

Der Parameter "object content" wird bei der Konstruktion dem zweiten Konstruktorparameter "IACObject content" übergeben. 

Der Parameter "object[] acParameter" muss die Parameter enthalten, die dem vierten Konstruktorparameter als "ACValueList parameter" übergeben werden. Sie können dies auf zwei unterschiedliche Arten programmieren:

  • object[] acParameter enthält nur ein Element vom Typ ACValueList in der bereits alle benötigten Parameter laut ACClassConstructorInfo enthalten sind. Rufen Sie dafür die Methode ACClass.TypeACSignature() des entsprechenden iPlus-Typs auf, den Sie instanzieren möchten. Damit erhalten sie die eine ACValueList zurück, mit den notwendigen Parametern die Sie dann als einziges Element im acParameter-Array setzen.
  • object[] acParameter enthält exakt die Parameter, die laut ACClassConstructorInfo benötigt werden. Die StartACComponent-Methode wandelt intern das Array in eine ACValueList um.

 

 

2. Stoppen bzw. Entladen von Komponenten

Für das Stoppen und Entladen sind folgende Methoden (unterschiedliche Signaturen) in der ACComponent-Klasse definiert:

public virtual bool Stop()

Methode um sich selbst (this) zu stoppen.

 

public bool StopACComponent(string acIdentifier, bool deleteACClassTask = false)

Methode um eine Kind-Komponente mit dem acIdentifier zu stoppen.

 

public bool StopACComponent(IACComponent acComponent, bool deleteACClassTask = false)

Methode um die übergebene acComponent zu stoppen.


Folgende Methoden stehen für die Suche nach Instanzen im Anwendungsbaum zu Verfügung:

public IACComponent FindParentACComponent(Type type)

public IACComponent FindParentACComponent(ACClass type)

public TResult FindParentACComponent<TResult>() where TResult : IACComponent

public TResult FindParentACComponent<TResult>(Func<IACComponent, bool> selector) where TResult : IACComponent

public List<IACComponent> FindChildACComponents(Type typeOfComponent, int maxChildDepth = 0)

public List<IACComponent> FindChildACComponents(ACClass acClass, int maxChildDepth = 0)

public List<TResult> FindChildACComponents<TResult>(int maxChildDepth = 0) where TResult : IACComponent

public List<TResult> FindChildACComponents<TResult>(Func<IACComponent, bool> selector, Func<IACComponent, bool> deselector = null, int maxChildDepth = 0) where TResult : IACComponent

Beispiele: