Erweiterte Programmierung


In diesem Kapitel lernen Sie wie man mit iPlus sogenannte Prozessanwendungen programmiert. Eine Prozessanwendung ist eine mittels der iPlus-Entwicklungsumgebung erstellte Kundenendlösung mit der Geschäfts-, Produktions- oder sonstige Prozesse eines Unternehmens oder eines technischen Systems abgebildet sind.

Die Grundlage dafür sind Anwendungsdefinitionen und Anwendungsprojekte in denen diverse Klassen deklariert und instanziiert werden, die auf ACComponents basieren.

Das folgende Klassendiagramm ist eine Erweiterung des ACComponent-Klassendiagramms und stellt einen Auszug der Klassenbibliothek, mit dem Fokus auf industrielle Anlagen und Maschinen, dar:

 

In diesem Kapitel liegt der Fokus auf Klassen, die meist ein physikalisches Objekt in der Realität abbilden, und in Anwendungsbäumen konfiguriert sind. Beim Hochfahren des iPlus-Dienstes werden diese Klassen instanziiert und stellen das gesamtphysikalische Modell dar. Um ein technisches Gesamtsystem vollständig zu beschreiben und zu steuern zu können benötigt man auch nichtphysikalische Elemente wie zum Beispiel Funktionen oder sonstige logische Objekte. Auch diese werden in den Anwendungsbäumen konfiguriert und werden beim Starten sofort instanziiert. Wir sprechen daher von sogenannten statischen Instanzen, weil Sie nicht "just in time" während der Laufzeit in das Modell geladen werden. Das Gegenteil sind dynamische Instanzen auf die im nächsten Kapitel "Workflowklassen" näher eingegangen wird.

Klassen die

  • statisch instanziiert werden, sind von den Basisklassen PAClassPhysicalBase und PAProcessFunction abgeleitet und Klassen die
  • dynamisch instanziiert werden, sind von PWBase abgeleitet.

Alle Prozessanwendungs-Klassen sind Alarmfähig und sind daher indirekte Ableitungen von "gip.core.autocomponent.PAClassAlarmingBase". 

Wenn eine Prozessanwendungsklasse ein physikalisches Objekt repräsentieren soll, dann ist sie indirekt von "gip.core.PAClassPhysicalBase" abgeleitet. Andernfalls, wenn es sich um ein logisches Objekt handelt (immateriell), dann ist die Klasse indirekt von "gip.core.autocomponent.PABase" abgeleitet.


Die primitivste Variante eines physikalischen Objektes ist ein Modul (Klasse "gip.core.autocomponent.PAModule"). Der Begriff ist abgeleitet von den Begriffen "Control Module" und "Equipment Module" aus der Norm "DIN EN 61512". 

Wenn Sie also elektronische Bauteile oder Baugruppen abbilden wollen, leiten Sie von dieser Basisklasse ab. In der Assembly "gip.core.processapplication" sind bereits einige Bauteile vordefiniert:

 

Sensoren

Für Sensoren dient die Basisklasse PASensorBase. Sensoren werden dann unterschieden nach digitalen Sensoren (PAESensorDigital) und analogen Sensoren (PAESensorAnalog).

Digitale Sensoren

  • PAEContactor: Motorschutzschalter
  • PAESafetyRelay: Schutzrelais
  • PAEEmergencySwitch: Not-Aus-Schalter
  • PAEJamSensor: Staumelder
  • PAEMaterialSensor: Materialmelder
  • PAEMisalignment: Schieflaufwächter
  • PAERotationControl: Drehwächter
  • PAEThermistor: Thermischer Motorschutz

Analoge Sensoren 

  • PAEThermometer: Thermometer
  • PAEHygrometer: Feuchtigkeitssensor
  • PAEAmmeter: Stommessgerät
  • PAEScaleGravimetric: Gravimetrische Waage
  • PAEScaleVolumetric: Volumentrische Waage

 

Steuerungsmodule

Steuerungsmodule elektrische Bauteile die angesteuert werden können. Sie werden unterschieden nach Aktoren (PAEActuatorBase) oder Antrieben (PAEDriveBase). 

