Mô hình quy trình
Bước, luồng, cổng rẽ nhánh — cấu trúc dữ liệu nền tảng.
Mọi thứ trong công cụ đều xoay quanh một cấu trúc dữ liệu duy nhất: đồ thị có hướng gồm bước (node) và luồng (edge).
Bước
| Thuộc tính | Ý nghĩa |
|---|---|
id | Mã duy nhất, dùng để nối luồng. Đổi id thì luồng tự cập nhật theo. |
name | Tên hiển thị trên sơ đồ |
type | event · task · gateway |
subtype | Sự kiện: start/end/intermediate.
Cổng: exclusive/parallel/inclusive |
time | Thời lượng một lần chạy, tính bằng phút |
cost | Chi phí một lần chạy |
Cổng rẽ nhánh thường có
time = 0 và cost = 0 vì nó là điểm ra
quyết định chứ không phải công việc. Nhưng nếu việc ra quyết định thật sự tốn thời gian
(họp xét duyệt chẳng hạn), bạn cứ điền — công thức xử lý như mọi bước khác.
Luồng
Mỗi luồng nối source tới target, có thể mang label
(nhãn điều kiện) và probability (xác suất). Xác suất chỉ có ý nghĩa khi bước
nguồn có từ hai luồng ra trở lên.
Ba loại điểm rẽ
| Loại | Ký hiệu | Cách hiểu | Ảnh hưởng tới tính toán |
|---|---|---|---|
| Loại trừ (XOR) | ◇ ✕ | Chọn đúng một nhánh | Sinh nhiều kịch bản, kỳ vọng có trọng số |
| Song song (AND) | ◇ + | Mọi nhánh cùng chạy | Thời gian lấy max, chi phí lấy tổng |
| Bao gồm (OR) | ◇ O | Một hoặc nhiều nhánh | Hiện được xử lý như AND (ước lượng thận trọng) |
Cổng gộp
Một cổng song song cần có cổng gộp tương ứng để công cụ biết các nhánh hợp lại ở đâu. Cổng gộp được tìm tự động: đó là bước gần nhất mà mọi nhánh của cổng rẽ đều đi qua.
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
Không tìm được cổng gộp, công cụ sẽ cảnh báo và tạm tính cổng đó như cổng loại trừ. Kết quả
vẫn ra nhưng thấp hơn thực tế — hãy vẽ thêm cổng gộp thay vì bỏ qua cảnh báo.
Cách dữ liệu được lưu
Mỗi quy trình lưu hai thứ: file gốc bạn tải lên (nguyên vẹn, tải về lại được) và mô hình hiện hành dạng JSON — đây mới là thứ dùng để tính toán và là thứ bạn sửa trên giao diện.
{
"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"}
}
Chỗ nào khó hiểu hoặc còn thiếu?