Skip to main content

tango/corba/
mod.rs

1//! The CORBA backend, the protocol every Tango implementation speaks, with the ZMQ event system
2//! as part of it.
3
4mod orb;
5mod tango;
6mod zmq;
7
8use std::ops::ControlFlow;
9use std::sync::{Arc, OnceLock};
10use std::time::SystemTime;
11use std::future::Future;
12use std::borrow::Cow;
13use std::pin::Pin;
14use std::task;
15
16use futures::future::{ready, BoxFuture};
17use futures::stream::BoxStream;
18use tracing::{debug, trace, warn};
19use uuid::Uuid;
20
21use crate::dev::{AttrConfig, AttrInfo, AttrQuality, AttrRead, AttrReady, AttrValueEvent, CommandInfo, DevInfo, DevSource, DevState, Named, NamedResult, RecordedResult};
22use crate::backend::{Backend, BackendDev, BackendServer, BackendServerConfig};
23use crate::error::{reason, Error, ErrorKind, Result};
24use crate::value::{Scalar, Spectrum, Value};
25use crate::future::resolve;
26
27use orb::client::{Client, ClientObject, ClientRequest, ClientReply};
28use orb::server::{Server, ServerObject, ServerRequest, ServerReply};
29use orb::cdr::{CdrWrite, CdrRead, CdrReader, CdrWriter};
30use orb::giop::ReplyStatus;
31use zmq::{Consumer, Supplier};
32use tango::{
33    tango_repo_id, 
34    ClientIdent,
35    NamedError,
36    write_command_data, read_command_data,
37    read_attr, write_attr,
38    read_command_info, read_attr_info,
39    read_dev_info, write_dev_info,
40    write_command_info, write_attr_info,
41    read_command_history, write_command_history,
42    read_attr_history, write_attr_history,
43};
44
45
46/// Special name for reading all attributes.
47const ALL_ATTRIBUTES: &str = "All attributes";
48/// Special name for reading all attributes on IDL v3+.
49const ALL_ATTRIBUTES_3: &str = "All attributes_3";
50
51/// Static client identifier.
52static CLIENT_ID: ClientIdent = ClientIdent::Java { 
53    main_class: Cow::Borrowed("rstango"), 
54    uuid: Uuid::nil(),
55};
56
57// =========================== //
58//        Common backend       //
59// =========================== //
60
61/// A pure rust CORBA backend.
62#[derive(Debug)]
63pub struct CorbaBackend {
64    /// The ORB client.
65    client: OnceLock<Client>,
66    /// The event consumer, shared with every device connected through this backend, and
67    /// only started by the first subscription.
68    consumer: Arc<OnceLock<Consumer>>,
69}
70
71impl CorbaBackend {
72
73    pub fn new() -> Arc<Self> {
74        Arc::new(Self {
75            client: OnceLock::new(),
76            consumer: Arc::new(OnceLock::new()),
77        })
78    }
79
80    fn ensure_client(&self) -> Result<&Client> {
81
82        if let Some(client) = self.client.get() {
83            return Ok(client);
84        }
85
86        let client = Client::new()?;
87        Ok(self.client.get_or_init(|| client))
88
89    }
90
91    /// Find the most recent device IDL version the object implements and build the device
92    /// backend speaking it, failing with [`ErrorKind::DevNotConnected`] if it implements none.
93    async fn check_device(&self, object: ClientObject) -> Result<Arc<dyn BackendDev>> {
94
95        // Iterate over the versions to find the largest one supported. We iterate in
96        // reverse order because nowadays it's much more likely to encounter a recent
97        // version than an old one!
98        let mut versions_it = DevVersion::ALL.into_iter().rev();
99        let version = loop {
100
101            let Some(version) = versions_it.next() else {
102                return Err(Error::new(ErrorKind::DevNotConnected)
103                    .with_description(format!("Object \"{}\" implements no supported version of the device interface",
104                        object.object().escape_ascii())));
105            };
106
107            let reply = object.request(b"_is_a", &version.repo_id()).await.map_err(corba_err)?;
108            let (mut reader, exception_id) = reply.read().map_err(corba_err)?;
109
110            if let Some(exception_id) = exception_id.as_deref() {
111                return Err(unexpected_user_exception_err(exception_id));
112            }
113
114            if reader.read_bool()? {
115                break version;
116            }
117
118        };
119        
120        debug!(?version, "Connected to the device over CORBA");
121
122        Ok(Arc::new(CorbaDevBackend {
123            object,
124            version,
125            consumer: Arc::clone(&self.consumer),
126        }))
127
128    }
129
130}
131
132impl Backend for CorbaBackend {
133
134    fn connect(&self, itr: &str) -> BoxFuture<'_, Result<Arc<dyn BackendDev>>> {
135        if let Some(_corbaloc) = itr.strip_prefix("corbaloc:iiop:") {
136            Box::pin(ready(Err(Error::not_implemented()
137                .with_description("Scheme corbaloc:iiop: is not implemented"))))
138        } else if let Some(ior) = itr.strip_prefix("IOR:") {
139            let ior = ior.to_string();
140            Box::pin(async move {
141                let client = self.ensure_client()?;
142                let object = client.connect_object_ior(&ior, &DevVersion::REPO_ID).await.map_err(corba_err)?;
143                self.check_device(object).await
144            })
145        } else {
146            Box::pin(ready(Err(Error::new(ErrorKind::NotHandled))))
147        }
148    }
149
150    fn connect_at(&self, host: &str, port: u16, name: &str) -> BoxFuture<'_, Result<Arc<dyn BackendDev>>> {
151        let host = host.to_string();
152        let object: Arc<[u8]> = Arc::from(name.to_ascii_lowercase().into_bytes());
153        Box::pin(async move {
154            let client = self.ensure_client()?;
155            let object = client.connect_object_iiop(&host, port, None, None, object).await.map_err(corba_err)?;
156            self.check_device(object).await
157        })
158    }
159
160    fn serve<'a>(&'a self, config: &'a BackendServerConfig) -> BoxFuture<'a, Result<Arc<dyn BackendServer>>> {
161        Box::pin(async move {
162
163            // The port the server is bound to is what the references it hands out then
164            // point at, see [`CorbaServerBackend::build_ior`].
165            let host = config.host.as_deref().unwrap_or("0.0.0.0");
166            let ports = config.port.unwrap_or((0, 0));
167
168            let addrs = resolve(host).await?;
169
170            // A name is bound at the first of its addresses that can be, as the standard
171            // library does.
172            let mut last_err = None;
173            for addr in addrs {
174                match Server::bind(addr, ports) {
175                    Ok(server) => {
176
177                        debug!(addr = %server.local_addr(), addrs = ?server.addrs(), "Serving devices over CORBA");
178                        
179                        let supplier = config.admin_name.as_deref()
180                            .map(|admin_name| Supplier::new(
181                                admin_name,
182                                config.tango_host.as_ref().map(|(host, port)| (&**host, *port)),
183                                &server.host(),
184                                server.local_addr().port(),
185                                server.local_addr().ip(),
186                                config.zmq_heartbeat_port.unwrap_or((0, 0)),
187                                config.zmq_event_port.unwrap_or((0, 0)),
188                                server.addrs(),
189                            ));
190
191                        return Ok(Arc::new(CorbaServerBackend { server, supplier }) as Arc<dyn BackendServer>);
192
193                    }
194                    Err(err) => last_err = Some(err),
195                }
196            }
197
198            Err(last_err.unwrap_or_else(|| Error::new(ErrorKind::Corba)
199                .with_description(format!("Host {host:?} resolves to no address to bind on ports {}..={}", ports.0, ports.1))))
200
201        })
202    }
203
204}
205
206// =========================== //
207//        Client backend       //
208// =========================== //
209
210/// Our internal backend to device proxies.
211#[derive(Debug)]
212struct CorbaDevBackend {
213    object: ClientObject,
214    version: DevVersion,
215    /// The event consumer of the backend, see [`Self::consumer`].
216    consumer: Arc<OnceLock<Consumer>>,
217}
218
219impl CorbaDevBackend {
220
221    /// Send a request to this device, over the object's own connection.
222    ///
223    /// The request goes out as soon as this returns, and not once the future it gives
224    /// back is polled, see [`ClientObject::request`]: whatever was borrowed to build
225    /// `input` need not outlive this call, only the future it returns has to be awaited.
226    fn request(&self, operation: &[u8], input: &dyn CdrWrite) -> CorbaDevRequest {
227        CorbaDevRequest { inner: self.object.request(operation, input) }
228    }
229
230    /// The event consumer of the backend, started if this is the first subscription. Two
231    /// first subscriptions at once may both start one, the one that is not kept stopping
232    /// as soon as it is dropped.
233    fn consumer(&self) -> Result<&Consumer> {
234
235        if let Some(consumer) = self.consumer.get() {
236            return Ok(consumer);
237        }
238
239        let consumer = Consumer::start()?;
240        Ok(self.consumer.get_or_init(|| consumer))
241
242    }
243
244}
245
246impl BackendDev for CorbaDevBackend {
247
248    fn name(&self) -> BoxFuture<'_, Result<Arc<str>>> {
249        let request = self.request(b"_get_name", &());
250        Box::pin(async move {
251            let reply = request.await?;
252            Ok(Arc::from(reply.read::<String>()?))
253        })
254    }
255
256    fn admin_dev_name(&self) -> BoxFuture<'_, Result<Arc<str>>> {
257        let request = self.request(b"_get_adm_name", &());
258        Box::pin(async move {
259            let reply = request.await?;
260            Ok(Arc::from(reply.read::<String>()?))
261        })
262    }
263
264    fn description(&self) -> BoxFuture<'_, Result<Arc<str>>> {
265        let request = self.request(b"_get_description", &());
266        Box::pin(async move {
267            let reply = request.await?;
268            Ok(Arc::from(reply.read::<String>()?))
269        })
270    }
271
272    fn ping(&self) -> BoxFuture<'_, Result<()>> {
273        let request = self.request(b"ping", &());
274        Box::pin(async move {
275            let reply = request.await?;
276            reply.read::<()>()
277        })
278    }
279
280    fn info(&self) -> BoxFuture<'_, Result<DevInfo>> {
281
282        // IDL variants:
283        // DevInfo info()
284        // DevInfo_3 info_3()
285        // DevInfo_6 info_6()
286        //
287        // The second member is the version of the structure the info is read in, which is
288        // the one of the operation and not the one of the device: v2 answers with the v1
289        // structure, and v4 and v5 with the v3 one.
290        let (operation, info_version): (&[u8], DevVersion) = match self.version {
291            DevVersion::V1 |
292            DevVersion::V2 => (b"info", DevVersion::V1),
293            DevVersion::V3 |
294            DevVersion::V4 |
295            DevVersion::V5 => (b"info_3", DevVersion::V3),
296            DevVersion::V6 => (b"info_6", DevVersion::V6),
297        };
298
299        let request = self.request(operation, &());
300
301        Box::pin(async move {
302            let reply = request.await?;
303            let mut reader = reply.read_simple()?;
304            read_dev_info(&mut reader, info_version)
305        })
306
307    }
308
309    fn state(&self) -> BoxFuture<'_, Result<DevState>> {
310        let request = self.request(b"_get_state", &());
311        Box::pin(async move {
312            let reply = request.await?;
313            reply.read::<DevState>()
314        })
315    }
316
317    fn status(&self) -> BoxFuture<'_, Result<Scalar<str>>> {
318        let request = self.request(b"_get_status", &());
319        Box::pin(async move {
320            let reply = request.await?;
321            reply.read::<Scalar<str>>()
322        })
323    }
324
325    fn black_box(&self, max: Option<usize>) -> BoxFuture<'_, Result<Spectrum<str>>> {
326        let max = max.and_then(|n| i32::try_from(n).ok()).unwrap_or(i32::MAX);
327        let request = self.request(b"black_box", &max);
328        Box::pin(async move {
329            let reply = request.await?;
330            reply.read::<Spectrum<str>>()
331        })
332    }
333
334    fn command(&self, name: Scalar<str>, input: Value, source: DevSource) -> BoxFuture<'_, Result<Value>> {
335
336        // IDL variants:
337        // any command_inout(in string command, in any argin)
338        // any command_inout_2(in string command, in any argin, in DevSource source)
339        // any command_inout_4(in string command, in any argin, in DevSource source, in ClntIdent cl_ident)
340
341        struct Input<'a> {
342            version: DevVersion,
343            name: &'a str,
344            input: Value,
345            source: DevSource,
346        }
347
348        impl CdrWrite for Input<'_> {
349            fn write(&self, buf: &mut CdrWriter) -> Result<()> {
350                buf.write_string(self.name)?;
351                write_command_data(buf, &self.input)?;
352                if self.version >= DevVersion::V2 {
353                    buf.write(&self.source)?;
354                }
355                if self.version >= DevVersion::V4 {
356                    buf.write(&CLIENT_ID)?;
357                }
358                Ok(())
359            }
360        }
361
362        let operation: &[u8] = match self.version {
363            DevVersion::V1 => b"command_inout",
364            DevVersion::V2 |
365            DevVersion::V3 => b"command_inout_2",
366            DevVersion::V4 |
367            DevVersion::V5 |
368            DevVersion::V6 => b"command_inout_4",
369        };
370
371        let request = self.request(operation, &Input { version: self.version, name: name.value(), input, source });
372
373        Box::pin(async move {
374            let reply = request.await?;
375            read_command_data(&mut reply.read_simple()?)
376        })
377
378    }
379
380    fn read_attr(&self, names: Option<Spectrum<str>>, source: DevSource) -> BoxFuture<'_, Result<Vec<NamedResult<AttrRead<Value>>>>> {
381
382        // IDL variants:
383        // AttributeValueList read_attributes(in DevVarStringArray names)
384        // AttributeValueList read_attributes_2(in DevVarStringArray names, in DevSource source)
385        // AttributeValueList_3 read_attributes_3(in DevVarStringArray names, in DevSource source)
386        // AttributeValueList_4 read_attributes_4(in DevVarStringArray names, in DevSource source, in ClntIdent cl_ident)
387        // AttributeValueList_5 read_attributes_5(in DevVarStringArray names, in DevSource source, in ClntIdent cl_ident)
388
389        struct Input<'a> {
390            version: DevVersion,
391            names: &'a dyn CdrWrite,
392            source: DevSource,
393        }
394
395        impl CdrWrite for Input<'_> {
396            fn write(&self, buf: &mut CdrWriter) -> Result<()> {
397                buf.write(&self.names)?;
398                if self.version >= DevVersion::V2 {
399                    buf.write(&self.source)?;
400                }
401                if self.version >= DevVersion::V4 {
402                    buf.write(&CLIENT_ID)?;
403                }
404                Ok(())
405            }
406        }
407        
408        if let Some(names) = &names
409        && let Err(err) = check_no_all_marker(names) {
410            return Box::pin(ready(Err(err)));
411        }
412
413        let operation: &[u8] = match self.version {
414            DevVersion::V1 => b"read_attributes",
415            DevVersion::V2 => b"read_attributes_2",
416            DevVersion::V3 => b"read_attributes_3",
417            DevVersion::V4 => b"read_attributes_4",
418            DevVersion::V5 |
419            DevVersion::V6 => b"read_attributes_5",
420        };
421
422        let version = self.version;
423        let names = match &names {
424            Some(names) => names as &dyn CdrWrite,
425            None => &[ALL_ATTRIBUTES],
426        };
427
428        let request = self.request(operation, &Input { version, names, source });
429
430        Box::pin(async move {
431            let reply = request.await?;
432            read_attr_list(&mut reply.read_simple()?, version)
433        })
434
435    }
436
437    fn write_attr(&self, values: Vec<Named<Value>>) -> BoxFuture<'_, Result<Vec<Result<()>>>> {
438
439        // IDL variants:
440        // void write_attributes(in AttributeValueList values)
441        // void write_attributes_3(in AttributeValueList values)
442        // void write_attributes_4(in AttributeValueList_4 values, in ClntIdent cl_ident)
443        //
444        // Note that v2 has no variant of its own and reuses the v1 one, and that v3 only
445        // adds MultiDevFailed to the raised exceptions, the argument staying the same.
446
447        struct Input<'a> {
448            /// The version of the structure the values go out in, which is the one of the
449            /// operation and *not* the one of the device, see below.
450            version: DevVersion,
451            values: &'a [Named<Value>],
452        }
453
454        impl CdrWrite for Input<'_> {
455            fn write(&self, buf: &mut CdrWriter) -> Result<()> {
456                buf.write_sequence_len(self.values.len())?;
457                for Named { name, value } in self.values {
458                    write_attr(buf, self.version, name.value(), &Ok(AttrRead {
459                        value: None,
460                        w_value: Some(value.clone()),
461                        quality: AttrQuality::Valid,
462                        time: SystemTime::UNIX_EPOCH,
463                    }))?;
464                }
465                if self.version >= DevVersion::V4 {
466                    buf.write(&CLIENT_ID)?;
467                }
468                Ok(())
469            }
470        }
471
472        // The second member is the version of the structure the values are written in,
473        // which is the one of the operation and not the one of the device: v3 takes the v1
474        // structure like v1 and v2 do, and v5 and v6 have no write operation of their own
475        // so they take the v4 one. Sending the structure of the device version instead
476        // makes the server fail to unmarshal the request entirely.
477        let (operation, input_version): (&[u8], DevVersion) = match self.version {
478            DevVersion::V1 |
479            DevVersion::V2 => (b"write_attributes", DevVersion::V1),
480            DevVersion::V3 => (b"write_attributes_3", DevVersion::V1),
481            DevVersion::V4 |
482            DevVersion::V5 |
483            DevVersion::V6 => (b"write_attributes_4", DevVersion::V4),
484        };
485
486        let request = self.request(operation, &Input { version: input_version, values: &values });
487
488        let count = values.len();
489
490        Box::pin(async move {
491
492            let reply = request.await?;
493            let (mut reader, exception_id) = reply.read_raw()?;
494
495            let Some(exception_id) = exception_id else {
496                return Ok((0..count).map(|_| Ok(())).collect());
497            };
498
499            if exception_id.as_ref() != tango_repo_id!("MultiDevFailed") {
500                return Err(unexpected_user_exception_err(&exception_id));
501            }
502
503            // A `MultiDevFailed` only names the attributes that failed to write, the
504            // ones behind them in the list having gone through: unlike a `DevFailed`,
505            // this is per-value and not the request as a whole.
506            let mut results: Vec<Result<()>> = (0..count).map(|_| Ok(())).collect();
507            for failure in reader.read::<Vec<NamedError>>()? {
508                if let Some(result) = results.get_mut(failure.index) {
509                    *result = Err(failure.err.unwrap_or_else(|| Error::new(ErrorKind::Internal)
510                        .with_description("The device reported a failed attribute write holding no error")));
511                }
512            }
513
514            Ok(results)
515
516        })
517
518    }
519
520    fn write_read_attr(&self, values: Vec<Named<Value>>) -> BoxFuture<'_, Result<Vec<NamedResult<AttrRead<Value>>>>> {
521
522        // IDL variants:
523        // AttributeValueList_4 write_read_attributes_4(in AttributeValueList_4 values, in ClntIdent cl_ident)
524        // AttributeValueList_5 write_read_attributes_5(in AttributeValueList_4 values, in DevVarStringArray r_names, in ClntIdent cl_ident)
525        //
526        // Note that this operation only exists from v4 onward, and that v5 is the first
527        // one able to read back attributes other than the written ones. Both variants take
528        // the v4 structure for what they write, only what they answer with differs.
529
530        struct Input<'a> {
531            version: DevVersion,
532            values: &'a [Named<Value>],
533        }
534
535        impl CdrWrite for Input<'_> {
536            fn write(&self, buf: &mut CdrWriter) -> Result<()> {
537
538                // Hardcoded v4 and not the device's version: both operations take the very
539                // same AttributeValueList_4, v5 having never added a list of its own for
540                // what it writes, only for what it answers with.
541                buf.write_sequence_len(self.values.len())?;
542                for Named { name, value } in self.values {
543                    write_attr(buf, DevVersion::V4, name.value(), &Ok(AttrRead {
544                        value: None,
545                        w_value: Some(value.clone()),
546                        quality: AttrQuality::Valid,
547                        time: SystemTime::UNIX_EPOCH,
548                    }))?;
549                }
550
551                // Only v5 onward has this second list, of the attributes to read back, and
552                // we have no way of telling which ones the caller wants apart from the
553                // written ones, so it reads those back.
554                if self.version >= DevVersion::V5 {
555                    buf.write_sequence_len(self.values.len())?;
556                    for write in self.values {
557                        buf.write_string(write.name.value())?;
558                    }
559                }
560
561                buf.write(&CLIENT_ID)?;
562                Ok(())
563
564            }
565        }
566
567        // The second member is the version the *reply* is read with, which is the one of
568        // the operation and not the one of the device: v4 answers with its own list, v5
569        // with the one it added, and v6 reuses the v5 operation and therefore its list.
570        // So a v6 device is read back as a v5 one here, unlike everywhere else.
571        let (operation, reply_version): (&[u8], DevVersion) = match self.version {
572            DevVersion::V1 |
573            DevVersion::V2 |
574            DevVersion::V3 => {
575                return Box::pin(ready(Err(Error::not_implemented()
576                    .with_description("Writing and reading attributes back in a single request needs a device of IDL v4 or later")
577                    .with_append_description(format_args!("Got: {:?}", self.version)))));
578            }
579            DevVersion::V4 => (b"write_read_attributes_4", DevVersion::V4),
580            DevVersion::V5 |
581            DevVersion::V6 => (b"write_read_attributes_5", DevVersion::V5),
582        };
583
584        let request = self.request(operation, &Input { version: self.version, values: &values });
585
586        // Only needed if the write raises `MultiDevFailed`, to name the failure of every
587        // value nothing more specific was reported for.
588        let names = values.into_iter().map(|write| write.name).collect::<Vec<_>>();
589
590        Box::pin(async move {
591
592            let reply = request.await?;
593            let (mut reader, exception_id) = reply.read_raw()?;
594
595            let Some(exception_id) = exception_id else {
596                return read_attr_list(&mut reader, reply_version);
597            };
598
599            if exception_id.as_ref() != tango_repo_id!("MultiDevFailed") {
600                return Err(unexpected_user_exception_err(&exception_id));
601            }
602
603            // A `MultiDevFailed` here means none of the values was read back at all, so
604            // every one of them is reported as a failure, the ones it does not name
605            // getting a generic error of their own.
606            let mut failures = reader.read::<Vec<NamedError>>()?;
607            let entries = names.into_iter().enumerate().map(|(index, name)| {
608                let err = failures.iter_mut()
609                    .find(|failure| failure.index == index)
610                    .and_then(|failure| failure.err.take())
611                    .unwrap_or_else(|| Error::new(ErrorKind::Internal)
612                        .with_description("The device wrote this attribute but read none of them back, another one having failed"));
613                Named::new(name, Err(err))
614            }).collect();
615
616            Ok(entries)
617
618        })
619
620    }
621
622    fn command_info(&self, names: Option<Spectrum<str>>) -> BoxFuture<'_, Result<Vec<NamedResult<CommandInfo>>>> {
623
624        // IDL variants:
625        // DevCmdInfoList command_list_query()
626        // DevCmdInfo command_query(in string command)
627        // DevCmdInfoList_2 command_list_query_2()
628        // DevCmdInfo_2 command_query_2(in string command)
629        //
630        // Note that these take no name list, so querying a subset means either one
631        // request per name or filtering the whole list afterward. We do the former, which
632        // is what cppTango does too. The v2 variants only add the display level to the
633        // returned structures, and no version after them has one of its own.
634
635        // The second member is the version of the structure the infos are read in, which
636        // is the one of the operation and not the one of the device, every version from
637        // v2 onward answering with the v2 structure.
638        let (query_operation, list_operation, info_version): (&[u8], &[u8], DevVersion) = match self.version {
639            DevVersion::V1 => (b"command_query", b"command_list_query", DevVersion::V1),
640            _ => (b"command_query_2", b"command_list_query_2", DevVersion::V2),
641        };
642
643        if let Some(names) = names {
644
645            // One request per name, sent right away and all in flight together, each
646            // answering with the info of that very command, so there is no order to
647            // check here, see [`ClientObject::request`].
648            let requests = (0..names.len())
649                .map(|index| names.get_scalar([index]).expect("a name at every index"))
650                .map(|name| {
651                    let request = self.request(query_operation, &name.value());
652                    (name, request)
653                })
654                .collect::<Vec<_>>();
655
656            Box::pin(async move {
657
658                let mut entries = Vec::with_capacity(requests.len());
659
660                for (name, request) in requests {
661
662                    let res: Result<CommandInfo> = async {
663                        let reply = request.await?;
664                        let mut reader = reply.read_simple()?;
665                        read_command_info(&mut reader, info_version)?.1
666                    }.await;
667
668                    // The request of a single command failing, an unknown command name
669                    // being the usual reason, only fails that one command: it is
670                    // reported for it and the following ones are still awaited, so
671                    // that the caller gets exactly one answer for each name it gave.
672                    // Only a failure that makes the rest pointless aborts the batch.
673                    match res {
674                        Err(err) if matches!(err.kind(), ErrorKind::TimedOut | ErrorKind::DevNotConnected) => {
675                            return Err(err);
676                        }
677                        res => entries.push(Named::new(name, res)),
678                    }
679
680                }
681
682                Ok(entries)
683
684            })
685
686        } else {
687
688            // List everything!
689            let request = self.request(list_operation, &());
690
691            Box::pin(async move {
692                let reply = request.await?;
693                let mut reader = reply.read_simple()?;
694                let len = reader.read_sequence_len()?;
695                let mut entries = Vec::with_capacity(len);
696                for _ in 0..len {
697                    let (name, info) = read_command_info(&mut reader, info_version)?;
698                    entries.push(Named::new(Scalar::<str>::from(&*name), info));
699                }
700                Ok(entries)
701            })
702
703        }
704
705    }
706
707    fn set_attr_config(&self, configs: Vec<Named<AttrConfig>>) -> BoxFuture<'_, Result<Vec<Result<()>>>> {
708
709        // IDL variants:
710        // void set_attribute_config(in AttributeConfigList new_conf)
711        // void set_attribute_config_3(in AttributeConfigList_3 new_conf)
712        // void set_attribute_config_4(in AttributeConfigList_3 new_conf, in ClntIdent cl_ident)
713        // void set_attribute_config_5(in AttributeConfigList_5 new_conf, in ClntIdent cl_ident)
714        //
715        // Note that v2 has no variant of its own and reuses the v1 one, as v4 does with the
716        // v3 structure. The structure sent is the whole one a configuration is read in,
717        // what the attribute is included: its type above all, which is what its limits are
718        // written as. That part is never changed by the device, so it is taken from the
719        // attribute itself, asked for first, which is what a client of cppTango does as
720        // well, reading a configuration before sending it back changed.
721
722        struct Input<'a> {
723            /// The version of the structure the configurations go out in.
724            version: DevVersion,
725            infos: &'a [(Scalar<str>, AttrInfo)],
726            /// Whether the operation takes the identification of the client after them.
727            with_client: bool,
728        }
729
730        impl CdrWrite for Input<'_> {
731            fn write(&self, buf: &mut CdrWriter) -> Result<()> {
732                buf.write_sequence_len(self.infos.len())?;
733                for (name, info) in self.infos {
734                    write_attr_info(buf, self.version, name.value(), info)?;
735                }
736                if self.with_client {
737                    buf.write(&CLIENT_ID)?;
738                }
739                Ok(())
740            }
741        }
742
743        // The second member is the version of the structure the configurations are
744        // written in, which is the one of the operation and not the one of the device.
745        let (operation, config_version, with_client): (&'static [u8], DevVersion, bool) = match self.version {
746            DevVersion::V1 |
747            DevVersion::V2 => (b"set_attribute_config", DevVersion::V1, false),
748            DevVersion::V3 => (b"set_attribute_config_3", DevVersion::V3, false),
749            DevVersion::V4 => (b"set_attribute_config_4", DevVersion::V3, true),
750            DevVersion::V5 |
751            DevVersion::V6 => (b"set_attribute_config_5", DevVersion::V5, true),
752        };
753
754        let names = configs.iter().map(|config| config.name.value()).collect::<Vec<_>>();
755        let current = self.attr_info(Some(Spectrum::<str>::from(&names[..])));
756        let count = configs.len();
757
758        Box::pin(async move {
759
760            // An attribute that cannot be described fails the whole call, which is what
761            // the device would do of it anyway, a single DevFailed being all it raises.
762            let mut infos = Vec::with_capacity(count);
763            for (Named { name, value: config }, current) in configs.into_iter().zip(current.await?) {
764                let mut info = current.value?;
765                info.config = config;
766                infos.push((name, info));
767            }
768
769            // The device raises a single DevFailed for the first attribute it failed to
770            // change, the ones before having been changed and the ones behind not even
771            // tried, which it does not tell apart: only a single attribute is known to be
772            // the one it is about, and the call fails as a whole otherwise.
773            let reply = self.request(operation, &Input { version: config_version, infos: &infos, with_client }).await?;
774            let (mut reader, exception_id) = reply.inner.read().map_err(corba_err)?;
775            match exception_id.as_deref() {
776                None => Ok((0..count).map(|_| Ok(())).collect()),
777                Some(tango_repo_id!("DevFailed")) => {
778                    let err = reader.read::<Error>()?;
779                    match count {
780                        1 => Ok(vec![Err(err)]),
781                        _ => Err(err),
782                    }
783                }
784                Some(exception_id) => Err(unexpected_user_exception_err(exception_id)),
785            }
786
787        })
788
789    }
790
791    fn attr_info(&self, names: Option<Spectrum<str>>) -> BoxFuture<'_, Result<Vec<NamedResult<AttrInfo>>>> {
792
793        // IDL variants:
794        // AttributeConfigList get_attribute_config(in DevVarStringArray names)
795        // AttributeConfigList_2 get_attribute_config_2(in DevVarStringArray names)
796        // AttributeConfigList_3 get_attribute_config_3(in DevVarStringArray names)
797        // AttributeConfigList_5 get_attribute_config_5(in DevVarStringArray names)
798        //
799        // Note that v4 has no variant of its own and reuses the v3 one, and that the
800        // 'all attributes' marker to pass for reading them all is version-dependent
801        // here, ALL_ATTRIBUTES_3 from v3 onward and ALL_ATTRIBUTES before.
802
803        // The second member is the version of the structure the configurations are read
804        // in, which is the one of the operation and not the one of the device, a v4 device
805        // answering with the v3 structure and a v6 one with the v5 structure.
806        let (operation, config_version): (&[u8], DevVersion) = match self.version {
807            DevVersion::V1 => (b"get_attribute_config", DevVersion::V1),
808            DevVersion::V2 => (b"get_attribute_config_2", DevVersion::V2),
809            DevVersion::V3 |
810            DevVersion::V4 => (b"get_attribute_config_3", DevVersion::V3),
811            DevVersion::V5 |
812            DevVersion::V6 => (b"get_attribute_config_5", DevVersion::V5),
813        };
814
815        if let Some(names) = &names
816        && let Err(err) = check_no_all_marker(names) {
817            return Box::pin(ready(Err(err)));
818        }
819
820        // Unlike the commands, a single request carries every name, so an unknown one
821        // fails the whole request, which is the device raising and not something we could
822        // report for that one attribute only.
823        let request = match &names {
824            Some(names) => self.request(operation, names),
825            // The marker itself is what changed in v3 and not the operation taking it, so
826            // it follows the version of the structure like the operation does.
827            None if config_version >= DevVersion::V3 => self.request(operation, &[ALL_ATTRIBUTES_3]),
828            None => self.request(operation, &[ALL_ATTRIBUTES]),
829        };
830
831        // Kept so that the order can still be checked once the reply comes back.
832        let expected = names;
833
834        Box::pin(async move {
835
836            let reply = request.await?;
837            let mut reader = reply.read_simple()?;
838            let len = reader.read_sequence_len()?;
839            let mut entries = Vec::with_capacity(len);
840
841            for index in 0..len {
842
843                let (name, info) = read_attr_info(&mut reader, config_version)?;
844
845                // When the attributes have been named, the device answers with them in the
846                // order they were asked for, which the caller is allowed to rely on to tell
847                // one answer from the next, so it is checked here.
848                if let Some(expected) = expected.as_ref().and_then(|names| names.get([index]))
849                && !name.eq_ignore_ascii_case(expected) {
850                    return Err(Error::new(ErrorKind::InvalidData)
851                        .with_description(format!("Attribute {expected:?} is answered with the configuration of another attribute"))
852                        .with_append_description(format_args!("Got: {name:?}")));
853                }
854
855                entries.push(Named::new(Scalar::<str>::from(&*name), info));
856
857            }
858
859            Ok(entries)
860
861        })
862
863    }
864
865    fn subscribe_attr_value(&self, admin: Arc<dyn BackendDev>, name: Scalar<str>, event: AttrValueEvent) -> BoxFuture<'_, Result<BoxStream<'static, Result<AttrRead<Value>>>>> {
866        match self.consumer() {
867            Ok(consumer) => zmq::subscribe_attr_value(consumer, self.name(), admin, name, event),
868            Err(err) => Box::pin(ready(Err(err))),
869        }
870    }
871
872    fn subscribe_attr_ready(&self, admin: Arc<dyn BackendDev>, name: Scalar<str>) -> BoxFuture<'_, Result<BoxStream<'static, Result<AttrReady>>>> {
873        match self.consumer() {
874            Ok(consumer) => zmq::subscribe_attr_ready(consumer, self.name(), admin, name),
875            Err(err) => Box::pin(ready(Err(err))),
876        }
877    }
878
879    fn subscribe_attr_config(&self, admin: Arc<dyn BackendDev>, name: Scalar<str>) -> BoxFuture<'_, Result<BoxStream<'static, Result<AttrInfo>>>> {
880        match self.consumer() {
881            Ok(consumer) => zmq::subscribe_attr_config(consumer, self.name(), admin, name),
882            Err(err) => Box::pin(ready(Err(err))),
883        }
884    }
885
886    fn command_history(&self, name: Scalar<str>, max: usize) -> BoxFuture<'_, Result<Vec<RecordedResult<Value>>>> {
887
888        // IDL variants:
889        // DevCmdHistoryList command_inout_history_2(in string command, in long n)
890        // DevCmdHistory_4 command_inout_history_4(in string command, in long n)
891        //
892        // Note that v3 has no variant of its own and reuses the v2 one, and that v1 has no
893        // history at all, the polling having come with v2.
894
895        let operation: &[u8] = match self.version {
896            DevVersion::V1 => return Box::pin(ready(Err(no_history_err(self.version)))),
897            DevVersion::V2 |
898            DevVersion::V3 => b"command_inout_history_2",
899            DevVersion::V4 |
900            DevVersion::V5 |
901            DevVersion::V6 => b"command_inout_history_4",
902        };
903
904        let version = self.version;
905        let request = self.request(operation, &HistoryInput { name: name.value(), max });
906
907        Box::pin(async move {
908            let reply = request.await?;
909            read_command_history(&mut reply.read_simple()?, version)
910        })
911
912    }
913
914    fn attr_history(&self, name: Scalar<str>, max: usize) -> BoxFuture<'_, Result<Vec<RecordedResult<AttrRead<Value>>>>> {
915
916        // IDL variants:
917        // DevAttrHistoryList read_attribute_history_2(in string name, in long n)
918        // DevAttrHistoryList_3 read_attribute_history_3(in string name, in long n)
919        // DevAttrHistory_4 read_attribute_history_4(in string name, in long n)
920        // DevAttrHistory_5 read_attribute_history_5(in string name, in long n)
921
922        let operation: &[u8] = match self.version {
923            DevVersion::V1 => return Box::pin(ready(Err(no_history_err(self.version)))),
924            DevVersion::V2 => b"read_attribute_history_2",
925            DevVersion::V3 => b"read_attribute_history_3",
926            DevVersion::V4 => b"read_attribute_history_4",
927            DevVersion::V5 |
928            DevVersion::V6 => b"read_attribute_history_5",
929        };
930
931        let version = self.version;
932        let request = self.request(operation, &HistoryInput { name: name.value(), max });
933
934        Box::pin(async move {
935            let reply = request.await?;
936            read_attr_history(&mut reply.read_simple()?, version)
937        })
938
939    }
940
941}
942
943/// The arguments every history operation takes: the name of the command or the attribute,
944/// and how many records at most, which the IDL declares as a long.
945struct HistoryInput<'a> {
946    name: &'a str,
947    max: usize,
948}
949
950impl CdrWrite for HistoryInput<'_> {
951    fn write(&self, buf: &mut CdrWriter) -> Result<()> {
952        buf.write_string(self.name)?;
953        buf.write_long(i32::try_from(self.max).unwrap_or(i32::MAX))
954    }
955}
956
957/// The error asking a device of that version for a history fails with, which only exists
958/// from v2 on.
959fn no_history_err(version: DevVersion) -> Error {
960    Error::new(ErrorKind::Reason(reason::API_NotSupported))
961        .with_description(format!("A device of IDL {version:?} has no history to give"))
962}
963
964/// A request to a device, resolving to its reply.
965struct CorbaDevRequest {
966    inner: ClientRequest,
967}
968
969impl Future for CorbaDevRequest {
970    type Output = Result<CorbaDevReply>;
971    fn poll(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> task::Poll<Self::Output> {
972        Pin::new(&mut self.get_mut().inner).poll(cx)
973            .map(|inner| inner
974                .map(|inner| CorbaDevReply { inner })
975                .map_err(corba_err))
976    }
977}
978
979/// The failure of a CORBA call to a device, which is taken as the device not being
980/// reachable, or no longer: whatever it is, the connection to it is not one to go on with.
981/// The CORBA failure becomes the parent of an [`ErrorKind::DevNotConnected`], which is what
982/// tells a caller to find the device again, see [`crate::dev::DevProxy`]. What the device
983/// itself answered with, a `DevFailed` above all, is left as it is.
984fn corba_err(err: Error) -> Error {
985    if err.kind() != ErrorKind::Corba {
986        return err;
987    }
988    Error::new(ErrorKind::DevNotConnected).with_parent(err)
989}
990
991/// The reply of a device to a request.
992#[derive(Debug)]
993struct CorbaDevReply {
994    inner: ClientReply,
995}
996
997impl CorbaDevReply {
998
999    /// Read this reply, decoding the `DevFailed` every operation of the IDL may raise on
1000    /// its own into an [`Err`] of its own: nothing that reads a reply afterward has to
1001    /// deal with that one. Whichever other user exception turned up, if any, is left for
1002    /// the caller to make sense of, see [`Self::read_simple`] for operations that raise
1003    /// nothing else.
1004    fn read_raw(&self) -> Result<(CdrReader<'_>, Option<Cow<'_, str>>)> {
1005        let (mut reader, exception_id) = self.inner.read().map_err(corba_err)?;
1006        if let Some(tango_repo_id!("DevFailed")) = exception_id.as_deref() {
1007            Err(match reader.read::<Error>() {
1008                Ok(err) => err,
1009                Err(err) => err,
1010            })
1011        } else {
1012            Ok((reader, exception_id))
1013        }
1014    }
1015
1016    /// Same as [`Self::read`], for an operation that raises nothing but `DevFailed`:
1017    /// whichever other user exception turned up is a failure of its own, nothing here
1018    /// ever expecting one.
1019    fn read_simple(&self) -> Result<CdrReader<'_>> {
1020        let (reader, exception_id) = self.read_raw()?;
1021        if let Some(exception_id) = exception_id.as_deref() {
1022            Err(unexpected_user_exception_err(exception_id))
1023        } else {
1024            Ok(reader)
1025        }
1026    }
1027
1028    /// Same as [`Self::read_simple`], decoding the answer straight out of the reader.
1029    fn read<'a, O: CdrRead<'a>>(&'a self) -> Result<O> {
1030        self.read_simple()?.read()
1031    }
1032
1033}
1034
1035/// Read a list of attribute readings, one entry per attribute, in the structure of that
1036/// version.
1037fn read_attr_list(reader: &mut CdrReader<'_>, version: DevVersion) -> Result<Vec<NamedResult<AttrRead<Value>>>> {
1038    let len = reader.read_sequence_len()?;
1039    let mut entries = Vec::with_capacity(len);
1040    for _ in 0..len {
1041        entries.push(read_attr(reader, version)?);
1042    }
1043    Ok(entries)
1044}
1045
1046// =========================== //
1047//       Server backend        //
1048// =========================== //
1049
1050/// The IDL version the objects served by this backend speak, which is what a client is
1051/// told they are and therefore which operations it sends them.
1052///
1053/// Six, the latest, whose only operation of its own, `info_6`, is answered: a client of an
1054/// older release still reaches the objects, which announce every earlier interface as well
1055/// and answer `_is_a` for each of them, see [`SERVED_REPO_IDS`].
1056const SERVED_VERSION: DevVersion = DevVersion::V6;
1057
1058/// The Tango library release this backend tells its clients it is, in the encoding cppTango
1059/// uses, `major * 100 + minor * 10 + patch`: the first cppTango release whose devices are
1060/// of IDL v6, as the ones it serves are, see [`SERVED_VERSION`].
1061const LIB_VERSION: i32 = 1000;
1062
1063/// The interfaces a served device announces, most derived first, which is every version of
1064/// the Tango IDL up to the one this backend speaks, each of them deriving from the previous
1065/// one, see [`ServerObject::repo_ids`].
1066const SERVED_REPO_IDS: [&str; SERVED_VERSION.number() as usize] = {
1067    let mut ids = [""; SERVED_VERSION.number() as usize];
1068    let mut index = 0;
1069    while index < ids.len() {
1070        ids[index] = DevVersion::REPO_ID[SERVED_VERSION as usize - index];
1071        index += 1;
1072    }
1073    ids
1074};
1075
1076/// Internal structure implementing [`BackendServer`]
1077#[derive(Debug)]
1078struct CorbaServerBackend {
1079    /// The CORBA server itself, which is where the devices are served from and what the
1080    /// references handed out for them are built by. It serves from the moment it is bound
1081    /// and needs nothing run for it to, see [`Server::bind`].
1082    server: Server<CorbaServerObject>,
1083    /// What the events of the devices are published with, none when the server was not
1084    /// given the name of its admin device, which names them, see
1085    /// [`BackendServerConfig::admin_name`].
1086    supplier: Option<Supplier>,
1087}
1088
1089impl CorbaServerBackend {
1090
1091    /// The supplier of the events, failing if there is none, see the `supplier` field.
1092    fn supplier(&self) -> Result<&Supplier> {
1093        self.supplier.as_ref().ok_or_else(|| Error::not_implemented()
1094            .with_description("This server publishes no event over ZMQ"))
1095    }
1096
1097    /// The device of that name, which must be lowercased, if it is served here, see
1098    /// [`CorbaServerObject::name`].
1099    fn served(&self, name: &str) -> Option<Arc<dyn BackendDev>> {
1100        self.server.get(name.as_bytes()).map(|object| Arc::clone(&object.dev))
1101    }
1102
1103    /// Whether a device of that name, which must be lowercased, is served here, see
1104    /// [`Self::served`].
1105    fn is_served(&self, name: &str) -> bool {
1106        self.server.is_registered(name.as_bytes())
1107    }
1108
1109}
1110
1111impl BackendServer for CorbaServerBackend {
1112
1113    fn add(&self, name: &str, dev: Arc<dyn BackendDev>) -> Result<Arc<str>> {
1114
1115        // Tango addresses a device by its own name, lowercased, which is both what the
1116        // reference the server gives back carries and what makes a plain
1117        // 'corbaloc:iiop:host:port/dev/name' reach it. Registering it is all there is to
1118        // it, the server answering from the moment it is registered.
1119        let name = Arc::<str>::from(name.to_ascii_lowercase());
1120        let itr = self.server.register(Arc::new(CorbaServerObject {
1121            name: Arc::clone(&name),
1122            dev,
1123        })).map(Arc::<str>::from);
1124
1125        if itr.is_ok() {
1126            debug!(dev = %name, "Serving the device over CORBA");
1127        }
1128
1129        itr
1130
1131    }
1132
1133    fn remove(&self, name: &str) -> bool {
1134        let id = name.to_ascii_lowercase().into_bytes();
1135        self.server.deregister(&id)
1136    }
1137
1138    fn host(&self) -> Arc<str> {
1139        self.server.host()
1140    }
1141
1142    fn port(&self) -> u16 {
1143        self.server.local_addr().port()
1144    }
1145
1146    fn version(&self) -> i32 {
1147        SERVED_VERSION.number() as i32
1148    }
1149
1150    fn stop(&self) {
1151        if let Some(supplier) = &self.supplier {
1152            supplier.stop();
1153        }
1154        self.server.stop();
1155    }
1156
1157    fn wait(&self) -> BoxFuture<'_, ()> {
1158        Box::pin(self.server.wait())
1159    }
1160
1161    fn zmq_event_subscription_change(&self, args: Spectrum<str>) -> BoxFuture<'_, Result<(Spectrum<i32>, Spectrum<str>)>> {
1162        Box::pin(async move {
1163            let supplier = self.supplier()?;
1164            supplier.subscription_change(&args, |name| self.served(name), SERVED_VERSION.number()).await
1165        })
1166    }
1167
1168    fn event_confirm_subscription(&self, args: Spectrum<str>) -> BoxFuture<'_, Result<()>> {
1169        Box::pin(async move {
1170            let supplier = self.supplier()?;
1171            supplier.confirm_subscription(&args, |name| self.is_served(name)).await
1172        })
1173    }
1174
1175}
1176
1177/// One served device, seen as the CORBA object a client sends its requests to: this is the
1178/// mirror of [`CorbaDevBackend`], turning the operations of the Tango IDL into calls of the
1179/// device backend instead of the other way around.
1180#[derive(Debug)]
1181struct CorbaServerObject {
1182    /// The name of the device, lowercased, which is also its object key, see
1183    /// [`ServerObject::id`].
1184    name: Arc<str>,
1185    /// The device itself, whatever it is: this backend only knows it through the trait.
1186    dev: Arc<dyn BackendDev>,
1187}
1188
1189impl ServerObject for CorbaServerObject {
1190
1191    type RequestFuture = BoxFuture<'static, Result<ServerReply>>;
1192
1193    fn id(&self) -> &[u8] {
1194        self.name.as_bytes()
1195    }
1196
1197    fn repo_ids(&self) -> &[&str] {
1198        &SERVED_REPO_IDS
1199    }
1200
1201    fn request(self: Arc<Self>, request: ServerRequest) -> Self::RequestFuture {
1202        // Which operation this is for rides in the request and is read back out of the
1203        // octets it arrived as, so nothing of it is copied here to outlive the call.
1204        Box::pin(async move { self.handle_request(&request).await })
1205    }
1206
1207}
1208
1209/// What an operation came to, turned into what goes back to the client.
1210///
1211/// Whatever it failed with is answered as the `DevFailed` every operation of the IDL
1212/// raises, whatever its kind: a device fails with any kind it likes, and what it says is
1213/// what a Tango client reads. Only a reply that could not be written at all is given back
1214/// to the backend, which turns it into the CORBA system exception that says what it is.
1215fn answer(request: &ServerRequest, res: Result<ServerReply>) -> Result<ServerReply> {
1216
1217    let err = match res {
1218        Ok(reply) => return Ok(reply),
1219        Err(err) => err,
1220    };
1221
1222    let mut reply = request.reply_user_exception(tango_repo_id!("DevFailed"))?;
1223    reply.write(&err)?;
1224
1225    Ok(reply)
1226
1227}
1228
1229impl CorbaServerObject {
1230
1231    /// Answer one request of this object, which is what the backend drives, see
1232    /// [`ServerObject::request`]. A failure is answered as a `DevFailed`, see [`answer`].
1233    async fn handle_request(&self, request: &ServerRequest) -> Result<ServerReply> {
1234
1235        let operation = request.operation();
1236        let res = self.dispatch(operation, request).await;
1237
1238        if let Err(err) = &res {
1239            trace!(dev = %self.name, operation = %String::from_utf8_lossy(operation), %err, "Failed to run an operation");
1240        }
1241
1242        answer(request, res)
1243
1244    }
1245
1246    /// Run one operation of the Tango IDL, reading its arguments out of the octets of the
1247    /// request and writing what it answers straight into the ones of the reply.
1248    ///
1249    /// Failing with [`ErrorKind::NotHandled`] is an operation this object does not have,
1250    /// which the client is told about like any other failure, see [`answer`].
1251    async fn dispatch(&self, operation: &[u8], request: &ServerRequest) -> Result<ServerReply> {
1252
1253        let mut input = request.read();
1254
1255        // The answer is written straight into the octets that go out, so the reply is
1256        // started before the operation runs and is what it writes into. One that raises
1257        // afterwards drops this and starts another, see [`Self::handle_request`].
1258        let mut reply = request.reply(ReplyStatus::NoException)?;
1259        let output = &mut reply;
1260
1261        match operation {
1262            b"_get_name" => output.write_string(&self.dev.name().await?)?,
1263            b"_get_adm_name" => output.write_string(&self.dev.admin_dev_name().await?)?,
1264            b"_get_description" => output.write_string(&self.dev.description().await?)?,
1265            b"_get_state" => output.write(&self.dev.state().await?)?,
1266            b"_get_status" => output.write_string(&self.dev.status().await?.value())?,
1267            b"ping" => self.dev.ping().await?,
1268
1269            // IDL variants:
1270            // DevInfo info()
1271            // DevInfo_3 info_3()
1272            // DevInfo_6 info_6()
1273            b"info" | b"info_3" | b"info_6" => {
1274                let version = match operation {
1275                    b"info" => DevVersion::V1,
1276                    b"info_3" => DevVersion::V3,
1277                    _ => DevVersion::V6,
1278                };
1279                write_dev_info(output, version, &self.dev.info().await?)?;
1280            }
1281            b"black_box" => {
1282
1283                // 'n' is a long and not a sequence length, a negative one meaning that the
1284                // client wants the whole of it.
1285                let max = input.read_long()?;
1286                let entries = self.dev.black_box(usize::try_from(max).ok()).await?;
1287                output.write(&entries)?;
1288
1289            }
1290            b"command_inout" | 
1291            b"command_inout_2" | 
1292            b"command_inout_4" => {
1293
1294                // IDL variants:
1295                // any command_inout(in string command, in any argin)
1296                // any command_inout_2(in string command, in any argin, in DevSource source)
1297                // any command_inout_4(in string command, in any argin, in DevSource source, in ClntIdent cl_ident)
1298                //
1299                // The identity of the client is read by none of them: this implementation has
1300                // nowhere to report it and nothing follows it in the request.
1301
1302                let name = input.read::<Scalar<str>>()?;
1303                let arg = read_command_data(&mut input)?;
1304
1305                // v1 has no source of its own, and the device is what a client that cannot
1306                // ask for anything else gets.
1307                let source = if operation == b"command_inout" {
1308                    DevSource::Dev
1309                } else {
1310                    input.read()?
1311                };
1312
1313                let answer = self.dev.command(name, arg, source).await?;
1314                write_command_data(output, &answer)?;
1315
1316            }
1317            b"read_attributes" |
1318            b"read_attributes_2" |
1319            b"read_attributes_3" |
1320            b"read_attributes_4" |
1321            b"read_attributes_5" => {
1322
1323                // IDL variants:
1324                // AttributeValueList read_attributes(in DevVarStringArray names)
1325                // AttributeValueList read_attributes_2(in DevVarStringArray names, in DevSource source)
1326                // AttributeValueList_3 read_attributes_3(in DevVarStringArray names, in DevSource source)
1327                // AttributeValueList_4 read_attributes_4(in DevVarStringArray names, in DevSource source, in ClntIdent cl_ident)
1328                // AttributeValueList_5 read_attributes_5(in DevVarStringArray names, in DevSource source, in ClntIdent cl_ident)
1329
1330                // The version of the structure the values go out in, which is the one of
1331                // the operation and not the one of this object: a client that speaks an
1332                // older version is answered in the one it asked in.
1333                let version = match operation {
1334                    b"read_attributes" => DevVersion::V1,
1335                    b"read_attributes_2" => DevVersion::V2,
1336                    b"read_attributes_3" => DevVersion::V3,
1337                    b"read_attributes_4" => DevVersion::V4,
1338                    _ => DevVersion::V5,
1339                };
1340
1341                let names = input.read::<Spectrum<str>>()?;
1342                let source = if version >= DevVersion::V2 {
1343                    input.read()?
1344                } else {
1345                    DevSource::Dev
1346                };
1347
1348                let readings = self.dev.read_attr(all_attributes(names), source).await?;
1349
1350                output.write_sequence_len(readings.len())?;
1351                for Named { name, value: read } in &readings {
1352                    write_attr(output, version, name.value(), read)?;
1353                }
1354
1355            }
1356            b"write_attributes" | 
1357            b"write_attributes_3" | 
1358            b"write_attributes_4" => {
1359
1360                // IDL variants:
1361                // void write_attributes(in AttributeValueList values)
1362                // void write_attributes_3(in AttributeValueList values)
1363                // void write_attributes_4(in AttributeValueList_4 values, in ClntIdent cl_ident)
1364                //
1365                // Note that v2 has no variant of its own and reuses the v1 one, and that v3
1366                // only adds MultiDevFailed to the raised exceptions, the argument staying the
1367                // same.
1368
1369                let version = if operation == b"write_attributes_4" {
1370                    DevVersion::V4
1371                } else {
1372                    DevVersion::V1
1373                };
1374
1375                let values = read_attr_values(&mut input, version)?;
1376                let names = values.iter().map(|write| write.name.clone()).collect::<Vec<_>>();
1377
1378                let results = self.dev.write_attr(values).await?;
1379
1380                // One outcome per value and in the order they were given, so which
1381                // attribute a failure is of is where it lies and not anything the backend
1382                // says: only the index is kept, and the names given are what the name of it
1383                // is looked up in below, see [`BackendDev::write_attr`].
1384                let mut failures = results.into_iter()
1385                    .enumerate()
1386                    .filter_map(|(index, res)| res.err().map(|err| (index, err)))
1387                    .collect::<Vec<_>>();
1388
1389                if !failures.is_empty() {
1390
1391                    // v1 has no exception for some of the attributes having failed, so the
1392                    // first failure is what the whole call failed with, and the client is
1393                    // not told which one of them it was.
1394                    if version < DevVersion::V4 {
1395                        return Err(failures.swap_remove(0).1);
1396                    }
1397
1398                    // Which starts the reply over as that exception, dropping whatever
1399                    // of a normal answer had been written into it.
1400                    *output = request.reply_user_exception(tango_repo_id!("MultiDevFailed"))?;
1401                    output.write_sequence_len(failures.len())?;
1402                    for (index, err) in failures {
1403                        output.write(&NamedError {
1404                            name: Cow::Borrowed(names.get(index).map(|name| name.value()).unwrap_or("")),
1405                            index,
1406                            err: Some(err),
1407                        })?;
1408                    }
1409
1410                }
1411
1412            }
1413            b"write_read_attributes_4" | 
1414            b"write_read_attributes_5" => {
1415
1416                // IDL variants:
1417                // AttributeValueList_4 write_read_attributes_4(in AttributeValueList_4 values, in ClntIdent cl_ident)
1418                // AttributeValueList_5 write_read_attributes_5(in AttributeValueList_4 values, in DevVarStringArray r_names, in ClntIdent cl_ident)
1419                //
1420                // Both take the very same v4 list of values, only what they answer with
1421                // differs. The list of the attributes v5 lets a client read back other than
1422                // the written ones is read and ignored: the backend reads back what it wrote.
1423
1424                let reply_version = if operation == b"write_read_attributes_5" {
1425                    DevVersion::V5
1426                } else {
1427                    DevVersion::V4
1428                };
1429
1430                let values = read_attr_values(&mut input, DevVersion::V4)?;
1431
1432                // A failure of one of the attributes is carried by its own entry of the
1433                // answer, which has a place for one, so there is no MultiDevFailed to
1434                // raise here unlike above.
1435                let readings = self.dev.write_read_attr(values).await?;
1436
1437                output.write_sequence_len(readings.len())?;
1438                for Named { name, value: read } in &readings {
1439                    write_attr(output, reply_version, name.value(), read)?;
1440                }
1441
1442            }
1443            b"command_query" | 
1444            b"command_query_2" => {
1445
1446                // IDL variants:
1447                // DevCmdInfo command_query(in string command)
1448                // DevCmdInfo_2 command_query_2(in string command)
1449
1450                let version = if operation == b"command_query_2" {
1451                    DevVersion::V2
1452                } else {
1453                    DevVersion::V1
1454                };
1455
1456                let name = input.read::<Scalar<str>>()?;
1457                let names = name.clone().reshape([1]).expect("one name");
1458
1459                // One command was asked about, so the first entry is the answer and
1460                // anything behind it is a backend that answered more than it was asked.
1461                let infos = self.dev.command_info(Some(names)).await?;
1462
1463                let Some(Named { name, value: info }) = infos.into_iter().next() else {
1464                    return Err(Error::new(ErrorKind::CommandNotFound)
1465                        .with_description(format!("Command {:?} is not defined on the device", name.value())));
1466                };
1467
1468                write_command_info(output, version, name.value(), &info?)?;
1469
1470            }
1471            b"command_list_query" | 
1472            b"command_list_query_2" => {
1473
1474                // IDL variants:
1475                // DevCmdInfoList command_list_query()
1476                // DevCmdInfoList_2 command_list_query_2()
1477
1478                let version = if operation == b"command_list_query_2" {
1479                    DevVersion::V2
1480                } else {
1481                    DevVersion::V1
1482                };
1483
1484                let infos = self.dev.command_info(None).await?;
1485
1486                // The commands that cannot be described are left out, so how many go out
1487                // is only known once they have all been written, see [`SequenceLen`].
1488                let out = &mut **output;
1489                let len = SequenceLen::start(out)?;
1490                let mut count = 0;
1491
1492                for Named { name, value: info } in infos {
1493
1494                    // One command that cannot be described is not worth failing the whole
1495                    // listing for, so it is simply left out of it.
1496                    let info = match info {
1497                        Ok(info) => info,
1498                        Err(err) => {
1499                            warn!(dev = %self.name, command = %name.value(), %err, "Failed to describe the command, leaving it out of the listing");
1500                            continue;
1501                        }
1502                    };
1503
1504                    write_command_info(out, version, name.value(), &info)?;
1505                    count += 1;
1506
1507                }
1508
1509                len.finish(out, count)?;
1510
1511            }
1512            b"set_attribute_config" |
1513            b"set_attribute_config_3" |
1514            b"set_attribute_config_4" |
1515            b"set_attribute_config_5" => {
1516
1517                // IDL variants:
1518                // void set_attribute_config(in AttributeConfigList new_conf)
1519                // void set_attribute_config_3(in AttributeConfigList_3 new_conf)
1520                // void set_attribute_config_4(in AttributeConfigList_3 new_conf, in ClntIdent cl_ident)
1521                // void set_attribute_config_5(in AttributeConfigList_5 new_conf, in ClntIdent cl_ident)
1522                //
1523                // Note that the configuration of v2 is never sent, v2 reusing the v1 operation,
1524                // and that the client identification is left unread. What an attribute *is*,
1525                // which the structure carries along with its configuration, is left out, the
1526                // configuration being all a client may change of it.
1527
1528                let version = match operation {
1529                    b"set_attribute_config" => DevVersion::V1,
1530                    b"set_attribute_config_5" => DevVersion::V5,
1531                    _ => DevVersion::V3,
1532                };
1533
1534                let len = input.read_sequence_len()?;
1535                let mut configs = Vec::with_capacity(len);
1536                for _ in 0..len {
1537                    let (name, info) = read_attr_info(&mut input, version)?;
1538                    configs.push(Named::new(Scalar::from(&*name), info?.config));
1539                }
1540
1541                // Every operation raises a single DevFailed, so the first attribute that
1542                // failed is what the whole call fails with, the others having been changed.
1543                let results = self.dev.set_attr_config(configs).await?;
1544                if let Some(err) = results.into_iter().find_map(Result::err) {
1545                    return Err(err);
1546                }
1547
1548            }
1549            b"get_attribute_config" |
1550            b"get_attribute_config_2" |
1551            b"get_attribute_config_3" |
1552            b"get_attribute_config_5" => {
1553
1554                // IDL variants:
1555                // AttributeConfigList get_attribute_config(in DevVarStringArray names)
1556                // AttributeConfigList_2 get_attribute_config_2(in DevVarStringArray names)
1557                // AttributeConfigList_3 get_attribute_config_3(in DevVarStringArray names)
1558                // AttributeConfigList_5 get_attribute_config_5(in DevVarStringArray names)
1559                //
1560                // Note that v4 has no variant of its own and reuses the v3 one.
1561
1562                let version = match operation {
1563                    b"get_attribute_config" => DevVersion::V1,
1564                    b"get_attribute_config_2" => DevVersion::V2,
1565                    b"get_attribute_config_3" => DevVersion::V3,
1566                    _ => DevVersion::V5,
1567                };
1568
1569                let names = input.read::<Spectrum<str>>()?;
1570                let requested = all_attributes(names);
1571                let named = requested.is_some();
1572
1573                let infos = self.dev.attr_info(requested).await?;
1574
1575                let out = &mut **output;
1576                let len = SequenceLen::start(out)?;
1577                let mut count = 0;
1578
1579                for Named { name, value: info } in infos {
1580
1581                    // An attribute the client named and that cannot be described is what
1582                    // the whole request fails with, the client having no place to be told
1583                    // about that one only. One that it did not name, the listing being of
1584                    // every attribute, is simply left out.
1585                    let info = match info {
1586                        Ok(info) => info,
1587                        Err(err) if named => return Err(err),
1588                        Err(err) => {
1589                            warn!(dev = %self.name, attr = %name.value(), %err, "Failed to describe the attribute, leaving it out of the listing");
1590                            continue;
1591                        }
1592                    };
1593
1594                    write_attr_info(out, version, name.value(), &info)?;
1595                    count += 1;
1596
1597                }
1598
1599                len.finish(out, count)?;
1600
1601            }
1602            b"command_inout_history_2" |
1603            b"command_inout_history_4" => {
1604
1605                // IDL variants:
1606                // DevCmdHistoryList command_inout_history_2(in string command, in long n)
1607                // DevCmdHistory_4 command_inout_history_4(in string command, in long n)
1608
1609                let version = match operation {
1610                    b"command_inout_history_2" => DevVersion::V2,
1611                    _ => DevVersion::V4,
1612                };
1613
1614                let name = input.read::<Scalar<str>>()?;
1615                let max = usize::try_from(input.read_long()?).unwrap_or(0);
1616
1617                // The structure announces the type of the results even when every record
1618                // failed, which is the one the command advertises.
1619                let infos = self.dev.command_info(Some(Spectrum::from(&[name.value()][..]))).await?;
1620                let info = single_info(infos)?;
1621                let history = self.dev.command_history(name, max).await?;
1622                write_command_history(output, version, info.value.out_kind, &history)?;
1623
1624            }
1625            b"read_attribute_history_2" |
1626            b"read_attribute_history_3" |
1627            b"read_attribute_history_4" |
1628            b"read_attribute_history_5" => {
1629
1630                // IDL variants:
1631                // DevAttrHistoryList read_attribute_history_2(in string name, in long n)
1632                // DevAttrHistoryList_3 read_attribute_history_3(in string name, in long n)
1633                // DevAttrHistory_4 read_attribute_history_4(in string name, in long n)
1634                // DevAttrHistory_5 read_attribute_history_5(in string name, in long n)
1635
1636                let version = match operation {
1637                    b"read_attribute_history_2" => DevVersion::V2,
1638                    b"read_attribute_history_3" => DevVersion::V3,
1639                    b"read_attribute_history_4" => DevVersion::V4,
1640                    _ => DevVersion::V5,
1641                };
1642
1643                let name = input.read::<Scalar<str>>()?;
1644                let max = usize::try_from(input.read_long()?).unwrap_or(0);
1645
1646                // As for a command, the structure announces the type of the values, and
1647                // the name the attribute is defined with.
1648                let infos = self.dev.attr_info(Some(Spectrum::from(&[name.value()][..]))).await?;
1649                let Named { name: attr_name, value: info } = single_info(infos)?;
1650                let history = self.dev.attr_history(name, max).await?;
1651                write_attr_history(output, version, attr_name.value(), info.data_kind, info.data_format, &history)?;
1652
1653            }
1654            // The operations the IDL declares and that this implementation has nothing to
1655            // answer with, either because the device backend has no such notion — the
1656            // pipes and the static information of a device — or because it is read-only,
1657            // the configuration of an attribute being given by its definition and not by a
1658            // client.
1659            //
1660            // These are told apart from an operation that does not exist at all, so that the
1661            // DevFailed a client is answered with says that this server does not implement
1662            // them, and not that they should never have been sent.
1663            b"get_pipe_config_5" |
1664            b"set_pipe_config_5" |
1665            b"read_pipe_5" |
1666            b"write_pipe_5" |
1667            b"write_read_pipe_5" => {
1668                return Err(Error::not_implemented()
1669                    .with_description(format!("Operation {:?} is not implemented in this server", String::from_utf8_lossy(operation))));
1670            }
1671            _ => {
1672                return Err(Error::new(ErrorKind::NotHandled));
1673            }
1674        }
1675
1676        Ok(reply)
1677
1678    }
1679
1680}
1681
1682/// The one entry of the infos a single name was asked for, failing as that name did.
1683fn single_info<T>(infos: Vec<NamedResult<T>>) -> Result<Named<T>> {
1684    let Named { name, value } = infos.into_iter().next()
1685        .ok_or_else(|| Error::new(ErrorKind::Internal)
1686            .with_description("The device gave no info for the name it was asked"))?;
1687    value.map(|value| Named::new(name, value))
1688}
1689
1690/// The names of a listing operation as the backend takes them, which is [`None`] for every
1691/// object of the device: 'all attributes' is a name of its own in the IDL and not the name
1692/// of an attribute, and the marker to use for it changed in v3, so both are recognized.
1693fn all_attributes(names: Spectrum<str>) -> Option<Spectrum<str>> {
1694    if has_all_marker(&names) {
1695        None
1696    } else {
1697        Some(names)
1698    }
1699}
1700
1701/// Whether the 'all attributes' marker is among these names, of either version, see
1702/// [`all_attributes`].
1703fn has_all_marker(names: &Spectrum<str>) -> bool {
1704    names.iter().any(|name| name == ALL_ATTRIBUTES || name == ALL_ATTRIBUTES_3)
1705}
1706
1707/// Refuse names given to a request that hold the 'all attributes' marker, which would ask
1708/// a device for every attribute where specific ones were meant.
1709fn check_no_all_marker(names: &Spectrum<str>) -> Result<()> {
1710    if has_all_marker(names) {
1711        return Err(Error::new(ErrorKind::AttrNameInvalid)
1712            .with_description("Attribute names must not contain the 'all attributes' marker")
1713            .with_append_description(format_args!("Got: {names:?}")));
1714    }
1715    Ok(())
1716}
1717
1718/// Read the list of values of an attribute writing operation, out of the octets it was
1719/// received in: only what the client wrote is kept of every entry, which is the only thing
1720/// those operations carry a value for.
1721fn read_attr_values(input: &mut CdrReader<'_>, version: DevVersion) -> Result<Vec<Named<Value>>> {
1722
1723    let len = input.read_sequence_len()?;
1724    let mut values = Vec::with_capacity(len);
1725
1726    for _ in 0..len {
1727
1728        let Named { name, value: read } = read_attr(input, version)?;
1729
1730        // A client that sends an attribute to write with no value in it is one whose
1731        // request makes no sense, there being nothing to write.
1732        let value = read?.w_value.ok_or_else(|| Error::new(ErrorKind::InvalidData)
1733            .with_description(format!("Attribute {:?} has no value to write", name.value())))?;
1734
1735        values.push(Named::new(name, value));
1736
1737    }
1738
1739    Ok(values)
1740
1741}
1742
1743/// The length of a sequence, kept for once its entries are written and how many there are
1744/// is known, which is how a listing that leaves some entries out goes out without being
1745/// filtered first, see [`CdrWriter::set_position`].
1746struct SequenceLen(usize);
1747
1748impl SequenceLen {
1749
1750    /// Leave the room for the length behind, where the sequence starts.
1751    fn start(buf: &mut CdrWriter) -> Result<Self> {
1752        let pos = buf.position();
1753        buf.write_sequence_len(0)?;
1754        Ok(Self(pos))
1755    }
1756
1757    /// Fill the length in, now that it is known, and carry on writing where the entries
1758    /// stopped.
1759    fn finish(self, buf: &mut CdrWriter, len: usize) -> Result<()> {
1760        let end = buf.position();
1761        buf.set_position(self.0);
1762        buf.write_sequence_len(len)?;
1763        buf.set_position(end);
1764        Ok(())
1765    }
1766
1767}
1768
1769/// Describe the Tango version of the device interface.
1770#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
1771pub(crate) enum DevVersion {
1772    V1,
1773    V2,
1774    V3,
1775    V4,
1776    V5,
1777    V6,
1778}
1779
1780impl DevVersion {
1781
1782    /// An array with all IDL versions in order.
1783    pub const ALL: [Self; 6] = [
1784        Self::V1,
1785        Self::V2,
1786        Self::V3,
1787        Self::V4,
1788        Self::V5,
1789        Self::V6,
1790    ];
1791
1792    const REPO_ID: [&str; 6] = [
1793        tango_repo_id!("Device"),
1794        tango_repo_id!("Device_2"),
1795        tango_repo_id!("Device_3"),
1796        tango_repo_id!("Device_4"),
1797        tango_repo_id!("Device_5"),
1798        tango_repo_id!("Device_6"),
1799    ];
1800
1801    #[inline]
1802    pub const fn repo_id(&self) -> &'static str {
1803        Self::REPO_ID[*self as usize]
1804    }
1805
1806    /// The IDL release number this version is known as in the Tango protocol, which every
1807    /// place the version travels as a number uses, one-based unlike the discriminant.
1808    #[inline]
1809    pub const fn number(&self) -> u32 {
1810        *self as u32 + 1
1811    }
1812
1813    /// The version of that IDL release number, see [`Self::number`], none for a release
1814    /// this crate does not know.
1815    #[inline]
1816    pub const fn from_number(number: u32) -> Option<Self> {
1817        match number {
1818            1..=6 => Some(Self::ALL[number as usize - 1]),
1819            _ => None,
1820        }
1821    }
1822
1823}
1824
1825fn unexpected_user_exception_err(exception_id: &str) -> Error {
1826    Error::new(ErrorKind::Internal)
1827        .with_description(format!("User exception {exception_id:?} is unexpected"))
1828}
1829
1830// =========================== //
1831//          Utilities          //
1832// =========================== //
1833
1834/// Try to do some binding action against a port range.
1835/// 
1836/// The implemented algorithm is to start with a random port, and then increment one by
1837/// one on retries.
1838/// 
1839/// The given function should return a control flow to either continue, or break with a
1840/// definitive result, which is either a critical error, or a success.
1841fn try_port_range<F, R>(ports: (u16, u16), mut func: F) -> ControlFlow<Result<R>>
1842where
1843    F: FnMut(u16) -> ControlFlow<Result<R>>,
1844{
1845    debug_assert!(ports.0 <= ports.1, "invalid port range");
1846    let mut port = rand::random_range(ports.0..=ports.1);
1847    let mut retry = ports.1 - ports.0;
1848    loop {
1849        func(port)?;
1850        if retry == 0 {
1851            break;
1852        }
1853        retry -= 1;
1854        if port == ports.1 {
1855            port = ports.0;
1856        } else {
1857            port += 1;
1858        }
1859    }
1860    // If we did not found a port in range, we return continue to signifies that the 
1861    // caller should either continue searching, or make its own error!
1862    ControlFlow::Continue(())
1863}
1864
1865#[cfg(test)]
1866mod tests {
1867
1868    use std::net::TcpListener;
1869    use std::time::Duration;
1870
1871    use crate::future::block_on_timeout;
1872
1873    use super::*;
1874
1875    /// A device whose server is not there is not connected, the CORBA failure being the
1876    /// parent.
1877    #[test]
1878    fn connect_refused() {
1879
1880        let backend = CorbaBackend::new();
1881        let port = TcpListener::bind("127.0.0.1:0").unwrap().local_addr().unwrap().port();
1882
1883        let err = block_on_timeout(backend.connect_at("127.0.0.1", port, "test/dev/1"), Duration::from_secs(5))
1884            .unwrap()
1885            .unwrap_err();
1886
1887        assert_eq!(err.kind(), ErrorKind::DevNotConnected);
1888        assert_eq!(err.parent().unwrap().kind(), ErrorKind::Corba);
1889
1890    }
1891
1892}