Aktoren

  • PAEActuator1way: Ein-Wege-Stellglied
    • PAEActValve: Ein-Wege-Ventil
    • PAEActSlider: Schieber
    • PAEActFlap: Klappe
  • PAEActuator2way: Zwei-Wege-Stellglied (Ein Eingang, Zwei Ausgänge)
    • PAEActFlap2: Zwei-Wege-Klappe
    • PAEActValve2: Zwei-Wege-Ventil
  • PAEActuator1way_3pos: 1-Wege Stellglied mit 3 Positionen
    • PAEActDosingSlider: Dosierschieber
    • PAEActValve_3pos: Dosierventil Grob/Fein
  • PAEActuator2way_In: Zwei-Wege-Stellglied (Zwei Eingänge, Ein Ausgang)
    • PAEActDiverter: Zwei-Wege-Weiche
    • PAEActValve2_In: Zwei-Wege-Ventil ohne Grundstellung
  • PAEActuator3way: Drei-Wege-Stellglied
    • PAEActValve3: Drei-Wege-Ventil
    • PAEActValve3_3Flange: Drei-Wege-Ventil mit drei Flanschen
  • PAEActuator2way_3pos: Zwei-Wege-Stellglied mit Grundstellung
    • PAEActValve2_3pos: Zwei-Wege-Ventil mit Grundstellung
  • PAEActuator2way_1plus2: Zwei-Wege-Stellglied mit 2 Positionen gleichzeitig
    • PAEActValve2_1plus2: Zwei-Wege-Ventil mit 2 Positionen gleichzeitig
  • PAEActuator2way_Analog: Zwei-Wege-Stellglied analog
    • PAEActMixingValve: Mischventil
    • PAEActValve2_Analog: Mischventil 

Antriebe

Motoren werden unterschieden nach Motoren mit Anlaufschaltung (PAEEMotorStartCtrl) oder frequenzgeregelten Motoren (PAEEMotorFreqCtrl).

  • PAEEMotor1D: Motor mit einer Drehrichtung
  • PAEEMotor1D2P: Motoren mit einer Drehrichtung und 2 Polen 
  • PAEEMotor2D: Motor mit zwei Drehrichtungen
  • PAEEMotor2D2P: Motoren mit einer Drehrichtung und 2 Polen
  • PAEEMotorFreq1D: Frequenzgeregelter Motor mit einer Drehrichtung
  • PAEEMotorFreq2D: Frequenzgeregelter Motor mit zwei Drehrichtungen

Entsprechend dem Kompositionsprinzip können Module rekursiv zu größeren Baugruppen zusammengefasst werden. Größere Baugruppen wie z.B. eine Maschine sind nicht nur physikalische Objekte, sondern haben einen Zweck. Sie stellen Funktionen bereit damit Produktionsprozesse oder Prozesse im Allgemeinen durchgeführt werden können. Diese werden daher "Prozessmodule" genannt und die Basisklasse ist "gip.core.autocomponent.PAProcessModule". Prozessmodule bestehen aus einer Sammlung von Prozessfunktionen (PAProcessFunction) die weiter unten beschrieben sind. 

Prozessfunktionen, erscheinen als asynchrone Methoden an einem Prozessmodul, die in der Regel von Workflowklassen gestartet werden. Damit diese Methodenaufrufe über einen standardisierten Weg erfolgen, implementiert PAProcessModule die Schnittstelle IACComponentTaskExec.

Asynchrone Aufrufe dürfen allerdings nur dann erfolgen, wenn das Prozessmodul zuerst durch eine Workflowgruppe belegt worden ist (Lesen Sie dazu den Abschnitt "Laufzeitverhalten und physikalisches Modell" durch). Die "Belegung" eines Prozessmoduls erfolgt durch die Verwendung des Servicepunktes Semaphore:

[ACPropertyPoint(true,1)]
public ACPointServiceACObject Semaphore { get; }

Standardmäßig ist die Kapazität der Semaphore = 1. Das bedeutet das maximal eine Workflowgruppe zum gleichen Zeitpunkt das Prozessmodul bzw. Ressource belegen kann. Eine abweichende Kapazität können Sie durch Überschreiben der Methode public virtual uint OnGetSemaphoreCapacity() in Ihrer Ableitung einstellen.

Von PAProcessModule gibt es diverse Ableitungen für die MES-Erweiterungen in der Assembly "gip.mes.processapplication":

  • PAProcessModuleVB: Ein Prozessmodul, das mit Anwendungsdaten umgehen kann (Produktionsauftrag, Kommissionierauftrag,...). Endanwender interagieren mit Prozessmodulen über die Oberfläche, um zu den Anwendungsdaten zu gelangen und mittels Geschäftsobjekten die Daten zu verwalten.
  • PAMSilo & PAMTank: Eine Erweiterung von PAProcessModuleVB, um Behältnisse und deren Bestand zu verwalten.
  • PAMIntermediatebin: Zwischenbehältnis
  • PAMHopperscale: Behälterwaage
  • PAMParkingspace: Stellplatz
  • PAMLoadingstation: Befüllstation oder Annahmestelle
  • PAMBeltscale: Bandwaage
  • Viele weitere prozesstechnische Objekte...

 


