The process model
Steps, flows, gateways — the foundational data structures.
Everything in the tool revolves around a single data structure: a directed graph made of steps (nodes) and flows (edges).
Step
| Attribute | Meaning |
|---|---|
id | A unique code, used to connect flows. Change the id and the flows update with it. |
name | The name shown on the diagram |
type | event · task · gateway |
subtype | Event: start/end/intermediate/boundary.
Gateway: exclusive/parallel/inclusive/event_based |
wait | The time the case sits waiting, in minutes — nobody's effort is spent |
instances · sequential | A step that runs several instances within one pass |
attached_to | Which step the boundary event is attached to |
time | The duration of one run, in minutes |
cost | The cost of one run |
time = 0 and cost = 0 because it is a decision
point, not a piece of work. But if making the decision genuinely takes time (a review meeting, say),
go ahead and fill it in — the formulas treat it like any other step.
Flow
Each flow connects a source to a target, and can carry a label
(the condition label) and a probability. Probability only means something when the source
step has two or more outgoing flows.
Three kinds of branch point
| Kind | Symbol | How to read it | Effect on the calculation |
|---|---|---|---|
| Exclusive (XOR) | ◇ ✕ | Exactly one branch is taken | Generates several scenarios, expected value is weighted |
| Parallel (AND) | ◇ + | Every branch runs | Time takes the max, cost takes the sum |
| Inclusive (OR) | ◇ O | One or more branches | Currently handled as AND (a conservative estimate) |
Join gateway
A parallel gateway needs a matching join gateway so the tool knows where the branches come back together. The join is found automatically: it is the nearest step that every branch of the split passes through.
def find_join(split, adjacency):
# Tập node đến được từ mỗi nhánh
reach_sets = [reachable_from(s, adjacency) | {s} for s in adjacency[split]]
common = set.intersection(*reach_sets) # nhánh nào cũng đi qua
common.discard(split)
if not common:
return None # không có cổng gộp
distances = bfs_distances(split, adjacency)
return min(common, key=lambda n: distances.get(n, 10**6)) # gần nhất
How the data is stored
Each process stores two things: the original file you uploaded (intact, downloadable again) and the current model as JSON — and it is the model that is used for the calculation and that you edit in the interface.
{
"nodes": [
{"id": "pr", "name": "Lập phiếu yêu cầu (PR)", "type": "task",
"subtype": "", "time": 30, "cost": 90000}
],
"edges": [
{"source": "pr", "target": "gw_value", "label": "", "probability": null}
],
"geometry": {"pr": {"x": 120, "y": 200, "w": 160, "h": 80}},
"meta": {"currency": "VND", "source_format": "draw.io"}
}