1use crate::NQuin;
5
6pub use crate::frame_layout::{CONTROL_BIT, FEEDBACK_BIT, STABILIZATION_BIT};
8
9pub mod advanced;
11pub use advanced::{
12 adaptive_gains, mimo_output, mimo_step, mit_rule_adapt, mpc_control, scheduled_gain,
13};
14
15use super::epistemic_boundaries::{
16 degrade_claim_to_socratic, detect_referral_by_severity, detect_referral_trigger,
17 identify_degradation_vector, DegradationVector, ReferralTrigger, SocraticDegradation,
18};
19
20pub fn enforce_linguistic_degradation(claim_quin: &NQuin) -> Option<SocraticDegradation> {
22 let vector = identify_degradation_vector(claim_quin);
23 if vector != DegradationVector::Unknown {
24 return degrade_claim_to_socratic(vector);
25 }
26 None
27}
28
29pub fn enforce_guided_referral(claim_quin: &NQuin) -> Option<ReferralTrigger> {
35 if let Some(t) = detect_referral_trigger(claim_quin) {
37 return Some(t);
38 }
39 detect_referral_by_severity(identify_degradation_vector(claim_quin), claim_quin.metadata)
41}
42
43#[derive(Debug, Clone)]
45pub struct ControlState {
46 pub setpoint: f64, pub process_variable: f64, pub error: f64, pub integral: f64, pub derivative: f64, pub last_error: f64, pub last_time: u64, }
54
55impl ControlState {
56 pub fn new(setpoint: f64, initial_value: f64) -> Self {
63 let error = setpoint - initial_value;
64 Self {
65 setpoint,
66 process_variable: initial_value,
67 error,
68 integral: 0.0,
69 derivative: 0.0,
70 last_error: error,
71 last_time: 0,
72 }
73 }
74
75 pub fn update(&mut self, new_value: f64, current_time: u64) {
77 self.process_variable = new_value;
78 self.last_error = self.error;
79 self.error = self.setpoint - new_value;
80
81 let dt = current_time.saturating_sub(self.last_time) as f64;
86 if dt > 0.0 {
87 self.derivative = (self.error - self.last_error) / dt;
88 self.integral += self.error * dt;
89 }
90
91 self.last_time = current_time;
92 }
93
94 pub fn reset_integral(&mut self) {
96 self.integral = 0.0;
97 }
98}
99
100#[derive(Debug, Clone)]
102pub struct PidParameters {
103 pub kp: f64, pub ki: f64, pub kd: f64, pub output_min: f64,
107 pub output_max: f64,
108}
109
110impl PidParameters {
111 pub fn conservative_power_system() -> Self {
113 Self {
114 kp: 0.5,
115 ki: 0.1,
116 kd: 0.05,
117 output_min: 0.0,
118 output_max: 100.0,
119 }
120 }
121
122 pub fn aggressive_response() -> Self {
124 Self {
125 kp: 1.0,
126 ki: 0.5,
127 kd: 0.2,
128 output_min: 0.0,
129 output_max: 100.0,
130 }
131 }
132}
133
134#[derive(Debug, Clone)]
136pub struct FeedbackController {
137 pub name: String,
138 pub parameters: PidParameters,
139 pub state: ControlState,
140 pub enabled: bool,
141}
142
143impl FeedbackController {
144 pub fn new(name: String, setpoint: f64, initial_value: f64, params: PidParameters) -> Self {
146 Self {
147 name,
148 parameters: params,
149 state: ControlState::new(setpoint, initial_value),
150 enabled: true,
151 }
152 }
153
154 pub fn compute_output(&mut self) -> f64 {
156 if !self.enabled {
157 return 0.0;
158 }
159
160 let proportional = self.parameters.kp * self.state.error;
162 let integral = self.parameters.ki * self.state.integral;
163 let derivative = self.parameters.kd * self.state.derivative;
164
165 let mut output = proportional + integral + derivative;
166
167 output = output.clamp(self.parameters.output_min, self.parameters.output_max);
169
170 if output >= self.parameters.output_max && self.state.error > 0.0 {
172 self.state.reset_integral();
173 } else if output <= self.parameters.output_min && self.state.error < 0.0 {
174 self.state.reset_integral();
175 }
176
177 output
178 }
179
180 pub fn update(&mut self, new_value: f64) -> f64 {
182 let current_time = std::time::SystemTime::now()
183 .duration_since(std::time::UNIX_EPOCH)
184 .unwrap_or_default()
185 .as_secs();
186
187 self.state.update(new_value, current_time);
188 self.compute_output()
189 }
190
191 pub fn set_setpoint(&mut self, new_setpoint: f64) {
193 self.state.setpoint = new_setpoint;
194 self.state.error = new_setpoint - self.state.process_variable;
195 }
196
197 pub fn set_enabled(&mut self, enabled: bool) {
199 self.enabled = enabled;
200 if !enabled {
201 self.state.reset_integral();
202 }
203 }
204}
205
206#[derive(Debug, Clone)]
208pub struct PowerSystemController {
209 pub battery_voltage_controller: FeedbackController,
210 pub solar_current_controller: FeedbackController,
211 pub load_balance_controller: FeedbackController,
212 pub system_state: PowerSystemState,
213}
214
215#[derive(Debug, Clone)]
216pub struct PowerSystemState {
217 pub battery_voltage: f64, pub solar_current: f64, pub load_current: f64, pub battery_soc: f64, pub solar_irradiance: f64, pub ambient_temperature: f64, }
224
225impl PowerSystemController {
226 pub fn new() -> Self {
228 let battery_voltage_controller = FeedbackController::new(
230 "Battery Voltage".to_string(),
231 12.6, 12.5, PidParameters::conservative_power_system(),
234 );
235
236 let solar_current_controller = FeedbackController::new(
238 "Solar Current".to_string(),
239 10.0, 5.0, PidParameters::aggressive_response(),
242 );
243
244 let load_balance_controller = FeedbackController::new(
246 "Load Balance".to_string(),
247 0.0, 2.0, PidParameters::conservative_power_system(),
250 );
251
252 Self {
253 battery_voltage_controller,
254 solar_current_controller,
255 load_balance_controller,
256 system_state: PowerSystemState {
257 battery_voltage: 12.5,
258 solar_current: 5.0,
259 load_current: 2.0,
260 battery_soc: 80.0,
261 solar_irradiance: 800.0,
262 ambient_temperature: 25.0,
263 },
264 }
265 }
266
267 pub fn update(&mut self, measurements: PowerSystemState) -> Vec<ControlAction> {
269 self.system_state = measurements.clone();
270
271 let mut actions = Vec::new();
272
273 let voltage_output = self
275 .battery_voltage_controller
276 .update(measurements.battery_voltage);
277 if voltage_output > 50.0 {
278 actions.push(ControlAction::IncreaseCharging);
279 } else if voltage_output < -50.0 {
280 actions.push(ControlAction::DecreaseCharging);
281 }
282
283 let solar_target = self.calculate_solar_target(measurements.solar_irradiance);
285 self.solar_current_controller.set_setpoint(solar_target);
286 let solar_output = self
287 .solar_current_controller
288 .update(measurements.solar_current);
289 if solar_output > 50.0 {
290 actions.push(ControlAction::IncreaseSolarOutput);
291 } else if solar_output < -50.0 {
292 actions.push(ControlAction::DecreaseSolarOutput);
293 }
294
295 let net_current = measurements.solar_current - measurements.load_current;
297 let balance_output = self.load_balance_controller.update(net_current);
298 if balance_output > 50.0 {
299 actions.push(ControlAction::ReduceLoad);
300 } else if balance_output < -50.0 {
301 actions.push(ControlAction::IncreaseLoad);
302 }
303
304 actions
305 }
306
307 fn calculate_solar_target(&self, irradiance: f64) -> f64 {
309 let max_current = 20.0;
311 let efficiency_factor = 0.85; (irradiance / 1000.0) * max_current * efficiency_factor
313 }
314
315 pub fn get_health_status(&self) -> SystemHealth {
317 let voltage_error = (self.system_state.battery_voltage - 12.6).abs();
318 let soc_low = self.system_state.battery_soc < 20.0;
319 let high_temp = self.system_state.ambient_temperature > 35.0;
320
321 if voltage_error > 0.5 || soc_low || high_temp {
325 SystemHealth::Warning
326 } else if voltage_error > 0.2 {
327 SystemHealth::Caution
328 } else {
329 SystemHealth::Good
330 }
331 }
332}
333
334#[derive(Debug, Clone, PartialEq)]
336pub enum ControlAction {
337 IncreaseCharging,
338 DecreaseCharging,
339 IncreaseSolarOutput,
340 DecreaseSolarOutput,
341 IncreaseLoad,
342 ReduceLoad,
343 EmergencyShutdown,
344}
345
346#[derive(Debug, Clone, PartialEq)]
348pub enum SystemHealth {
349 Good,
350 Caution,
351 Warning,
352 Critical,
353}
354
355#[derive(Debug, Clone)]
357pub struct SanctuaryController {
358 pub perimeter_controller: FeedbackController,
359 pub intrusion_detection: bool,
360 pub sanctuary_radius: f64, pub current_position: (f64, f64),
362}
363
364impl SanctuaryController {
365 pub fn new(radius: f64) -> Self {
367 Self {
368 perimeter_controller: FeedbackController::new(
369 "Sanctuary Perimeter".to_string(),
370 radius, radius - 10.0, PidParameters::conservative_power_system(),
373 ),
374 intrusion_detection: true,
375 sanctuary_radius: radius,
376 current_position: (0.0, 0.0),
377 }
378 }
379
380 pub fn update(&mut self, position: (f64, f64)) -> Vec<SanctuaryAction> {
382 self.current_position = position;
383
384 let distance = (position.0.powi(2) + position.1.powi(2)).sqrt();
386
387 let error = self.sanctuary_radius - distance;
389 let output = self.perimeter_controller.update(error);
390
391 let mut actions = Vec::new();
392
393 if distance > self.sanctuary_radius {
394 actions.push(SanctuaryAction::IntrusionAlert);
395 if output > 70.0 {
396 actions.push(SanctuaryAction::ReturnToPerimeter);
397 }
398 } else if distance < self.sanctuary_radius * 0.5 {
399 actions.push(SanctuaryAction::NearingCenter);
400 }
401
402 actions
403 }
404}
405
406#[derive(Debug, Clone, PartialEq)]
408pub enum SanctuaryAction {
409 IntrusionAlert,
410 ReturnToPerimeter,
411 NearingCenter,
412 PerimeterBreach,
413}
414
415pub fn controller_to_quin(controller: &FeedbackController, context: u64) -> NQuin {
417 let mut quin = NQuin {
418 subject: crate::q_hash(&controller.name),
419 predicate: crate::q_hash("has_control_state"),
420 object: ((controller.state.setpoint * 1000.0) as u64) << 32
421 | ((controller.state.process_variable * 1000.0) as u64 & 0xFFFFFFFF),
422 context,
423 metadata: CONTROL_BIT | if controller.enabled { 1 } else { 0 },
424 parity: 0,
425 };
426 quin.parity = quin.subject ^ quin.predicate ^ quin.object ^ quin.context;
427 quin
428}
429
430pub fn create_power_scenario() -> PowerSystemController {
432 let mut controller = PowerSystemController::new();
433
434 let measurements = PowerSystemState {
436 battery_voltage: 13.2, solar_current: 15.0, load_current: 8.0, battery_soc: 85.0, solar_irradiance: 900.0, ambient_temperature: 30.0, };
443
444 let actions = controller.update(measurements);
445 println!("Power actions: {:?}", actions);
446
447 controller
448}
449
450#[cfg(test)]
451mod tests {
452 use super::*;
453
454 #[test]
455 fn test_pid_controller() {
456 let mut controller = FeedbackController::new(
457 "Test".to_string(),
458 10.0,
459 8.0,
460 PidParameters::conservative_power_system(),
461 );
462
463 let output1 = controller.update(8.0);
465 assert!(output1 > 0.0); let output2 = controller.update(9.0);
469 assert!(output2 < output1); }
471
472 #[test]
473 fn test_power_system_controller() {
474 let mut controller = PowerSystemController::new();
475
476 let measurements = PowerSystemState {
477 battery_voltage: 12.0, solar_current: 5.0,
479 load_current: 10.0,
480 battery_soc: 60.0,
481 solar_irradiance: 600.0,
482 ambient_temperature: 20.0,
483 };
484
485 let actions = controller.update(measurements);
486 assert!(!actions.is_empty());
487
488 let health = controller.get_health_status();
489 assert_eq!(health, SystemHealth::Warning); }
491
492 #[test]
493 fn test_sanctuary_controller() {
494 let mut controller = SanctuaryController::new(100.0);
495
496 let actions1 = controller.update((50.0, 50.0));
498 assert!(actions1.is_empty());
499
500 let actions2 = controller.update((110.0, 110.0));
502 assert!(actions2.contains(&SanctuaryAction::IntrusionAlert));
503 }
504
505 #[test]
506 fn test_control_state_update() {
507 let mut state = ControlState::new(10.0, 8.0);
508 assert_eq!(state.error, 2.0);
509
510 state.update(9.0, 1);
511 assert_eq!(state.error, 1.0);
512 assert_eq!(state.process_variable, 9.0);
513 assert!(state.integral > 0.0); }
515}