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}