Die Klasse "gip.core.autocomponent.PABase" besitzt die Eigenschaft ACState, um den aktuellen Prozesszustand zu beschreiben, in dem sich die Instanz gerade befindet. Eine Zustandsänderung führt dazu, dass die entsprechende statusabhängige Methode aufgerufen wird. Der Datentyp der ACState-Eigenschaft ist "gip.core.autocomponent.ACStateEnum" und er ist angelehnt an den ISA-S88-Standards:

public enum ACStateEnum : short
{
SMIdle = 0,
SMStarting = 100,
SMRunning = 200,
SMCompleted = 300,
SMResetting = 400,

SMPausing = 210,
SMPaused = 211,
SMResuming = 212,

SMHolding = 220,
SMHeld = 221,
SMRestarting = 222,

SMStopping = 310,
SMStopped = 311,

SMAborting = 320,
SMAborted = 321,

SMBreakPoint = 90,
SMBreakPointStart = 91,
}

Die möglichen Zustandsübergänge können Sie im Kapitel "Prozesssteuerung" nachlesen. Die von PABase abgeleiteten Klassen müssen nicht unbedingt alle Zustände und Zustandsübergänge unterstützen. Workflowklassen, die von PWBase abgeleitet sind, verwenden in der Regel nur einen Teil der ACState-Zustände. Funktionen, die von PAProcessFunction abgeleitet sind, verwenden meist alle Zustände. Schauen wir uns nun im nachfolgend an wie man Prozessfunktionen implementiert:


Bevor Sie diesen Abschnitt lesen machen Sie sich mit folgenden Konzepten vertraut:

  1. Statusabhängige Methoden
  2. Asynchrone Methoden
  3. Schnittstelle IACComponentTaskExec
  4. Physikalisches Modell aus Sicht des Anwenders.

PAProcessFunction ist eine abstrakte Klasse. Sie müssen sie ableiten, um sie verwenden zu können. PAProcessFunction ist so programmiert, dass Sie überwiegend aus virtuellen Methoden und Eigenschaften besteht, die bei Bedarf überschrieben werden können, um das Standardverhalten zu verändern. Einige davon müssen allerdings überschrieben werden. Welche das sind, wird weiter unten anhand des Beispielcodes erklärt.

 

Statusmethoden

Für jeden ACStateEnum-Wert, den die ACState-Eigenschaft annehmen, kann gibt es eine entsprechende Statusmethode:

public virtual void SMStarting()
public virtual void SMRunning()
public virtual void SMCompleted()
public virtual void SMResetting()
public virtual void SMPausing()
public virtual void SMPaused()
public virtual void SMResuming()
public virtual void SMHolding()
public virtual void SMHeld()
public virtual void SMRestarting()
public virtual void SMAborting()
public virtual void SMAborted()
public virtual void SMStopping()
public virtual void SMStopped()

Diese virtuellen Methoden werden entweder bei Statusänderung aufgerufen oder zyklisch in Zeitabständen von einer Sekunde. Der zyklische Aufruf wird aktiviert durch den Aufruf der Methode "PABase.SubscribeToProjectWorkCycle()" und deaktiviert durch den die Methode "PABase.UnSubscribeToProjectWorkCycle()". PAProcessFunction verwendet diese Methoden, falls ein Fehler aufgetreten ist, damit in zyklischen Zeitabständen geprüft wird, ob der Fehlerzustand in der Zwischenzeit behoben wurde. Danach wird UnSubscribeToProjectWorkCycle() aufgerufen, damit der Prozessor nicht unnötig belastet wird, wenn es nichts zu tun gibt. Falls Sie in Ihrer Ableitung solche Fehlerzustände in zyklischen Zeitabständen überwachen möchten, dann verwenden Sie ebenfalls diese zwei Methoden. Die zyklischen Aufrufe führt die Klasse PABase in der Methode DoWork() durch. Sie liest die ACState-Variable und schaut in der Methodenliste nach, ob es eine Methode mit dem gleichen Namen gibt. Wurde die Methode gefunden, dann wird diese aufgerufen. In der Methodenliste werden nur die Methoden aufgelistet, die mit der Attributklasse ACMethodState versehen wurden und die Eigenschaft IsPeriodic auf true gesetzt ist. Dies ist bei den oben aufgezählen Methoden  der Fall. Beispiel:

[ACMethodState("en{'Starting'}de{'Startend'}", 20, true)]
public virtual void SMStarting()

 

Transitions-Methoden 

