Skip to main content

connect_view_only_client/
connect_view_only_client.rs

1// Connect to a remote device as a *view-only* client: the connection lets you
2// browse components and read signals, but the server refuses configuration
3// writes.  The client type is picked in the add-device config, under the
4// "General" child object's "ClientType" selection property.
5//
6// Point OPENDAQ_DEVICE at a running openDAQ device (e.g. the reference device
7// simulator); it defaults to `daq.nd://127.0.0.1`, the openDAQ native
8// configuration protocol on the local machine.
9
10use opendaq::{CoreType, Device, Instance, PropertyObject, Value};
11
12/// Name of `object`'s first visible, non-read-only, non-callable property, or
13/// `None`.  Used to probe whether the connection actually refuses writes.
14fn first_writable_property_name(object: &PropertyObject) -> opendaq::Result<Option<String>> {
15    for property in object.visible_properties()? {
16        if property.read_only()? {
17            continue;
18        }
19        if matches!(property.value_type()?, CoreType::Func | CoreType::Proc) {
20            continue;
21        }
22        return Ok(Some(property.name()?));
23    }
24    Ok(None)
25}
26
27fn probe_view_only_device(device: &Device) -> opendaq::Result<()> {
28    println!("Connected to {} as a view-only client.", device.name()?);
29    println!("Visible signals: {}", device.signals_recursive()?.len());
30    match first_writable_property_name(device)? {
31        Some(name) => {
32            // Write the property's current value back to itself: harmless if
33            // it were allowed, but a view-only connection must reject it.
34            let value = device.property_value(&name)?;
35            match device.set_property_value(&name, value) {
36                Ok(()) => println!("Unexpected: writing {name:?} was allowed."),
37                Err(err) => {
38                    println!("Write to {name:?} refused, as expected for view-only: {err}")
39                }
40            }
41        }
42        None => println!("Device exposes no writable property to probe."),
43    }
44    Ok(())
45}
46
47fn main() -> opendaq::Result<()> {
48    let instance = Instance::new()?;
49
50    let config = instance
51        .create_default_add_device_config()?
52        .expect("default add-device config");
53
54    // The "General" child object holds the protocol-independent options,
55    // ClientType among them; its selection values name the available modes.
56    let general = config
57        .property_value("General")?
58        .into_object()?
59        .cast::<PropertyObject>()?;
60    let client_type = general
61        .property("ClientType")?
62        .expect("ClientType property");
63    println!("Available ClientType options:");
64    if let Value::Dict(mut options) = client_type.selection_values()? {
65        options.sort_by_key(|(key, _)| key.as_i64());
66        for (key, name) in options {
67            println!("  {key} = {name}");
68        }
69    }
70
71    config.set_property_value("General.ClientType", 2)?; // 2 = view-only
72    let connection_string =
73        std::env::var("OPENDAQ_DEVICE").unwrap_or_else(|_| "daq.nd://127.0.0.1".into());
74
75    match instance.add_device_with(&connection_string, Some(&config)) {
76        Ok(Some(device)) => probe_view_only_device(&device)?,
77        Ok(None) => println!("Could not connect to {connection_string}: no device returned."),
78        Err(err) => {
79            println!("Could not connect to {connection_string}: {err}");
80            println!("Start an openDAQ device/simulator there, or set OPENDAQ_DEVICE.");
81        }
82    }
83    Ok(())
84}