code wiki / (root) / nx_bom_rt.nx

nx_bom_rt.nx source

↩ module page · 158 lines · 6274 B

1// nx_bom.nx -- GO-TO-MARKET / BILL OF MATERIALS engine. The first hardware 2// rung made concrete: turn a gated DESIGN into a buildable bench prototype 3// -- the real components, their cost, mass, lead time, and make-vs-buy -- 4// with rollups (total cost, total mass, the longest-lead CRITICAL-PATH 5// item that gates the build schedule) and per-category subtotals. Generic 6// across any product; instantiated here for the water AWG bench prototype 7// (advances water-system from MRL 2 toward MRL 3 = manufacturing proof of 8// concept). 9// 10// INTEGER-EXACT: cost in cents, mass in grams, lead time in days. The 11// enclosure is MAKE (we 3D-print it with our own CAD/G-code capability = 12// vertical integration), everything else is BUY. 13// 14// grounded: bill_of_materials + design_for_manufacturability + contract_manufacture 15// genealogy_id: bill_of_materials_bench_prototype + nishi_go_to_market 16 17import "nx_syscalls.nx" 18 19// ===== Component categories ======================================= 20 21const BC_HARVEST: i64 = 0 // cooler + fans + prefilter (pull water from air) 22const BC_COLLECT: i64 = 1 // tray + tubing + pump + tank 23const BC_TREAT: i64 = 2 // UV-C + carbon (make it safe) 24const BC_MINERAL: i64 = 3 // remineralization media (make it taste + healthful) 25const BC_CONTROL: i64 = 4 // MCU + sensors + power + wiring 26const BC_ENCLOSURE: i64 = 5 // the case (we print it) 27 28const BM_BUY: i64 = 0 29const BM_MAKE: i64 = 1 30 31// ===== Part ids (0..15 ARE the water bench-prototype BOM, in order) = 32 33const BP_PELTIER: i64 = 0 34const BP_HEATSINK_FAN: i64 = 1 35const BP_INTAKE_FAN: i64 = 2 36const BP_PREFILTER: i64 = 3 37const BP_TRAY: i64 = 4 38const BP_TUBING: i64 = 5 39const BP_PUMP: i64 = 6 40const BP_UVC_LED: i64 = 7 41const BP_CARBON: i64 = 8 42const BP_MINERAL_CART: i64 = 9 43const BP_TANK: i64 = 10 44const BP_MCU: i64 = 11 45const BP_SENSORS: i64 = 12 46const BP_PSU: i64 = 13 47const BP_ENCLOSURE: i64 = 14 48const BP_WIRING: i64 = 15 49const BP_N: i64 = 16 50 51// ===== Struct: NxPart ============================================= 52 53struct NxPart { 54 part_id: i64, 55 category: i64, 56 unit_cost_cents: i64, 57 mass_g: i64, 58 make_buy: i64, 59 lead_days: i64, 60} 61 62func nx_bom_part(id: i64) -> *NxPart { 63 let p: *NxPart = (sys_mmap(64)) as *NxPart 64 p.part_id = id 65 p.category = BC_CONTROL 66 p.unit_cost_cents = 0 67 p.mass_g = 0 68 p.make_buy = BM_BUY 69 p.lead_days = 0 70 if id == BP_PELTIER { p.category = BC_HARVEST; p.unit_cost_cents = 500; p.mass_g = 25; p.lead_days = 7 } 71 if id == BP_HEATSINK_FAN { p.category = BC_HARVEST; p.unit_cost_cents = 1200; p.mass_g = 200; p.lead_days = 7 } 72 if id == BP_INTAKE_FAN { p.category = BC_HARVEST; p.unit_cost_cents = 800; p.mass_g = 150; p.lead_days = 5 } 73 if id == BP_PREFILTER { p.category = BC_HARVEST; p.unit_cost_cents = 600; p.mass_g = 50; p.lead_days = 5 } 74 if id == BP_TRAY { p.category = BC_COLLECT; p.unit_cost_cents = 1000; p.mass_g = 120; p.lead_days = 10 } 75 if id == BP_TUBING { p.category = BC_COLLECT; p.unit_cost_cents = 700; p.mass_g = 80; p.lead_days = 5 } 76 if id == BP_PUMP { p.category = BC_COLLECT; p.unit_cost_cents = 1500; p.mass_g = 180; p.lead_days = 10 } 77 if id == BP_UVC_LED { p.category = BC_TREAT; p.unit_cost_cents = 1800; p.mass_g = 30; p.lead_days = 14 } 78 if id == BP_CARBON { p.category = BC_TREAT; p.unit_cost_cents = 1200; p.mass_g = 150; p.lead_days = 7 } 79 if id == BP_MINERAL_CART { p.category = BC_MINERAL; p.unit_cost_cents = 2000; p.mass_g = 300; p.lead_days = 14 } 80 if id == BP_TANK { p.category = BC_COLLECT; p.unit_cost_cents = 1400; p.mass_g = 250; p.lead_days = 10 } 81 if id == BP_MCU { p.category = BC_CONTROL; p.unit_cost_cents = 1000; p.mass_g = 20; p.lead_days = 7 } 82 if id == BP_SENSORS { p.category = BC_CONTROL; p.unit_cost_cents = 2500; p.mass_g = 100; p.lead_days = 10 } 83 if id == BP_PSU { p.category = BC_CONTROL; p.unit_cost_cents = 1600; p.mass_g = 400; p.lead_days = 7 } 84 // The enclosure is MAKE: printed with our own CAD + G-code capability. 85 if id == BP_ENCLOSURE { p.category = BC_ENCLOSURE; p.unit_cost_cents = 800; p.mass_g = 600; p.make_buy = BM_MAKE; p.lead_days = 2 } 86 if id == BP_WIRING { p.category = BC_CONTROL; p.unit_cost_cents = 900; p.mass_g = 60; p.lead_days = 5 } 87 return p 88} 89 90// ===== Struct: NxBom (rollup) ===================================== 91 92struct NxBom { 93 total_cost_cents: i64, 94 total_mass_g: i64, 95 max_lead_days: i64, 96 part_count: i64, 97 make_count: i64, 98} 99 100func nx_bom_new() -> *NxBom { 101 let b: *NxBom = (sys_mmap(48)) as *NxBom 102 b.total_cost_cents = 0 103 b.total_mass_g = 0 104 b.max_lead_days = 0 105 b.part_count = 0 106 b.make_count = 0 107 return b 108} 109 110func nx_bom_add(b: *NxBom, part_id: i64, qty: i64) -> i64 { 111 let p: *NxPart = nx_bom_part(part_id) 112 b.total_cost_cents = b.total_cost_cents + p.unit_cost_cents * qty 113 b.total_mass_g = b.total_mass_g + p.mass_g * qty 114 if p.lead_days > b.max_lead_days { b.max_lead_days = p.lead_days } 115 b.part_count = b.part_count + qty 116 if p.make_buy == BM_MAKE { b.make_count = b.make_count + qty } 117 return 0 118} 119 120// ===== The water AWG bench prototype BOM ========================== 121// 122// Parts 0..15 in id order; two intake fans, one of everything else. 123 124func nx_bom_water_count() -> i64 { 125 return BP_N 126} 127 128func nx_bom_water_qty_at(i: i64) -> i64 { 129 if i == BP_INTAKE_FAN { return 2 } 130 return 1 131} 132 133func nx_bom_water_assemble() -> *NxBom { 134 let b: *NxBom = nx_bom_new() 135 var i: i64 = 0 136 while i < nx_bom_water_count() { 137 nx_bom_add(b, i, nx_bom_water_qty_at(i)) 138 i = i + 1 139 } 140 return b 141} 142 143// Per-category cost subtotal across the water BOM. 144func nx_bom_water_category_cost(cat: i64) -> i64 { 145 var c: i64 = 0 146 var i: i64 = 0 147 while i < nx_bom_water_count() { 148 let p: *NxPart = nx_bom_part(i) 149 if p.category == cat { c = c + p.unit_cost_cents * nx_bom_water_qty_at(i) } 150 i = i + 1 151 } 152 return c 153} 154 155// The critical-path lead time = the longest single-part lead (gates the build). 156func nx_bom_critical_path_days(b: *NxBom) -> i64 { 157 return b.max_lead_days 158}