Zustandswechsel erfolgen durch sogenannte "Transitionen", die entweder automatisch im Hintergrund ausgeführt werden oder durch UI-Interaktion, indem der Anwender eine der Transitions-Methoden aufruft:

public virtual void Pause()
public virtual void Resume()
public virtual void Hold()
public virtual void Restart()
public virtual void Abort()
public virtual void Stopp()
public virtual void Reset()

[ACMethodAsync("Process", "en{'Start'}de{'Start'}", (short)MISort.Start, false)]

public virtual ACMethodEventArgs Start(ACMethod acMethod)

Die Start-Methode unterscheidet sich gegenüber den anderen Transitions-Methoden darin, dass sie eine asynchrone Methode ist, um die Prozessfunktion überhaupt zu starten.

Der Aufruf von Transitionsmethoden ist durch die entsprechende IsEnabled-Methode abgesichert, damit nicht ungültige Zustandsübergänge stattfinden. Welche Zustandsübergänge erlaubt sind, wird mittels Zustandsübergangstabellen definiert. Die Zustandsübergangstabelle für ACState ist in der abstrakten Basisklasse gip.core.autocomponent.PAFuncStateConvBase implementiert.

 

Zustandsübergangstabelle PAFuncStateConvBase

In PAFuncStateConvBase sind zwei statische Methoden definiert, mit denen die Standard-Zustandsübergangstabelle abgefragt werden kann:

public static ACStateEnum GetDefaultNextACState(ACStateEnum acState, string transitionMethod = "")
public static bool IsEnabledTransitionDefault(ACStateEnum acState, string transitionMethod, PAProcessFunction paProcessFunction)

GetDefaultNextACState() gibt den nächsten Folgezustand, in Abhängigkeit des im Parameter "acState" übergebenen aktuellen Zustands, zurück. Der zweite optionale Parameter gibt an welche Transistion bzw. Transitionsmethode aufgerufen wird. Die Namen der Transistionsmethoden sind in den Konstanten der statischen Klasse ACStateConst deklariert:

ACStateConst.TMStart, ACStateConst.TMRun, ACStateConst.TMPause, ACStateConst.TMResume, ACStateConst.TMHold, ACStateConst.TMRestart, ACStateConst.TMAbort, ACStateConst.TMStopp, ACStateConst.TMReset

Wenn der Parameter "transitionMethod" leer gelassen wird, gibt den Standrad-Folgezustand zurück. Andernfalls der Folgezustand der eintreten wird, wenn die entsprechende Transitionsmethode aufgerufen wird. 

Welche Transintionsmethode in einem bestimmten Zustand überhaupt aufgerufen werden darf gibt die Methode "IsEnabledTransistionDefault()" zurück.

 

Individuelle Zustandsübergangstabelle

Wenn Sie einen abweichenden Zustandsautomaten programmieren möchten oder Sie ein externes System angebunden haben (z.B. über eine SPS- oder OPC-Verbindung) das andere Variablen verwendet, mit dem ein anderer Zustandsautomat implementiert ist und dieser in den iPlus-Zustandsautomaten überführt werden muss, dann leiten Sie die PAFuncStateConvBase-Klasse ab und fügen Sie Ihre Klasse als Kind-Komponente der PAProcessFunction hinzu. Es müssen folgende Methoden in Ihrer "Konverterklasse" implementiert werden:

public abstract ACStateEnum GetNextACState(PAProcessFunction sender, string transitionMethod = "");
public abstract bool IsEnabledTransition(PAProcessFunction sender, string transitionMethod);
public abstract MsgWithDetails SendACMethod(PAProcessFunction sender, ACMethod acMethod);
public abstract PAProcessFunction.CompleteResult ReceiveACMethodResult(PAProcessFunction sender, ACMethod acMethod, out MsgWithDetails msg);

Eine Prozessfunktion ruft anstatt der statischen Methode "GetDefaultNextACState" die von Ihnen implementierte Instanzmethode "GetNextACState()" auf und anstatt der statischen Methode "IsEnabledTransistionDefault()" die Instanzmethode "IsEnabledTransition()".

Im Zustand SMStarting ruft die Basisimplementierung die Methode "SendACMethod()" auf damit die Parameter an das externe System versendet werden. Im Zustand SMCompleted wird dagegen "ReceiveACMethodResult()" aufgerufen, um das Ergebnis vom externen System abzufragen. Diese beiden Methoden müssen Sie in Ihrer Konverterklasse ebenfalls ausprogrammieren.

