Code-based Rules

More complex scenarios, which are currently not supported by the graphical view can be implemented via the code editor in the Scala programming language. Similar to Mappings via drag and drop, there is no knowledge of the underlying communication protocol (e.g., MQTT, OPCUA, etc.) needed. Protocols are hidden behind the corresponding Information Models.

Basics - Rule construct

A Rule is always starting with a Trigger (1). The Trigger can represent a Variable, an Event or a Command; within one of the selected Information Models. After the trigger call mapTo (2) and define the function body by adding curly braces (3). Depending on the Trigger declare the TriggerInstance (4). Depending on the type of the Trigger use the naming accordingly:

RuleConstruct

The Source (5) is the content of the TriggerInstance (e.g., In case the Trigger is a Variable, then is the Source an Instance of that Variable) In order to assign the Source to the Target, add the := operator (6). The Target can be any variable you want to map to (7).

RuleConstruct

Variable to Event Mapping

In this case the mapping of the Complex Variable CurrentOrder in the EquipmentModel and of a Simple Variable in the EnterpriseModel to the EquipmentNewOrderStart Event in the MesModel is described.

  • Trigger: EquipmentModel.StartNewOrderFlag (line 1)

  • TriggerInstance of EquipmentModel.Alarm: variable (line 1)

  • Since values are assigned to an Event, call the function - send, on the EquipmentNewOrderStartEvent (line 2) and define the TriggerInstance - event (line 2).

  • The Targets are defined by entering the path of the variables in the event - event.EquipmentId (line 4).

Rule - StartOrder - Variable/Event
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
EquipmentModel.Alarm mapTo {variable =>
   MesModel.EquipmentAlarm.send(event => {
     Try {
       event.EquipmentId := EnterpriseModel.EquipmentName
       event.OrderNr := EquipmentModel.CurrentOrder.OrderNr
       event.MaterialID := EquipmentModel.CurrentMaterialID
       event.AlarmInfo := EquipmentModel.AlarmInfo
       CommunicationLogger.log(variable, event)
     }
   })
}

Event to Variable Mapping

In this case the mapping of values inside the TransferNewOrder Event from the MesModel into variables from the EquipmentModel is described.

  • The Trigger is defined by entering the path of the Event - MesModel.TransferNewOrder (line 1). Since an Event is used as Trigger, the TriggerInstance is named accordingly - event (line 1).

  • In the function body provide the Complex Variable NewOrder and the Simple Variable NewMESOrderFlag with data from the MesModels TransferNewOrder Event.

  • Targets are defined by entering the path of the variables like - EquipmentModel.NewOrder.OrderNr (line 3).

  • In order to assign values to OrderNr, MaterialNr and Quantity of the Complex Variable NewOrder, enter the TriggerInstance event followed by the variable name of the TransferNewOrder Event - event.OrderNr (line 3).

  • In this case it is also possible to provide the variable NewMesOrderFlag with a Boolean like - true (line 6).

Rule - TransferNewOrder - Event/Variable
1
2
3
4
5
6
7
8
MesModel.TransferNewOrder mapTo { event =>
   Try {
     EquipmentModel.NewOrder.OrderNr := event.OrderNr
     EquipmentModel.NewOrder.MaterialNr := event.MaterialNr
     EquipmentModel.NewOrder.Quantity := event.Quantity
     EquipmentModel.NewMESOrderFlag := true
   }
 }

Commands Mapping

The following scenario describes a Rule mapping incoming data from a file to MQTT. When the FileEvent is triggered - the rule executes first the DatabaseCommand to retrieve data from a database.

  • Trigger is defined by entering the path of the Event - file.FileEvent (line 1). Since an Event is used as Trigger, the TriggerInstance should be named accordingly - event (line 1).

  • Inside the function body execute a Command. The execution of a Command is defined by entering the path of the Command. At the end of the path, call the execute function (line 2). The TriggerInstance is named accordingly - command (line 4).

  • The lines 4-6 show the first part of the Command. Here assign values from the source model to the Command Parameters.

  • Since every Command has a Reply, we need to define the reply section - (line 8).

  • In this case send out the data over MQTT after the data is retrieved from the database. In the reply function body, enter the path of the MqttEvent. Since this is the 2nd Event, the TriggerInstance can be named - event1 (line 1).

  • Inside the function body assign values from the FileEvent (line 11-13) as well as from the Reply (line 14-15) to the MqttEvent.

Rule - File2MqttWithDB - Event/Commands
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
 file.FileEvent mapTo {event =>
   database.DatabaseCommand.execute(command => {
     Try {
       command.orderNr := event.orderNr
       command.materialNr := event.materialNr
       CommunicationLogger.log(event, command)
     }
   }, reply => {
     mqtt.MqttEvent.send(event1 => {
       Try {
         event1.Quality := event.quality
         event1.OrderNr := event.orderNr
         event1.MaterialNr := event.materialNr
         event1.Customer := reply.customer
         event1.Product := reply.product
         CommunicationLogger.log(reply, event1)
         }
     })
   })
 }

Mapping with Lists

The following scenario describes a Rule that is mapping incoming data from a file to MQTT. The MQTT Model contains a List called DataList. Note that lists can only be mapped in the code view.

  • Create a variable listItem that holds a reference of a newItem in the DataList (line 6)

  • Call the variable from the listItem and assign the value from the file event (line 8)

Rule - FileToMQTT - Lists
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
csv.FileEvent mapTo { event =>

     event.items.foreach { item =>
       mqtt.MqttEvent.send(event1 => {
         Try {
           val listItem = event1.DataList.newItem

           listItem.Timestamp := item.Timestamp
           listItem.Pressure := item.Alarmlevel

           CommunicationLogger.log(event, event1)
         }
       })
     }
 }

Rule Scheduler

Rules can be scheduled to run continuously at a fixed rate. Instead of having an element of the Information Model defined as a Trigger the fixedRateScheduler method can be used. Therefor define the Trigger as the following: _trigger.fixedRateScheduler(<Cron Expression>) (line 2).

Scheduled Rule
1
2
3
4
5
def rule_ScheduleNode(): Unit = {
   _trigger.fixedRateScheduler("0/1 * * * * ? *") mapTo(() => {
     model1.StringVariable := model2.StringVariable
   })
 }

Type Assignment

When both target and source nodes are of the same data type the assignment of variables can be shorten:

Type Assignment with Events
1
event1 := event2

Logging

Logging can be added in the Rule implementation by calling - CommunicationLogger.log (line 5)

Rule with Logging
1
2
3
4
5
6
7
8
EquipmentModel.Alarm mapTo {variable =>
   MesModel.EquipmentAlarm.send(event => {
     Try {
       event.EquipmentId := EnterpriseModel.EquipmentName
       CommunicationLogger.log(variable, event)
     }
   })
}

Compiling

You can compile the code for the selected Rule by clicking the “Compile” button (1) and check for compilation errors before saving the Rule.

CompileCode