Skip to main content

asyn_rs/
port.rs

1//! Port driver base and trait.
2//!
3//! # I/O Model
4//!
5//! Ports are driven by a `PortActor` running on a dedicated thread.
6//! The actor exclusively owns the driver and processes requests from a channel.
7//!
8//! **Cache path** (default `read_*`/`write_*` methods):
9//! - Default implementations operate on the parameter cache (non-blocking).
10//! - Background tasks update cache via `set_*_param()` + `call_param_callbacks()`.
11//!
12//! **Actor path** (requests submitted via [`crate::port_handle::PortHandle`]):
13//! - Each port gets a dedicated actor thread that dispatches requests to driver methods.
14//! - `can_block` indicates the port may perform blocking I/O.
15
16use std::collections::HashMap;
17use std::sync::Arc;
18use std::time::SystemTime;
19
20use std::any::Any;
21
22/// Per-address device state for multi-device ports.
23#[derive(Debug, Clone)]
24pub struct DeviceState {
25    pub connected: bool,
26    pub enabled: bool,
27    pub auto_connect: bool,
28}
29
30impl Default for DeviceState {
31    fn default() -> Self {
32        Self {
33            connected: true,
34            enabled: true,
35            auto_connect: true,
36        }
37    }
38}
39
40use crate::error::{AsynError, AsynResult, AsynStatus};
41use crate::exception::{AsynException, ExceptionEvent, ExceptionManager};
42use crate::interpose::{OctetInterpose, OctetInterposeStack};
43use crate::interrupt::{InterruptManager, InterruptValue};
44use crate::param::{EnumEntry, ParamList, ParamType};
45use crate::trace::TraceManager;
46use crate::user::AsynUser;
47
48/// C asyn `queueRequest` priority. In asyn-rs this exists as compatibility
49/// metadata only — there is no actual request queue or priority-based scheduling.
50/// Drivers manage their own async tasks directly.
51#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default)]
52pub enum QueuePriority {
53    Low = 0,
54    #[default]
55    Medium = 1,
56    High = 2,
57    /// Connect/disconnect operations — processed even when disabled/disconnected.
58    Connect = 3,
59}
60
61/// Port configuration flags.
62#[derive(Debug, Clone, Copy)]
63pub struct PortFlags {
64    /// True if port supports multiple sub-addresses (ASYN_MULTIDEVICE).
65    pub multi_device: bool,
66    /// True if port can block (ASYN_CANBLOCK).
67    ///
68    /// When `true`, the port gets a dedicated worker thread that serializes I/O via a
69    /// priority queue (matching C asyn's per-port thread model).
70    ///
71    /// When `false`, requests execute synchronously inline on the caller's thread
72    /// (no worker thread is spawned). This is appropriate for non-blocking drivers
73    /// whose `io_*` methods return immediately (e.g., cache-based parameter access).
74    pub can_block: bool,
75    /// True if port can be destroyed via shutdown_port (ASYN_DESTRUCTIBLE).
76    pub destructible: bool,
77}
78
79impl Default for PortFlags {
80    fn default() -> Self {
81        Self {
82            multi_device: false,
83            can_block: false,
84            destructible: true,
85        }
86    }
87}
88
89/// Base state shared by all port drivers.
90/// Contains the parameter library, interrupt manager, and connection state.
91///
92/// # Interpose concurrency
93///
94/// `interpose_octet` requires `&mut self` for all operations (both `push` and
95/// `dispatch_*`). Since `PortDriverBase` is always behind `Arc<Mutex<dyn PortDriver>>`,
96/// any access to `interpose_octet` requires the port lock. This naturally
97/// serializes interpose modifications with I/O dispatch — no additional
98/// synchronization is needed. **Callers must never modify the interpose stack
99/// without holding the port lock.**
100pub struct PortDriverBase {
101    pub port_name: String,
102    pub max_addr: usize,
103    pub flags: PortFlags,
104    pub params: ParamList,
105    pub interrupts: InterruptManager,
106    pub connected: bool,
107    pub enabled: bool,
108    pub auto_connect: bool,
109    /// Exception sink injected by [`crate::manager::PortManager`] on registration.
110    pub exception_sink: Option<Arc<ExceptionManager>>,
111    pub options: HashMap<String, String>,
112    pub interpose_octet: OctetInterposeStack,
113    pub trace: Option<Arc<TraceManager>>,
114    /// Per-address device state for multi-device ports.
115    pub device_states: HashMap<i32, DeviceState>,
116    /// Timestamp source callback for custom timestamps.
117    pub timestamp_source: Option<Arc<dyn Fn() -> SystemTime + Send + Sync>>,
118}
119
120impl PortDriverBase {
121    pub fn new(port_name: &str, max_addr: usize, flags: PortFlags) -> Self {
122        Self {
123            port_name: port_name.to_string(),
124            max_addr: max_addr.max(1),
125            flags,
126            params: ParamList::new(max_addr, flags.multi_device),
127            interrupts: InterruptManager::new(256),
128            connected: true,
129            enabled: true,
130            auto_connect: true,
131            exception_sink: None,
132            options: HashMap::new(),
133            interpose_octet: OctetInterposeStack::new(),
134            trace: None,
135            device_states: HashMap::new(),
136            timestamp_source: None,
137        }
138    }
139
140    /// Announce an exception through the global exception manager (if injected).
141    pub fn announce_exception(&self, exception: AsynException, addr: i32) {
142        if let Some(ref sink) = self.exception_sink {
143            sink.announce(&ExceptionEvent {
144                port_name: self.port_name.clone(),
145                exception,
146                addr,
147            });
148        }
149    }
150
151    /// Check that the port is both enabled and connected.
152    /// Returns `Err(Disabled)` or `Err(Disconnected)` otherwise.
153    pub fn check_ready(&self) -> AsynResult<()> {
154        if !self.enabled {
155            return Err(AsynError::Status {
156                status: AsynStatus::Disabled,
157                message: format!("port {} is disabled", self.port_name),
158            });
159        }
160        if !self.connected {
161            return Err(AsynError::Status {
162                status: AsynStatus::Disconnected,
163                message: format!("port {} is disconnected", self.port_name),
164            });
165        }
166        Ok(())
167    }
168
169    /// Check that port + device address are both ready.
170    /// For multi-device ports, checks per-address state in addition to port-level state.
171    pub fn check_ready_addr(&self, addr: i32) -> AsynResult<()> {
172        self.check_ready()?;
173        if self.flags.multi_device {
174            if let Some(ds) = self.device_states.get(&addr) {
175                if !ds.enabled {
176                    return Err(AsynError::Status {
177                        status: AsynStatus::Disabled,
178                        message: format!("port {} addr {} is disabled", self.port_name, addr),
179                    });
180                }
181                if !ds.connected {
182                    return Err(AsynError::Status {
183                        status: AsynStatus::Disconnected,
184                        message: format!("port {} addr {} is disconnected", self.port_name, addr),
185                    });
186                }
187            }
188        }
189        Ok(())
190    }
191
192    /// Get or create a device state for the given address.
193    pub fn device_state(&mut self, addr: i32) -> &mut DeviceState {
194        self.device_states.entry(addr).or_default()
195    }
196
197    /// Check if a specific device address is connected.
198    pub fn is_device_connected(&self, addr: i32) -> bool {
199        self.device_states
200            .get(&addr)
201            .map_or(true, |ds| ds.connected)
202    }
203
204    /// Set a specific device address as connected.
205    pub fn connect_addr(&mut self, addr: i32) {
206        self.device_state(addr).connected = true;
207        self.announce_exception(AsynException::Connect, addr);
208    }
209
210    /// Set a specific device address as disconnected.
211    pub fn disconnect_addr(&mut self, addr: i32) {
212        self.device_state(addr).connected = false;
213        self.announce_exception(AsynException::Connect, addr);
214    }
215
216    /// Enable a specific device address.
217    pub fn enable_addr(&mut self, addr: i32) {
218        self.device_state(addr).enabled = true;
219        self.announce_exception(AsynException::Enable, addr);
220    }
221
222    /// Disable a specific device address.
223    pub fn disable_addr(&mut self, addr: i32) {
224        self.device_state(addr).enabled = false;
225        self.announce_exception(AsynException::Enable, addr);
226    }
227
228    /// Set a custom timestamp source callback.
229    pub fn register_timestamp_source<F>(&mut self, source: F)
230    where
231        F: Fn() -> SystemTime + Send + Sync + 'static,
232    {
233        self.timestamp_source = Some(Arc::new(source));
234    }
235
236    /// Get current timestamp from the registered source, or SystemTime::now().
237    pub fn current_timestamp(&self) -> SystemTime {
238        self.timestamp_source
239            .as_ref()
240            .map_or_else(SystemTime::now, |f| f())
241    }
242
243    pub fn create_param(&mut self, name: &str, param_type: ParamType) -> AsynResult<usize> {
244        self.params.create_param(name, param_type)
245    }
246
247    pub fn find_param(&self, name: &str) -> Option<usize> {
248        self.params.find_param(name)
249    }
250
251    // --- Convenience param accessors ---
252
253    pub fn set_int32_param(&mut self, index: usize, addr: i32, value: i32) -> AsynResult<()> {
254        self.params.set_int32(index, addr, value)
255    }
256
257    pub fn get_int32_param(&self, index: usize, addr: i32) -> AsynResult<i32> {
258        self.params.get_int32(index, addr)
259    }
260
261    pub fn set_int64_param(&mut self, index: usize, addr: i32, value: i64) -> AsynResult<()> {
262        self.params.set_int64(index, addr, value)
263    }
264
265    pub fn get_int64_param(&self, index: usize, addr: i32) -> AsynResult<i64> {
266        self.params.get_int64(index, addr)
267    }
268
269    pub fn set_float64_param(&mut self, index: usize, addr: i32, value: f64) -> AsynResult<()> {
270        self.params.set_float64(index, addr, value)
271    }
272
273    pub fn get_float64_param(&self, index: usize, addr: i32) -> AsynResult<f64> {
274        self.params.get_float64(index, addr)
275    }
276
277    pub fn set_string_param(&mut self, index: usize, addr: i32, value: String) -> AsynResult<()> {
278        self.params.set_string(index, addr, value)
279    }
280
281    pub fn get_string_param(&self, index: usize, addr: i32) -> AsynResult<&str> {
282        self.params.get_string(index, addr)
283    }
284
285    pub fn set_uint32_param(
286        &mut self,
287        index: usize,
288        addr: i32,
289        value: u32,
290        mask: u32,
291    ) -> AsynResult<()> {
292        self.params.set_uint32(index, addr, value, mask)
293    }
294
295    pub fn get_uint32_param(&self, index: usize, addr: i32) -> AsynResult<u32> {
296        self.params.get_uint32(index, addr)
297    }
298
299    pub fn get_enum_param(&self, index: usize, addr: i32) -> AsynResult<(usize, Arc<[EnumEntry]>)> {
300        self.params.get_enum(index, addr)
301    }
302
303    pub fn set_enum_index_param(
304        &mut self,
305        index: usize,
306        addr: i32,
307        value: usize,
308    ) -> AsynResult<()> {
309        self.params.set_enum_index(index, addr, value)
310    }
311
312    pub fn set_enum_choices_param(
313        &mut self,
314        index: usize,
315        addr: i32,
316        choices: Arc<[EnumEntry]>,
317    ) -> AsynResult<()> {
318        self.params.set_enum_choices(index, addr, choices)
319    }
320
321    pub fn get_generic_pointer_param(
322        &self,
323        index: usize,
324        addr: i32,
325    ) -> AsynResult<Arc<dyn Any + Send + Sync>> {
326        self.params.get_generic_pointer(index, addr)
327    }
328
329    pub fn set_generic_pointer_param(
330        &mut self,
331        index: usize,
332        addr: i32,
333        value: Arc<dyn Any + Send + Sync>,
334    ) -> AsynResult<()> {
335        self.params.set_generic_pointer(index, addr, value)
336    }
337
338    pub fn set_param_timestamp(
339        &mut self,
340        index: usize,
341        addr: i32,
342        ts: SystemTime,
343    ) -> AsynResult<()> {
344        self.params.set_timestamp(index, addr, ts)
345    }
346
347    /// Push an interpose layer onto the octet I/O stack.
348    ///
349    /// **Concurrency**: requires `&mut self`, which means the caller must hold
350    /// the port lock (`Arc<Mutex<dyn PortDriver>>`). This ensures
351    /// interpose modifications are serialized with I/O dispatch.
352    pub fn push_octet_interpose(&mut self, layer: Box<dyn OctetInterpose>) {
353        self.interpose_octet.push(layer);
354    }
355
356    /// Flush changed parameters as interrupt notifications.
357    /// Equivalent to C asyn's callParamCallbacks().
358    pub fn call_param_callbacks(&mut self, addr: i32) -> AsynResult<()> {
359        let changed = self.params.take_changed(addr)?;
360        let now = self.current_timestamp();
361        for reason in changed {
362            let value = self.params.get_value(reason, addr)?.clone();
363            let ts = self.params.get_timestamp(reason, addr)?.unwrap_or(now);
364            self.interrupts.notify(InterruptValue {
365                reason,
366                addr,
367                value,
368                timestamp: ts,
369            });
370        }
371        Ok(())
372    }
373
374    /// Flush a single parameter's changed flag and notify if dirty.
375    /// Use this instead of `call_param_callbacks` when you want to avoid
376    /// flushing unrelated parameters (e.g. rapidly-updating CP-linked params).
377    pub fn call_param_callback(&mut self, addr: i32, reason: usize) -> AsynResult<()> {
378        if self.params.take_changed_single(reason, addr)? {
379            let now = self.current_timestamp();
380            let value = self.params.get_value(reason, addr)?.clone();
381            let ts = self.params.get_timestamp(reason, addr)?.unwrap_or(now);
382            self.interrupts.notify(InterruptValue {
383                reason,
384                addr,
385                value,
386                timestamp: ts,
387            });
388        }
389        Ok(())
390    }
391
392    /// Mark a parameter as changed without modifying its value.
393    ///
394    /// Use this to trigger I/O Intr on params whose data is served via
395    /// `read_*_array()` overrides rather than the param cache (e.g. pixel data).
396    pub fn mark_param_changed(&mut self, index: usize, addr: i32) -> AsynResult<()> {
397        self.params.mark_changed(index, addr)
398    }
399}
400
401/// Port driver trait. All methods have default implementations that operate
402/// on the parameter cache (no actual I/O).
403///
404/// Drivers performing real hardware I/O should:
405/// 1. Run I/O in a background task (e.g., tokio::spawn)
406/// 2. Update parameters via `base_mut().set_*_param()` + `call_param_callbacks()`
407/// 3. Let the default `read_*` methods return cached values
408///
409/// # LockPort/UnlockPort
410///
411/// C asyn provides `lockPort`/`unlockPort` for direct mutex locking. In asyn-rs,
412/// the port is always behind `Arc<Mutex<dyn PortDriver>>`, so callers hold the
413/// parking_lot mutex directly. For multi-request exclusive access, use
414/// `BlockProcess`/`UnblockProcess` via the worker queue.
415pub trait PortDriver: Send + Sync + 'static {
416    fn base(&self) -> &PortDriverBase;
417    fn base_mut(&mut self) -> &mut PortDriverBase;
418
419    // --- AsynCommon ---
420
421    fn connect(&mut self, _user: &AsynUser) -> AsynResult<()> {
422        self.base_mut().connected = true;
423        self.base().announce_exception(AsynException::Connect, -1);
424        Ok(())
425    }
426
427    fn disconnect(&mut self, _user: &AsynUser) -> AsynResult<()> {
428        self.base_mut().connected = false;
429        self.base().announce_exception(AsynException::Connect, -1);
430        Ok(())
431    }
432
433    fn enable(&mut self, _user: &AsynUser) -> AsynResult<()> {
434        self.base_mut().enabled = true;
435        self.base().announce_exception(AsynException::Enable, -1);
436        Ok(())
437    }
438
439    fn disable(&mut self, _user: &AsynUser) -> AsynResult<()> {
440        self.base_mut().enabled = false;
441        self.base().announce_exception(AsynException::Enable, -1);
442        Ok(())
443    }
444
445    fn connect_addr(&mut self, user: &AsynUser) -> AsynResult<()> {
446        self.base_mut().connect_addr(user.addr);
447        Ok(())
448    }
449
450    fn disconnect_addr(&mut self, user: &AsynUser) -> AsynResult<()> {
451        self.base_mut().disconnect_addr(user.addr);
452        Ok(())
453    }
454
455    fn enable_addr(&mut self, user: &AsynUser) -> AsynResult<()> {
456        self.base_mut().enable_addr(user.addr);
457        Ok(())
458    }
459
460    fn disable_addr(&mut self, user: &AsynUser) -> AsynResult<()> {
461        self.base_mut().disable_addr(user.addr);
462        Ok(())
463    }
464
465    fn get_option(&self, key: &str) -> AsynResult<String> {
466        self.base()
467            .options
468            .get(key)
469            .cloned()
470            .ok_or_else(|| AsynError::OptionNotFound(key.to_string()))
471    }
472
473    fn set_option(&mut self, key: &str, value: &str) -> AsynResult<()> {
474        self.base_mut()
475            .options
476            .insert(key.to_string(), value.to_string());
477        Ok(())
478    }
479
480    fn report(&self, level: i32) {
481        let base = self.base();
482        eprintln!("Port: {}", base.port_name);
483        eprintln!(
484            "  connected: {}, max_addr: {}, params: {}, options: {}",
485            base.connected,
486            base.max_addr,
487            base.params.len(),
488            base.options.len()
489        );
490        if level >= 1 {
491            for i in 0..base.params.len() {
492                if let (Some(name), Some(ptype)) =
493                    (base.params.param_name(i), base.params.param_type(i))
494                {
495                    if level >= 3 {
496                        let val = base
497                            .params
498                            .get_value(i, 0)
499                            .map(|v| format!("{v:?}"))
500                            .unwrap_or("?".into());
501                        eprintln!("  param[{i}]: {name} ({ptype:?}) = {val}");
502                    } else {
503                        eprintln!("  param[{i}]: {name} ({ptype:?})");
504                    }
505                }
506            }
507        }
508        if level >= 2 {
509            for (k, v) in &base.options {
510                eprintln!("  option: {k} = {v}");
511            }
512        }
513    }
514
515    // --- Scalar I/O (cache-based defaults, timeout not applicable) ---
516
517    fn read_int32(&mut self, user: &AsynUser) -> AsynResult<i32> {
518        self.base().check_ready()?;
519        self.base().params.get_int32(user.reason, user.addr)
520    }
521
522    fn write_int32(&mut self, user: &mut AsynUser, value: i32) -> AsynResult<()> {
523        self.base().check_ready()?;
524        self.base_mut()
525            .params
526            .set_int32(user.reason, user.addr, value)?;
527        self.base_mut().call_param_callbacks(user.addr)
528    }
529
530    fn read_int64(&mut self, user: &AsynUser) -> AsynResult<i64> {
531        self.base().check_ready()?;
532        self.base().params.get_int64(user.reason, user.addr)
533    }
534
535    fn write_int64(&mut self, user: &mut AsynUser, value: i64) -> AsynResult<()> {
536        self.base().check_ready()?;
537        self.base_mut()
538            .params
539            .set_int64(user.reason, user.addr, value)?;
540        self.base_mut().call_param_callbacks(user.addr)
541    }
542
543    fn get_bounds_int64(&self, _user: &AsynUser) -> AsynResult<(i64, i64)> {
544        Ok((i64::MIN, i64::MAX))
545    }
546
547    fn read_float64(&mut self, user: &AsynUser) -> AsynResult<f64> {
548        self.base().check_ready()?;
549        self.base().params.get_float64(user.reason, user.addr)
550    }
551
552    fn write_float64(&mut self, user: &mut AsynUser, value: f64) -> AsynResult<()> {
553        self.base().check_ready()?;
554        self.base_mut()
555            .params
556            .set_float64(user.reason, user.addr, value)?;
557        self.base_mut().call_param_callbacks(user.addr)
558    }
559
560    fn read_octet(&mut self, user: &AsynUser, buf: &mut [u8]) -> AsynResult<usize> {
561        self.base().check_ready()?;
562        let s = self.base().params.get_string(user.reason, user.addr)?;
563        let bytes = s.as_bytes();
564        let n = bytes.len().min(buf.len());
565        buf[..n].copy_from_slice(&bytes[..n]);
566        Ok(n)
567    }
568
569    fn write_octet(&mut self, user: &mut AsynUser, data: &[u8]) -> AsynResult<()> {
570        self.base().check_ready()?;
571        let s = String::from_utf8_lossy(data).into_owned();
572        self.base_mut()
573            .params
574            .set_string(user.reason, user.addr, s)?;
575        self.base_mut().call_param_callbacks(user.addr)
576    }
577
578    fn read_uint32_digital(&mut self, user: &AsynUser, mask: u32) -> AsynResult<u32> {
579        self.base().check_ready()?;
580        let val = self.base().params.get_uint32(user.reason, user.addr)?;
581        Ok(val & mask)
582    }
583
584    fn write_uint32_digital(
585        &mut self,
586        user: &mut AsynUser,
587        value: u32,
588        mask: u32,
589    ) -> AsynResult<()> {
590        self.base().check_ready()?;
591        self.base_mut()
592            .params
593            .set_uint32(user.reason, user.addr, value, mask)?;
594        self.base_mut().call_param_callbacks(user.addr)
595    }
596
597    // --- Enum I/O (cache-based defaults) ---
598
599    fn read_enum(&mut self, user: &AsynUser) -> AsynResult<(usize, Arc<[EnumEntry]>)> {
600        self.base().check_ready()?;
601        self.base().params.get_enum(user.reason, user.addr)
602    }
603
604    fn write_enum(&mut self, user: &mut AsynUser, index: usize) -> AsynResult<()> {
605        self.base().check_ready()?;
606        self.base_mut()
607            .params
608            .set_enum_index(user.reason, user.addr, index)?;
609        self.base_mut().call_param_callbacks(user.addr)
610    }
611
612    fn write_enum_choices(
613        &mut self,
614        user: &mut AsynUser,
615        choices: Arc<[EnumEntry]>,
616    ) -> AsynResult<()> {
617        self.base().check_ready()?;
618        self.base_mut()
619            .params
620            .set_enum_choices(user.reason, user.addr, choices)?;
621        self.base_mut().call_param_callbacks(user.addr)
622    }
623
624    // --- GenericPointer I/O (cache-based defaults) ---
625
626    fn read_generic_pointer(&mut self, user: &AsynUser) -> AsynResult<Arc<dyn Any + Send + Sync>> {
627        self.base().check_ready()?;
628        self.base()
629            .params
630            .get_generic_pointer(user.reason, user.addr)
631    }
632
633    fn write_generic_pointer(
634        &mut self,
635        user: &mut AsynUser,
636        value: Arc<dyn Any + Send + Sync>,
637    ) -> AsynResult<()> {
638        self.base().check_ready()?;
639        self.base_mut()
640            .params
641            .set_generic_pointer(user.reason, user.addr, value)?;
642        self.base_mut().call_param_callbacks(user.addr)
643    }
644
645    // --- Array I/O (default: not supported) ---
646
647    fn read_float64_array(&mut self, _user: &AsynUser, _buf: &mut [f64]) -> AsynResult<usize> {
648        Err(AsynError::InterfaceNotSupported("asynFloat64Array".into()))
649    }
650
651    fn write_float64_array(&mut self, user: &AsynUser, data: &[f64]) -> AsynResult<()> {
652        self.base_mut()
653            .params
654            .set_float64_array(user.reason, user.addr, data.to_vec())?;
655        self.base_mut().call_param_callbacks(user.addr)
656    }
657
658    fn read_int32_array(&mut self, _user: &AsynUser, _buf: &mut [i32]) -> AsynResult<usize> {
659        Err(AsynError::InterfaceNotSupported("asynInt32Array".into()))
660    }
661
662    fn write_int32_array(&mut self, user: &AsynUser, data: &[i32]) -> AsynResult<()> {
663        self.base_mut()
664            .params
665            .set_int32_array(user.reason, user.addr, data.to_vec())?;
666        self.base_mut().call_param_callbacks(user.addr)
667    }
668
669    fn read_int8_array(&mut self, _user: &AsynUser, _buf: &mut [i8]) -> AsynResult<usize> {
670        Err(AsynError::InterfaceNotSupported("asynInt8Array".into()))
671    }
672
673    fn write_int8_array(&mut self, user: &AsynUser, data: &[i8]) -> AsynResult<()> {
674        self.base_mut()
675            .params
676            .set_int8_array(user.reason, user.addr, data.to_vec())?;
677        self.base_mut().call_param_callbacks(user.addr)
678    }
679
680    fn read_int16_array(&mut self, _user: &AsynUser, _buf: &mut [i16]) -> AsynResult<usize> {
681        Err(AsynError::InterfaceNotSupported("asynInt16Array".into()))
682    }
683
684    fn write_int16_array(&mut self, user: &AsynUser, data: &[i16]) -> AsynResult<()> {
685        self.base_mut()
686            .params
687            .set_int16_array(user.reason, user.addr, data.to_vec())?;
688        self.base_mut().call_param_callbacks(user.addr)
689    }
690
691    fn read_int64_array(&mut self, _user: &AsynUser, _buf: &mut [i64]) -> AsynResult<usize> {
692        Err(AsynError::InterfaceNotSupported("asynInt64Array".into()))
693    }
694
695    fn write_int64_array(&mut self, user: &AsynUser, data: &[i64]) -> AsynResult<()> {
696        self.base_mut()
697            .params
698            .set_int64_array(user.reason, user.addr, data.to_vec())?;
699        self.base_mut().call_param_callbacks(user.addr)
700    }
701
702    fn read_float32_array(&mut self, _user: &AsynUser, _buf: &mut [f32]) -> AsynResult<usize> {
703        Err(AsynError::InterfaceNotSupported("asynFloat32Array".into()))
704    }
705
706    fn write_float32_array(&mut self, user: &AsynUser, data: &[f32]) -> AsynResult<()> {
707        self.base_mut()
708            .params
709            .set_float32_array(user.reason, user.addr, data.to_vec())?;
710        self.base_mut().call_param_callbacks(user.addr)
711    }
712
713    // --- I/O methods (worker thread calls these) ---
714    // Default: delegate to cache-based read_*/write_* for backward compat.
715    // Real I/O drivers override these for actual hardware access.
716
717    fn io_read_octet(&mut self, user: &AsynUser, buf: &mut [u8]) -> AsynResult<usize> {
718        self.read_octet(user, buf)
719    }
720
721    fn io_write_octet(&mut self, user: &mut AsynUser, data: &[u8]) -> AsynResult<()> {
722        self.write_octet(user, data)
723    }
724
725    fn io_read_int32(&mut self, user: &AsynUser) -> AsynResult<i32> {
726        self.read_int32(user)
727    }
728
729    fn io_write_int32(&mut self, user: &mut AsynUser, value: i32) -> AsynResult<()> {
730        self.write_int32(user, value)
731    }
732
733    fn io_read_int64(&mut self, user: &AsynUser) -> AsynResult<i64> {
734        self.read_int64(user)
735    }
736
737    fn io_write_int64(&mut self, user: &mut AsynUser, value: i64) -> AsynResult<()> {
738        self.write_int64(user, value)
739    }
740
741    fn io_read_float64(&mut self, user: &AsynUser) -> AsynResult<f64> {
742        self.read_float64(user)
743    }
744
745    fn io_write_float64(&mut self, user: &mut AsynUser, value: f64) -> AsynResult<()> {
746        self.write_float64(user, value)
747    }
748
749    fn io_read_uint32_digital(&mut self, user: &AsynUser, mask: u32) -> AsynResult<u32> {
750        self.read_uint32_digital(user, mask)
751    }
752
753    fn io_write_uint32_digital(
754        &mut self,
755        user: &mut AsynUser,
756        value: u32,
757        mask: u32,
758    ) -> AsynResult<()> {
759        self.write_uint32_digital(user, value, mask)
760    }
761
762    fn io_flush(&mut self, _user: &mut AsynUser) -> AsynResult<()> {
763        Ok(())
764    }
765
766    // --- drvUser ---
767
768    /// Resolve a driver info string to a parameter index.
769    /// Default: look up by parameter name.
770    fn drv_user_create(&self, drv_info: &str) -> AsynResult<usize> {
771        self.base()
772            .params
773            .find_param(drv_info)
774            .ok_or_else(|| AsynError::ParamNotFound(drv_info.to_string()))
775    }
776
777    // --- Capabilities ---
778
779    /// Declare the capabilities this driver supports.
780    /// Default implementation includes all scalar read/write operations.
781    fn capabilities(&self) -> Vec<crate::interfaces::Capability> {
782        crate::interfaces::default_capabilities()
783    }
784
785    /// Check if this driver supports a specific capability.
786    fn supports(&self, cap: crate::interfaces::Capability) -> bool {
787        self.capabilities().contains(&cap)
788    }
789
790    // --- Lifecycle ---
791
792    fn init(&mut self) -> AsynResult<()> {
793        Ok(())
794    }
795}
796
797#[cfg(test)]
798mod tests {
799    use super::*;
800    struct TestDriver {
801        base: PortDriverBase,
802    }
803
804    impl TestDriver {
805        fn new() -> Self {
806            let mut base = PortDriverBase::new("test", 1, PortFlags::default());
807            base.create_param("VAL", ParamType::Int32).unwrap();
808            base.create_param("TEMP", ParamType::Float64).unwrap();
809            base.create_param("MSG", ParamType::Octet).unwrap();
810            base.create_param("BITS", ParamType::UInt32Digital).unwrap();
811            Self { base }
812        }
813    }
814
815    impl PortDriver for TestDriver {
816        fn base(&self) -> &PortDriverBase {
817            &self.base
818        }
819        fn base_mut(&mut self) -> &mut PortDriverBase {
820            &mut self.base
821        }
822    }
823
824    #[test]
825    fn test_default_read_write_int32() {
826        let mut drv = TestDriver::new();
827        let mut user = AsynUser::new(0);
828        drv.write_int32(&mut user, 42).unwrap();
829        let user = AsynUser::new(0);
830        assert_eq!(drv.read_int32(&user).unwrap(), 42);
831    }
832
833    #[test]
834    fn test_default_read_write_float64() {
835        let mut drv = TestDriver::new();
836        let mut user = AsynUser::new(1);
837        drv.write_float64(&mut user, 3.14).unwrap();
838        let user = AsynUser::new(1);
839        assert!((drv.read_float64(&user).unwrap() - 3.14).abs() < 1e-10);
840    }
841
842    #[test]
843    fn test_default_read_write_octet() {
844        let mut drv = TestDriver::new();
845        let mut user = AsynUser::new(2);
846        drv.write_octet(&mut user, b"hello").unwrap();
847        let user = AsynUser::new(2);
848        let mut buf = [0u8; 32];
849        let n = drv.read_octet(&user, &mut buf).unwrap();
850        assert_eq!(&buf[..n], b"hello");
851    }
852
853    #[test]
854    fn test_default_read_write_uint32() {
855        let mut drv = TestDriver::new();
856        let mut user = AsynUser::new(3);
857        drv.write_uint32_digital(&mut user, 0xFF, 0x0F).unwrap();
858        let user = AsynUser::new(3);
859        assert_eq!(drv.read_uint32_digital(&user, 0xFF).unwrap(), 0x0F);
860    }
861
862    #[test]
863    fn test_connect_disconnect() {
864        let mut drv = TestDriver::new();
865        let user = AsynUser::default();
866        assert!(drv.base().connected);
867        drv.disconnect(&user).unwrap();
868        assert!(!drv.base().connected);
869        drv.connect(&user).unwrap();
870        assert!(drv.base().connected);
871    }
872
873    #[test]
874    fn test_drv_user_create() {
875        let drv = TestDriver::new();
876        assert_eq!(drv.drv_user_create("VAL").unwrap(), 0);
877        assert_eq!(drv.drv_user_create("TEMP").unwrap(), 1);
878        assert!(drv.drv_user_create("NOPE").is_err());
879    }
880
881    #[test]
882    fn test_call_param_callbacks() {
883        let mut drv = TestDriver::new();
884        let mut rx = drv.base_mut().interrupts.subscribe_async();
885
886        drv.base_mut().set_int32_param(0, 0, 100).unwrap();
887        drv.base_mut().set_float64_param(1, 0, 2.0).unwrap();
888        drv.base_mut().call_param_callbacks(0).unwrap();
889
890        let v1 = rx.try_recv().unwrap();
891        assert_eq!(v1.reason, 0);
892        let v2 = rx.try_recv().unwrap();
893        assert_eq!(v2.reason, 1);
894        assert!(rx.try_recv().is_err());
895    }
896
897    #[test]
898    fn test_no_callback_for_unchanged() {
899        let mut drv = TestDriver::new();
900        let mut rx = drv.base_mut().interrupts.subscribe_async();
901
902        drv.base_mut().set_int32_param(0, 0, 5).unwrap();
903        drv.base_mut().call_param_callbacks(0).unwrap();
904        let _ = rx.try_recv().unwrap(); // consume
905
906        // Set same value — no interrupt
907        drv.base_mut().set_int32_param(0, 0, 5).unwrap();
908        drv.base_mut().call_param_callbacks(0).unwrap();
909        assert!(rx.try_recv().is_err());
910    }
911
912    #[test]
913    fn test_array_not_supported_by_default() {
914        let mut drv = TestDriver::new();
915        let user = AsynUser::new(0);
916        let mut buf = [0f64; 10];
917        assert!(drv.read_float64_array(&user, &mut buf).is_err());
918        assert!(drv.write_float64_array(&user, &[1.0]).is_err());
919    }
920
921    #[test]
922    fn test_option_set_get() {
923        let mut drv = TestDriver::new();
924        drv.set_option("baud", "9600").unwrap();
925        assert_eq!(drv.get_option("baud").unwrap(), "9600");
926        drv.set_option("baud", "115200").unwrap();
927        assert_eq!(drv.get_option("baud").unwrap(), "115200");
928    }
929
930    #[test]
931    fn test_option_not_found() {
932        let drv = TestDriver::new();
933        let err = drv.get_option("nonexistent").unwrap_err();
934        assert!(matches!(err, AsynError::OptionNotFound(_)));
935    }
936
937    #[test]
938    fn test_report_no_panic() {
939        let mut drv = TestDriver::new();
940        drv.set_option("testkey", "testval").unwrap();
941        drv.base_mut().set_int32_param(0, 0, 42).unwrap();
942        for level in 0..=3 {
943            drv.report(level);
944        }
945    }
946
947    #[test]
948    fn test_callback_uses_param_timestamp() {
949        let mut drv = TestDriver::new();
950        let mut rx = drv.base_mut().interrupts.subscribe_async();
951
952        let custom_ts = SystemTime::UNIX_EPOCH + std::time::Duration::from_secs(1_000_000);
953        drv.base_mut().set_int32_param(0, 0, 77).unwrap();
954        drv.base_mut().set_param_timestamp(0, 0, custom_ts).unwrap();
955        drv.base_mut().call_param_callbacks(0).unwrap();
956
957        let v = rx.try_recv().unwrap();
958        assert_eq!(v.reason, 0);
959        assert_eq!(v.timestamp, custom_ts);
960    }
961
962    #[test]
963    fn test_default_read_write_enum() {
964        use crate::param::EnumEntry;
965
966        let mut base = PortDriverBase::new("test_enum", 1, PortFlags::default());
967        base.create_param("MODE", ParamType::Enum).unwrap();
968
969        struct EnumDriver {
970            base: PortDriverBase,
971        }
972        impl PortDriver for EnumDriver {
973            fn base(&self) -> &PortDriverBase {
974                &self.base
975            }
976            fn base_mut(&mut self) -> &mut PortDriverBase {
977                &mut self.base
978            }
979        }
980
981        let mut drv = EnumDriver { base };
982        let choices: Arc<[EnumEntry]> = Arc::from(vec![
983            EnumEntry {
984                string: "Off".into(),
985                value: 0,
986                severity: 0,
987            },
988            EnumEntry {
989                string: "On".into(),
990                value: 1,
991                severity: 0,
992            },
993        ]);
994        let mut user = AsynUser::new(0);
995        drv.write_enum_choices(&mut user, choices).unwrap();
996        drv.write_enum(&mut user, 1).unwrap();
997        let (idx, ch) = drv.read_enum(&AsynUser::new(0)).unwrap();
998        assert_eq!(idx, 1);
999        assert_eq!(ch[1].string, "On");
1000    }
1001
1002    #[test]
1003    fn test_enum_callback() {
1004        use crate::param::{EnumEntry, ParamValue};
1005
1006        let mut base = PortDriverBase::new("test_enum_cb", 1, PortFlags::default());
1007        base.create_param("MODE", ParamType::Enum).unwrap();
1008        let mut rx = base.interrupts.subscribe_async();
1009
1010        struct EnumDriver {
1011            base: PortDriverBase,
1012        }
1013        impl PortDriver for EnumDriver {
1014            fn base(&self) -> &PortDriverBase {
1015                &self.base
1016            }
1017            fn base_mut(&mut self) -> &mut PortDriverBase {
1018                &mut self.base
1019            }
1020        }
1021
1022        let mut drv = EnumDriver { base };
1023        let choices: Arc<[EnumEntry]> = Arc::from(vec![
1024            EnumEntry {
1025                string: "A".into(),
1026                value: 0,
1027                severity: 0,
1028            },
1029            EnumEntry {
1030                string: "B".into(),
1031                value: 1,
1032                severity: 0,
1033            },
1034        ]);
1035        drv.base_mut()
1036            .set_enum_choices_param(0, 0, choices)
1037            .unwrap();
1038        drv.base_mut().set_enum_index_param(0, 0, 1).unwrap();
1039        drv.base_mut().call_param_callbacks(0).unwrap();
1040
1041        let v = rx.try_recv().unwrap();
1042        assert_eq!(v.reason, 0);
1043        assert!(matches!(v.value, ParamValue::Enum { index: 1, .. }));
1044    }
1045
1046    #[test]
1047    fn test_default_read_write_generic_pointer() {
1048        let mut base = PortDriverBase::new("test_gp", 1, PortFlags::default());
1049        base.create_param("PTR", ParamType::GenericPointer).unwrap();
1050
1051        struct GpDriver {
1052            base: PortDriverBase,
1053        }
1054        impl PortDriver for GpDriver {
1055            fn base(&self) -> &PortDriverBase {
1056                &self.base
1057            }
1058            fn base_mut(&mut self) -> &mut PortDriverBase {
1059                &mut self.base
1060            }
1061        }
1062
1063        let mut drv = GpDriver { base };
1064        let data: Arc<dyn std::any::Any + Send + Sync> = Arc::new(99i32);
1065        let mut user = AsynUser::new(0);
1066        drv.write_generic_pointer(&mut user, data).unwrap();
1067        let val = drv.read_generic_pointer(&AsynUser::new(0)).unwrap();
1068        assert_eq!(*val.downcast_ref::<i32>().unwrap(), 99);
1069    }
1070
1071    #[test]
1072    fn test_generic_pointer_callback() {
1073        use crate::param::ParamValue;
1074
1075        let mut base = PortDriverBase::new("test_gp_cb", 1, PortFlags::default());
1076        base.create_param("PTR", ParamType::GenericPointer).unwrap();
1077        let mut rx = base.interrupts.subscribe_async();
1078
1079        struct GpDriver {
1080            base: PortDriverBase,
1081        }
1082        impl PortDriver for GpDriver {
1083            fn base(&self) -> &PortDriverBase {
1084                &self.base
1085            }
1086            fn base_mut(&mut self) -> &mut PortDriverBase {
1087                &mut self.base
1088            }
1089        }
1090
1091        let mut drv = GpDriver { base };
1092        let data: Arc<dyn std::any::Any + Send + Sync> = Arc::new(vec![1, 2, 3]);
1093        drv.base_mut()
1094            .set_generic_pointer_param(0, 0, data)
1095            .unwrap();
1096        drv.base_mut().call_param_callbacks(0).unwrap();
1097
1098        let v = rx.try_recv().unwrap();
1099        assert_eq!(v.reason, 0);
1100        assert!(matches!(v.value, ParamValue::GenericPointer(_)));
1101    }
1102
1103    #[test]
1104    fn test_interpose_push_requires_lock() {
1105        use crate::interpose::{OctetInterpose, OctetNext, OctetReadResult};
1106        use parking_lot::Mutex;
1107        use std::sync::Arc;
1108
1109        struct NoopInterpose;
1110        impl OctetInterpose for NoopInterpose {
1111            fn read(
1112                &mut self,
1113                user: &AsynUser,
1114                buf: &mut [u8],
1115                next: &mut dyn OctetNext,
1116            ) -> AsynResult<OctetReadResult> {
1117                next.read(user, buf)
1118            }
1119            fn write(
1120                &mut self,
1121                user: &mut AsynUser,
1122                data: &[u8],
1123                next: &mut dyn OctetNext,
1124            ) -> AsynResult<usize> {
1125                next.write(user, data)
1126            }
1127            fn flush(&mut self, user: &mut AsynUser, next: &mut dyn OctetNext) -> AsynResult<()> {
1128                next.flush(user)
1129            }
1130        }
1131
1132        let port: Arc<Mutex<dyn PortDriver>> = Arc::new(Mutex::new(TestDriver::new()));
1133
1134        {
1135            let mut guard = port.lock();
1136            guard
1137                .base_mut()
1138                .push_octet_interpose(Box::new(NoopInterpose));
1139            assert_eq!(guard.base().interpose_octet.len(), 1);
1140        }
1141    }
1142
1143    #[test]
1144    fn test_default_read_write_int64() {
1145        let mut base = PortDriverBase::new("test_i64", 1, PortFlags::default());
1146        base.create_param("BIG", ParamType::Int64).unwrap();
1147
1148        struct I64Driver {
1149            base: PortDriverBase,
1150        }
1151        impl PortDriver for I64Driver {
1152            fn base(&self) -> &PortDriverBase {
1153                &self.base
1154            }
1155            fn base_mut(&mut self) -> &mut PortDriverBase {
1156                &mut self.base
1157            }
1158        }
1159
1160        let mut drv = I64Driver { base };
1161        let mut user = AsynUser::new(0);
1162        drv.write_int64(&mut user, i64::MAX).unwrap();
1163        assert_eq!(drv.read_int64(&AsynUser::new(0)).unwrap(), i64::MAX);
1164    }
1165
1166    #[test]
1167    fn test_get_bounds_int64_default() {
1168        let base = PortDriverBase::new("test_bounds", 1, PortFlags::default());
1169        struct BoundsDriver {
1170            base: PortDriverBase,
1171        }
1172        impl PortDriver for BoundsDriver {
1173            fn base(&self) -> &PortDriverBase {
1174                &self.base
1175            }
1176            fn base_mut(&mut self) -> &mut PortDriverBase {
1177                &mut self.base
1178            }
1179        }
1180        let drv = BoundsDriver { base };
1181        let (lo, hi) = drv.get_bounds_int64(&AsynUser::default()).unwrap();
1182        assert_eq!(lo, i64::MIN);
1183        assert_eq!(hi, i64::MAX);
1184    }
1185
1186    #[test]
1187    fn test_per_addr_device_state() {
1188        let mut base = PortDriverBase::new(
1189            "multi",
1190            4,
1191            PortFlags {
1192                multi_device: true,
1193                can_block: false,
1194                destructible: true,
1195            },
1196        );
1197        base.create_param("V", ParamType::Int32).unwrap();
1198
1199        // Default: all connected
1200        assert!(base.is_device_connected(0));
1201        assert!(base.is_device_connected(1));
1202
1203        // Disable addr 1
1204        base.device_state(1).enabled = false;
1205        assert!(base.check_ready_addr(0).is_ok());
1206        let err = base.check_ready_addr(1).unwrap_err();
1207        assert!(format!("{err}").contains("disabled"));
1208
1209        // Disconnect addr 2
1210        base.device_state(2).connected = false;
1211        let err = base.check_ready_addr(2).unwrap_err();
1212        assert!(format!("{err}").contains("disconnected"));
1213    }
1214
1215    #[test]
1216    fn test_per_addr_single_device_ignored() {
1217        let mut base = PortDriverBase::new("single", 1, PortFlags::default());
1218        base.create_param("V", ParamType::Int32).unwrap();
1219        // For single-device, per-addr check passes even if no device state
1220        assert!(base.check_ready_addr(0).is_ok());
1221    }
1222
1223    #[test]
1224    fn test_timestamp_source() {
1225        let mut base = PortDriverBase::new("ts_test", 1, PortFlags::default());
1226        base.create_param("V", ParamType::Int32).unwrap();
1227
1228        let fixed_ts = SystemTime::UNIX_EPOCH + std::time::Duration::from_secs(999999);
1229        base.register_timestamp_source(move || fixed_ts);
1230
1231        assert_eq!(base.current_timestamp(), fixed_ts);
1232    }
1233
1234    #[test]
1235    fn test_timestamp_source_in_callbacks() {
1236        let mut base = PortDriverBase::new("ts_cb", 1, PortFlags::default());
1237        base.create_param("V", ParamType::Int32).unwrap();
1238        let mut rx = base.interrupts.subscribe_async();
1239
1240        let fixed_ts = SystemTime::UNIX_EPOCH + std::time::Duration::from_secs(123456);
1241        base.register_timestamp_source(move || fixed_ts);
1242
1243        struct TsDriver {
1244            base: PortDriverBase,
1245        }
1246        impl PortDriver for TsDriver {
1247            fn base(&self) -> &PortDriverBase {
1248                &self.base
1249            }
1250            fn base_mut(&mut self) -> &mut PortDriverBase {
1251                &mut self.base
1252            }
1253        }
1254        let mut drv = TsDriver { base };
1255        drv.base_mut().set_int32_param(0, 0, 42).unwrap();
1256        drv.base_mut().call_param_callbacks(0).unwrap();
1257
1258        let v = rx.try_recv().unwrap();
1259        // Should use fixed_ts since no per-param timestamp is set
1260        assert_eq!(v.timestamp, fixed_ts);
1261    }
1262
1263    #[test]
1264    fn test_queue_priority_connect() {
1265        assert!(QueuePriority::Connect > QueuePriority::High);
1266    }
1267
1268    #[test]
1269    fn test_port_flags_destructible_default() {
1270        let flags = PortFlags::default();
1271        assert!(flags.destructible);
1272    }
1273
1274    // --- Phase 2B: per-addr connect/disconnect/enable/disable ---
1275
1276    #[test]
1277    fn test_connect_addr() {
1278        let mut base = PortDriverBase::new(
1279            "multi_conn",
1280            4,
1281            PortFlags {
1282                multi_device: true,
1283                can_block: false,
1284                destructible: true,
1285            },
1286        );
1287        base.create_param("V", ParamType::Int32).unwrap();
1288
1289        base.disconnect_addr(1);
1290        assert!(!base.is_device_connected(1));
1291        assert!(base.check_ready_addr(1).is_err());
1292
1293        base.connect_addr(1);
1294        assert!(base.is_device_connected(1));
1295        assert!(base.check_ready_addr(1).is_ok());
1296    }
1297
1298    #[test]
1299    fn test_enable_disable_addr() {
1300        let mut base = PortDriverBase::new(
1301            "multi_en",
1302            4,
1303            PortFlags {
1304                multi_device: true,
1305                can_block: false,
1306                destructible: true,
1307            },
1308        );
1309        base.create_param("V", ParamType::Int32).unwrap();
1310
1311        base.disable_addr(2);
1312        let err = base.check_ready_addr(2).unwrap_err();
1313        assert!(format!("{err}").contains("disabled"));
1314
1315        base.enable_addr(2);
1316        assert!(base.check_ready_addr(2).is_ok());
1317    }
1318
1319    #[test]
1320    fn test_port_level_overrides_addr() {
1321        let mut base = PortDriverBase::new(
1322            "multi_override",
1323            4,
1324            PortFlags {
1325                multi_device: true,
1326                can_block: false,
1327                destructible: true,
1328            },
1329        );
1330        base.create_param("V", ParamType::Int32).unwrap();
1331
1332        // Port-level disabled overrides addr-level enabled
1333        base.enabled = false;
1334        base.enable_addr(0); // addr 0 is enabled, but port is disabled
1335        let err = base.check_ready_addr(0).unwrap_err();
1336        assert!(format!("{err}").contains("disabled"));
1337    }
1338
1339    #[test]
1340    fn test_per_addr_exception_announced() {
1341        use std::sync::atomic::{AtomicI32, Ordering};
1342
1343        let mut base = PortDriverBase::new(
1344            "multi_exc",
1345            4,
1346            PortFlags {
1347                multi_device: true,
1348                can_block: false,
1349                destructible: true,
1350            },
1351        );
1352        base.create_param("V", ParamType::Int32).unwrap();
1353
1354        let exc_mgr = Arc::new(crate::exception::ExceptionManager::new());
1355        base.exception_sink = Some(exc_mgr.clone());
1356
1357        let last_addr = Arc::new(AtomicI32::new(-99));
1358        let last_addr2 = last_addr.clone();
1359        exc_mgr.add_callback(move |event| {
1360            last_addr2.store(event.addr, Ordering::Relaxed);
1361        });
1362
1363        base.disconnect_addr(3);
1364        assert_eq!(last_addr.load(Ordering::Relaxed), 3);
1365
1366        base.enable_addr(2);
1367        assert_eq!(last_addr.load(Ordering::Relaxed), 2);
1368    }
1369}