nx_maint_render.nx source
↩ module page · 148 lines · 6506 B
1// nx_maint_render.nx -- R6: the fronts. Renders a MaintResult + ActionPlan into
2// a self-contained, ZERO-JS, privacy-first HTML card -- in two modes: DIY
3// ("what to do yourself") and ENTERPRISE ("work order / dispatch"). Same core,
4// two fronts (the operator's monitor/fix/replace, DIYer -> enterprise).
5//
6// Operator doctrine (Vizsla/IoT render arcs): 0 <script>, 0 third-party loads,
7// data treated as valuable not stolen -> FAIL-CLOSED: the card self-scans and
8// nx_maint_render_is_clean REFUSES anything containing a script or an http load.
9//
10// Operator (2026-06-23): "from the hardware rung up each rung" -- R6 on top of
11// R5's ActionPlan. The last SOLO rung before R0 (live) needs hardware.
12//
13// NEVER-BRICK (#26): builds bytes into a caller buffer; no syscalls, no writes.
14//
15// genealogy_id: project-maintenance-platform-sclass-2026-06-23 (R6 fronts)
16// license_tier: ORIGINAL
17
18import "nx_maint_action.nx" // NX_HEALTH_* / NX_CAUSE_* / NX_ACT_* + plan
19const NX_MAGIC_2048: i64 = 2048
20
21const NX_RENDER_DIY: i64 = 0
22const NX_RENDER_ENTERPRISE: i64 = 1
23
24// ---- labels (human-readable; the front shows these, not enum ints) ----
25func nx_health_name(s: i64) -> *u8 {
26 if s == NX_HEALTH_OK { return "Healthy" as *u8 }
27 if s == NX_HEALTH_IDLE { return "Idle" as *u8 }
28 if s == NX_HEALTH_WATCH { return "Watch" as *u8 }
29 if s == NX_HEALTH_DEGRADED { return "Degraded" as *u8 }
30 if s == NX_HEALTH_URGENT { return "Urgent" as *u8 }
31 if s == NX_HEALTH_SAFETY { return "SAFETY HAZARD" as *u8 }
32 if s == NX_HEALTH_NODATA { return "No data" as *u8 }
33 return "Unknown" as *u8
34}
35func nx_health_color(s: i64) -> *u8 {
36 if s == NX_HEALTH_OK { return "#2e7d32" as *u8 }
37 if s == NX_HEALTH_WATCH { return "#f9a825" as *u8 }
38 if s == NX_HEALTH_DEGRADED { return "#ef6c00" as *u8 }
39 if s == NX_HEALTH_URGENT { return "#c62828" as *u8 }
40 if s == NX_HEALTH_SAFETY { return "#7f0000" as *u8 }
41 return "#9e9e9e" as *u8
42}
43func nx_cause_name(c: i64) -> *u8 {
44 if c == NX_CAUSE_OVERSIZED { return "Oversized / short-cycling" as *u8 }
45 if c == NX_CAUSE_DIRTY_FILTER { return "Dirty filter / fouled coil" as *u8 }
46 if c == NX_CAUSE_CONTROL_CAL { return "Control calibration" as *u8 }
47 if c == NX_CAUSE_IMBALANCE { return "Imbalance / wear" as *u8 }
48 if c == NX_CAUSE_BEARING { return "Failing bearing" as *u8 }
49 if c == NX_CAUSE_CONSUMABLE_SPENT { return "Consumable spent" as *u8 }
50 if c == NX_CAUSE_SEAL_LEAK { return "Seal / joint leak" as *u8 }
51 if c == NX_CAUSE_PUMP_OR_BIGLEAK { return "Weak pump / major leak" as *u8 }
52 if c == NX_CAUSE_CIRCUIT_LOAD { return "Circuit near capacity" as *u8 }
53 if c == NX_CAUSE_VOLTAGE_SUPPLY { return "Voltage supply issue" as *u8 }
54 if c == NX_CAUSE_SAFETY_HAZARD { return "Safety hazard" as *u8 }
55 return "Healthy" as *u8
56}
57func nx_action_name(a: i64) -> *u8 {
58 if a == NX_ACT_MONITOR { return "Monitor" as *u8 }
59 if a == NX_ACT_FIX_DIY { return "Fix yourself" as *u8 }
60 if a == NX_ACT_FIX_PRO { return "Call a pro" as *u8 }
61 if a == NX_ACT_REPLACE_CONSUMABLE { return "Replace consumable" as *u8 }
62 if a == NX_ACT_REPLACE_COMPONENT { return "Replace component" as *u8 }
63 if a == NX_ACT_REPLACE_UNIT { return "Replace unit" as *u8 }
64 if a == NX_ACT_SHUTDOWN_SAFETY { return "SHUT DOWN -- call a pro now" as *u8 }
65 return "No action needed" as *u8
66}
67
68// append a NUL-terminated string to buf at off; return new offset.
69func mr_cat(buf: *u8, off: i64, s: *u8) -> i64 {
70 var i: i64 = 0
71 while s[i] != (0 as u8) {
72 buf[off + i] = s[i]
73 i = i + 1
74 }
75 return off + i
76}
77
78func mr_contains(buf: *u8, len: i64, needle: *u8) -> i64 {
79 var i: i64 = 0
80 while i < len {
81 var j: i64 = 0
82 var m: i64 = 1
83 var go: i64 = 1
84 while go == 1 {
85 if needle[j] == (0 as u8) { go = 0 }
86 else {
87 if (i + j) >= len { m = 0; go = 0 }
88 else {
89 if buf[i + j] != needle[j] { m = 0; go = 0 }
90 else { j = j + 1 }
91 }
92 }
93 }
94 if m == 1 { return 1 }
95 i = i + 1
96 }
97 return 0
98}
99
100// FAIL-CLOSED privacy guarantee: refuse anything with a script or an http load.
101func nx_maint_render_is_clean(buf: *u8, len: i64) -> i64 {
102 if mr_contains(buf, len, "<script" as *u8) == 1 { return 0 }
103 if mr_contains(buf, len, "http" as *u8) == 1 { return 0 }
104 return 1
105}
106
107// Render the card. Returns length (>=0), or -1 if cap too small.
108func nx_maint_render_card(buf: *u8, cap: i64, asset_name: *u8, severity: i64,
109 cause: i64, action: i64, diy: i64, urgency: i64, mode: i64) -> i64 {
110 if cap < NX_MAGIC_2048 { return 0 - 1 }
111 var o: i64 = 0
112 o = mr_cat(buf, o, "<!doctype html><html><head><meta charset=utf-8><style>" as *u8)
113 o = mr_cat(buf, o, "body{font:15px sans-serif;margin:20px;color:#222}" as *u8)
114 o = mr_cat(buf, o, ".card{border:1px solid #ddd;border-left:6px solid " as *u8)
115 o = mr_cat(buf, o, nx_health_color(severity))
116 o = mr_cat(buf, o, ";border-radius:8px;padding:16px;max-width:440px}" as *u8)
117 o = mr_cat(buf, o, ".badge{display:inline-block;padding:2px 9px;border-radius:4px;color:#fff;background:" as *u8)
118 o = mr_cat(buf, o, nx_health_color(severity))
119 o = mr_cat(buf, o, "}.who{color:#666}</style></head><body><div class=card><div class=badge>" as *u8)
120 o = mr_cat(buf, o, nx_health_name(severity))
121 o = mr_cat(buf, o, "</div><h2>" as *u8)
122 o = mr_cat(buf, o, asset_name)
123 o = mr_cat(buf, o, "</h2>" as *u8)
124 if mode == NX_RENDER_ENTERPRISE {
125 o = mr_cat(buf, o, "<p><b>Work order</b> · Priority: " as *u8)
126 o = mr_cat(buf, o, nx_health_name(urgency))
127 o = mr_cat(buf, o, "</p>" as *u8)
128 }
129 o = mr_cat(buf, o, "<p><b>Likely cause:</b> " as *u8)
130 o = mr_cat(buf, o, nx_cause_name(cause))
131 o = mr_cat(buf, o, "</p><p><b>" as *u8)
132 if mode == NX_RENDER_ENTERPRISE {
133 o = mr_cat(buf, o, "Dispatch:" as *u8)
134 } else {
135 o = mr_cat(buf, o, "What to do:" as *u8)
136 }
137 o = mr_cat(buf, o, "</b> " as *u8)
138 o = mr_cat(buf, o, nx_action_name(action))
139 o = mr_cat(buf, o, "</p><p class=who>" as *u8)
140 if diy == 1 {
141 o = mr_cat(buf, o, "You can do this yourself." as *u8)
142 } else {
143 o = mr_cat(buf, o, "Route to a professional." as *u8)
144 }
145 o = mr_cat(buf, o, "</p></div></body></html>" as *u8)
146 buf[o] = 0 as u8
147 return o
148}