nx_build_plan_test.nx source
↩ module page · 118 lines · 5731 B
1// nx_build_plan_test.nx -- verify multi-process schedule solver.
2//
3// Closed-form invariants:
4// (a) plan_new sane: n=0, capacity correct, printer_free zeroed
5// (b) add_part returns sequential ids; bad inputs return negative
6// (c) set_dep updates child's bitmap; self-dep rejected
7// (d) Independent-parts case (no deps, distinct processes):
8// all start at T=0 in parallel, total = max(each)
9// (e) Sequential dep chain (A->B->C): B starts at A.end, C at B.end
10// (f) Same-process serialization: 2 FDM parts share 1 printer,
11// second starts at first.end
12// (g) Cross-process Christus demo: FDM PLA body (240m) + DIW cement
13// (60m+1440m cure) + FDM metal (30m+2880m sinter) all start at
14// T=0 because distinct processes do not contend; total =
15// max(240, 1500, 2910) = 2910 min ≈ 48.5 hours
16// (h) Cycle detection: A<->B returns NX_BUILD_ERR_CYCLE
17//
18// expect_exit: 0
19// license_tier: ORIGINAL
20
21import "nx_syscalls.nx"
22import "nx_print_process.nx"
23import "nx_build_plan.nx"
24
25func main() -> i64 {
26 // --- (a) plan_new sane defaults ---
27 let plan: *NxBuildPlan = nx_build_plan_new(8)
28 if (plan as i64) == 0 { return 5 }
29 if plan.n_parts != 0 { return 10 }
30 if plan.capacity != 8 { return 11 }
31 if plan.printer_free[NX_PROCESS_FDM_POLYMER] != 0 { return 12 }
32 if plan.printer_free[NX_PROCESS_DIW_CEMENT] != 0 { return 13 }
33
34 // --- (b) add_part returns sequential ids ---
35 let id0: i64 = nx_build_plan_add_part(plan, NX_PROCESS_FDM_POLYMER, 60, 0)
36 if id0 != 0 { return 20 }
37 let id1: i64 = nx_build_plan_add_part(plan, NX_PROCESS_DIW_CEMENT, 30, 1440)
38 if id1 != 1 { return 21 }
39 if plan.n_parts != 2 { return 22 }
40
41 // Bad input: invalid process class.
42 let bad: i64 = nx_build_plan_add_part(plan, 999, 60, 0)
43 if bad >= 0 { return 23 }
44
45 // --- (c) set_dep updates child's bitmap; self-dep rejected ---
46 if nx_build_part_set_dep(plan, 1, 0) != 0 { return 30 }
47 let p1: *NxBuildPart = (((plan.parts as i64) + 1 * NX_BUILD_PART_BYTES)) as *NxBuildPart
48 if p1.dep_bitmap != 1 { return 31 } // bit 0 = depends on part 0
49 if nx_build_part_set_dep(plan, 0, 0) >= 0 { return 32 } // self-dep error
50 // Reset dep for next test:
51 p1.dep_bitmap = 0
52
53 // --- (d) Independent parts, distinct processes ---
54 let total_d: i64 = nx_build_plan_solve(plan)
55 let p0: *NxBuildPart = plan.parts
56 if p0.start_time_min != 0 { return 40 }
57 if p1.start_time_min != 0 { return 41 }
58 if p0.end_time_min != 60 { return 42 } // 60 + 0 cure
59 if p1.end_time_min != 1470 { return 43 } // 30 + 1440 cure
60 if total_d != 1470 { return 44 }
61
62 // --- (e) Sequential dep chain A -> B -> C ---
63 let plan2: *NxBuildPlan = nx_build_plan_new(8)
64 let a: i64 = nx_build_plan_add_part(plan2, NX_PROCESS_FDM_POLYMER, 100, 0)
65 let b: i64 = nx_build_plan_add_part(plan2, NX_PROCESS_FDM_POLYMER, 50, 0)
66 let c: i64 = nx_build_plan_add_part(plan2, NX_PROCESS_FDM_POLYMER, 20, 0)
67 nx_build_part_set_dep(plan2, b, a)
68 nx_build_part_set_dep(plan2, c, b)
69 let t_e: i64 = nx_build_plan_solve(plan2)
70 let pa: *NxBuildPart = plan2.parts
71 let pb: *NxBuildPart = (((plan2.parts as i64) + 1 * NX_BUILD_PART_BYTES)) as *NxBuildPart
72 let pc: *NxBuildPart = (((plan2.parts as i64) + 2 * NX_BUILD_PART_BYTES)) as *NxBuildPart
73 if pa.start_time_min != 0 { return 50 }
74 if pb.start_time_min != 100 { return 51 }
75 if pc.start_time_min != 150 { return 52 }
76 if t_e != 170 { return 53 }
77
78 // --- (f) Same-process serialization: 2 FDM parts share 1 printer ---
79 let plan3: *NxBuildPlan = nx_build_plan_new(8)
80 nx_build_plan_add_part(plan3, NX_PROCESS_FDM_POLYMER, 80, 0)
81 nx_build_plan_add_part(plan3, NX_PROCESS_FDM_POLYMER, 40, 0)
82 nx_build_plan_solve(plan3)
83 let q0: *NxBuildPart = plan3.parts
84 let q1: *NxBuildPart = (((plan3.parts as i64) + 1 * NX_BUILD_PART_BYTES)) as *NxBuildPart
85 if q0.start_time_min != 0 { return 60 }
86 if q1.start_time_min != 80 { return 61 } // serialized on same printer
87
88 // --- (g) Christus 3-process demo ---
89 let chris: *NxBuildPlan = nx_build_plan_new(8)
90 let body: i64 = nx_build_plan_add_part(chris, NX_PROCESS_FDM_POLYMER, 240, 0)
91 let base: i64 = nx_build_plan_add_part(chris, NX_PROCESS_DIW_CEMENT, 60, 1440)
92 let crown: i64 = nx_build_plan_add_part(chris, NX_PROCESS_FDM_METAL, 30, 2880)
93 let total_chris: i64 = nx_build_plan_solve(chris)
94 let pb_chris: *NxBuildPart = chris.parts
95 let pbase_chris: *NxBuildPart = (((chris.parts as i64) + 1 * NX_BUILD_PART_BYTES)) as *NxBuildPart
96 let pcrown: *NxBuildPart = (((chris.parts as i64) + 2 * NX_BUILD_PART_BYTES)) as *NxBuildPart
97 if pb_chris.start_time_min != 0 { return 70 }
98 if pbase_chris.start_time_min != 0 { return 71 }
99 if pcrown.start_time_min != 0 { return 72 }
100 if pb_chris.end_time_min != 240 { return 73 }
101 if pbase_chris.end_time_min != 1500 { return 74 } // 60 + 1440
102 if pcrown.end_time_min != 2910 { return 75 } // 30 + 2880
103 if total_chris != 2910 { return 76 }
104 // 48.5 hours -- robot pickup at T+2910 min (48h 30min)
105
106 // --- (h) Cycle detection ---
107 let plan_cyc: *NxBuildPlan = nx_build_plan_new(4)
108 nx_build_plan_add_part(plan_cyc, NX_PROCESS_FDM_POLYMER, 10, 0)
109 nx_build_plan_add_part(plan_cyc, NX_PROCESS_FDM_POLYMER, 10, 0)
110 nx_build_part_set_dep(plan_cyc, 0, 1)
111 nx_build_part_set_dep(plan_cyc, 1, 0)
112 let cyc_result: i64 = nx_build_plan_solve(plan_cyc)
113 if cyc_result >= 0 { return 80 } // negative = error
114 let expected_cyc: i64 = 0 - NX_BUILD_ERR_CYCLE
115 if cyc_result != expected_cyc { return 81 }
116
117 return 0
118}