Konverterklassen haben in der Regel noch weitere netzwerkfähige Zieleingenschaften deklariert, die wiederum per Eigenschaftsbindung an Quelleigenschaften von Kommunikationstreiber-Klassen gebunden sind. Diese Eigenschaften geben im Grunde genommen den "Zustandsautomaten ihres eigenen Standards" wider. Programmieren Sie Ihre Konverterklasse Ereignisorientiert, so dass Sie auf Änderungsereignisse dieser Eigenschaften reagieren und diese in den ACState-Zustandsautomaten überführen und direkt die CurrentACState-Eigenschaft der Elternprozessfunktion (ParentACComponent) setzen.

 

Beispiel

Folgender Programmcode wurde dem Beispielprojekt entnommen, das Sie auf der Startseite herunterladen können, um die Schritte nachvollziehen zu können wie man eine eigene Prozessfunktion implementiert:

 

  1. Leiten Sie von PAProcessFunction ab. Die ACClassInfo-Attributklasse muss folgendermaßen konstruiert werden:
    1. Verwenden Sie "Global.ACKinds.TPAProcessFunction" um iPlus bekannt zugeben, dass es um eine Prozessfunktion handelt.
    2. "Global.ACStorableTypes.Required" muss verwendet werden, weil z.B. die ACState-Eigenschaft persistierbar ist und der Zustand beim Neustart des Dienstes wiederhergestellt werden muss.
    3. Referenzieren Sie die zugehörige Workflowklasse, die mit dieser Prozessfunktion kompatibel ist. In diesem Beispiel ist das "PWOrder.PWClassName" die Sie als Beispiel im nachfolgenden Kapitel (PWNodeProcessMethod) kennen lernen werden.
  2. Deklarieren Sie Parameter- und Rückgabeliste der virtuellen Methode (ACMethod), die beim asynchronen Aufruf der Start-Methode übergeben wird. Zum Schluss generieren Sie eine Instanz von ACMethodWrapper, um die virtuelle Methode der iPlus-Runtime bekannt zu geben, indem Sie "RegisterVirtualMethod()" aufrufen.
  3. Deklarieren einen Execute-Helper für die statischen Aufrufe, indem Sie den statischen Execute-Handler als Delegat der RegisterExecuteHandler()-Methode übergeben.
  4. Überschreiben Sie die Start-Methode und veröffentlichen Sie sie, indem Sie ACMethodAsync-Attribuklasse verwenden.
  5. Ab diesem Moment ist eine Prozessfunktion vollständig programmiert und kann in der iPlus-Entwicklungsumgebung verwendet werden. Jedoch fehlt noch der eigentliche Programmcode für das, was die Funktion eigentlich machen soll. Deshalb ist diesem Code-Beispiel die SMRunning-Methode überschrieben worden und aus Demonstrationszwecken wartet sie drei zyklische Aufrufe ab, weil sie zuerst mit "SubscribeToProjectWorkCycle" aktiviert worden ist und danach die zyklische Abarbeitung mit "UnSubscribeToProjectWorkCycle" deaktiviert wird.
  6. In diesem Schritt wird die eigentliche Funktionalität programmiert. In diesem Beispiel ist der eigentliche Zweck "Auftragsdaten" in eine Datei zu schreiben die vom asynchronen Aufrufer (Workflowklasse) im Parameter "Content" übergeben worden sind. Weil das Schreiben einer Datei eine längere Zeit in Anspruch nehmen könnte, wird dieser Schreibvorgang an die DelegateQueue der Wurzel-ACComponent (Applicationmanager) übergeben. Damit ist der aufrufende Thread (zyklische Abarbeitung) nicht blockiert und andere Funktionen werden anschließend sofort bedient.
  7. Im letzten Schritt bringen Sie den Zustandsautomaten in den "SMCompleted"-Zustand. Wenn die Hilfseigenschaft ACStateConverter nicht "null" ist, dann haben Sie eine eigene Konverterklasse im Anwendungsbaum als Kindinstanz hinzugefügt. In diesem Fall entscheidet Ihre Konverterklasse darüber was der Folgezustand sein wird - diese könnte unter Umständen auch ein andere Zustand als "SMCompleted" sein. Wenn die Hilfseigenschaft ACStateConverter "null" ist, wird die Standardimplementierung verwendet und SMCompleted zurückgegeben.

Zum Abschluss noch ein wichtiger Hinweis: Überschreiben Sie immer die "SMIdle()"-Methode, um ihre privaten Feldwerte zurückzusetzen. Im SMIdle-Zustand muss eine Funktion immer in den Grundzustand gebracht werden - so wie sie initial bei der Generierung ursprünglich im Heap angelegt worden ist!