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.get(&addr).map_or(true, |ds| ds.connected)
200    }
201
202    /// Set a specific device address as connected.
203    pub fn connect_addr(&mut self, addr: i32) {
204        self.device_state(addr).connected = true;
205        self.announce_exception(AsynException::Connect, addr);
206    }
207
208    /// Set a specific device address as disconnected.
209    pub fn disconnect_addr(&mut self, addr: i32) {
210        self.device_state(addr).connected = false;
211        self.announce_exception(AsynException::Connect, addr);
212    }
213
214    /// Enable a specific device address.
215    pub fn enable_addr(&mut self, addr: i32) {
216        self.device_state(addr).enabled = true;
217        self.announce_exception(AsynException::Enable, addr);
218    }
219
220    /// Disable a specific device address.
221    pub fn disable_addr(&mut self, addr: i32) {
222        self.device_state(addr).enabled = false;
223        self.announce_exception(AsynException::Enable, addr);
224    }
225
226    /// Set a custom timestamp source callback.
227    pub fn register_timestamp_source<F>(&mut self, source: F)
228    where
229        F: Fn() -> SystemTime + Send + Sync + 'static,
230    {
231        self.timestamp_source = Some(Arc::new(source));
232    }
233
234    /// Get current timestamp from the registered source, or SystemTime::now().
235    pub fn current_timestamp(&self) -> SystemTime {
236        self.timestamp_source.as_ref().map_or_else(SystemTime::now, |f| f())
237    }
238
239    pub fn create_param(&mut self, name: &str, param_type: ParamType) -> AsynResult<usize> {
240        self.params.create_param(name, param_type)
241    }
242
243    pub fn find_param(&self, name: &str) -> Option<usize> {
244        self.params.find_param(name)
245    }
246
247    // --- Convenience param accessors ---
248
249    pub fn set_int32_param(&mut self, index: usize, addr: i32, value: i32) -> AsynResult<()> {
250        self.params.set_int32(index, addr, value)
251    }
252
253    pub fn get_int32_param(&self, index: usize, addr: i32) -> AsynResult<i32> {
254        self.params.get_int32(index, addr)
255    }
256
257    pub fn set_int64_param(&mut self, index: usize, addr: i32, value: i64) -> AsynResult<()> {
258        self.params.set_int64(index, addr, value)
259    }
260
261    pub fn get_int64_param(&self, index: usize, addr: i32) -> AsynResult<i64> {
262        self.params.get_int64(index, addr)
263    }
264
265    pub fn set_float64_param(&mut self, index: usize, addr: i32, value: f64) -> AsynResult<()> {
266        self.params.set_float64(index, addr, value)
267    }
268
269    pub fn get_float64_param(&self, index: usize, addr: i32) -> AsynResult<f64> {
270        self.params.get_float64(index, addr)
271    }
272
273    pub fn set_string_param(&mut self, index: usize, addr: i32, value: String) -> AsynResult<()> {
274        self.params.set_string(index, addr, value)
275    }
276
277    pub fn get_string_param(&self, index: usize, addr: i32) -> AsynResult<&str> {
278        self.params.get_string(index, addr)
279    }
280
281    pub fn set_uint32_param(
282        &mut self,
283        index: usize,
284        addr: i32,
285        value: u32,
286        mask: u32,
287    ) -> AsynResult<()> {
288        self.params.set_uint32(index, addr, value, mask)
289    }
290
291    pub fn get_uint32_param(&self, index: usize, addr: i32) -> AsynResult<u32> {
292        self.params.get_uint32(index, addr)
293    }
294
295    pub fn get_enum_param(&self, index: usize, addr: i32) -> AsynResult<(usize, Arc<[EnumEntry]>)> {
296        self.params.get_enum(index, addr)
297    }
298
299    pub fn set_enum_index_param(&mut self, index: usize, addr: i32, value: usize) -> AsynResult<()> {
300        self.params.set_enum_index(index, addr, value)
301    }
302
303    pub fn set_enum_choices_param(&mut self, index: usize, addr: i32, choices: Arc<[EnumEntry]>) -> AsynResult<()> {
304        self.params.set_enum_choices(index, addr, choices)
305    }
306
307    pub fn get_generic_pointer_param(&self, index: usize, addr: i32) -> AsynResult<Arc<dyn Any + Send + Sync>> {
308        self.params.get_generic_pointer(index, addr)
309    }
310
311    pub fn set_generic_pointer_param(&mut self, index: usize, addr: i32, value: Arc<dyn Any + Send + Sync>) -> AsynResult<()> {
312        self.params.set_generic_pointer(index, addr, value)
313    }
314
315    pub fn set_param_timestamp(&mut self, index: usize, addr: i32, ts: SystemTime) -> AsynResult<()> {
316        self.params.set_timestamp(index, addr, ts)
317    }
318
319    /// Push an interpose layer onto the octet I/O stack.
320    ///
321    /// **Concurrency**: requires `&mut self`, which means the caller must hold
322    /// the port lock (`Arc<Mutex<dyn PortDriver>>`). This ensures
323    /// interpose modifications are serialized with I/O dispatch.
324    pub fn push_octet_interpose(&mut self, layer: Box<dyn OctetInterpose>) {
325        self.interpose_octet.push(layer);
326    }
327
328    /// Flush changed parameters as interrupt notifications.
329    /// Equivalent to C asyn's callParamCallbacks().
330    pub fn call_param_callbacks(&mut self, addr: i32) -> AsynResult<()> {
331        let changed = self.params.take_changed(addr)?;
332        let now = self.current_timestamp();
333        for reason in changed {
334            let value = self.params.get_value(reason, addr)?.clone();
335            let ts = self.params.get_timestamp(reason, addr)?.unwrap_or(now);
336            self.interrupts.notify(InterruptValue {
337                reason,
338                addr,
339                value,
340                timestamp: ts,
341            });
342        }
343        Ok(())
344    }
345}
346
347/// Port driver trait. All methods have default implementations that operate
348/// on the parameter cache (no actual I/O).
349///
350/// Drivers performing real hardware I/O should:
351/// 1. Run I/O in a background task (e.g., tokio::spawn)
352/// 2. Update parameters via `base_mut().set_*_param()` + `call_param_callbacks()`
353/// 3. Let the default `read_*` methods return cached values
354///
355/// # LockPort/UnlockPort
356///
357/// C asyn provides `lockPort`/`unlockPort` for direct mutex locking. In asyn-rs,
358/// the port is always behind `Arc<Mutex<dyn PortDriver>>`, so callers hold the
359/// parking_lot mutex directly. For multi-request exclusive access, use
360/// `BlockProcess`/`UnblockProcess` via the worker queue.
361pub trait PortDriver: Send + Sync + 'static {
362    fn base(&self) -> &PortDriverBase;
363    fn base_mut(&mut self) -> &mut PortDriverBase;
364
365    // --- AsynCommon ---
366
367    fn connect(&mut self, _user: &AsynUser) -> AsynResult<()> {
368        self.base_mut().connected = true;
369        self.base().announce_exception(AsynException::Connect, -1);
370        Ok(())
371    }
372
373    fn disconnect(&mut self, _user: &AsynUser) -> AsynResult<()> {
374        self.base_mut().connected = false;
375        self.base().announce_exception(AsynException::Connect, -1);
376        Ok(())
377    }
378
379    fn enable(&mut self, _user: &AsynUser) -> AsynResult<()> {
380        self.base_mut().enabled = true;
381        self.base().announce_exception(AsynException::Enable, -1);
382        Ok(())
383    }
384
385    fn disable(&mut self, _user: &AsynUser) -> AsynResult<()> {
386        self.base_mut().enabled = false;
387        self.base().announce_exception(AsynException::Enable, -1);
388        Ok(())
389    }
390
391    fn connect_addr(&mut self, user: &AsynUser) -> AsynResult<()> {
392        self.base_mut().connect_addr(user.addr);
393        Ok(())
394    }
395
396    fn disconnect_addr(&mut self, user: &AsynUser) -> AsynResult<()> {
397        self.base_mut().disconnect_addr(user.addr);
398        Ok(())
399    }
400
401    fn enable_addr(&mut self, user: &AsynUser) -> AsynResult<()> {
402        self.base_mut().enable_addr(user.addr);
403        Ok(())
404    }
405
406    fn disable_addr(&mut self, user: &AsynUser) -> AsynResult<()> {
407        self.base_mut().disable_addr(user.addr);
408        Ok(())
409    }
410
411    fn get_option(&self, key: &str) -> AsynResult<String> {
412        self.base().options.get(key)
413            .cloned()
414            .ok_or_else(|| AsynError::OptionNotFound(key.to_string()))
415    }
416
417    fn set_option(&mut self, key: &str, value: &str) -> AsynResult<()> {
418        self.base_mut().options.insert(key.to_string(), value.to_string());
419        Ok(())
420    }
421
422    fn report(&self, level: i32) {
423        let base = self.base();
424        eprintln!("Port: {}", base.port_name);
425        eprintln!("  connected: {}, max_addr: {}, params: {}, options: {}",
426            base.connected, base.max_addr, base.params.len(), base.options.len());
427        if level >= 1 {
428            for i in 0..base.params.len() {
429                if let (Some(name), Some(ptype)) = (base.params.param_name(i), base.params.param_type(i)) {
430                    if level >= 3 {
431                        let val = base.params.get_value(i, 0)
432                            .map(|v| format!("{v:?}")).unwrap_or("?".into());
433                        eprintln!("  param[{i}]: {name} ({ptype:?}) = {val}");
434                    } else {
435                        eprintln!("  param[{i}]: {name} ({ptype:?})");
436                    }
437                }
438            }
439        }
440        if level >= 2 {
441            for (k, v) in &base.options {
442                eprintln!("  option: {k} = {v}");
443            }
444        }
445    }
446
447    // --- Scalar I/O (cache-based defaults, timeout not applicable) ---
448
449    fn read_int32(&mut self, user: &AsynUser) -> AsynResult<i32> {
450        self.base().check_ready()?;
451        self.base().params.get_int32(user.reason, user.addr)
452    }
453
454    fn write_int32(&mut self, user: &mut AsynUser, value: i32) -> AsynResult<()> {
455        self.base().check_ready()?;
456        self.base_mut()
457            .params
458            .set_int32(user.reason, user.addr, value)?;
459        self.base_mut().call_param_callbacks(user.addr)
460    }
461
462    fn read_int64(&mut self, user: &AsynUser) -> AsynResult<i64> {
463        self.base().check_ready()?;
464        self.base().params.get_int64(user.reason, user.addr)
465    }
466
467    fn write_int64(&mut self, user: &mut AsynUser, value: i64) -> AsynResult<()> {
468        self.base().check_ready()?;
469        self.base_mut()
470            .params
471            .set_int64(user.reason, user.addr, value)?;
472        self.base_mut().call_param_callbacks(user.addr)
473    }
474
475    fn get_bounds_int64(&self, _user: &AsynUser) -> AsynResult<(i64, i64)> {
476        Ok((i64::MIN, i64::MAX))
477    }
478
479    fn read_float64(&mut self, user: &AsynUser) -> AsynResult<f64> {
480        self.base().check_ready()?;
481        self.base().params.get_float64(user.reason, user.addr)
482    }
483
484    fn write_float64(&mut self, user: &mut AsynUser, value: f64) -> AsynResult<()> {
485        self.base().check_ready()?;
486        self.base_mut()
487            .params
488            .set_float64(user.reason, user.addr, value)?;
489        self.base_mut().call_param_callbacks(user.addr)
490    }
491
492    fn read_octet(&mut self, user: &AsynUser, buf: &mut [u8]) -> AsynResult<usize> {
493        self.base().check_ready()?;
494        let s = self.base().params.get_string(user.reason, user.addr)?;
495        let bytes = s.as_bytes();
496        let n = bytes.len().min(buf.len());
497        buf[..n].copy_from_slice(&bytes[..n]);
498        Ok(n)
499    }
500
501    fn write_octet(&mut self, user: &mut AsynUser, data: &[u8]) -> AsynResult<()> {
502        self.base().check_ready()?;
503        let s = String::from_utf8_lossy(data).into_owned();
504        self.base_mut()
505            .params
506            .set_string(user.reason, user.addr, s)?;
507        self.base_mut().call_param_callbacks(user.addr)
508    }
509
510    fn read_uint32_digital(&mut self, user: &AsynUser, mask: u32) -> AsynResult<u32> {
511        self.base().check_ready()?;
512        let val = self.base().params.get_uint32(user.reason, user.addr)?;
513        Ok(val & mask)
514    }
515
516    fn write_uint32_digital(
517        &mut self,
518        user: &mut AsynUser,
519        value: u32,
520        mask: u32,
521    ) -> AsynResult<()> {
522        self.base().check_ready()?;
523        self.base_mut()
524            .params
525            .set_uint32(user.reason, user.addr, value, mask)?;
526        self.base_mut().call_param_callbacks(user.addr)
527    }
528
529    // --- Enum I/O (cache-based defaults) ---
530
531    fn read_enum(&mut self, user: &AsynUser) -> AsynResult<(usize, Arc<[EnumEntry]>)> {
532        self.base().check_ready()?;
533        self.base().params.get_enum(user.reason, user.addr)
534    }
535
536    fn write_enum(&mut self, user: &mut AsynUser, index: usize) -> AsynResult<()> {
537        self.base().check_ready()?;
538        self.base_mut().params.set_enum_index(user.reason, user.addr, index)?;
539        self.base_mut().call_param_callbacks(user.addr)
540    }
541
542    fn write_enum_choices(&mut self, user: &mut AsynUser, choices: Arc<[EnumEntry]>) -> AsynResult<()> {
543        self.base().check_ready()?;
544        self.base_mut().params.set_enum_choices(user.reason, user.addr, choices)?;
545        self.base_mut().call_param_callbacks(user.addr)
546    }
547
548    // --- GenericPointer I/O (cache-based defaults) ---
549
550    fn read_generic_pointer(&mut self, user: &AsynUser) -> AsynResult<Arc<dyn Any + Send + Sync>> {
551        self.base().check_ready()?;
552        self.base().params.get_generic_pointer(user.reason, user.addr)
553    }
554
555    fn write_generic_pointer(&mut self, user: &mut AsynUser, value: Arc<dyn Any + Send + Sync>) -> AsynResult<()> {
556        self.base().check_ready()?;
557        self.base_mut().params.set_generic_pointer(user.reason, user.addr, value)?;
558        self.base_mut().call_param_callbacks(user.addr)
559    }
560
561    // --- Array I/O (default: not supported) ---
562
563    fn read_float64_array(&mut self, _user: &AsynUser, _buf: &mut [f64]) -> AsynResult<usize> {
564        Err(AsynError::InterfaceNotSupported(
565            "asynFloat64Array".into(),
566        ))
567    }
568
569    fn write_float64_array(&mut self, _user: &AsynUser, _data: &[f64]) -> AsynResult<()> {
570        Err(AsynError::InterfaceNotSupported(
571            "asynFloat64Array".into(),
572        ))
573    }
574
575    fn read_int32_array(&mut self, _user: &AsynUser, _buf: &mut [i32]) -> AsynResult<usize> {
576        Err(AsynError::InterfaceNotSupported("asynInt32Array".into()))
577    }
578
579    fn write_int32_array(&mut self, _user: &AsynUser, _data: &[i32]) -> AsynResult<()> {
580        Err(AsynError::InterfaceNotSupported("asynInt32Array".into()))
581    }
582
583    fn read_int8_array(&mut self, _user: &AsynUser, _buf: &mut [i8]) -> AsynResult<usize> {
584        Err(AsynError::InterfaceNotSupported("asynInt8Array".into()))
585    }
586
587    fn write_int8_array(&mut self, _user: &AsynUser, _data: &[i8]) -> AsynResult<()> {
588        Err(AsynError::InterfaceNotSupported("asynInt8Array".into()))
589    }
590
591    fn read_int16_array(&mut self, _user: &AsynUser, _buf: &mut [i16]) -> AsynResult<usize> {
592        Err(AsynError::InterfaceNotSupported("asynInt16Array".into()))
593    }
594
595    fn write_int16_array(&mut self, _user: &AsynUser, _data: &[i16]) -> AsynResult<()> {
596        Err(AsynError::InterfaceNotSupported("asynInt16Array".into()))
597    }
598
599    fn read_int64_array(&mut self, _user: &AsynUser, _buf: &mut [i64]) -> AsynResult<usize> {
600        Err(AsynError::InterfaceNotSupported("asynInt64Array".into()))
601    }
602
603    fn write_int64_array(&mut self, _user: &AsynUser, _data: &[i64]) -> AsynResult<()> {
604        Err(AsynError::InterfaceNotSupported("asynInt64Array".into()))
605    }
606
607    fn read_float32_array(&mut self, _user: &AsynUser, _buf: &mut [f32]) -> AsynResult<usize> {
608        Err(AsynError::InterfaceNotSupported("asynFloat32Array".into()))
609    }
610
611    fn write_float32_array(&mut self, _user: &AsynUser, _data: &[f32]) -> AsynResult<()> {
612        Err(AsynError::InterfaceNotSupported("asynFloat32Array".into()))
613    }
614
615    // --- I/O methods (worker thread calls these) ---
616    // Default: delegate to cache-based read_*/write_* for backward compat.
617    // Real I/O drivers override these for actual hardware access.
618
619    fn io_read_octet(&mut self, user: &AsynUser, buf: &mut [u8]) -> AsynResult<usize> {
620        self.read_octet(user, buf)
621    }
622
623    fn io_write_octet(&mut self, user: &mut AsynUser, data: &[u8]) -> AsynResult<()> {
624        self.write_octet(user, data)
625    }
626
627    fn io_read_int32(&mut self, user: &AsynUser) -> AsynResult<i32> {
628        self.read_int32(user)
629    }
630
631    fn io_write_int32(&mut self, user: &mut AsynUser, value: i32) -> AsynResult<()> {
632        self.write_int32(user, value)
633    }
634
635    fn io_read_int64(&mut self, user: &AsynUser) -> AsynResult<i64> {
636        self.read_int64(user)
637    }
638
639    fn io_write_int64(&mut self, user: &mut AsynUser, value: i64) -> AsynResult<()> {
640        self.write_int64(user, value)
641    }
642
643    fn io_read_float64(&mut self, user: &AsynUser) -> AsynResult<f64> {
644        self.read_float64(user)
645    }
646
647    fn io_write_float64(&mut self, user: &mut AsynUser, value: f64) -> AsynResult<()> {
648        self.write_float64(user, value)
649    }
650
651    fn io_read_uint32_digital(&mut self, user: &AsynUser, mask: u32) -> AsynResult<u32> {
652        self.read_uint32_digital(user, mask)
653    }
654
655    fn io_write_uint32_digital(
656        &mut self,
657        user: &mut AsynUser,
658        value: u32,
659        mask: u32,
660    ) -> AsynResult<()> {
661        self.write_uint32_digital(user, value, mask)
662    }
663
664    fn io_flush(&mut self, _user: &mut AsynUser) -> AsynResult<()> {
665        Ok(())
666    }
667
668    // --- drvUser ---
669
670    /// Resolve a driver info string to a parameter index.
671    /// Default: look up by parameter name.
672    fn drv_user_create(&self, drv_info: &str) -> AsynResult<usize> {
673        self.base()
674            .params
675            .find_param(drv_info)
676            .ok_or_else(|| AsynError::ParamNotFound(drv_info.to_string()))
677    }
678
679    // --- Capabilities ---
680
681    /// Declare the capabilities this driver supports.
682    /// Default implementation includes all scalar read/write operations.
683    fn capabilities(&self) -> Vec<crate::interfaces::Capability> {
684        crate::interfaces::default_capabilities()
685    }
686
687    /// Check if this driver supports a specific capability.
688    fn supports(&self, cap: crate::interfaces::Capability) -> bool {
689        self.capabilities().contains(&cap)
690    }
691
692    // --- Lifecycle ---
693
694    fn init(&mut self) -> AsynResult<()> {
695        Ok(())
696    }
697}
698
699#[cfg(test)]
700mod tests {
701    use super::*;
702    struct TestDriver {
703        base: PortDriverBase,
704    }
705
706    impl TestDriver {
707        fn new() -> Self {
708            let mut base = PortDriverBase::new("test", 1, PortFlags::default());
709            base.create_param("VAL", ParamType::Int32).unwrap();
710            base.create_param("TEMP", ParamType::Float64).unwrap();
711            base.create_param("MSG", ParamType::Octet).unwrap();
712            base.create_param("BITS", ParamType::UInt32Digital).unwrap();
713            Self { base }
714        }
715    }
716
717    impl PortDriver for TestDriver {
718        fn base(&self) -> &PortDriverBase {
719            &self.base
720        }
721        fn base_mut(&mut self) -> &mut PortDriverBase {
722            &mut self.base
723        }
724    }
725
726    #[test]
727    fn test_default_read_write_int32() {
728        let mut drv = TestDriver::new();
729        let mut user = AsynUser::new(0);
730        drv.write_int32(&mut user, 42).unwrap();
731        let user = AsynUser::new(0);
732        assert_eq!(drv.read_int32(&user).unwrap(), 42);
733    }
734
735    #[test]
736    fn test_default_read_write_float64() {
737        let mut drv = TestDriver::new();
738        let mut user = AsynUser::new(1);
739        drv.write_float64(&mut user, 3.14).unwrap();
740        let user = AsynUser::new(1);
741        assert!((drv.read_float64(&user).unwrap() - 3.14).abs() < 1e-10);
742    }
743
744    #[test]
745    fn test_default_read_write_octet() {
746        let mut drv = TestDriver::new();
747        let mut user = AsynUser::new(2);
748        drv.write_octet(&mut user, b"hello").unwrap();
749        let user = AsynUser::new(2);
750        let mut buf = [0u8; 32];
751        let n = drv.read_octet(&user, &mut buf).unwrap();
752        assert_eq!(&buf[..n], b"hello");
753    }
754
755    #[test]
756    fn test_default_read_write_uint32() {
757        let mut drv = TestDriver::new();
758        let mut user = AsynUser::new(3);
759        drv.write_uint32_digital(&mut user, 0xFF, 0x0F).unwrap();
760        let user = AsynUser::new(3);
761        assert_eq!(drv.read_uint32_digital(&user, 0xFF).unwrap(), 0x0F);
762    }
763
764    #[test]
765    fn test_connect_disconnect() {
766        let mut drv = TestDriver::new();
767        let user = AsynUser::default();
768        assert!(drv.base().connected);
769        drv.disconnect(&user).unwrap();
770        assert!(!drv.base().connected);
771        drv.connect(&user).unwrap();
772        assert!(drv.base().connected);
773    }
774
775    #[test]
776    fn test_drv_user_create() {
777        let drv = TestDriver::new();
778        assert_eq!(drv.drv_user_create("VAL").unwrap(), 0);
779        assert_eq!(drv.drv_user_create("TEMP").unwrap(), 1);
780        assert!(drv.drv_user_create("NOPE").is_err());
781    }
782
783    #[test]
784    fn test_call_param_callbacks() {
785        let mut drv = TestDriver::new();
786        let rx = drv.base_mut().interrupts.subscribe_sync().unwrap();
787
788        drv.base_mut().set_int32_param(0, 0, 100).unwrap();
789        drv.base_mut().set_float64_param(1, 0, 2.0).unwrap();
790        drv.base_mut().call_param_callbacks(0).unwrap();
791
792        let v1 = rx.try_recv().unwrap();
793        assert_eq!(v1.reason, 0);
794        let v2 = rx.try_recv().unwrap();
795        assert_eq!(v2.reason, 1);
796        assert!(rx.try_recv().is_err());
797    }
798
799    #[test]
800    fn test_no_callback_for_unchanged() {
801        let mut drv = TestDriver::new();
802        let rx = drv.base_mut().interrupts.subscribe_sync().unwrap();
803
804        drv.base_mut().set_int32_param(0, 0, 5).unwrap();
805        drv.base_mut().call_param_callbacks(0).unwrap();
806        let _ = rx.try_recv().unwrap(); // consume
807
808        // Set same value — no interrupt
809        drv.base_mut().set_int32_param(0, 0, 5).unwrap();
810        drv.base_mut().call_param_callbacks(0).unwrap();
811        assert!(rx.try_recv().is_err());
812    }
813
814    #[test]
815    fn test_array_not_supported_by_default() {
816        let mut drv = TestDriver::new();
817        let user = AsynUser::new(0);
818        let mut buf = [0f64; 10];
819        assert!(drv.read_float64_array(&user, &mut buf).is_err());
820        assert!(drv.write_float64_array(&user, &[1.0]).is_err());
821    }
822
823    #[test]
824    fn test_option_set_get() {
825        let mut drv = TestDriver::new();
826        drv.set_option("baud", "9600").unwrap();
827        assert_eq!(drv.get_option("baud").unwrap(), "9600");
828        drv.set_option("baud", "115200").unwrap();
829        assert_eq!(drv.get_option("baud").unwrap(), "115200");
830    }
831
832    #[test]
833    fn test_option_not_found() {
834        let drv = TestDriver::new();
835        let err = drv.get_option("nonexistent").unwrap_err();
836        assert!(matches!(err, AsynError::OptionNotFound(_)));
837    }
838
839    #[test]
840    fn test_report_no_panic() {
841        let mut drv = TestDriver::new();
842        drv.set_option("testkey", "testval").unwrap();
843        drv.base_mut().set_int32_param(0, 0, 42).unwrap();
844        for level in 0..=3 {
845            drv.report(level);
846        }
847    }
848
849    #[test]
850    fn test_callback_uses_param_timestamp() {
851        let mut drv = TestDriver::new();
852        let rx = drv.base_mut().interrupts.subscribe_sync().unwrap();
853
854        let custom_ts = SystemTime::UNIX_EPOCH + std::time::Duration::from_secs(1_000_000);
855        drv.base_mut().set_int32_param(0, 0, 77).unwrap();
856        drv.base_mut().set_param_timestamp(0, 0, custom_ts).unwrap();
857        drv.base_mut().call_param_callbacks(0).unwrap();
858
859        let v = rx.try_recv().unwrap();
860        assert_eq!(v.reason, 0);
861        assert_eq!(v.timestamp, custom_ts);
862    }
863
864    #[test]
865    fn test_default_read_write_enum() {
866        use crate::param::EnumEntry;
867
868        let mut base = PortDriverBase::new("test_enum", 1, PortFlags::default());
869        base.create_param("MODE", ParamType::Enum).unwrap();
870
871        struct EnumDriver { base: PortDriverBase }
872        impl PortDriver for EnumDriver {
873            fn base(&self) -> &PortDriverBase { &self.base }
874            fn base_mut(&mut self) -> &mut PortDriverBase { &mut self.base }
875        }
876
877        let mut drv = EnumDriver { base };
878        let choices: Arc<[EnumEntry]> = Arc::from(vec![
879            EnumEntry { string: "Off".into(), value: 0, severity: 0 },
880            EnumEntry { string: "On".into(), value: 1, severity: 0 },
881        ]);
882        let mut user = AsynUser::new(0);
883        drv.write_enum_choices(&mut user, choices).unwrap();
884        drv.write_enum(&mut user, 1).unwrap();
885        let (idx, ch) = drv.read_enum(&AsynUser::new(0)).unwrap();
886        assert_eq!(idx, 1);
887        assert_eq!(ch[1].string, "On");
888    }
889
890    #[test]
891    fn test_enum_callback() {
892        use crate::param::{EnumEntry, ParamValue};
893
894        let mut base = PortDriverBase::new("test_enum_cb", 1, PortFlags::default());
895        base.create_param("MODE", ParamType::Enum).unwrap();
896        let rx = base.interrupts.subscribe_sync().unwrap();
897
898        struct EnumDriver { base: PortDriverBase }
899        impl PortDriver for EnumDriver {
900            fn base(&self) -> &PortDriverBase { &self.base }
901            fn base_mut(&mut self) -> &mut PortDriverBase { &mut self.base }
902        }
903
904        let mut drv = EnumDriver { base };
905        let choices: Arc<[EnumEntry]> = Arc::from(vec![
906            EnumEntry { string: "A".into(), value: 0, severity: 0 },
907            EnumEntry { string: "B".into(), value: 1, severity: 0 },
908        ]);
909        drv.base_mut().set_enum_choices_param(0, 0, choices).unwrap();
910        drv.base_mut().set_enum_index_param(0, 0, 1).unwrap();
911        drv.base_mut().call_param_callbacks(0).unwrap();
912
913        let v = rx.try_recv().unwrap();
914        assert_eq!(v.reason, 0);
915        assert!(matches!(v.value, ParamValue::Enum { index: 1, .. }));
916    }
917
918    #[test]
919    fn test_default_read_write_generic_pointer() {
920        let mut base = PortDriverBase::new("test_gp", 1, PortFlags::default());
921        base.create_param("PTR", ParamType::GenericPointer).unwrap();
922
923        struct GpDriver { base: PortDriverBase }
924        impl PortDriver for GpDriver {
925            fn base(&self) -> &PortDriverBase { &self.base }
926            fn base_mut(&mut self) -> &mut PortDriverBase { &mut self.base }
927        }
928
929        let mut drv = GpDriver { base };
930        let data: Arc<dyn std::any::Any + Send + Sync> = Arc::new(99i32);
931        let mut user = AsynUser::new(0);
932        drv.write_generic_pointer(&mut user, data).unwrap();
933        let val = drv.read_generic_pointer(&AsynUser::new(0)).unwrap();
934        assert_eq!(*val.downcast_ref::<i32>().unwrap(), 99);
935    }
936
937    #[test]
938    fn test_generic_pointer_callback() {
939        use crate::param::ParamValue;
940
941        let mut base = PortDriverBase::new("test_gp_cb", 1, PortFlags::default());
942        base.create_param("PTR", ParamType::GenericPointer).unwrap();
943        let rx = base.interrupts.subscribe_sync().unwrap();
944
945        struct GpDriver { base: PortDriverBase }
946        impl PortDriver for GpDriver {
947            fn base(&self) -> &PortDriverBase { &self.base }
948            fn base_mut(&mut self) -> &mut PortDriverBase { &mut self.base }
949        }
950
951        let mut drv = GpDriver { base };
952        let data: Arc<dyn std::any::Any + Send + Sync> = Arc::new(vec![1, 2, 3]);
953        drv.base_mut().set_generic_pointer_param(0, 0, data).unwrap();
954        drv.base_mut().call_param_callbacks(0).unwrap();
955
956        let v = rx.try_recv().unwrap();
957        assert_eq!(v.reason, 0);
958        assert!(matches!(v.value, ParamValue::GenericPointer(_)));
959    }
960
961    #[test]
962    fn test_interpose_push_requires_lock() {
963        use crate::interpose::{OctetInterpose, OctetNext, OctetReadResult};
964        use std::sync::Arc;
965        use parking_lot::Mutex;
966
967        struct NoopInterpose;
968        impl OctetInterpose for NoopInterpose {
969            fn read(&mut self, user: &AsynUser, buf: &mut [u8], next: &mut dyn OctetNext) -> AsynResult<OctetReadResult> {
970                next.read(user, buf)
971            }
972            fn write(&mut self, user: &mut AsynUser, data: &[u8], next: &mut dyn OctetNext) -> AsynResult<usize> {
973                next.write(user, data)
974            }
975            fn flush(&mut self, user: &mut AsynUser, next: &mut dyn OctetNext) -> AsynResult<()> {
976                next.flush(user)
977            }
978        }
979
980        let port: Arc<Mutex<dyn PortDriver>> = Arc::new(Mutex::new(TestDriver::new()));
981
982        {
983            let mut guard = port.lock();
984            guard.base_mut().push_octet_interpose(Box::new(NoopInterpose));
985            assert_eq!(guard.base().interpose_octet.len(), 1);
986        }
987    }
988
989    #[test]
990    fn test_default_read_write_int64() {
991        let mut base = PortDriverBase::new("test_i64", 1, PortFlags::default());
992        base.create_param("BIG", ParamType::Int64).unwrap();
993
994        struct I64Driver { base: PortDriverBase }
995        impl PortDriver for I64Driver {
996            fn base(&self) -> &PortDriverBase { &self.base }
997            fn base_mut(&mut self) -> &mut PortDriverBase { &mut self.base }
998        }
999
1000        let mut drv = I64Driver { base };
1001        let mut user = AsynUser::new(0);
1002        drv.write_int64(&mut user, i64::MAX).unwrap();
1003        assert_eq!(drv.read_int64(&AsynUser::new(0)).unwrap(), i64::MAX);
1004    }
1005
1006    #[test]
1007    fn test_get_bounds_int64_default() {
1008        let base = PortDriverBase::new("test_bounds", 1, PortFlags::default());
1009        struct BoundsDriver { base: PortDriverBase }
1010        impl PortDriver for BoundsDriver {
1011            fn base(&self) -> &PortDriverBase { &self.base }
1012            fn base_mut(&mut self) -> &mut PortDriverBase { &mut self.base }
1013        }
1014        let drv = BoundsDriver { base };
1015        let (lo, hi) = drv.get_bounds_int64(&AsynUser::default()).unwrap();
1016        assert_eq!(lo, i64::MIN);
1017        assert_eq!(hi, i64::MAX);
1018    }
1019
1020    #[test]
1021    fn test_per_addr_device_state() {
1022        let mut base = PortDriverBase::new("multi", 4, PortFlags {
1023            multi_device: true,
1024            can_block: false,
1025            destructible: true,
1026        });
1027        base.create_param("V", ParamType::Int32).unwrap();
1028
1029        // Default: all connected
1030        assert!(base.is_device_connected(0));
1031        assert!(base.is_device_connected(1));
1032
1033        // Disable addr 1
1034        base.device_state(1).enabled = false;
1035        assert!(base.check_ready_addr(0).is_ok());
1036        let err = base.check_ready_addr(1).unwrap_err();
1037        assert!(format!("{err}").contains("disabled"));
1038
1039        // Disconnect addr 2
1040        base.device_state(2).connected = false;
1041        let err = base.check_ready_addr(2).unwrap_err();
1042        assert!(format!("{err}").contains("disconnected"));
1043    }
1044
1045    #[test]
1046    fn test_per_addr_single_device_ignored() {
1047        let mut base = PortDriverBase::new("single", 1, PortFlags::default());
1048        base.create_param("V", ParamType::Int32).unwrap();
1049        // For single-device, per-addr check passes even if no device state
1050        assert!(base.check_ready_addr(0).is_ok());
1051    }
1052
1053    #[test]
1054    fn test_timestamp_source() {
1055        let mut base = PortDriverBase::new("ts_test", 1, PortFlags::default());
1056        base.create_param("V", ParamType::Int32).unwrap();
1057
1058        let fixed_ts = SystemTime::UNIX_EPOCH + std::time::Duration::from_secs(999999);
1059        base.register_timestamp_source(move || fixed_ts);
1060
1061        assert_eq!(base.current_timestamp(), fixed_ts);
1062    }
1063
1064    #[test]
1065    fn test_timestamp_source_in_callbacks() {
1066        let mut base = PortDriverBase::new("ts_cb", 1, PortFlags::default());
1067        base.create_param("V", ParamType::Int32).unwrap();
1068        let rx = base.interrupts.subscribe_sync().unwrap();
1069
1070        let fixed_ts = SystemTime::UNIX_EPOCH + std::time::Duration::from_secs(123456);
1071        base.register_timestamp_source(move || fixed_ts);
1072
1073        struct TsDriver { base: PortDriverBase }
1074        impl PortDriver for TsDriver {
1075            fn base(&self) -> &PortDriverBase { &self.base }
1076            fn base_mut(&mut self) -> &mut PortDriverBase { &mut self.base }
1077        }
1078        let mut drv = TsDriver { base };
1079        drv.base_mut().set_int32_param(0, 0, 42).unwrap();
1080        drv.base_mut().call_param_callbacks(0).unwrap();
1081
1082        let v = rx.try_recv().unwrap();
1083        // Should use fixed_ts since no per-param timestamp is set
1084        assert_eq!(v.timestamp, fixed_ts);
1085    }
1086
1087    #[test]
1088    fn test_queue_priority_connect() {
1089        assert!(QueuePriority::Connect > QueuePriority::High);
1090    }
1091
1092    #[test]
1093    fn test_port_flags_destructible_default() {
1094        let flags = PortFlags::default();
1095        assert!(flags.destructible);
1096    }
1097
1098    // --- Phase 2B: per-addr connect/disconnect/enable/disable ---
1099
1100    #[test]
1101    fn test_connect_addr() {
1102        let mut base = PortDriverBase::new("multi_conn", 4, PortFlags {
1103            multi_device: true,
1104            can_block: false,
1105            destructible: true,
1106        });
1107        base.create_param("V", ParamType::Int32).unwrap();
1108
1109        base.disconnect_addr(1);
1110        assert!(!base.is_device_connected(1));
1111        assert!(base.check_ready_addr(1).is_err());
1112
1113        base.connect_addr(1);
1114        assert!(base.is_device_connected(1));
1115        assert!(base.check_ready_addr(1).is_ok());
1116    }
1117
1118    #[test]
1119    fn test_enable_disable_addr() {
1120        let mut base = PortDriverBase::new("multi_en", 4, PortFlags {
1121            multi_device: true,
1122            can_block: false,
1123            destructible: true,
1124        });
1125        base.create_param("V", ParamType::Int32).unwrap();
1126
1127        base.disable_addr(2);
1128        let err = base.check_ready_addr(2).unwrap_err();
1129        assert!(format!("{err}").contains("disabled"));
1130
1131        base.enable_addr(2);
1132        assert!(base.check_ready_addr(2).is_ok());
1133    }
1134
1135    #[test]
1136    fn test_port_level_overrides_addr() {
1137        let mut base = PortDriverBase::new("multi_override", 4, PortFlags {
1138            multi_device: true,
1139            can_block: false,
1140            destructible: true,
1141        });
1142        base.create_param("V", ParamType::Int32).unwrap();
1143
1144        // Port-level disabled overrides addr-level enabled
1145        base.enabled = false;
1146        base.enable_addr(0); // addr 0 is enabled, but port is disabled
1147        let err = base.check_ready_addr(0).unwrap_err();
1148        assert!(format!("{err}").contains("disabled"));
1149    }
1150
1151    #[test]
1152    fn test_per_addr_exception_announced() {
1153        use std::sync::atomic::{AtomicI32, Ordering};
1154
1155        let mut base = PortDriverBase::new("multi_exc", 4, PortFlags {
1156            multi_device: true,
1157            can_block: false,
1158            destructible: true,
1159        });
1160        base.create_param("V", ParamType::Int32).unwrap();
1161
1162        let exc_mgr = Arc::new(crate::exception::ExceptionManager::new());
1163        base.exception_sink = Some(exc_mgr.clone());
1164
1165        let last_addr = Arc::new(AtomicI32::new(-99));
1166        let last_addr2 = last_addr.clone();
1167        exc_mgr.add_callback(move |event| {
1168            last_addr2.store(event.addr, Ordering::Relaxed);
1169        });
1170
1171        base.disconnect_addr(3);
1172        assert_eq!(last_addr.load(Ordering::Relaxed), 3);
1173
1174        base.enable_addr(2);
1175        assert_eq!(last_addr.load(Ordering::Relaxed), 2);
1176    }
1177}