Processing Networks
The HumanOS® Kernel comes with a built-in semantic processing network (SPN) capability. This allows to process and correlate data.
These processing networks are not hardwired! Typically, they are placed in the system as a schema. Due to rules, a processing network class is dynamically instantiated and wired to data nodes; and removed dynamically once removal rules are triggered.
In the following graphic notations, we use following terminology:
Name | Short. | Description |
---|---|---|
Processing Node | Proc | Processing nodes describe active transformation and evaluation nodes. Also referred as processors. |
Processing Network | N | The processing network is a special group node containing the complete processing schema |
Input Ports | Pi | Input ports are openings of processing nodes. They allow the processing node to access the outside world (reading). |
Output Port | Po | Output ports are openings of processing nodes. They allow the processing node to access the outside world (writing). |
Link | Link | Links between ports or ports and data nodes. Also referred as port couplings, they transfer the information from output ports to input ports of other processors. |
The root object is a "ProcessingNetwork".
Attribute | Description | Data Type |
---|---|---|
Id | Id of the network. Empty if multiple instances can be created from that schema | System.Guid |
Name | Name of the network | System.String |
Processors | Array of processors in the network | TProcessingNodeInfo[] |
Links | Array of interconnecting links between the processors. | TProcessingLinkInfo[] |
DataNodes | Array of data nodes. | TDataNodeInfo[] |
Example:
{
"ProcessingNetworks": [
{
"Id": "77B6AACB-B306-4E50-901A-9E12CC706E1D",
"Name": "HumanOS.PeSeL.Correlations.DataProcessors",
"Processors": [],
"Links": [],
"DataNodes": []
}
]
}
Processor Nodes
A processor defines an active code able to process data periodically or event-driven.
The processor node is described by following attributes:
Attribute | Description | Data Type |
---|---|---|
Id | Id of the processor. Empty if processor can be instantiated multiple times. | System.Guid |
Name | Name of the processor | System.String |
Type | Type of processor. | EProcessingNodeType |
AsynchProcessing | The processing is decoupled from the event sender if set to True. If the processing uses much processor time, it is recommended to set the flag to True. Only for event-based processors. | System.Boolean |
SamplingRate | The rate at which the data is renewed (default 0s). | System.Float |
InitialData | Initial data of the processor. Depends on the processor type. | System.String[] |
Ports | Communication ports | TPortInfo[] |
Following processor types are available:
Type | Description |
---|---|
SituationProcessingNode | Processor for data processing and situation analysis. The correlation logic is limited to simple predicates for situation evaluation. |
ExtendedSituationProcessingNode | Processor for data processing and situation analysis. The correlation logic can be implemented as C#-script. This allows complex situation and correlation analysis. |
CSharpScriptProcessingNode | Processor for data processing and situation analysis based on a script file. |
DataAggregator | Processor to aggregate inputs to a complex entity. |
ProxyProcessingNode | Proxy processor to bind an external processor logic from a PeMiL Plugin by typename |
CSharpScriptStreamProcessingNode | [DEPRECATED] Use CSharpScriptProcessingNode instead and set SamplingRate greater than zero. |
EventProcessingProxyNode | [DEPRECATED] Use ProxyProcessingNode instead and set SamplingRate to zero. |
StreamProcessingProxyNode | [DEPRECATED] Use ProxyProcessingNode instead and set SamplingRate greater than zero. |
Event Driven Processors
If the SamplingRate
is set to 0
(zero) the processor is triggered each time a value changes (event driven).
Event Driven Delayed Processors
Event driven processors support an additional setting called DelayTime
. This allows to delay the trigger to process the events and is often used to
filter high frequency event changes.
Property | Description | Data Type |
---|---|---|
DelayTime | [OPTIONAL] The delay time delays the processing in milliseconds. | System.Int32 |
The delay time can be helpful if several events trigger almost at the same time.
This could cause immediate situation states, that are not really interesting to higher level systems.
In this case, a delay helps to reduce immediate situations delivering only the final situation after n
milliseconds.
Example of a delay time of 3 seconds:
{
"Id": "35C93A18-F43A-4866-AFEC-1F25A36B8D6E",
"Name": "MachineStateProcessor",
"Type": "SituationProcessingNode",
"AsynchProcessing": false,
"Properties": [
{
"Name": "DelayTime",
"Value": 3000,
"DataType": "System.Int32"
}
]
}
Sampled Processors
If the SamplingRate
is set greater than 0
(zero), the processor is triggered periodically (in seconds).
The sampled processors are recommended for streaming data, such as axis values. This reduces the CPU load of the executing process.
Ports
Each processor needs port for communication. Ports are sub elements of the processor. Ports are described by following attributes:
Attribute | Description | Data Type |
---|---|---|
Name | Name of the port. The name is used in all correlation predicates to reference that data acquired through the corresponding port. | System.String |
DataType | Datatype of the information used to process. | System.Type |
FlowDirection | Direction of the information flow.
| EPortDirection |
Protocol | Communication protocol | EPortProtocol |
Necessity | Is the port used as mandatory or optional for the processing engine | ENecessity |
Public | Flag, if the port is public. Ports must be set to public when they should be automatically wired with external data nodes. Additionally, they need the port matching property. | System.Boolean |
TriggerType | Defines how an event driven processor is triggered by the port. This value is ignored for sampled processors. | EPortTriggerType |
Available Protocols
Following values can be used for the communication protocol:
Protocol | Description |
---|---|
EventPassing | Information is event based |
Streaming | Information is stream based |
Messaging | Information is received as message (alarm). DataType must be HumanOS.Kernel.DataModel.TAbstractEventItem, HumanOS.Kernel.Base |
Trigger Types
Following trigger types are available:
TriggerType | Description |
---|---|
Default | Default settings of the HumanOS Kernel. Currently set to OnChange |
OnEvent | The processor is triggered if an event passes through the port. (An event can contain the same value again, but with a different timestamp). |
OnChange | The processor is triggered if the value, the state or the meta data changes. |
OnPositiveFlank | The processor is triggered if the value has a positive flank to its previous value. |
OnNegativeFlank | The processor is triggered if the value has a negative flank to its previous value. |
None | The processor is not triggered at all. |
OnPositiveFlank
andOnNegativeFlank
can only be used for numerical values and strings. For strings, a positive flank is a string in higher alphabetical order, a negative flank vis-versa.
Necessity of Ports
The following necessity values can be set to input ports:
Necessity | Description |
---|---|
Optional | Default value of the port. The processing engine can work even if this port has no valid data (DataState != good) |
Mandatory | The processing engine can work only if all mandatory ports have valid data (DataState == good) |
If a processing node has mandatory ports, the output ports are set to BadNotActive if at least one of the mandatory ports are not ready.
For port matching check Port Matching Rules.
Example of a processing node with one input port:
{
"Id": "41612BA0-AFC9-43E2-AE86-86A97E22B7B1",
"Name": "RunningStateProcessor",
"Type": "SituationProcessingNode",
"AsynchProcessing": false,
"InitialData": [],
"Ports": [
{
"Name": "AvailableState",
"Public": true,
"DataType": "System.Int32",
"FlowDirection": "Input",
"Protocol": "EventPassing",
"Necessity": "Mandatory",
"Properties": [
{
"Name": "PortMatchId",
"Value": "AvailableType"
}
]
},
{
"Name": "ProgramState",
"Public": true,
"DataType": "System.Int32",
"FlowDirection": "Input",
"Protocol": "EventPassing",
"Necessity": "Optional",
"Properties": [
{
"Name": "PortMatchId",
"Value": "ProgramStateType"
}
]
},
{
"Name": "RunningState",
"Public": true,
"DataType": "System.Int32",
"FlowDirection": "Output",
"Protocol": "EventPassing",
"Properties": [
{
"Name": "PortMatchId",
"Value": "RunningStateType"
}
]
}
]
}
Links
Linking ports and data nodes within a processing network is described by Links.
Name | Description | Data Type |
---|---|---|
Name | Name of the link | System.String |
TypeName | HumanOS class name of the link. | System.Type |
Node1RefName | Reference to an output port | System.String |
Node2RefName | Reference to an input port | System.String |
DataType | Defines the data type of the link | System.String |
Example of internal linking two processors:
{
"Id": "77B6AACB-B306-4E50-901A-9E12CC706E1D",
"Name": "HumanOS.PeSeL.Correlations.DataProcessors",
"Processors": [
{
"Id": "35C93A18-F43A-4866-AFEC-1F25A36B8D6E",
"Name": "Processing1",
"Type": "SituationProcessingNode",
"AsynchProcessing": false,
"InitialData": [
"InValue < 0 => (-1)",
"InValue > 0 => 1",
"=> 0"
],
"Ports": [
{
"Name": "InValue",
"DataType": "System.Int32",
"FlowDirection": "Input",
"Protocol": "EventPassing"
},
{
"Name": "InverterOut",
"DataType": "System.Int32",
"FlowDirection": "Output",
"Protocol": "EventPassing"
}
]
},
{
"Id": "CFEF5DC4-CF1D-44AF-82C3-BD3289C1815F",
"Name": "Processing2",
"Type": "SituationProcessingNode",
"AsynchProcessing": false,
"InitialData": [
" => AmplifierIn*1.1"
],
"Ports": [
{
"Name": "AmplifierIn",
"DataType": "System.Int32",
"FlowDirection": "Input",
"Protocol": "EventPassing"
},
{
"Name": "OutValue",
"DataType": "System.Double",
"FlowDirection": "Output",
"Protocol": "EventPassing"
}
]
}
],
"Links": [
{
"Name": "Connection1",
"Node1RefName": "InverterOut",
"Node2RefName": "AmplifierIn",
"DataType": "System.Int32"
}
]
}
Situation Processing Node
The situation processing node can process complex situations out of several data access points and messages.
Initial Data - Correlation Predicates
The initial data node contains all correlations of the situation processing. Predicates are processed from top to bottom. The processing stops on the first predicate whose condition returns true.
Predicates take the form: <condition> => <result>
The =>
can be read as "leads-to".
Variable names can be used within the condition and result terms. The variable name corresponds to the name of the input ports.
Else-Statement
Your list of predicates should end with an "else"-statement, otherwise processors might miss some situations.
The else-statement is a simple "true", or leave the condition empty:
true => 100
or
=> 100
Logical Expressions
Several variables can be combined in logical expressions. Possible expressions are:
and
or&&
or
or||
HumanOS uses the standard operator precedence that is and
goes before or
.
It is good practice to use brackets to indicate the your precedence.
{
"Id": "35C93A18-F43A-4866-AFEC-1F25A36B8D6E",
"Name": "MachineStateProcessor",
"Type": "SituationProcessingNode",
"InitialData": [
"AvailableState <= 0 => 0",
"ProgramState == 5 => 1",
"OperationMode != 4 => 100",
"OperationMode == 4 and ProgramState == 1 => 200",
"OperationMode == 4 and ProgramState == 2 => 300",
"OperationMode == 4 and ProgramState == 4 => 301",
"=> 302"
]
}
It is also possible to use regular C# expressions.
Example
{
"Id": "35C93A18-F43A-4866-AFEC-1F25A36B8D6E",
"Name": "MachineStateProcessor",
"Type": "SituationProcessingNode",
"InitialData": [
"AvailableState <= 0 => 0",
"ProgramState == 5 => 1",
"OperationMode != 4 => 100",
"OperationMode == 4 and ProgramName.Contains(\"warmlauf\", StringComparison.OrdinalIgnoreCase) => 200",
"=> 0"
]
}
Generating more than one Output Value
In general, the situation processor is used to correlate several input values to on output value. But, it is possible to generate multiple output values at the same time.
For instance, if some text should describe the generated value, it is useful to generate the value and description at the same time to prevent inconsistency at the end of a processing network.
See also Inconsistent Data Correlations.
Example
Following example is used to generate the machine state and is corresponding machine state name.
The two output ports MachineState
and MachineStateName
are written at the same time, if a predicate is triggered and executed.
{
"Id": "35C93A18-F43A-4866-AFEC-1F25A36B8D6E",
"Name": "MachineStateProcessor",
"Type": "SituationProcessingNode",
"InitialData": [
"AvailableState <= 0 => MachineState=0; MachineStateName=\"POWER OFF\"",
"ProgramState == 5 => MachineState=1; MachineStateName=\"STOP\"",
"OperationMode != 4 => MachineState=100; MachineStateName=\"PRODUCTION\"",
"OperationMode == 4 and ProgramName.Contains(\"warmlauf\", StringComparison.OrdinalIgnoreCase) => MachineState=200; MachineStateName=\"SETUP\"",
"=> MachineState=0; MachineStateName=\"UNKNOWN\""
]
}
C#-Script Processing Node
The extended version of the situation processing node allows more flexible data correlation. Instead of predicates, a C# script is used to describe the processing algorithm.
By default, the script files are placed in the folder $(ConfigPath)\Scripts\Processing\
.
In case the script file belongs to a device.json file, the script files are placed in the folder $(ConfigPath)\$(plugin-name)\
.
Initial Data
The script file is declared in the initial data section.
Example
{
"Id": "35C93A18-F43A-4866-AFEC-1F25A36B8D6E",
"Name": "MachineStateProcessor",
"Type": "CSharpScriptProcessingNode",
"AsynchProcessing": false,
"InitialData": [
"MyScriptFile.cs"
]
}
Message Handling
The situation processing also allows to access alarm messages. At least one input port must be linked to the alarm event pool to get messages from hardware devices.
Following methods can be used to handle messages:
Method | Description | Parameters |
---|---|---|
hasAlarmMessage | Checks if an alarm message is pending. Example: bool hasAlarmMessage(string strPortName, string strConditionName) | strPortName: Name of the message port; strConditionName: Name of the condition e.g. Alarm 100 |
getAlarmMessage | Gets the alarm message if available (otherwise null). Example: TAlarmItem getAlarmMessage(string strPortName, string strConditionName) | strPortName: Name of the message port; strConditionName: Name of the condition e.g. Alarm 100 |
getAllAlarmMessages | Gets all pending alarm message. Example: List<TAlarmItem> getAllAlarmMessages(string strPortName) | strPortName: Name of the message port |
The TAlarmItem contains several fields that could be used for processing. See TAlarmItem reference.
Example
{
"Id": "{94CC989E-B9FE-49B2-83CD-C874B05DB23D}",
"Name": "AlarmingProcessor",
"Type": "CSharpScriptProcessingNode",
"AsynchProcessing": false,
"Properties": [
{
"Name": "DelayTime",
"Value": 500,
"DataType": "System.Int32"
}
],
"InitialData": [
"TAlarmProcessor.cs"
],
"Ports": [
{
"Name": "Alarms",
"Public": true,
"DataType": "HumanOS.Kernel.DataModel.TAbstractEventItem, HumanOS.Kernel.Base",
"FlowDirection": "Input",
"Protocol": "Messaging",
"Properties": [
{
"Name": "PortMatchId",
"Value": "Port_Alarms"
}
]
},
{
"Name": "NumberOfActiveAlarms",
"Public": true,
"DataType": "System.Int32",
"FlowDirection": "Output",
"Protocol": "EventPassing",
"Properties": [
{
"Name": "PortMatchId",
"Value": "Port_NumberOfActiveAlarms"
}
]
}
]
}
Example of C# script
using HumanOS.Kernel.DataModel;
using HumanOS.Kernel.Processing;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace HumanOS.IoT.Gateway.Config.Scripts.Processing
{
/// <summary>
/// Implements the mt connect alarm processor
/// </summary>
internal class TAlarmProcessor : TAbstractProcessingScriptObject
{
/// <see cref="TAbstractProcessingScriptObject"/>
public override void process(IProcessingNode Processor)
{
// get all alarm messages from ae pool
List<TAlarmItem> lstAlarmItems = Processor.getAllAlarmMessages("Alarms");
// set number of active alarms
Processor.setProperty<int>("NumberOfActiveAlarms", lstAlarmItems.Count);
}
}
}
Proxy Processing Nodes
A proxy processing node encapsulates an external processor. Typically, the external processor is loaded as a PeMiL-plugin.
The property ProcessorType
specifies the processor class provided by an external plugin.
Property | Description | Data Type |
---|---|---|
ProcessorType | Type name of the processor class to load from PeMiL plugin. | System.String |
Example of a TensorFlow processor:
{
"Id": "35C93A18-F43A-4866-AFEC-1F25A36B8D6E",
"Name": "TensorFlow Processing",
"Type": "EventProcessingProxyNode",
"AsynchProcessing": false,
"InitialData": [],
"Properties": [
{
"Name": "ProcessorType",
"Value": "HumanOS.PeMiL.Processors.TensorFlow.TTensorFlowProcessor, HumanOS.PeMiL.Processors.TensorFlow"
}
]
}
Data Aggregation
Data aggregation processor can be used to create a complex data object (entity) out of single data nodes (values). Therefore, a processor collects different data in its inputs and aggregates them to a new complex object.
The data aggregation processor requires an output port of type HumanOS.Kernel.DataModel.Entity.TGenericEntity, HumanOS.Kernel.Base
.
The generic entity data can be processed by different pervasive service plugins, e.g. OPC-UA server or different data logger plugins.
Example
{
"Id": "dcbece37-8fcd-46cc-8e11-a5ac76b67fad",
"Name": "DataAggregatorProcessor",
"InitialData": [],
"Type": "DataAggregator",
"Ports": [
{
"Name": "Value1",
"DataType": "System.Int32",
"Public": true,
"Properties": [
{
"Name": "PortMatchId",
"DataType": "System.String",
"Value": "Value1Type"
}
]
},
{
"Name": "Value2",
"DataType": "System.Double",
"Public": true,
"Properties": [
{
"Name": "PortMatchId",
"DataType": "System.String",
"Value": "Value2Type"
}
]
},
{
"Name": "OutputPort",
"DataType": "HumanOS.Kernel.DataModel.Entity.TGenericEntity, HumanOS.Kernel.Base",
"FlowDirection": "Output",
"Public": true,
"Properties": [
{
"Name": "PortMatchId",
"DataType": "System.String",
"Value": "OutType"
}
]
}
]
}
Trouble Shooting
Inconsistent Data Correlations
In larger and interconnected processing networks, it can happen that same signals arrive earlier than others. This phenomena occurs due to async processing of the network and non-deterministic thread scheduling of the hosting operating system.
There are different ways to overcome such a behavior:
-
Try to process information that belongs together in one processor. Here, you can use multiple outputs or the datatype
TGenericEntity
as a structure to combine all relevant information in one -
Use a trigger to collect all relevant information first and then to process them once the trigger is set.