instrument_core/
session.rs1use crate::address::ResourceAddress;
2use crate::connect::ConnectOptions;
3use crate::diagnostics::Diagnostics;
4use crate::dialect::{resolve_dialect, DialectProfile};
5use crate::error::{Error, Result};
6use crate::identity::{DeviceIdentity, Idn};
7use crate::ieee4882::Ieee4882;
8use crate::kind::InstrumentKind;
9use crate::scpi::ScpiSession;
10use crate::transport::DynTransport;
11use std::sync::{Arc, Mutex};
12
13pub trait SessionOpener: Send + Sync {
15 fn open(&self, address: &ResourceAddress, opts: &ConnectOptions) -> Result<DynTransport>;
16}
17
18pub struct InstrumentSession {
20 pub address: ResourceAddress,
21 pub scpi: ScpiSession,
22 identity: DeviceIdentity,
23}
24
25impl InstrumentSession {
26 pub fn new(
27 address: ResourceAddress,
28 transport: DynTransport,
29 opts: ConnectOptions,
30 identity: DeviceIdentity,
31 ) -> Result<Self> {
32 Self::new_with_diagnostics(address, transport, opts, identity, None)
33 }
34
35 pub fn new_with_diagnostics(
36 address: ResourceAddress,
37 transport: DynTransport,
38 opts: ConnectOptions,
39 identity: DeviceIdentity,
40 diagnostics: Option<Diagnostics>,
41 ) -> Result<Self> {
42 let mut scpi = ScpiSession::new(transport, opts)?;
43 if let Some(diag) = diagnostics {
44 scpi = scpi.with_diagnostics(diag);
45 }
46 Ok(Self {
47 address,
48 scpi,
49 identity,
50 })
51 }
52
53 pub fn address_str(&self) -> &str {
54 &self.address.raw
55 }
56
57 pub fn identity(&self) -> &DeviceIdentity {
58 &self.identity
59 }
60
61 pub fn dialect_for(&self, kind: InstrumentKind) -> &'static DialectProfile {
63 resolve_dialect(
64 kind,
65 self.identity.manufacturer.as_deref(),
66 self.identity.model.as_deref(),
67 )
68 }
69
70 pub fn scpi(&self) -> &ScpiSession {
71 &self.scpi
72 }
73
74 pub fn scpi_mut(&mut self) -> &mut ScpiSession {
75 &mut self.scpi
76 }
77
78 pub fn idn(&mut self) -> Result<Idn> {
79 self.scpi_comm("idn", |scpi| Ieee4882::new(scpi).idn())
80 }
81
82 fn scpi_comm<T>(
83 &mut self,
84 command: &str,
85 f: impl FnOnce(&mut ScpiSession) -> Result<T>,
86 ) -> Result<T> {
87 match f(&mut self.scpi) {
88 Ok(v) => Ok(v),
89 Err(e) => Err(e.with_comm_context(&self.address.raw, Some(command), 1)),
90 }
91 }
92
93 pub fn reset(&mut self) -> Result<()> {
94 self.scpi_comm("*RST", |scpi| Ieee4882::new(scpi).reset())
95 }
96
97 pub fn clear_status(&mut self) -> Result<()> {
98 self.scpi_comm("*CLS", |scpi| Ieee4882::new(scpi).clear_status())
99 }
100
101 pub fn wait_complete(&mut self) -> Result<()> {
102 self.scpi_comm("*OPC?", |scpi| Ieee4882::new(scpi).wait_complete())
103 }
104
105 pub fn check_errors(&mut self) -> Result<Vec<String>> {
106 self.scpi_comm("SYST:ERR?", |scpi| scpi.check_errors())
107 }
108}
109
110pub struct SessionPool {
112 session: Arc<Mutex<InstrumentSession>>,
113}
114
115impl SessionPool {
116 pub fn new(session: InstrumentSession) -> Self {
117 Self {
118 session: Arc::new(Mutex::new(session)),
119 }
120 }
121
122 pub fn session(&self) -> Arc<Mutex<InstrumentSession>> {
123 self.session.clone()
124 }
125
126 pub fn open_session(&self) -> Result<PooledSession> {
127 Ok(PooledSession {
128 session: self.session.clone(),
129 })
130 }
131}
132
133pub struct PooledSession {
135 session: Arc<Mutex<InstrumentSession>>,
136}
137
138impl PooledSession {
139 pub fn lock(&self) -> std::sync::MutexGuard<'_, InstrumentSession> {
140 self.session.lock().unwrap()
141 }
142}
143
144pub fn ensure_kind_supported(
146 address: &ResourceAddress,
147 kind: InstrumentKind,
148 supported: &[InstrumentKind],
149) -> Result<()> {
150 if supported.contains(&kind) {
151 return Ok(());
152 }
153 Err(Error::UnsupportedKind {
154 address: address.raw.clone(),
155 kind,
156 supported: supported.to_vec(),
157 })
158}