1mod 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
46const ALL_ATTRIBUTES: &str = "All attributes";
48const ALL_ATTRIBUTES_3: &str = "All attributes_3";
50
51static CLIENT_ID: ClientIdent = ClientIdent::Java {
53 main_class: Cow::Borrowed("rstango"),
54 uuid: Uuid::nil(),
55};
56
57#[derive(Debug)]
63pub struct CorbaBackend {
64 client: OnceLock<Client>,
66 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 async fn check_device(&self, object: ClientObject) -> Result<Arc<dyn BackendDev>> {
94
95 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 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 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#[derive(Debug)]
212struct CorbaDevBackend {
213 object: ClientObject,
214 version: DevVersion,
215 consumer: Arc<OnceLock<Consumer>>,
217}
218
219impl CorbaDevBackend {
220
221 fn request(&self, operation: &[u8], input: &dyn CdrWrite) -> CorbaDevRequest {
227 CorbaDevRequest { inner: self.object.request(operation, input) }
228 }
229
230 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 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 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 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 struct Input<'a> {
448 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 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 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 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 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 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 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 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 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 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 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 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 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 struct Input<'a> {
723 version: DevVersion,
725 infos: &'a [(Scalar<str>, AttrInfo)],
726 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 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 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 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 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 let request = match &names {
824 Some(names) => self.request(operation, names),
825 None if config_version >= DevVersion::V3 => self.request(operation, &[ALL_ATTRIBUTES_3]),
828 None => self.request(operation, &[ALL_ATTRIBUTES]),
829 };
830
831 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 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 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 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
943struct 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
957fn 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
964struct 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
979fn 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#[derive(Debug)]
993struct CorbaDevReply {
994 inner: ClientReply,
995}
996
997impl CorbaDevReply {
998
999 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 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 fn read<'a, O: CdrRead<'a>>(&'a self) -> Result<O> {
1030 self.read_simple()?.read()
1031 }
1032
1033}
1034
1035fn 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
1046const SERVED_VERSION: DevVersion = DevVersion::V6;
1057
1058const LIB_VERSION: i32 = 1000;
1062
1063const 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#[derive(Debug)]
1078struct CorbaServerBackend {
1079 server: Server<CorbaServerObject>,
1083 supplier: Option<Supplier>,
1087}
1088
1089impl CorbaServerBackend {
1090
1091 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 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 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 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#[derive(Debug)]
1181struct CorbaServerObject {
1182 name: Arc<str>,
1185 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 Box::pin(async move { self.handle_request(&request).await })
1205 }
1206
1207}
1208
1209fn 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 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 async fn dispatch(&self, operation: &[u8], request: &ServerRequest) -> Result<ServerReply> {
1252
1253 let mut input = request.read();
1254
1255 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 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 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 let name = input.read::<Scalar<str>>()?;
1303 let arg = read_command_data(&mut input)?;
1304
1305 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 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 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 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 if version < DevVersion::V4 {
1395 return Err(failures.swap_remove(0).1);
1396 }
1397
1398 *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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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
1682fn 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
1690fn all_attributes(names: Spectrum<str>) -> Option<Spectrum<str>> {
1694 if has_all_marker(&names) {
1695 None
1696 } else {
1697 Some(names)
1698 }
1699}
1700
1701fn has_all_marker(names: &Spectrum<str>) -> bool {
1704 names.iter().any(|name| name == ALL_ATTRIBUTES || name == ALL_ATTRIBUTES_3)
1705}
1706
1707fn 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
1718fn 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 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
1743struct SequenceLen(usize);
1747
1748impl SequenceLen {
1749
1750 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 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#[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 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 #[inline]
1809 pub const fn number(&self) -> u32 {
1810 *self as u32 + 1
1811 }
1812
1813 #[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
1830fn 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 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 #[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}