nx_build_plan_sim.nx source
↩ module page · 127 lines · 5164 B
1// nx_build_plan_sim.nx -- simulator for the multi-process build plan.
2//
3// =====================================================================
4// CARDINAL: [[feedback-simulation-discipline-for-every-substrate]]
5//
6// Second-substrate demonstration of the simulation discipline. First
7// stone was nx_print_sim (3d-print physics). THIS file applies the
8// same SimulatorState + step + sealed-enum issue framework to a
9// completely different substrate (multi-process schedule solver) --
10// proving the discipline generalizes.
11//
12// Industry: there is no production cross-process build-plan
13// scheduler at all (humans + spreadsheets coordinate today), let
14// alone a simulator that predicts schedule failures.
15//
16// What this simulator predicts on a SOLVED build plan:
17// DEADLINE_MISSED total schedule > caller-supplied deadline
18// POST_CURE_OVERRUN any part's post-cure > caller max
19// PRINTER_IDLE_WASTE one process's printer idle most of its
20// used window (heuristic candidate -- queued
21// per the cardinal's no-heuristic rule)
22//
23// FIRST STONE here ships the first two (real time-arithmetic, not
24// heuristic). PRINTER_IDLE_WASTE deferred until we have a clearer
25// physical definition.
26// =====================================================================
27//
28// license_tier: ORIGINAL
29
30import "nx_syscalls.nx"
31import "nx_print_process.nx"
32import "nx_build_plan.nx"
33
34// ===== sealed issue-kind enum ====================================
35
36const NX_BUILD_SIM_ISSUE_NONE: i64 = 0
37const NX_BUILD_SIM_ISSUE_DEADLINE_MISSED: i64 = 1
38const NX_BUILD_SIM_ISSUE_POST_CURE_OVERRUN: i64 = 2
39const NX_BUILD_SIM_ISSUE_PRINTER_IDLE_WASTE: i64 = 3 // queued (next stone)
40const NX_BUILD_SIM_ISSUE_N: i64 = 4
41
42func nx_build_sim_issue_is_valid(k: i64) -> i64 {
43 if k < 0 { return 0 }
44 if k >= NX_BUILD_SIM_ISSUE_N { return 0 }
45 return 1
46}
47
48// ===== issue record ==============================================
49//
50// part_id: index into plan.parts (or -1 for plan-wide).
51// magnitude_min: severity in minutes (e.g., overshoot of deadline).
52
53struct NxBuildPlanSimIssue {
54 kind: i64,
55 part_id: i64,
56 magnitude_min: i64,
57 reserved: i64, // pad to 32 bytes for future use
58}
59
60const NX_BUILD_PLAN_SIM_ISSUE_BYTES: i64 = 32
61
62// ===== simulator check ===========================================
63//
64// Runs all failure-prediction checks on a SOLVED build plan. Writes
65// NxBuildPlanSimIssue records into out_issues (caller-supplied
66// buffer of capacity out_issues_cap). Returns the number of issues
67// written, capped at out_issues_cap.
68//
69// Caller responsibility: nx_build_plan_solve(plan) must have been
70// called BEFORE this function (so end_time_min fields are populated).
71//
72// Inputs (caller-supplied constraints):
73// deadline_min total wall-clock the schedule must fit within
74// max_cure_min longest acceptable per-part post-cure
75//
76// Returns 0 if no issues (schedule fits all constraints).
77
78func nx_build_plan_sim_check(plan: *NxBuildPlan,
79 deadline_min: i64,
80 max_cure_min: i64,
81 out_issues: *NxBuildPlanSimIssue,
82 out_issues_cap: i64) -> i64 {
83 if (plan as i64) == 0 { return 0 }
84 if (out_issues as i64) == 0 { return 0 }
85 if out_issues_cap <= 0 { return 0 }
86
87 var n_written: i64 = 0
88
89 // ---- Check 1: DEADLINE_MISSED ----
90 // Real time arithmetic: plan.total_minutes vs caller deadline.
91 // No heuristic threshold; the deadline is the operator's actual
92 // constraint.
93 if deadline_min > 0 {
94 if plan.total_minutes > deadline_min {
95 if n_written < out_issues_cap {
96 let ip: *NxBuildPlanSimIssue = (((out_issues as i64) + n_written * NX_BUILD_PLAN_SIM_ISSUE_BYTES)) as *NxBuildPlanSimIssue
97 ip.kind = NX_BUILD_SIM_ISSUE_DEADLINE_MISSED
98 ip.part_id = -1 // plan-wide
99 ip.magnitude_min = plan.total_minutes - deadline_min
100 ip.reserved = 0
101 n_written = n_written + 1
102 }
103 }
104 }
105
106 // ---- Check 2: POST_CURE_OVERRUN ----
107 // Real per-part comparison against caller's max.
108 if max_cure_min > 0 {
109 var pi: i64 = 0
110 while pi < plan.n_parts {
111 let pp: *NxBuildPart = (((plan.parts as i64) + pi * NX_BUILD_PART_BYTES)) as *NxBuildPart
112 if pp.post_cure_minutes > max_cure_min {
113 if n_written < out_issues_cap {
114 let ip2: *NxBuildPlanSimIssue = (((out_issues as i64) + n_written * NX_BUILD_PLAN_SIM_ISSUE_BYTES)) as *NxBuildPlanSimIssue
115 ip2.kind = NX_BUILD_SIM_ISSUE_POST_CURE_OVERRUN
116 ip2.part_id = pi
117 ip2.magnitude_min = pp.post_cure_minutes - max_cure_min
118 ip2.reserved = 0
119 n_written = n_written + 1
120 }
121 }
122 pi = pi + 1
123 }
124 }
125
126 return n_written
127}