tango_client/
proxy.rs

1//! Binding to the Tango Device proxy API.
2
3use std::ffi::CString;
4use std::mem;
5use std::ptr;
6use libc::{self, c_char, c_void};
7
8use crate::c;
9use crate::error::{TangoResult, TangoError};
10use crate::types::*;
11
12
13/// A proxy to a remote Tango device.
14pub struct DeviceProxy {
15    ptr: *mut c_void,
16}
17
18impl Drop for DeviceProxy {
19    fn drop(&mut self) {
20        let error_stack = unsafe { c::tango_delete_device_proxy(self.ptr) };
21        if !error_stack.is_null() {
22            // we need to construct the error to deallocate the stack
23            drop(TangoError::from_stack(error_stack));
24        }
25    }
26}
27
28impl DeviceProxy {
29    /// Connect to the Tango device.
30    ///
31    /// Address can either be in the form `a/b/c` or the long form
32    /// `tango://db_host:db_port/a/b/c`.
33    pub fn new(address: &str) -> TangoResult<DeviceProxy> {
34        let mut dev = ptr::null_mut();
35        let address = CString::new(address).unwrap();
36        tango_call!(tango_create_device_proxy,
37                    DeviceProxy { ptr: dev },
38                    address.as_ptr() as *mut c_char, &mut dev)
39    }
40
41    /// Return the communication timeout in ms.
42    pub fn get_timeout(&self) -> TangoResult<i32> {
43        let mut res = 0;
44        tango_call!(tango_get_timeout_millis,
45                    res,
46                    self.ptr, &mut res)
47    }
48
49    /// Set the communication timeout in ms.
50    pub fn set_timeout(&mut self, timeout: i32) -> TangoResult<()> {
51        tango_call!(tango_set_timeout_millis,
52                    (),
53                    self.ptr, timeout)
54    }
55
56    /// Return the source used by command/attribute queries.
57    pub fn get_source(&self) -> TangoResult<DevSource> {
58        let mut source = 0;
59        tango_call!(tango_get_source,
60                    DevSource::from_c(source),
61                    self.ptr, &mut source)
62    }
63
64    /// Set the source used by command/attribute queries.
65    pub fn set_source(&mut self, source: DevSource) -> TangoResult<()> {
66        tango_call!(tango_set_source,
67                    (),
68                    self.ptr, source as u32)
69    }
70
71    /// Lock the device for use by other clients.
72    pub fn lock(&mut self) -> TangoResult<()> {
73        tango_call!(tango_lock, (), self.ptr)
74    }
75
76    /// Unlock the device.
77    pub fn unlock(&mut self) -> TangoResult<()> {
78        tango_call!(tango_unlock, (), self.ptr)
79    }
80
81    /// Query if the device is locked.
82    pub fn is_locked(&self) -> TangoResult<bool> {
83        let mut res = false;
84        tango_call!(tango_is_locked,
85                    res,
86                    self.ptr, &mut res)
87    }
88
89    /// Query if the device is locked by this client.
90    pub fn is_locked_by_me(&self) -> TangoResult<bool> {
91        let mut res = false;
92        tango_call!(tango_is_locked_by_me,
93                    res,
94                    self.ptr, &mut res)
95    }
96
97    /// Return a human-readable description of the device's lock status.
98    pub fn locking_status(&self) -> TangoResult<String> {
99        let mut resptr = ptr::null_mut();
100        tango_call!(tango_locking_status,
101                    unsafe {
102                        let res = string_from(resptr);
103                        libc::free(resptr as *mut c_void);
104                        res
105                    },
106                    self.ptr, &mut resptr)
107    }
108
109    /// Query information about a single command.
110    pub fn command_query(&self, cmd_name: &str) -> TangoResult<CommandInfo> {
111        let c_name = CString::new(cmd_name).unwrap();
112        let mut cmdinfo = unsafe { mem::zeroed() };
113        tango_call!(tango_command_query,
114                    unsafe { CommandInfo::from_c(cmdinfo, true) },
115                    self.ptr, c_name.as_ptr() as *mut c_char, &mut cmdinfo)
116    }
117
118    /// Query information about all commands the device offers.
119    pub fn command_list_query(&self) -> TangoResult<Vec<CommandInfo>> {
120        let mut infolist = unsafe { mem::zeroed() };
121        tango_call!(tango_command_list_query, (), self.ptr, &mut infolist)?;
122        let mut res = Vec::with_capacity(infolist.length as usize);
123        unsafe {
124            for i in 0..infolist.length {
125                let cmd_ptr = ptr::read(infolist.sequence.offset(i as isize));
126                res.push(CommandInfo::from_c(cmd_ptr, false));
127            }
128            c::tango_free_CommandInfoList(&mut infolist);
129        }
130        Ok(res)
131    }
132
133    /// Execute a command on the device.
134    pub fn command_inout(&mut self, cmd_name: &str, argin: CommandData) -> TangoResult<CommandData> {
135        let c_name = CString::new(cmd_name).unwrap();
136        let mut argin = unsafe { argin.into_c() };
137        let mut argout = unsafe { mem::zeroed() };
138        let res = tango_call!(tango_command_inout,
139                              unsafe { CommandData::from_c(argout) },
140                              self.ptr, c_name.as_ptr() as *mut c_char,
141                              &mut argin, &mut argout);
142        unsafe { CommandData::free_c_data(argin) };
143        res
144    }
145
146    /// Query names of all attributes the device offers.
147    pub fn get_attribute_list(&self) -> TangoResult<Vec<String>> {
148        let mut namelist = unsafe { mem::zeroed() };
149        tango_call!(tango_get_attribute_list, (), self.ptr, &mut namelist)?;
150        let mut res = Vec::with_capacity(namelist.length as usize);
151        unsafe {
152            for i in 0..namelist.length {
153                let name = ptr::read(namelist.sequence.offset(i as isize));
154                res.push(string_from(name));
155            }
156            c::tango_free_VarStringArray(&mut namelist);
157        }
158        Ok(res)
159    }
160
161    /// Query information about one or more attributes of the device.
162    pub fn get_attribute_config(&self, attr_names: &[&str]) -> TangoResult<Vec<AttributeInfo>> {
163        let mut namelist = unsafe { mem::zeroed::<c::VarStringArray>() };
164        let mut infolist = unsafe { mem::zeroed::<c::AttributeInfoList>() };
165        let mut ptr_vec = Vec::with_capacity(attr_names.len());
166        for name in attr_names {
167            ptr_vec.push(CString::new(*name).unwrap().into_raw());
168        }
169        namelist.length = attr_names.len() as u32;
170        namelist.sequence = ptr_vec.as_mut_ptr();
171        tango_call!(tango_get_attribute_config, (), self.ptr, &mut namelist, &mut infolist)?;
172        let mut res = Vec::with_capacity(infolist.length as usize);
173        unsafe {
174            for i in 0..infolist.length {
175                let info = ptr::read(infolist.sequence.offset(i as isize));
176                res.push(AttributeInfo::from_c(info));
177            }
178            c::tango_free_AttributeInfoList(&mut infolist);
179            for ptr in ptr_vec {
180                drop(CString::from_raw(ptr));
181            }
182        }
183        Ok(res)
184    }
185
186    /// Query information about all attributes of the device.
187    pub fn attribute_list_query(&self) -> TangoResult<Vec<AttributeInfo>> {
188        let mut infolist = unsafe { mem::zeroed() };
189        tango_call!(tango_attribute_list_query, (), self.ptr, &mut infolist)?;
190        let mut res = Vec::with_capacity(infolist.length as usize);
191        unsafe {
192            for i in 0..infolist.length {
193                let info = ptr::read(infolist.sequence.offset(i as isize));
194                res.push(AttributeInfo::from_c(info));
195            }
196            c::tango_free_AttributeInfoList(&mut infolist);
197        }
198        Ok(res)
199    }
200
201    /// Read a single attribute of the device.
202    pub fn read_attribute(&mut self, attr_name: &str) -> TangoResult<AttributeData> {
203        let c_name = CString::new(attr_name).unwrap();
204        let mut data = unsafe { mem::zeroed() };
205        tango_call!(tango_read_attribute,
206                    unsafe { AttributeData::from_c(data, true) },
207                    self.ptr, c_name.as_ptr() as *mut c_char, &mut data)
208    }
209
210    /// Write a single attribute of the device.
211    pub fn write_attribute(&mut self, attr_data: AttributeData) -> TangoResult<()> {
212        let mut data = unsafe { attr_data.into_c() };
213        let res = tango_call!(tango_write_attribute, (),
214                              self.ptr, &mut data);
215        unsafe { AttributeData::free_c_data(data) };
216        res
217    }
218
219    /// Read one or more attributes of the device.
220    pub fn read_attributes(&mut self, attr_names: &[&str]) -> TangoResult<Vec<AttributeData>> {
221        let mut namelist = unsafe { mem::zeroed::<c::VarStringArray>() };
222        let mut datalist = unsafe { mem::zeroed::<c::AttributeDataList>() };
223        let mut ptr_vec = Vec::with_capacity(attr_names.len());
224        for name in attr_names {
225            ptr_vec.push(CString::new(*name).unwrap().into_raw());
226        }
227        namelist.length = attr_names.len() as u32;
228        namelist.sequence = ptr_vec.as_mut_ptr();
229        tango_call!(tango_read_attributes, (), self.ptr, &mut namelist, &mut datalist)?;
230        let mut res = Vec::with_capacity(datalist.length as usize);
231        unsafe {
232            for i in 0..datalist.length {
233                let data = ptr::read(datalist.sequence.offset(i as isize));
234                res.push(AttributeData::from_c(data, false));
235            }
236            c::tango_free_AttributeDataList(&mut datalist);
237            for ptr in ptr_vec {
238                drop(CString::from_raw(ptr));
239            }
240        }
241        Ok(res)
242    }
243
244    /// Write one or more attributes of the device.
245    pub fn write_attributes(&mut self, attr_data: Vec<AttributeData>) -> TangoResult<()> {
246        let mut datalist = unsafe { mem::zeroed::<c::AttributeDataList>() };
247        let mut ptr_vec = Vec::with_capacity(attr_data.len());
248        datalist.length = attr_data.len() as u32;
249        for data in attr_data {
250            ptr_vec.push(unsafe { data.into_c() });
251        }
252        datalist.sequence = ptr_vec.as_mut_ptr();
253        let res = tango_call!(tango_write_attributes, (),
254                              self.ptr, &mut datalist);
255        unsafe {
256            for ptr in ptr_vec {
257                AttributeData::free_c_data(ptr);
258            }
259        }
260        res
261    }
262
263    /// Return the value of one or more properties of the device.
264    ///
265    /// The values in the input `DbDatum`s are ignored.
266    pub fn get_device_property(&self, prop_list: Vec<DbDatum>) -> TangoResult<Vec<DbDatum>> {
267        let mut db_data = unsafe { mem::zeroed::<c::DbData>() };
268        let mut ptr_vec = Vec::with_capacity(prop_list.len());
269        let mut cstr_vec = Vec::with_capacity(prop_list.len());
270        db_data.length = prop_list.len() as u32;
271        for datum in prop_list {
272            let (datum, cstr) = unsafe { datum.into_c() };
273            ptr_vec.push(datum);
274            cstr_vec.push(cstr);
275        }
276        db_data.sequence = ptr_vec.as_mut_ptr();
277        tango_call!(tango_get_device_property, (), self.ptr, &mut db_data)?;
278        let mut res = Vec::with_capacity(db_data.length as usize);
279        unsafe {
280            for i in 0..db_data.length {
281                let db_datum = ptr::read(db_data.sequence.offset(i as isize));
282                res.push(DbDatum::from_c(db_datum, false));
283            }
284            c::tango_free_DbData(&mut db_data);
285        }
286        Ok(res)
287    }
288
289    /// Set one or more properties of the device.
290    pub fn put_device_property(&mut self, prop_list: Vec<DbDatum>) -> TangoResult<()> {
291        let mut db_data = unsafe { mem::zeroed::<c::DbData>() };
292        let mut ptr_vec = Vec::with_capacity(prop_list.len());
293        let mut cstr_vec = Vec::with_capacity(prop_list.len());
294        db_data.length = prop_list.len() as u32;
295        for datum in prop_list {
296            let (datum, cstr) = unsafe { datum.into_c() };
297            ptr_vec.push(datum);
298            cstr_vec.push(cstr);
299        }
300        db_data.sequence = ptr_vec.as_mut_ptr();
301        let res = tango_call!(tango_put_device_property, (),
302                              self.ptr, &mut db_data);
303        unsafe {
304            for ptr in ptr_vec {
305                DbDatum::free_c_data(ptr);
306            }
307        }
308        res
309    }
310
311    /// Delete one or more properties of the device.
312    pub fn delete_device_property(&mut self, prop_list: &[&str]) -> TangoResult<()> {
313        let mut db_data = unsafe { mem::zeroed::<c::DbData>() };
314        let mut ptr_vec = Vec::with_capacity(prop_list.len());
315        let mut cstr_vec = Vec::with_capacity(prop_list.len());
316        db_data.length = prop_list.len() as u32;
317        for prop in prop_list {
318            let datum = DbDatum::name_only(prop);
319            let (datum, cstr) = unsafe { datum.into_c() };
320            ptr_vec.push(datum);
321            cstr_vec.push(cstr);
322        }
323        db_data.sequence = ptr_vec.as_mut_ptr();
324        let res = tango_call!(tango_delete_device_property, (),
325                              self.ptr, &mut db_data);
326        unsafe {
327            for ptr in ptr_vec {
328                DbDatum::free_c_data(ptr);
329            }
330        }
331        res
332    }
333}