pokeys_thread/state.rs
1//! # Device State Management
2//!
3//! This module provides types for managing device state and sharing it between threads.
4//! The `DeviceState` struct represents the state of a device, while the `SharedDeviceState`
5//! struct provides thread-safe access to the state.
6//!
7//! ## Usage Example
8//!
9//! ```ignore
10//! use pokeys_thread::{ThreadControllerBuilder, ThreadController, DeviceOperations};
11//! use std::time::Duration;
12//!
13//! // Create a thread controller
14//! let mut controller = ThreadControllerBuilder::new().build();
15//!
16//! // Discover USB devices
17//! let devices = controller.discover_usb_devices().unwrap();
18//!
19//! if !devices.is_empty() {
20//! // Start a thread for the first device
21//! let thread_id = controller.start_usb_device_thread(devices[0]).unwrap();
22//!
23//! // Get the device state
24//! let state = controller.get_state(thread_id).unwrap();
25//! println!("Device serial number: {}", state.device_data.serial_number);
26//!
27//! // Get the shared state for more advanced operations
28//! let shared_state = controller.get_shared_state(thread_id).unwrap();
29//!
30//! // Read a digital input
31//! if let Some(value) = shared_state.get_digital_input(1) {
32//! println!("Digital input 1: {}", value);
33//! }
34//!
35//! // Create an observer to monitor state changes
36//! let observer = controller.create_observer(thread_id).unwrap();
37//!
38//! // Wait for a state change with timeout
39//! if let Some(change) = observer.wait_for_change(Duration::from_secs(1)) {
40//! println!("State change: {:?}", change);
41//! }
42//! }
43//! ```
44
45use crossbeam_channel::{Receiver, Sender};
46use parking_lot::{Mutex, RwLock};
47use pokeys_lib::encoders::EncoderData;
48use pokeys_lib::io::PinData;
49use pokeys_lib::pwm::PwmData;
50use pokeys_lib::{DeviceData, DeviceInfo};
51use serde::{Deserialize, Serialize};
52use std::collections::HashMap;
53use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
54
55/// Thread status enumeration.
56///
57/// Represents the current status of a device thread.
58#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
59pub enum ThreadStatus {
60 /// Thread is not running
61 Stopped,
62 /// Thread is running
63 Running,
64 /// Thread is paused
65 Paused,
66 /// Thread is in error state
67 Error,
68}
69
70/// Device state that is shared between threads.
71///
72/// This struct contains all the state information for a device,
73/// including device information, pin data, encoder data, and PWM data.
74#[derive(Debug, Clone, Serialize, Deserialize)]
75pub struct DeviceState {
76 /// Device information
77 pub device_info: DeviceInfo,
78 /// Device data
79 pub device_data: DeviceData,
80 /// Device model
81 pub model: Option<pokeys_lib::models::DeviceModel>,
82 /// Pin data
83 pub pins: Vec<PinData>,
84 /// Encoder data
85 pub encoders: Vec<EncoderData>,
86 /// PWM data
87 pub pwm: PwmData,
88 /// Last update timestamp
89 pub last_update: u64,
90 /// Thread status
91 pub status: ThreadStatus,
92 /// Error message if any
93 pub error_message: Option<String>,
94 /// Custom state values
95 pub custom_values: HashMap<String, String>,
96}
97
98impl DeviceState {
99 /// Create a new device state.
100 ///
101 /// # Parameters
102 ///
103 /// * `device_info` - The device information.
104 /// * `device_data` - The device data.
105 ///
106 /// # Returns
107 ///
108 /// A new device state.
109 pub fn new(device_info: DeviceInfo, device_data: DeviceData) -> Self {
110 Self {
111 device_info,
112 device_data,
113 model: None,
114 pins: Vec::new(),
115 encoders: Vec::new(),
116 pwm: PwmData::new(),
117 last_update: 0,
118 status: ThreadStatus::Stopped,
119 error_message: None,
120 custom_values: HashMap::new(),
121 }
122 }
123
124 /// Update the state from a PoKeys device.
125 ///
126 /// # Parameters
127 ///
128 /// * `device` - The PoKeys device to update from.
129 pub fn update_from_device(&mut self, device: &pokeys_lib::PoKeysDevice) {
130 self.device_info = device.info.clone();
131 self.device_data = device.device_data.clone();
132 self.model = device.model.clone();
133 self.pins = device.pins.clone();
134 self.encoders = device.encoders.clone();
135 self.pwm = device.pwm.clone();
136 self.last_update = std::time::SystemTime::now()
137 .duration_since(std::time::UNIX_EPOCH)
138 .unwrap_or_default()
139 .as_millis() as u64;
140 }
141
142 /// Get a digital input value.
143 ///
144 /// # Parameters
145 ///
146 /// * `pin` - The pin number to read.
147 ///
148 /// # Returns
149 ///
150 /// The value of the digital input (true for high, false for low),
151 /// or None if the pin is invalid.
152 pub fn get_digital_input(&self, pin: u32) -> Option<bool> {
153 if pin == 0 || pin as usize > self.pins.len() {
154 return None;
155 }
156
157 let pin_index = (pin - 1) as usize;
158 Some(self.pins[pin_index].digital_value_get != 0)
159 }
160
161 /// Get an analog input value.
162 ///
163 /// # Parameters
164 ///
165 /// * `pin` - The pin number to read.
166 ///
167 /// # Returns
168 ///
169 /// The value of the analog input (0-4095 for 12-bit ADC),
170 /// or None if the pin is invalid.
171 pub fn get_analog_input(&self, pin: u32) -> Option<u32> {
172 if pin == 0 || pin as usize > self.pins.len() {
173 return None;
174 }
175
176 let pin_index = (pin - 1) as usize;
177 Some(self.pins[pin_index].analog_value)
178 }
179
180 /// Get an encoder value.
181 ///
182 /// # Parameters
183 ///
184 /// * `encoder_index` - The encoder index to read.
185 ///
186 /// # Returns
187 ///
188 /// The value of the encoder, or None if the encoder index is invalid.
189 pub fn get_encoder_value(&self, encoder_index: u32) -> Option<i32> {
190 if encoder_index as usize >= self.encoders.len() {
191 return None;
192 }
193
194 Some(self.encoders[encoder_index as usize].encoder_value)
195 }
196
197 /// Get a PWM duty cycle.
198 ///
199 /// # Parameters
200 ///
201 /// * `channel` - The PWM channel to read.
202 ///
203 /// # Returns
204 ///
205 /// The duty cycle of the PWM channel (0-4095 for 12-bit PWM),
206 /// or None if the channel is invalid.
207 pub fn get_pwm_duty_cycle(&self, channel: usize) -> Option<u32> {
208 if channel >= self.pwm.pwm_values.len() {
209 return None;
210 }
211
212 Some(self.pwm.pwm_values[channel])
213 }
214}
215
216/// State change notification type.
217///
218/// Represents the type of state change that occurred.
219#[derive(Debug, Clone, PartialEq, Eq)]
220pub enum StateChangeType {
221 /// Digital input changed
222 DigitalInput { pin: u32, value: bool },
223 /// Digital output changed
224 DigitalOutput { pin: u32, value: bool },
225 /// Analog input changed
226 AnalogInput { pin: u32, value: u32 },
227 /// Analog output changed
228 AnalogOutput { pin: u32, value: u32 },
229 /// Encoder value changed
230 EncoderValue { index: u32, value: i32 },
231 /// PWM duty cycle changed
232 PwmDutyCycle { channel: usize, duty: u32 },
233 /// Thread status changed
234 ThreadStatus { status: ThreadStatus },
235 /// Error occurred
236 Error { message: Option<String> },
237 /// Custom value changed
238 CustomValue { key: String, value: String },
239 /// Full state update
240 FullUpdate,
241}
242
243/// Thread-safe device state container.
244///
245/// This struct provides thread-safe access to device state
246/// and allows for state change notifications.
247pub struct SharedDeviceState {
248 /// Device state
249 state: RwLock<DeviceState>,
250 /// Is the thread running
251 running: AtomicBool,
252 /// Is the thread paused
253 paused: AtomicBool,
254 /// Last update timestamp
255 last_update: AtomicU64,
256 /// State change notification sender
257 notification_tx: Mutex<Option<Sender<StateChangeType>>>,
258}
259
260impl SharedDeviceState {
261 /// Create a new shared device state.
262 ///
263 /// # Parameters
264 ///
265 /// * `device_info` - The device information.
266 /// * `device_data` - The device data.
267 ///
268 /// # Returns
269 ///
270 /// A new shared device state.
271 pub fn new(device_info: DeviceInfo, device_data: DeviceData) -> Self {
272 Self {
273 state: RwLock::new(DeviceState::new(device_info, device_data)),
274 running: AtomicBool::new(false),
275 paused: AtomicBool::new(false),
276 last_update: AtomicU64::new(0),
277 notification_tx: Mutex::new(None),
278 }
279 }
280
281 /// Set up state change notifications.
282 ///
283 /// # Returns
284 ///
285 /// A receiver for state change notifications.
286 pub fn setup_notifications(&self) -> Receiver<StateChangeType> {
287 let (tx, rx) = crossbeam_channel::unbounded();
288 *self.notification_tx.lock() = Some(tx);
289 rx
290 }
291
292 /// Send a state change notification.
293 ///
294 /// # Parameters
295 ///
296 /// * `change_type` - The type of state change.
297 fn notify(&self, change_type: StateChangeType) {
298 if let Some(tx) = &*self.notification_tx.lock() {
299 let _ = tx.send(change_type);
300 }
301 }
302
303 /// Get the current thread status.
304 ///
305 /// # Returns
306 ///
307 /// The current thread status.
308 pub fn status(&self) -> ThreadStatus {
309 if !self.running.load(Ordering::Relaxed) {
310 ThreadStatus::Stopped
311 } else if self.paused.load(Ordering::Relaxed) {
312 ThreadStatus::Paused
313 } else {
314 ThreadStatus::Running
315 }
316 }
317
318 /// Set the thread as running.
319 ///
320 /// # Parameters
321 ///
322 /// * `running` - Whether the thread is running.
323 pub fn set_running(&self, running: bool) {
324 let old_status = self.status();
325 self.running.store(running, Ordering::Relaxed);
326 let new_status = self.status();
327 if old_status != new_status {
328 self.notify(StateChangeType::ThreadStatus { status: new_status });
329 }
330 }
331
332 /// Set the thread as paused.
333 ///
334 /// # Parameters
335 ///
336 /// * `paused` - Whether the thread is paused.
337 pub fn set_paused(&self, paused: bool) {
338 let old_status = self.status();
339 self.paused.store(paused, Ordering::Relaxed);
340 let new_status = self.status();
341 if old_status != new_status {
342 self.notify(StateChangeType::ThreadStatus { status: new_status });
343 }
344 }
345
346 /// Update the device state from a PoKeys device and detect changes.
347 ///
348 /// # Parameters
349 ///
350 /// * `device` - The PoKeys device to update from.
351 pub fn update_from_device_with_notifications(&self, device: &pokeys_lib::PoKeysDevice) {
352 // First, collect the old state for comparison
353 let (old_pins, old_encoders, old_pwm) = self.with_state(|state| {
354 (
355 state.pins.clone(),
356 state.encoders.clone(),
357 state.pwm.clone(),
358 )
359 });
360
361 // Update the state
362 self.update(|state| {
363 state.update_from_device(device);
364 });
365
366 // Now detect changes and send notifications
367 let new_state = self.with_state(|state| {
368 (
369 state.pins.clone(),
370 state.encoders.clone(),
371 state.pwm.clone(),
372 )
373 });
374
375 let (new_pins, new_encoders, new_pwm) = new_state;
376
377 // Check for digital input changes
378 for (i, (old_pin, new_pin)) in old_pins.iter().zip(new_pins.iter()).enumerate() {
379 let pin_number = (i + 1) as u32;
380
381 // Digital input changes
382 if old_pin.digital_value_get != new_pin.digital_value_get {
383 let value = new_pin.digital_value_get != 0;
384 self.notify(StateChangeType::DigitalInput {
385 pin: pin_number,
386 value,
387 });
388 }
389
390 // Digital output changes
391 if old_pin.digital_value_set != new_pin.digital_value_set {
392 let value = new_pin.digital_value_set != 0;
393 self.notify(StateChangeType::DigitalOutput {
394 pin: pin_number,
395 value,
396 });
397 }
398
399 // Analog input changes
400 if old_pin.analog_value != new_pin.analog_value {
401 self.notify(StateChangeType::AnalogInput {
402 pin: pin_number,
403 value: new_pin.analog_value,
404 });
405 }
406 }
407
408 // Check for encoder changes
409 for (i, (old_encoder, new_encoder)) in
410 old_encoders.iter().zip(new_encoders.iter()).enumerate()
411 {
412 if old_encoder.encoder_value != new_encoder.encoder_value {
413 self.notify(StateChangeType::EncoderValue {
414 index: i as u32,
415 value: new_encoder.encoder_value,
416 });
417 }
418 }
419
420 // Check for PWM changes
421 for (i, (old_duty, new_duty)) in old_pwm
422 .pwm_values
423 .iter()
424 .zip(new_pwm.pwm_values.iter())
425 .enumerate()
426 {
427 if old_duty != new_duty {
428 self.notify(StateChangeType::PwmDutyCycle {
429 channel: i,
430 duty: *new_duty,
431 });
432 }
433 }
434 }
435 ///
436 /// # Parameters
437 ///
438 /// * `update_fn` - A function that updates the device state.
439 pub fn update(&self, update_fn: impl FnOnce(&mut DeviceState)) {
440 let mut state = self.state.write();
441 update_fn(&mut state);
442 self.last_update.store(
443 std::time::SystemTime::now()
444 .duration_since(std::time::UNIX_EPOCH)
445 .unwrap_or_default()
446 .as_millis() as u64,
447 Ordering::Relaxed,
448 );
449 self.notify(StateChangeType::FullUpdate);
450 }
451
452 /// Read the device state.
453 ///
454 /// # Parameters
455 ///
456 /// * `read_fn` - A function that reads the device state.
457 ///
458 /// # Returns
459 ///
460 /// The result of the read function.
461 pub fn read<T>(&self, read_fn: impl FnOnce(&DeviceState) -> T) -> T {
462 let state = self.state.read();
463 read_fn(&state)
464 }
465
466 /// Get the last update timestamp.
467 ///
468 /// # Returns
469 ///
470 /// The last update timestamp.
471 pub fn last_update(&self) -> u64 {
472 self.last_update.load(Ordering::Relaxed)
473 }
474
475 /// Get a digital input value.
476 ///
477 /// # Parameters
478 ///
479 /// * `pin` - The pin number to read.
480 ///
481 /// # Returns
482 ///
483 /// The value of the digital input (true for high, false for low),
484 /// or None if the pin is invalid.
485 pub fn get_digital_input(&self, pin: u32) -> Option<bool> {
486 self.read(|state| state.get_digital_input(pin))
487 }
488
489 /// Get an analog input value.
490 ///
491 /// # Parameters
492 ///
493 /// * `pin` - The pin number to read.
494 ///
495 /// # Returns
496 ///
497 /// The value of the analog input (0-4095 for 12-bit ADC),
498 /// or None if the pin is invalid.
499 pub fn get_analog_input(&self, pin: u32) -> Option<u32> {
500 self.read(|state| state.get_analog_input(pin))
501 }
502
503 /// Get an encoder value.
504 ///
505 /// # Parameters
506 ///
507 /// * `encoder_index` - The encoder index to read.
508 ///
509 /// # Returns
510 ///
511 /// The value of the encoder, or None if the encoder index is invalid.
512 pub fn get_encoder_value(&self, encoder_index: u32) -> Option<i32> {
513 self.read(|state| state.get_encoder_value(encoder_index))
514 }
515
516 /// Get a PWM duty cycle.
517 ///
518 /// # Parameters
519 ///
520 /// * `channel` - The PWM channel to read.
521 ///
522 /// # Returns
523 ///
524 /// The duty cycle of the PWM channel (0-4095 for 12-bit PWM),
525 /// or None if the channel is invalid.
526 pub fn get_pwm_duty_cycle(&self, channel: usize) -> Option<u32> {
527 self.read(|state| state.get_pwm_duty_cycle(channel))
528 }
529
530 /// Set a digital output value.
531 ///
532 /// # Parameters
533 ///
534 /// * `pin` - The pin number to set.
535 /// * `value` - The value to set (true for high, false for low).
536 pub fn set_digital_output(&self, pin: u32, value: bool) {
537 self.update(|state| {
538 if pin > 0 && pin as usize <= state.pins.len() {
539 let pin_index = (pin - 1) as usize;
540 state.pins[pin_index].digital_value_set = if value { 1 } else { 0 };
541 self.notify(StateChangeType::DigitalOutput { pin, value });
542 }
543 });
544 }
545
546 /// Set an analog output value.
547 ///
548 /// # Parameters
549 ///
550 /// * `pin` - The pin number to set.
551 /// * `value` - The value to set (0-4095 for 12-bit DAC).
552 pub fn set_analog_output(&self, pin: u32, value: u32) {
553 self.update(|state| {
554 if pin > 0 && pin as usize <= state.pins.len() {
555 let pin_index = (pin - 1) as usize;
556 state.pins[pin_index].analog_value = value;
557 self.notify(StateChangeType::AnalogOutput { pin, value });
558 }
559 });
560 }
561
562 /// Set a PWM duty cycle.
563 ///
564 /// # Parameters
565 ///
566 /// * `channel` - The PWM channel to set.
567 /// * `duty` - The duty cycle to set (0-4095 for 12-bit PWM).
568 pub fn set_pwm_duty_cycle(&self, channel: usize, duty: u32) {
569 self.update(|state| {
570 if channel < state.pwm.pwm_values.len() {
571 state.pwm.pwm_values[channel] = duty;
572 self.notify(StateChangeType::PwmDutyCycle { channel, duty });
573 }
574 });
575 }
576
577 /// Set a custom value.
578 ///
579 /// # Parameters
580 ///
581 /// * `key` - The key of the custom value.
582 /// * `value` - The value to set.
583 pub fn set_custom_value(&self, key: &str, value: &str) {
584 self.update(|state| {
585 state
586 .custom_values
587 .insert(key.to_string(), value.to_string());
588 self.notify(StateChangeType::CustomValue {
589 key: key.to_string(),
590 value: value.to_string(),
591 });
592 });
593 }
594
595 /// Get a custom value.
596 ///
597 /// # Parameters
598 ///
599 /// * `key` - The key of the custom value.
600 ///
601 /// # Returns
602 ///
603 /// The custom value, or None if the key is not found.
604 pub fn get_custom_value(&self, key: &str) -> Option<String> {
605 self.read(|state| state.custom_values.get(key).cloned())
606 }
607
608 /// Set an error message.
609 ///
610 /// # Parameters
611 ///
612 /// * `error` - The error message, or None to clear the error.
613 pub fn set_error(&self, error: Option<String>) {
614 self.update(|state| {
615 state.error_message = error.clone();
616 self.notify(StateChangeType::Error { message: error });
617 });
618 }
619
620 /// Get the error message.
621 ///
622 /// # Returns
623 ///
624 /// The error message, or None if there is no error.
625 pub fn get_error(&self) -> Option<String> {
626 self.read(|state| state.error_message.clone())
627 }
628
629 /// Access the device state with a function.
630 ///
631 /// # Parameters
632 ///
633 /// * `f` - A function that takes a reference to the device state and returns a value.
634 ///
635 /// # Returns
636 ///
637 /// The result of the function.
638 pub fn with_state<F, T>(&self, f: F) -> T
639 where
640 F: FnOnce(&DeviceState) -> T,
641 {
642 let state = self.state.read();
643 f(&state)
644 }
645}