Advanced programming


In the code sample of the chapter "Starting an ACComponent" the method "ACUrlCommand" was used to query the existence of an instance. The ACUrlCommand is a universal method that can be used to query the existence of an instance via a string (ACUrl) to:

  1. get references to components,
  2. query property values,
  3. execute method calls,
  4. start and stop Components,
  5. and send messages to other components.

In order to distinguish which of the previously listed functions (a-e) has to be executed via an ACUrlCommand, special command characters must be inserted in the ACUrl string - similar to the URL parameters for http or REST.

This method has the following signature:

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

In the first parameter, the command string (ACUrl) is passed. To call a method that has several parameters, enter the parameters in the following argument list. You can read more about this in the chapter "Synchronous Methods".

The following example code refers to a graphical example of an application tree from the parent chapter. grafische Beispiel eines Anwendungsbaumes aus dem übergeordneten Kapitel. It contains examples for the command types a and d.

 

ACUrl string command characters:

CharacterDescription
\Path separator. If the separator is set to the beginning, this means the beginning of an absolute addressing. Then an Identifier must follow. Example: \Mixery\Scale1
.\Relative addressierung: An object is adressed which is located in the directory (child) below. Then an Identifier must follow. Example: .\Flap1
..\Relative addressing: An object is located in the parent directory. Then an Identifier can follow. Example: ..\  or ..\Mixer1
?Check for the existence of an ACComponent instance. The question mark always has to be at the beginning of the character string! It is followd by the Url. Example: ?.\Flap1 or ?\Mixery\Scale1
#Start a new ACComponent instance (force an instance). Then an Identifier must follow.
Example: #Flap1 or .\#Flap1 or absolute \Mixery\Scale1\#Flap1
~Stopping an ACComponent instance. Then an Identifier must follow.
Example: ~Flap1 or .\~Flap1 or absolute \Mixery\Scale1\~Flap1
!Call a method. This must be followed by a Method Name (Identifier).
Example: !Open or .\Flap1!Open or absolute \Mixery\Scale1\Flap1!Open
§Access to a translated text. Then an Identifier of the translation text must follow.
Example: §Warning50001 or .\Flap1§Warning50001 or absolute \Mixery\Scale1\Flap1§Warning50001
^Sending a custom message/command.

In the User Management it can be defined which application should be started under which user account. If the server flag is set for the assigned application, then the instances defined in the application tree are instantiated as real .NET classes ("Real"-Components) when the application is started. If, on the other hand, the client flag is set, "proxy" instances are created instead. All property accesses and method calls to such instances take place in the background. This means that these accesses are delegated via network to another iPlus instance, where these instances exist as real classes. (Sources for the proxy design pattern and the principle functionality:

So if you write program code and reference instances that may be instantiated on other network nodes (or in another .NET application domain within the same machine) as real components, you must never cast into concrete .NET classes. Method calls must then always be made using ACUrlCommand or ExecuteMethod calls, for example.

In this case, you can only work with the following classes or work as a reference:

  • ACComponent (Base class of all components. Use this class so that you do not have to distinguish between a concrete class and a proxy class. It makes the code easier to read.)
  • ACComponentProxy (Base class for all proxies)
  • PWNodeProxy (Special derivation of ACComponentProxy, if workflow nodes are used. This means that on the server side these are derivatives of the PWBase class)
  • ApplicationManagerProxy (Special derivation for the root node. I.e. on the server side these are derivatives of the ApplicationManager class)

 


So that your own class can be instantiated by the iPlus runtime, you must declare it according to the following rules:

  1. Your class must be an indirect derivation of ACComponent.
  2. You must insert an ACClassInfo attribute class before the class declaration.
  3. The constructor of your class must not be parameterless, must be open, and must have an exactly predefined signature.
  4. OPTIONAL: Insert the ACClassConstructorInfo attribute class before the class declaration.

Sample Code:

 

 

1. Indirect derivation of ACComponent

There are a variety of base classes from which you can derive to program your own ACComponent. The following UML diagram shows the most important classes in the iPlus framework from which you should make a derivation (abstract classes are written in italic):

 

  • ACBSO: Abstract base class for businessobjects (dynamic instantiation during runtime; are mainly child elements of the businessobject instance). For programming programs that are opened by users from the menu.
    • ACBSOvb: Derivation of ACBSO, which makes it possible to work with the iPlsu database context (MES data).
    • ACBSONav: Abstract base class for navigable businessobjects that require access to database tables.
      • ACBSOvbNav: Derivation of ACBSONav, which enables you to work with the iPlus database context (MES data) and navigate/search in application tables.
  • PAClassAlarmingBase: Abstract base class for all instances that are instantiated on the server side as real instances of the iPlus framework in the application trees. This basic class facilitates the handling of hierarchical alarms and their long-term storage.
  • ACComponentManager: A root instance must be generated for each projected application. This is the base class for this.
    • ApplicationManager: Default class Projected applications - can also be derived specific to a project.
    • Businessobjects: Root application predefined by the iPlus framework that only manages business objects (dynamic instances).
  • PAClassPhysicalBase: Basis class for all objects in the application tree that represent a physical object.
    • PAModule: Physical component / assembly (pump, conveyor belt, valve, gripper arm, ...) see: https://www.isa.org/intech/20150402/ )
    • PAProcessModule: Usually physical assembly (but also virtual) consisting of modules and functions in which an automomer machining process can run. Process modules are allocated to workflow groups and the program flow is controlled by workflows.
      • PAProcessModuleVB: Assemblies from the iPlus-MES world that work with data from the application database context.
  • PABase: Base class for state-oriented instances that have a state machine and can perform tasks asynchronously over a longer period of time.
    • PAProcessFunction: Base class for functions that are child objects of PAProcessModule and describe which functionalities/interfaces an assembly has and can execute.
    • PWBase: Base class for any instances that are created dynamically at runtime and unloaded again when they are no longer needed. Which instances are to be created is described using workflows that the end user or project engineer creates or programs in advance.

 

2. Using ACClassInfo-Attribute classes

In order to be able to work with your self-defined classes in the iPlus development environment (configure application tree, create designs for your class, store configuration parameters...) they must first be made known.

This registration for the iPlus-Runtime is done by holding down the left control key during iPlus login and pressing the login button with the left mouse button.

The iPlus runtime then begins to examine all assemblies in the program directory.

It searches for classes that have an ACClassInfo attribute and registers them in the iPlus database as a known type (you can also define classes that are not indirect derivatives of ACComponent so that they can later be bound to the iPlus controls and rights management can also be done). However, these instances usually have to be instantiated manually). Attention: If the ACClassInfo attribute is forgotten, no properties and methods are analyzed via .NET reflection! The ACClassInfo attribute exists with different constructors, the following parameters are declared, which are necessary, so that the iPlus runtime later knows how to handle this class:

