nx_chemical_dispenser.nx source
↩ module page · 210 lines · 11361 B
1// nx_chemical_dispenser.nx -- controlled-bolus chemical output (scent / pheromone / dose).
2//
3// module: nishi-core.perception.chemical_dispenser
4// depends: nishi-core.perception.profile + nishi-core.perception.transducer_characterizer +
5// nishi-core.perception.provenance_curve_store + nishi-core.io.syscalls
6// disk_kb: 5
7// capability: PERCEPTION
8// wired_status: PARTIAL_WIRED
9//
10// MISSING_CAPABILITIES:
11// - VALVE_DRIVER_DISPATCH (control solenoid / piezo / peristaltic valve
12// via MCU GPIO + PWM; depends on nx_mcu_gpio + nx_mcu_pwm from embedded
13// substrate arc)
14// - CARTRIDGE_INVENTORY_TRACK (per-cartridge remaining mass; depletes
15// with each dispense; surfaces low-inventory alert)
16// - DISPENSE_PROFILE_LOAD (per-cartridge-lot calibrated dispense curve
17// from nx_provenance_curve_store, type DISPENSE_PROFILE)
18// - LICENSE_TAG_ENFORCE (refuse to dispense substances without
19// license/safety tag in cartridge manifest)
20// - SAFETY_INTERLOCK (refuse dispense if subject too close to source,
21// if temperature exceeds safe range, if previous dispense too recent)
22// - INTEGRATION_WITH_REACTIVE_BEHAVIOR (composes with future
23// nx_reactive_behavior so scent + audio + haptic fire as a
24// coordinated prey-flight choreography in the dog toy)
25//
26// license_tier: PUBLIC_NISHI_SUBSTRATE
27// genealogy_id: feedback-substrate-primitives-meta-not-one-off_2026 +
28// feedback-multi-species-perceptual-substrate-not-anthropocentric_2026 +
29// feedback-substrate-does-heavy-lifting-user-is-partner-not-gate +
30// reference-ingest-citation-and-license-discipline +
31// project-prey-spec-dog-toy +
32// feedback-build-intelligence-never-strip-features
33//
34// Per cardinal [[feedback-multi-species-perceptual-substrate-not-anthropocentric]]:
35// Scent is the KILLER FEATURE of the prey-spec dog toy. TV-quality audio
36// + scent absent = the dog's nose says "nothing real" and the toy fails
37// the perceptual gate. THIS primitive operates the cartridge mechanism
38// that releases a controlled bolus of species-appropriate scent at
39// substrate-determined timing (proximity-triggered prey-flight, periodic
40// "alive" pulses, calming-pheromone steady-state, etc.).
41//
42// Reuse set served by this single primitive:
43// - Prey-spec dog toy scent dispense (PRIMARY use case; rabbit / squirrel /
44// bird scent per nx_perceptual_profile target)
45// - Calming pheromone diffuser (dog separation anxiety; cat Feliway-style
46// synthetic facial pheromone substitute)
47// - Working-dog training scent dispense (controlled doses for SAR /
48// drug / explosive detection training; substrate logs doses for
49// training-record provenance)
50// - Beekeeping queen-pheromone supplementation (controlled lemongrass /
51// synthetic QMP dispensing in nuc boxes)
52// - Aquaculture chemical conditioning (controlled food-attractant or
53// stress-reduction additive dispense)
54// - Veterinary controlled medication delivery (microdose nebulization)
55// - Humane pest deterrence (species-aversive scent dispense at
56// entry points instead of poison)
57
58import "nx_syscalls.nx"
59import "nx_perceptual_profile.nx"
60import "nx_transducer_characterizer.nx"
61import "nx_provenance_curve_store.nx"
62
63// ===== Dispense mechanism sealed enum =============================
64
65const NX_CHEM_DISP_SOLENOID_VALVE: i64 = 1 // electromagnetic pulse valve
66const NX_CHEM_DISP_PIEZO_VALVE: i64 = 2 // piezoelectric microvalve
67const NX_CHEM_DISP_PERISTALTIC_PUMP: i64 = 3 // tube-roller pump
68const NX_CHEM_DISP_ATOMIZER_PIEZO: i64 = 4 // mesh-nebulizer for fine particles
69const NX_CHEM_DISP_HEATED_RELEASE: i64 = 5 // thermal volatilization (wax-bed)
70const NX_CHEM_DISP_FAN_DIFFUSION: i64 = 6 // fan-assisted ambient diffusion
71const NX_CHEM_DISP_REFILLABLE_WICK: i64 = 7 // passive wick; rate set by air flow
72
73func nx_chem_disp_mech_name(m: i64) -> *u8 {
74 if m == NX_CHEM_DISP_SOLENOID_VALVE { return "SOLENOID_VALVE" }
75 if m == NX_CHEM_DISP_PIEZO_VALVE { return "PIEZO_VALVE" }
76 if m == NX_CHEM_DISP_PERISTALTIC_PUMP { return "PERISTALTIC_PUMP" }
77 if m == NX_CHEM_DISP_ATOMIZER_PIEZO { return "ATOMIZER_PIEZO" }
78 if m == NX_CHEM_DISP_HEATED_RELEASE { return "HEATED_RELEASE" }
79 if m == NX_CHEM_DISP_FAN_DIFFUSION { return "FAN_DIFFUSION" }
80 if m == NX_CHEM_DISP_REFILLABLE_WICK { return "REFILLABLE_WICK" }
81 return "UNKNOWN_DISP_MECHANISM"
82}
83
84// ===== Substance class sealed enum ================================
85//
86// What KIND of substance is being dispensed. Substrate gates dispense
87// by license + safety per substance class; refuses unregistered substances.
88
89const NX_CHEM_SUBST_PREY_SCENT: i64 = 1 // rabbit / squirrel / bird / mouse
90const NX_CHEM_SUBST_CALMING_PHEROMONE: i64 = 2 // synthetic DAP, FAS, etc.
91const NX_CHEM_SUBST_TRAINING_TARGET: i64 = 3 // SAR / detection training scent
92const NX_CHEM_SUBST_QUEEN_PHEROMONE: i64 = 4 // bee
93const NX_CHEM_SUBST_FOOD_ATTRACTANT: i64 = 5 // aquaculture / wildlife monitoring
94const NX_CHEM_SUBST_AVERSIVE_SCENT: i64 = 6 // humane pest deterrence
95const NX_CHEM_SUBST_VETERINARY_MED: i64 = 7 // requires veterinary license
96const NX_CHEM_SUBST_USER_DEFINED: i64 = 8
97
98func nx_chem_subst_name(s: i64) -> *u8 {
99 if s == NX_CHEM_SUBST_PREY_SCENT { return "PREY_SCENT" }
100 if s == NX_CHEM_SUBST_CALMING_PHEROMONE { return "CALMING_PHEROMONE" }
101 if s == NX_CHEM_SUBST_TRAINING_TARGET { return "TRAINING_TARGET" }
102 if s == NX_CHEM_SUBST_QUEEN_PHEROMONE { return "QUEEN_PHEROMONE" }
103 if s == NX_CHEM_SUBST_FOOD_ATTRACTANT { return "FOOD_ATTRACTANT" }
104 if s == NX_CHEM_SUBST_AVERSIVE_SCENT { return "AVERSIVE_SCENT" }
105 if s == NX_CHEM_SUBST_VETERINARY_MED { return "VETERINARY_MED" }
106 if s == NX_CHEM_SUBST_USER_DEFINED { return "USER_DEFINED" }
107 return "UNKNOWN_SUBSTANCE"
108}
109
110// ===== Dispense verdicts ==========================================
111
112const NX_CHEM_DISP_OK: i64 = 0
113const NX_CHEM_DISP_FAIL_BAD_MECHANISM: i64 = 1
114const NX_CHEM_DISP_FAIL_BAD_SUBSTANCE: i64 = 2
115const NX_CHEM_DISP_FAIL_CARTRIDGE_EMPTY: i64 = 3
116const NX_CHEM_DISP_FAIL_CARTRIDGE_NOT_CALIBRATED: i64 = 4 // no DISPENSE_PROFILE curve
117const NX_CHEM_DISP_FAIL_LICENSE_REFUSED: i64 = 5 // substance lacks
118 // license/safety tag
119const NX_CHEM_DISP_FAIL_SAFETY_INTERLOCK: i64 = 6 // proximity / temperature /
120 // cooldown violation
121const NX_CHEM_DISP_FAIL_VALVE_STUCK: i64 = 7 // mechanical failure detected
122const NX_CHEM_DISP_FAIL_DEPENDENCY_MISSING: i64 = 8 // PARTIAL_WIRED default
123
124func nx_chem_disp_verdict_name(v: i64) -> *u8 {
125 if v == NX_CHEM_DISP_OK { return "OK" }
126 if v == NX_CHEM_DISP_FAIL_BAD_MECHANISM { return "FAIL_BAD_MECHANISM" }
127 if v == NX_CHEM_DISP_FAIL_BAD_SUBSTANCE { return "FAIL_BAD_SUBSTANCE" }
128 if v == NX_CHEM_DISP_FAIL_CARTRIDGE_EMPTY { return "FAIL_CARTRIDGE_EMPTY" }
129 if v == NX_CHEM_DISP_FAIL_CARTRIDGE_NOT_CALIBRATED { return "FAIL_CARTRIDGE_NOT_CALIBRATED" }
130 if v == NX_CHEM_DISP_FAIL_LICENSE_REFUSED { return "FAIL_LICENSE_REFUSED" }
131 if v == NX_CHEM_DISP_FAIL_SAFETY_INTERLOCK { return "FAIL_SAFETY_INTERLOCK" }
132 if v == NX_CHEM_DISP_FAIL_VALVE_STUCK { return "FAIL_VALVE_STUCK" }
133 if v == NX_CHEM_DISP_FAIL_DEPENDENCY_MISSING { return "FAIL_DEPENDENCY_MISSING" }
134 return "UNKNOWN_CHEM_DISP_VERDICT"
135}
136
137// ===== Dispense parameters struct =================================
138//
139// Bundled per [[feedback-nishilang-16-arg-function-limit]].
140
141struct NxChemDispenseParams {
142 mechanism: i64 // NX_CHEM_DISP_*
143 substance_class: i64 // NX_CHEM_SUBST_*
144 bolus_microliters_x10: i64 // tenth-microliters to avoid float; e.g., 5 = 0.5 μL
145 duration_ms: i64 // how long the valve stays open
146 target_species_profile: i64 // for species-appropriate substance selection
147 minimum_distance_to_subject_cm: i64 // safety interlock
148 minimum_cooldown_ms: i64 // prevent saturating air with substance
149 cartridge_serial_ptr: *u8
150 cartridge_serial_len: i64 // identifies which cartridge calibration to load
151 operator_authorized: i64 // 0/1; veterinary substances require 1
152}
153
154// ===== Top-level entry stubs ======================================
155
156// nx_chem_dispense -- emit a controlled bolus of substance through the
157// named mechanism + cartridge. Substrate enforces license, safety,
158// cooldown, calibration; dispatches MCU control signals.
159
160func nx_chem_dispense(params_ptr: *NxChemDispenseParams) -> i64 {
161 if params_ptr == 0 as *NxChemDispenseParams { return NX_CHEM_DISP_FAIL_BAD_SUBSTANCE }
162 // PARTIAL_WIRED: valve dispatch + safety interlock + license enforce queued.
163 return NX_CHEM_DISP_FAIL_DEPENDENCY_MISSING
164}
165
166// nx_chem_dispense_cartridge_remaining_uL_x10 -- inspector: how much
167// substance is left in the cartridge (tenth-microliters)? Surfaces to
168// audit dashboard for refill alerts.
169
170func nx_chem_dispense_cartridge_remaining_uL_x10(cartridge_serial_ptr: *u8,
171 cartridge_serial_len: i64) -> i64 {
172 if cartridge_serial_len <= 0 { return -1 }
173 return -1
174}
175
176// nx_chem_dispense_register_cartridge -- caller registers a new cartridge
177// with manifest (substance + license + initial volume + safety profile).
178// Substrate refuses substances without license/safety tag per
179// [[reference-ingest-citation-and-license-discipline]].
180
181func nx_chem_dispense_register_cartridge(cartridge_serial_ptr: *u8, cartridge_serial_len: i64,
182 substance_class: i64,
183 initial_volume_uL_x10: i64,
184 license_manifest_hash_ptr: *u8,
185 license_manifest_hash_len: i64) -> i64 {
186 if cartridge_serial_len <= 0 { return 0 }
187 if license_manifest_hash_len <= 0 { return 0 } // refuse unlicensed
188 if substance_class < NX_CHEM_SUBST_PREY_SCENT { return 0 }
189 if substance_class > NX_CHEM_SUBST_USER_DEFINED { return 0 }
190 if initial_volume_uL_x10 <= 0 { return 0 }
191 return 0
192}
193
194// nx_chem_dispense_is_safe_to_fire -- inspector: would a dispense
195// right now pass all safety interlocks (distance / temperature /
196// cooldown)? Used by nx_reactive_behavior to choreograph without
197// triggering FAIL_SAFETY_INTERLOCK mid-sequence.
198
199func nx_chem_dispense_is_safe_to_fire(cartridge_serial_ptr: *u8, cartridge_serial_len: i64,
200 current_distance_cm: i64) -> i64 {
201 if cartridge_serial_len <= 0 { return 0 }
202 if current_distance_cm < 0 { return 0 }
203 return 0
204}
205
206// nx_chem_dispense_get_last_verdict -- inspector.
207
208func nx_chem_dispense_get_last_verdict() -> i64 {
209 return NX_CHEM_DISP_FAIL_DEPENDENCY_MISSING
210}