Skip to main content
Version: 2.8

Workflows

The HumanOS® comes with a built-in workflow engine. It allows to design and run complex workflows along different actors such as humans or computer agents.

Workflows are defined as schema or as classes. Due to rules, a workflow class is dynamically instantiated and started. And of course, also removed dynamically once removal rules are triggered.

In this document, the UML is used to express workflows. There are some extensions to UML necessary to express the full power of HumanOS® Workflows. Following terminology is used:

NameShort.Description
SceneActivityScA scene activity contains the whole workflow including exception handling.
ActivityAAn activity is a set of instructions that can be processed by agents, humans etc. They are embedded in a workflow.
ActorActorAn instance that is able to process an activity
Control FlowF~C~Flow to the next activity
Object FlowF~O~Flow of objects, messages etc. (communication between activities).

The workflow is defined by following attributes:

AttributeDescriptionData Type
IdId of the workflow. Empty if the schema can be instantiated multiple times.System.Guid
NameName of the workflowSystem.String
NodesArray of activity coordination nodes
ControlFlowsControl flow between the activity coordination nodes
ObjectFlowsCommunication links between ports of an activity and object nodes (space)
ArgumentsArray of arguments to be passed when starting the workflow.

Example:

{
"Name": "WorkflowSchema1",
"Id": "8E5BEB09-8BA9-456A-957C-10444A26096D",
"Workflows": [
{
"Name": "Collaboration Workflow",
"Id": "2D962A90-8BD5-44DE-A478-208DE2D49B67",
"Nodes": [],
"ControlFlows": [],
"ObjectFlows": []
}
]
}

Passing Arguments

Arguments can be passed to a workflow. All arguments are stored to the activity context at the start.

  • See Commands for notation on arguments.

Example with arguments:

{
"Name": "WorkflowSchema1",
"Id": "8E5BEB09-8BA9-456A-957C-10444A26096D",
"Workflows": [
{
"Name": "Collaboration Workflow",
"Id": "2D962A90-8BD5-44DE-A478-208DE2D49B67",
"Arguments": [
{
"Name": "Value",
"DataType": "System.String",
"Type": "Input"
}
],
"Nodes": [],
"ControlFlows": [],
"ObjectFlows": []
}
]
}

The arguments can then be used inside the workflow, either for guard expressions (see [Guards]) or in C# scripts (see [Scripted Operation]).

Activity Coordination Node

There exist different node types to coordinate the workflow:

TypeDescription
InitialNode
Used to start a workflow (entry point).
DecisionNode
Node to branch the control flow according to some conditions.
MergeNode
Node to combine two control flows to one.
ForkNode
Node to fork the control flow into two concurrently running flows (multithreading)
JoinNode
Node to join concurrently running control flows back to one. The flow will continue after all incoming flows are triggered.
GenericActivityAn activity node.
ActivityFinalNode
Node to terminate the workflow.
FlowFinalNode
Node to stop a control flow. This will not terminate the workflow, just consume a control flow.
ExceptionHandlerSpecial node to catch exception within a workflow and to continue an alternative control flow.
ObjectNodeObject space to exchange information between activities.

Each node is declared as a "MemberInfo".

AttributeDescriptionData Type
NameName of the nodeSystem.String
TypeType of node, e. g. InitialNodeSystem.String

Example:

{
"Name": "Start",
"Type": "InitialNode"
}

Control Flows

The control flow is a relational link between nodes.

AttributeDescriptionData Type
NameName of the flow.System.String
Node1RefNameReference name of the source nodeSystem.String
Node2RefNameReference name of the target nodeSystem.String
GuardConditionGuard used from outgoing control flows from DecisionNodesSystem.String

Guards

After decision nodes, the guards of a control flow are checked to decide which flow to take. For all other coordination nodes, guards are ignored:

Example:

Example:

{
"Name": "Collaboration Workflow",
"Id": "2D962A90-8BD5-44DE-A478-208DE2D49B67",
"Nodes": [
{
"Name": "Start",
"Type": "InitialNode"
},
{
"Name": "Activity1",
"Type": "GenericActivity"
},
{
"Name": "End",
"Type": "ActivityFinalNode"
}
],
"ControlFlows": [
{
"Node1RefName": "Start",
"Node2RefName": "Activity1"
},
{
"Node1RefName": "Activity1",
"Node2RefName": "End"
}
]
}

Activity Nodes

An activity node describes a set of operations, a work or procedure to be performed by an agent (see Agents and Skills).

Other than UML, there exists no such construct like "swimming-planes". HumanOS™ selects agents according to their skills and the skills needed to perform an activity.

An activity is described by following attributes:

AttributeDescriptionData Type
IdId of the activity. Empty if the schema can be instantiated multiple times.System.Guid
NameName of the activitySystem.String
TypeType of activity, = GenericActivitySystem.Type
ActorSkillSkills needed to perform the activity (see [Agents and Skills])System.Guid
AutoResetFlag, to reset the activity after its completion. This is needed, if the activity can be executed multiple times.System.Boolean
DescriptionSome descriptionSystem.String
OperationsArray of operations to be executed

Example:

{
"Id": "6FA7E8CB-D2D1-4C22-9122-A7DAFFD2B5C7",
"Name": "Activity_ManageDeviceReferences",
"Type": "GenericActivity",
"AutoReset": true,
"ActorSkill": "54C4FA92-F385-4644-B929-5650310B4930",
"Operations": []
}

Operations

Each activity can have a list of operations.

AttributeDescriptionData Type
NameName of the operation.System.String
TypeType of operation
  • HumanOperation: Operation is only descriptive (for humans)
  • CSharpScriptOperation: Executes a C# script.
  • CommandedOperation: Executes a command node
System.Type
InstructionInstruction to be executed (only for Human- and CSharpScriptOperation)System.String
CommandInstructionInstruction for CommandedOperation
Condition[OPT] Condition expression. Default is true.System.String

Scripted Operation

The workflow script class must be derived from the TAbstractOperationScriptObject. The method run() implements the operation to be performed.

Following objects are passed to the script as arguments:

  • IKernelAccess Kernel: access interface to the kernel
  • IActivity Activity: access to the activity object and the calling context

The script files are located in $(ConfigPath)\Scripts\Workflows.

Example:

public class TWorkflow_MyOperation : TAbstractOperationScriptObject
{
///<see cref="TAbstractOperationScriptObject"/>
public override void run(IKernelAccess Kernel, IActivity Activity)
{
string strHostName = Activity.Context.getValue<string>("HostName");
TLogger.writeInfo(strHostName + ": BasicConfiguration started...");

IGroupRelation Device = Activity.Context.getValue<IGroupRelation>("Device");

Dictionary<string, object> dicInput = new Dictionary<string, object>();
dicInput["ObjectId"] = Activity.Context.getValue<string>("ObjectId");
dicInput["Action"] = "A0"; //no action
dicInput["Status"] = "S3"; //next step is user configuration
Dictionary<string, object> dicOutput = new Dictionary<string, object>();
TCommandResult Result = TCommandHelper.call(Kernel,
Guid.Parse(GUID_iDoit_LogicSetDeviceStatusAndAction), dicInput, dicOutput);
if (Result.State != EProcessingState.Good)
{
throw new ArgumentException(strHostName +
": Could not set the device status and action.", Result.ErrorInfo);
}
TLogger.writeInfo(strHostName + ": BasicConfiguration finished. Reboot required.");
}
}
  • More information about operation scripting, see "HumanOS® Kernel SDK Manual".

Exception Handling in Workflows

If an exception is thrown during processing, the complete workflow stops with error. There is an extension allowing to handle the exceptions explicitly in a separate control flow of the workflow.

  • A Workflow can also contain several exception handlers. Make sure, each handler declares another exception class type.
  • To handle all exceptions with one exception handler, declare ExceptionTypeName as System.Exception.

Example:

{
"Id": "B65C2AD1-78E8-479A-80D2-84FC86D32AA9",
"Name": "MyWorkflow",
"Nodes": [
{
"Id": "7E4BD883-92AE-4DBF-AC32-0D380BB9F216",
"Name": "Start",
"Type": "InitialNode"
},
{
"Id": "6FA7E8CB-D2D1-4C22-9122-A7DAFFD2B5C7",
"Name": "Activity1 ",
"Type": "GenericActivity",
"AutoReset": true,
"ActorSkill": "54C4FA92-F385-4644-B929-5650310B4930",
"Operations": [
{
"Name": "Operation1",
"Type": "CSharpScriptOperation",
"Instruction": "MyScript.cs"
}
]
},
{
"Id": "ADD55B7C-59F1-4CF7-9257-509B19E70C6F",
"Name": "ExceptionHandler",
"Type": "ExceptionHandler",
"Properties": [
{
"Name": "ExceptionTypeName",
"Value": "System.Exception"
}
]
},
{
"Id": "6B43DBD5-92FA-4304-8BDA-A2AB5793644A",
"Name": "TerminateActivity",
"Type": "GenericActivity",
"AutoReset": true,
"ActorSkill": "54C4FA92-F385-4644-B929-5650310B4930",
"Operations": [
{
"Name": "Operation1",
"Type": "CSharpScriptOperation",
"Instruction": "TTerminateHumanOS.cs"
}
]
},
{
"Id": "111C3571-62A1-4BC5-84FE-1D872953C1BE",
"Name": "End",
"Type": "ActivityFinalNode"
}
],
"ControlFlows": [
{
"Name": "Flow_Start_Activity1",
"Node1RefName": "Start",
"Node2RefName": " Activity1"
},
{
"Name": "Flow_Activity1_End",
"Node1RefName": "Activity1",
"Node2RefName": "End"
},
{
"Name": "Flow_ExceptionHandler_TerminateActivity",
"Node1RefName": "ExceptionHandler",
"Node2RefName": "TerminateActivity"
},
{
"Name": "Flow_TerminateActivity_End",
"Node1RefName": "TerminateActivity",
"Node2RefName": "End"
}
]
}

Critical Workflow Termination

To implement system relevant or critical workflows, the script of the error-handler-activity should pass the exception to the kernel object. This will immediately terminate the HumanOS process with that error.

using HumanOS.Kernel.DataModel;
using HumanOS.Kernel.Workflow.Activity;
using HumanOS.Kernel.Workflow.Instruction;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace HumanOS.Kernel.Test.TestFiles.Scripts.Workflows
{
/// <summary>
/// Handles the critical exceptions
/// </summary>
public class THandleExceptions : TAbstractOperationScriptObject
{
///<see cref="TAbstractOperationScriptObject"/>
public override void run(IKernelAccess Kernel, IActivity Activity)
{
Kernel.postFatalError(Activity.Context.ErrorInfo);
}
}
}