ParameterDescription
string acPackageName[Mandatory] Assignment to a package name. The package name is used for software licensing with the end customer.

string
acCaptionTranslation

[Mandatory] Translation tuple for the translation of the class. Syntax for translation tuple

  • I18N-Prefix: e.g. en, de, fr...
  • Curly bracket open and single apostrophe: {'
  • Text in corresponding language
  • Single apostrophe and curly bracket closing: '}

Example: en{'My english text'}de{'Mein deutscher Text'}

Global.ACKinds
acType

[Mandatory] Assignment to a category. The category serves on the one hand to simplify the use of the class in the development environment by showing or hiding certain classes depending on the context in which the class is needed, and on the other hand to program for project-specific logics to handle classes that are not yet instantiated at that time.
Global.ACStorableTypes
acStorableType
[Mandatory] Specifies whether the state of this instance (perilizable properties) may be saved or not. This only works for classes instantiated in the application tree. Classes instantiated in the Root project are usually not perilizable. Multiple instantiatable classes are derivatives of ACBSO (business objects) in the rules. In this case, isMultiInstance must have the value True.
bool
isMultiInstance
[Mandatory] Indicates whether the class can be instantiated more than once as a child object. Since the ACIdentifier must be unique in the member list/child list, the iPlus runtime automatically assigns a sequential instance number when a new instance has to be created. This instance number is appended to the ACIdentifier string by means of brackets. Example: "BSOMaterial(1), BSOMaterial(2)".
bool
isRightmanagement
[Optional] Flag whether this class is responsible for rights management and whether rights for properties, methods and designs can be assigned in group management.
string
pwInfoACClass
[Optional] Only relevant for classes that are derivatives of PAProcessFunction. pwInfoACClass specifies the class name of the workflow class (a derivative of PWNodeProcessMethod) that is compatible with this PAProcessFunction. Other wording: Specifies the workflow class that can call this function and knows its parameter list.
string
qryConfig
[Optional] Only relevant for classes that are derivatives of ACBSONav (Navigable Business Objects). qryConfig specifies the name of the primary ACQueryDefinition to be loaded by default to filter the records displayed in Explorer. You can use the iPlus development environment to look up which queries exist in the path \Root\Queries. New queries are automatically entered by declaring the ACQueryInfoPrimary attribute using an entity class and then starting the program with Ctrl-Login.
string bsoConfig[Optional] Only relevant for classes that are entity classes. bsoConfig specifies the name of the business object class that is responsible for managing this entity class. If this entity class is displayed in an "item control" such as a combo box, you can use the context menu to navigate to the relevant business object and display or manage the corresponding entity.
Int16
sortIndex
[Optional] Sort sequence for presenting the class in the iPlus development environment.
object[]
acClassChilds

[Optional] Array to define relationships to other entity objects. Example:

[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. Signature of the constructor

Each constructor of a class derived from ACComponent must have the same signature:

ParameterBeschreibung
ACClass
acType

The table ACClass contains meta information for a class that is necessary for iPlus runtime to be able to instantiate this class. This includes the following properties/table fields:

  1. ParentACClass (ParentACClassID): Relation Relation to parent class. This relationship defines the tree structure of an application is defined (Composition).
  2. BasedOnACClass (BasedOnACClassID): Relation to the base class from which this class is derived. This relationship defines the inheritance of a class.
  3. AssemblyQualifiedName: Specifies which .NET-Type to instantiate using Reflection

ACClass inheritance relationships are often virtual. This means that although the same .NET type is created, it is two different types or instances from the point of view of the iPlus framework. Each instance defined in the application project is automatically a new ACClass that is virtually inherited. However, from a .NET point of view, the same .NET type is instantiated if the AssemblyQualifiedName is the same for two different instances.

IACObject
content

Instances that are not derivatives of ACBSO (usually persistent instances in application trees) gets an ACClassTask object as "content" passed. ACClassTask table entries ensure that the state of all persistable instances in the application trees is persisted and that after the restart of the iPlus service, the last state of the process (all object states before the last shutdown) can be restored again.

For dynamic instances (derivatives of ACBSO, derivatives of PARole ...) that are not persistable this paramater usually set to null.

IACObject
parentACObject

Parent-ACComponent under which this instance is created as a child object.
ACValueList
parameter

Individual construction parameters are passed via ACValueList. An ACValueList is a list that contains entries from ACValue. ACValue is a class that has essentially three properties:

  1. string ACIdentifier: Unique ID/Name of the parameter
  2. object Value: parameter value
  3. Type ObjectFullType: Datatype of parameter value


If you want to start a component yourself (see section below) you need to instance ACValueList. So that you don't have to enter the parameter entries yourself, call the ACClass.TypeACSignature () method of the corresponding iPlus type you want to instance (= parameter acType). The iPlus framework through the ACClassConstructorInfo-attribute class knows which can be inserted in addition to the ACClassInfo attribute class prior to class declaration (see point 4).

string
acIdentifier = ""

Unique ID, within the child instances of the Parent ACComponent, which this instance will receive. If string is empty, the runtime assgins the ID itself using the ACType parameter identifier.

 

4. Optional: ACClassConstructorInfo

The meta information about the construction parameters that a class expects in the "ACValueList parameter" should be defined via the ACClassConstructorInfo attribute class. The ACClassConstructorInfo attribute class is only an object array, which in turn contains entries with object arrays (see code example above). The inner object[] entries may only contain three entries in the following order:

Index 0:
string
Name of the parameter that is later in the ACIdentifier of the ACValue entry.
Index 1:
Global.ParamOption
Specifies whether the parameter is Mandatory or Optional.

Index 2:
System.Type

.NET-Typ, of the parameter value.

 


Whenever you have made changes to your classes, properties or methods and these have the attribute classes ACClassInfo, ACPropertyInfo, ACMethodInfo .... so that they are announced in the iPlus type system, you must 

hold the left control key while  iPlus login and press the login button with the left mouse button!


In the previous section you have learned how to start and stop ACComponents via ACUrlCommand. You can do the same directly without ACUrlCommand.

 

1. Starting or instantiating components

The following methods (different signatures) are defined in the ACComponent class for this purpose:

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 = "")

 

In the parameter "acIdentifer" you pass the name of the child class/instance that was defined by the development environment in the application tree below the class you are currently in (this-Pointer). If no class with this ACIdentifier could be found in the application tree, the system checks whether it is a business object class (derived from ACBSO). If so, a new dynamic "child instance" is created below the instance in which they are currently located.

The parameter "object content" is passed to the second contructor parameter "IACObject content" during construction. 

The parameter "object[] acParameter" must contain the parameters that are passed to the fourth constructor parameter as the "ACValueList parameter". You can do this in two different ways:

  • object[] acParameter contains only one element of the type ACValueList which contains all required parameters according to ACClassConstructorInfo. To do this, call the method ACClass.TypeACSignature() of the corresponding iPlus type that you want to instantiate. This way you get the ACValueList filled with the necessary parameters which you then set as the only element in the acParameter array.
  • object[] acParameter contains exactly the parameters that are required according to ACClassConstructorInfo. The StartACComponent method internally converts the array into an ACValueList.

 

 

2. Stopping or unloading components

The following methods (different signatures) are defined in the ACComponent class for this purpose:

public virtual bool Stop()

Method to stop yourself (this).

 

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

Method to stop a child component with an acIdentifier.

 

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

Method to stop the acComponent passed.


You can use the following methods to search for instances in the application tree:

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

 

Examples: