diff --git a/BPMNEngine/.vs/BPMNEngine/DesignTimeBuild/.dtbcache.v2 b/BPMNEngine/.vs/BPMNEngine/DesignTimeBuild/.dtbcache.v2
index 53296ba..7718f08 100644
Binary files a/BPMNEngine/.vs/BPMNEngine/DesignTimeBuild/.dtbcache.v2 and b/BPMNEngine/.vs/BPMNEngine/DesignTimeBuild/.dtbcache.v2 differ
diff --git a/BPMNEngine/.vs/BPMNEngine/v17/.futdcache.v2 b/BPMNEngine/.vs/BPMNEngine/v17/.futdcache.v2
index 0beb67a..d43800f 100644
Binary files a/BPMNEngine/.vs/BPMNEngine/v17/.futdcache.v2 and b/BPMNEngine/.vs/BPMNEngine/v17/.futdcache.v2 differ
diff --git a/BPMNEngine/.vs/BPMNEngine/v17/.suo b/BPMNEngine/.vs/BPMNEngine/v17/.suo
index c290388..3c899f6 100644
Binary files a/BPMNEngine/.vs/BPMNEngine/v17/.suo and b/BPMNEngine/.vs/BPMNEngine/v17/.suo differ
diff --git a/BPMNEngine/.vs/BPMNEngine/v17/TestStore/0/testlog.manifest b/BPMNEngine/.vs/BPMNEngine/v17/TestStore/0/testlog.manifest
index bde5c72..96dcbf4 100644
Binary files a/BPMNEngine/.vs/BPMNEngine/v17/TestStore/0/testlog.manifest and b/BPMNEngine/.vs/BPMNEngine/v17/TestStore/0/testlog.manifest differ
diff --git a/BPMNEngine/.vs/ProjectEvaluation/bpmnengine.metadata.v7.bin b/BPMNEngine/.vs/ProjectEvaluation/bpmnengine.metadata.v7.bin
index 03c72a3..561a207 100644
Binary files a/BPMNEngine/.vs/ProjectEvaluation/bpmnengine.metadata.v7.bin and b/BPMNEngine/.vs/ProjectEvaluation/bpmnengine.metadata.v7.bin differ
diff --git a/BPMNEngine/.vs/ProjectEvaluation/bpmnengine.projects.v7.bin b/BPMNEngine/.vs/ProjectEvaluation/bpmnengine.projects.v7.bin
index 3d43513..55c8d3e 100644
Binary files a/BPMNEngine/.vs/ProjectEvaluation/bpmnengine.projects.v7.bin and b/BPMNEngine/.vs/ProjectEvaluation/bpmnengine.projects.v7.bin differ
diff --git a/BPMNEngine/BusinessProcess.Actions.cs b/BPMNEngine/BusinessProcess.Actions.cs
index eaf4a11..e1a3a52 100644
--- a/BPMNEngine/BusinessProcess.Actions.cs
+++ b/BPMNEngine/BusinessProcess.Actions.cs
@@ -57,6 +57,13 @@ internal void ProcessStepComplete(ProcessInstance instance, string sourceID, str
}
});
}
+ if (elem is SubProcess subProcess)
+ {
+ ReadOnlyProcessVariablesContainer vars = new(sourceID, instance);
+ subProcess.Children
+ .Where(child=>instance.State.Path.AbortableSteps.Contains(child.ID))
+ .ForEach(child=>AbortStep(instance, sourceID, child, vars));
+ }
}
WriteLogLine(sourceID, LogLevel.Debug, new StackFrame(1, true), DateTime.Now, string.Format("Process Step[{0}] has been completed", sourceID));
if (outgoingID != null)
@@ -322,41 +329,48 @@ internal void ProcessEvent(ProcessInstance instance, string sourceID, AEvent evn
evnt,
new ReadOnlyProcessVariablesContainer(evnt.ID, instance)
);
- if (evnt is EndEvent event1 && event1.IsProcessEnd)
+ if (evnt is EndEvent endEvent)
{
- if (!event1.IsTermination)
+ var sp = endEvent.SubProcess as SubProcess;
+ if (sp!=null &&
+ (
+ !endEvent.IsProcessEnd
+ ||(endEvent.IsProcessEnd && !endEvent.IsTermination)
+ )
+ ){
+ instance.State.Path.SucceedFlowNode(sp);
+ BusinessProcess.TriggerDelegateAsync(
+ instance.Delegates.Events.SubProcesses.Completed,
+ sp,
+ new ReadOnlyProcessVariablesContainer(sp.ID, instance)
+ );
+ }
+ else if (endEvent.IsProcessEnd)
{
- SubProcess sp = (SubProcess)event1.SubProcess;
- if (sp != null)
+ if (!endEvent.IsTermination)
{
- instance.State.Path.SucceedFlowNode(sp);
- BusinessProcess.TriggerDelegateAsync(
- instance.Delegates.Events.SubProcesses.Completed,
- sp,
- new ReadOnlyProcessVariablesContainer(sp.ID, instance)
- );
+ if (sp==null)
+ {
+ BusinessProcess.TriggerDelegateAsync(
+ instance.Delegates.Events.Processes.Completed,
+ endEvent.Process,
+ new ReadOnlyProcessVariablesContainer(evnt.ID, instance)
+ );
+ instance.CompleteProcess();
+ }
}
else
{
+ ReadOnlyProcessVariablesContainer vars = new(evnt.ID, instance);
+ instance.State.AbortableSteps.ForEach(str => { AbortStep(instance, evnt.ID, GetElement(str), vars); });
BusinessProcess.TriggerDelegateAsync(
- instance.Delegates.Events.Processes.Completed,
- event1.Process,
+ instance.Delegates.Events.Processes.Completed,
+ endEvent.Process,
new ReadOnlyProcessVariablesContainer(evnt.ID, instance)
);
instance.CompleteProcess();
}
}
- else
- {
- ReadOnlyProcessVariablesContainer vars = new(evnt.ID, instance);
- instance.State.AbortableSteps.ForEach(str => { AbortStep(instance, evnt.ID, GetElement(str), vars); });
- BusinessProcess.TriggerDelegateAsync(
- instance.Delegates.Events.Processes.Completed,
- event1.Process,
- new ReadOnlyProcessVariablesContainer(evnt.ID, instance)
- );
- instance.CompleteProcess();
- }
}
}
}
diff --git a/BPMNEngine/BusinessProcess.cs b/BPMNEngine/BusinessProcess.cs
index 390e19e..7cf1dba 100644
--- a/BPMNEngine/BusinessProcess.cs
+++ b/BPMNEngine/BusinessProcess.cs
@@ -291,7 +291,7 @@ public IProcessInstance LoadState(Utf8JsonReader reader,
/// Used to set the logging level for the process state document
/// a process instance if the process was successfully started
public IProcessInstance BeginProcess(
- Dictionary pars,
+ Dictionary pars=null,
ProcessEvents events = null,
StepValidations validations = null,
ProcessTasks tasks = null,
diff --git a/BPMNEngine/Elements/Diagrams/Edge.cs b/BPMNEngine/Elements/Diagrams/Edge.cs
index dcad88b..bf474a4 100644
--- a/BPMNEngine/Elements/Diagrams/Edge.cs
+++ b/BPMNEngine/Elements/Diagrams/Edge.cs
@@ -38,7 +38,7 @@ public override RectF Rectangle
});
Label l = Label;
_rectangle = new RectF(_rectangle.Value.X-3.5f, _rectangle.Value.Y-3.5f, _rectangle.Value.Width+6.5f, _rectangle.Value.Height+6.5f);
- if (l != null)
+ if (l != null && l.Bounds!=null)
_rectangle = MergeRectangle(_rectangle.Value,l.Bounds.Rectangle);
}
return _rectangle.Value;
@@ -122,7 +122,7 @@ public void Render(ICanvas surface, ProcessPath path,Definition definition)
}
}
- if (Label!=null)
+ if (Label!=null && Label.Bounds!=null)
{
surface.FontColor = color;
surface.DrawString(elem.ToString(), Label.Bounds.Rectangle, HorizontalAlignment.Center, VerticalAlignment.Center);
diff --git a/BPMNEngine/Elements/Diagrams/Label.cs b/BPMNEngine/Elements/Diagrams/Label.cs
index b1d3a17..2bbb885 100644
--- a/BPMNEngine/Elements/Diagrams/Label.cs
+++ b/BPMNEngine/Elements/Diagrams/Label.cs
@@ -7,21 +7,10 @@ namespace BPMNEngine.Elements.Diagrams
[ValidParent(typeof(Shape))]
internal class Label : AParentElement
{
- public Bounds Bounds
- => Children.OfType().FirstOrDefault();
+ public Bounds? Bounds
+ => Children.OfType().FirstOrDefault();
public Label(XmlElement elem, XmlPrefixMap map, AElement parent)
: base(elem, map, parent) { }
-
- public override bool IsValid(out IEnumerable err)
- {
- var res = base.IsValid(out err);
- if (Bounds == null)
- {
- err = (err??Array.Empty()).Concat(new string[] { "No bounds for the label are specified." });
- return false;
- }
- return res;
- }
}
}
diff --git a/BPMNEngine/Elements/SubProcess.cs b/BPMNEngine/Elements/SubProcess.cs
index 71437b9..93fb1f9 100644
--- a/BPMNEngine/Elements/SubProcess.cs
+++ b/BPMNEngine/Elements/SubProcess.cs
@@ -32,7 +32,7 @@ public override bool IsValid(out IEnumerable err)
var res = base.IsValid(out err);
bool hasStart = Children.Any(elem => elem is StartEvent || (elem is IntermediateCatchEvent ice && ice.SubType.HasValue));
bool hasEnd = Children.Any(elem=>elem is EndEvent);
- bool hasIncoming = this.Incoming!=null || Children.Any(elem=>elem is IntermediateCatchEvent ice && ice.SubType.HasValue);
+ bool hasIncoming = Incoming.Any() || Children.Any(elem=>elem is IntermediateCatchEvent ice && ice.SubType.HasValue);
var terr = new List();
if (!(hasStart && hasEnd && hasIncoming))
{
diff --git a/UnitTest/AssemblyInfo.cs b/UnitTest/AssemblyInfo.cs
index 574539f..1b6cd92 100644
--- a/UnitTest/AssemblyInfo.cs
+++ b/UnitTest/AssemblyInfo.cs
@@ -1,3 +1,3 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
-[assembly: Parallelize(Workers = 50, Scope = ExecutionScope.MethodLevel)]
\ No newline at end of file
+[assembly: Parallelize(Workers = 25, Scope = ExecutionScope.MethodLevel)]
\ No newline at end of file
diff --git a/UnitTest/DiagramLoading.cs b/UnitTest/DiagramLoading.cs
index e4d04c2..0576a89 100644
--- a/UnitTest/DiagramLoading.cs
+++ b/UnitTest/DiagramLoading.cs
@@ -120,13 +120,15 @@ public void LoadInvalidDiagram()
Assert.IsTrue(res.Contains("No bounds specified for the shape."));
Assert.IsTrue(res.Contains("End Events cannot have an outgoing path."));
Assert.IsTrue(res.Contains("End Events must have an incoming path."));
- Assert.IsTrue(res.Contains("No bounds for the label are specified."));
Assert.IsTrue(res.Contains("No child elements found in the definition."));
Assert.IsTrue(res.Contains("Collaboration requires at least 1 child element."));
Assert.IsTrue(res.Contains("Right value specified more than once."));
Assert.IsTrue(res.Contains("Left value specified more than once."));
Assert.IsTrue(res.Contains("Right and Left value missing."));
Assert.IsTrue(res.Contains("Left value missing."));
+ Assert.IsTrue(res.Contains("A Sub Process Must have a StartEvent or valid IntermediateCatchEvent"));
+ Assert.IsTrue(res.Contains("A Sub Process Must have a valid Incoming path, achieved through an incoming flow or IntermediateCatchEvent"));
+ Assert.IsTrue(res.Contains("A Sub Process Must have an EndEvent"));
}
[TestMethod]
diff --git a/UnitTest/SubProcesses.cs b/UnitTest/SubProcesses.cs
new file mode 100644
index 0000000..ecef22b
--- /dev/null
+++ b/UnitTest/SubProcesses.cs
@@ -0,0 +1,122 @@
+using BPMNEngine;
+using BPMNEngine.Interfaces.Tasks;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+namespace UnitTest
+{
+ [TestClass]
+ public class SubProcesses
+ {
+ private const string SIGNAL = "interupted";
+
+ private const string SUB_PROCESS_ID = "Activity_01eulv2";
+ private const string SUB_SUB_PROCESS_ID = "Activity_099a0io";
+
+ private const string MAIN_SIGNAL_ID = "Event_167oagf";
+ private const string SUB_SIGNAL_ID = "Event_1etf3o8";
+ private const string SUB_SUB_SIGNAL_ID = "Event_0s8bx7i";
+
+ private const string MAIN_TASK_ID = "Activity_1hle9w5";
+ private const string SUB_TASK_ID = "Activity_1ifnuk7";
+ private const string SUB_SUB_TASK_ID = "Activity_1e0arlx";
+
+ private static BusinessProcess _process;
+
+ [ClassInitialize()]
+ public static void Initialize(TestContext testContext)
+ {
+ _process = new BusinessProcess(Utility.LoadResourceDocument("SubProcesses/subprocesses.bpmn"));
+ }
+
+ [ClassCleanup]
+ public static void Cleanup()
+ {
+ _process.Dispose();
+ }
+
+ [TestMethod]
+ public void TestSubProcessCompletion()
+ {
+ var instance = _process.BeginProcess();
+ Assert.IsNotNull(instance);
+ Assert.IsTrue(instance.WaitForManualTask(SUB_TASK_ID, out IManualTask task));
+ Assert.IsNotNull(task);
+ task.MarkComplete();
+ Assert.IsTrue(Utility.WaitForCompletion(instance));
+ var state = instance.CurrentState;
+ Assert.IsTrue(Utility.StepCompleted(state, SUB_PROCESS_ID));
+ Assert.IsTrue(Utility.StepAborted(state, SUB_SUB_PROCESS_ID));
+ Assert.IsTrue(Utility.StepAborted(state, SUB_SUB_TASK_ID));
+ }
+
+ [TestMethod]
+ public void TestSubTaskSignal()
+ {
+ var instance = _process.BeginProcess();
+ Assert.IsNotNull(instance);
+ Assert.IsTrue(instance.WaitForManualTask(SUB_TASK_ID, out IManualTask task));
+ Assert.IsNotNull(task);
+ task.Signal(SIGNAL, out bool isAborted);
+ Assert.IsTrue(isAborted);
+ Assert.IsTrue(Utility.WaitForCompletion(instance));
+ var state = instance.CurrentState;
+ Assert.IsTrue(Utility.StepAborted(state, SUB_PROCESS_ID));
+ Assert.IsTrue(Utility.StepCompleted(state, SUB_SIGNAL_ID));
+ Assert.IsFalse(Utility.StepCompleted(state, SUB_SUB_SIGNAL_ID));
+ Assert.IsFalse(Utility.StepCompleted(state, MAIN_SIGNAL_ID));
+ Assert.IsTrue(Utility.StepAborted(state, SUB_SUB_PROCESS_ID));
+ Assert.IsTrue(Utility.StepAborted(state, SUB_PROCESS_ID));
+ }
+
+ [TestMethod]
+ public void TestSubSubProcessCompletion()
+ {
+ var instance = _process.BeginProcess();
+ Assert.IsNotNull(instance);
+ Assert.IsTrue(instance.WaitForManualTask(SUB_SUB_TASK_ID, out IManualTask task));
+ Assert.IsNotNull(task);
+ task.MarkComplete();
+ Assert.IsTrue(Utility.WaitForCompletion(instance));
+ var state = instance.CurrentState;
+ Assert.IsTrue(Utility.StepCompleted(state, SUB_PROCESS_ID));
+ Assert.IsTrue(Utility.StepCompleted(state, SUB_SUB_PROCESS_ID));
+ Assert.IsTrue(Utility.StepAborted(state, SUB_TASK_ID));
+ }
+
+ [TestMethod]
+ public void TestSubSubTaskSignal()
+ {
+ var instance = _process.BeginProcess();
+ Assert.IsNotNull(instance);
+ Assert.IsTrue(instance.WaitForManualTask(SUB_SUB_TASK_ID, out IManualTask task));
+ Assert.IsNotNull(task);
+ task.Signal(SIGNAL,out bool isAborted);
+ Assert.IsTrue(isAborted);
+ Assert.IsTrue(Utility.WaitForCompletion(instance));
+ var state = instance.CurrentState;
+ Assert.IsTrue(Utility.StepCompleted(state, SUB_PROCESS_ID));
+ Assert.IsTrue(Utility.StepCompleted(state, SUB_SUB_SIGNAL_ID));
+ Assert.IsFalse(Utility.StepCompleted(state, SUB_SIGNAL_ID));
+ Assert.IsFalse(Utility.StepCompleted(state, MAIN_SIGNAL_ID));
+ Assert.IsTrue(Utility.StepAborted(state, SUB_SUB_PROCESS_ID));
+ Assert.IsTrue(Utility.StepAborted(state, SUB_SUB_TASK_ID));
+ }
+
+ [TestMethod]
+ public void TestMainTaskSignal()
+ {
+ var instance = _process.BeginProcess();
+ Assert.IsNotNull(instance);
+ Assert.IsTrue(instance.WaitForManualTask(MAIN_TASK_ID, out IManualTask task));
+ Assert.IsNotNull(task);
+ task.Signal(SIGNAL, out bool isAborted);
+ Assert.IsFalse(isAborted);
+ Assert.IsTrue(Utility.WaitForCompletion(instance));
+ var state = instance.CurrentState;
+ Assert.IsFalse(Utility.StepCompleted(state, SUB_PROCESS_ID));
+ Assert.IsFalse(Utility.StepCompleted(state, SUB_SUB_SIGNAL_ID));
+ Assert.IsFalse(Utility.StepCompleted(state, SUB_SIGNAL_ID));
+ Assert.IsTrue(Utility.StepCompleted(state, MAIN_SIGNAL_ID));
+ }
+ }
+}
diff --git a/UnitTest/UnitTest.csproj b/UnitTest/UnitTest.csproj
index 3910735..7f49806 100644
--- a/UnitTest/UnitTest.csproj
+++ b/UnitTest/UnitTest.csproj
@@ -44,6 +44,7 @@
+
@@ -89,6 +90,7 @@
+
diff --git a/UnitTest/diagrams/DiagramLoading/invalid.bpmn b/UnitTest/diagrams/DiagramLoading/invalid.bpmn
index ec98081..2c350a1 100644
--- a/UnitTest/diagrams/DiagramLoading/invalid.bpmn
+++ b/UnitTest/diagrams/DiagramLoading/invalid.bpmn
@@ -192,6 +192,8 @@
+
+
diff --git a/UnitTest/diagrams/SubProcesses/subprocesses.bpmn b/UnitTest/diagrams/SubProcesses/subprocesses.bpmn
new file mode 100644
index 0000000..221970d
--- /dev/null
+++ b/UnitTest/diagrams/SubProcesses/subprocesses.bpmn
@@ -0,0 +1,260 @@
+
+
+
+
+ Flow_0ybu4zs
+
+
+ Flow_1iuw80e
+ Flow_1v689k6
+
+ Flow_1ogm67q
+
+
+ Flow_0sg21d8
+ Flow_0a6kp3f
+
+ Flow_0wg2h5i
+
+
+
+ Flow_0wg2h5i
+ Flow_0ey8aqt
+
+
+ Flow_0ey8aqt
+
+
+
+
+
+ Flow_1ogm67q
+ Flow_0sg21d8
+ Flow_06ya3yo
+
+
+
+
+ Flow_06ya3yo
+ Flow_1cury9n
+
+
+ Flow_1cury9n
+ Flow_0a6kp3f
+ Flow_06vs1x4
+ Flow_0122jm8
+
+
+
+ Flow_0122jm8
+
+
+
+
+
+ Flow_06vs1x4
+
+
+
+
+
+
+
+
+
+
+
+ Flow_1q3m6i8
+ Flow_1v689k6
+ Flow_1j6wmqe
+ Flow_0hvbkiq
+ Flow_081edq1
+
+
+
+ Flow_0ybu4zs
+ Flow_1iuw80e
+ Flow_1lu5lzt
+
+
+ Flow_081edq1
+
+
+
+
+ Flow_1lu5lzt
+ Flow_1q3m6i8
+
+
+ Flow_1j6wmqe
+
+
+
+
+
+
+
+
+ Flow_0hvbkiq
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+