motorcortex_rust/client/
request.rs

1use crate::client::{receive_message, Parameters};
2use crate::connection::{ConnectionManager, ConnectionOptions};
3use crate::msg::{
4    get_hash, get_hash_size, CreateGroupMsg, GetParameterListMsg,
5    GetParameterMsg, GetParameterTreeHashMsg, GetParameterTreeMsg, GroupStatusMsg, Hash, LoginMsg, LogoutMsg,
6    ParameterListMsg, ParameterMsg, ParameterTreeMsg, RemoveGroupMsg, SetParameterListMsg, SetParameterMsg,
7    StatusCode, StatusMsg,
8};
9use crate::parameter_value::{
10    decode_parameter_value, encode_parameter_value, GetParameterTuple, GetParameterValue,
11    SetParameterTuple, SetParameterValue,
12};
13use crate::ParameterTree;
14
15use prost::{DecodeError, Message};
16
17/// Represents a request client that manages socket-based connections
18/// and interacts with the server.
19pub struct Request {
20    connection_data: ConnectionManager,
21    /// Client's local representation of the parameter hierarchy.
22    parameter_tree: ParameterTree,
23}
24
25impl Request {
26    /// Creates a new `Request` instance with uninitialized members.
27    pub fn new() -> Self {
28        let sock = unsafe {
29            let mut sock: nng_c_sys::nng_socket = std::mem::zeroed();
30            nng_c_sys::nng_req0_open(&mut sock);
31            sock
32        };
33        Self {
34            connection_data: ConnectionManager::new(sock),
35            parameter_tree: ParameterTree::new(),
36        }
37    }
38
39    /// Establishes a connection to a specified server URL using the given configuration options.
40    ///
41    /// # Arguments:
42    /// * `url` - The server's address (e.g., "tcp://127.0.0.1:5555").
43    /// * `connection_options` - The connection settings, including TLS certificate and timeouts.
44    ///
45    /// # Returns:
46    /// * `Ok(())` if the connection is successful.
47    /// * `Err(String)` with the error message if the operation fails.
48    pub fn connect(
49        &mut self,
50        url: &str,
51        connection_options: ConnectionOptions,
52    ) -> Result<(), String> {
53        self.connection_data.connect(url, connection_options)
54    }
55
56    /// Disconnects the current connection and frees the associated resources.
57    ///
58    /// # Returns:
59    /// * `Ok(())` if the disconnection is successful.
60    /// * `Err(String)` with the error message if the operation fails.
61    pub fn disconnect(&mut self) -> Result<(), String> {
62        self.connection_data.disconnect()
63    }
64
65    /// Sends a login message to the server with the specified username and password.
66    ///
67    /// # Arguments:
68    /// * `username` - The username to authenticate with.
69    /// * `password` - The password for authentication.
70    ///
71    /// # Returns:
72    /// * `Ok(StatusCode)` - The status code representing the login response.
73    /// * `Err(String)` - An error message if the login fails.
74    pub fn login(&self, username: String, password: String) -> Result<StatusCode, String> {
75        let login_msg = LoginMsg {
76            header: None,
77            login: username,
78            password,
79        };
80
81        let buffer = Self::encode_with_hash(&login_msg)
82            .map_err(|e| format!("Failed to encode LoginMsg: {:?}", e))?;
83        self.send_message(&buffer)?;
84
85        let buf = receive_message(&self.connection_data.sock.as_ref().unwrap())
86            .map_err(|_| "Failed to receive status message".to_string())?;
87        let msg = Self::decode_status_msg(buf.as_slice())
88            .map_err(|e| format!("Failed to decode status message: {:?}", e))?;
89
90        Ok(StatusCode::try_from(msg.status).unwrap())
91    }
92
93    /// Sends a logout message to the server.
94    ///
95    /// # Returns:
96    /// * `Ok(StatusCode)` - The status code representing the logout response.
97    /// * `Err(String)` - An error message if the logout fails.
98    pub fn logout(&self) -> Result<StatusCode, String> {
99        let logout_msg = LogoutMsg { header: None };
100
101        let buffer = Self::encode_with_hash(&logout_msg)
102            .map_err(|e| format!("Failed to encode LogoutMsg: {:?}", e))?;
103        self.send_message(&buffer)?;
104
105        let buf = receive_message(&self.connection_data.sock.as_ref().unwrap())
106            .map_err(|_| "Failed to receive status message".to_string())?;
107        let msg = Self::decode_status_msg(buf.as_slice())
108            .map_err(|e| format!("Failed to decode status message: {:?}", e))?;
109
110        Ok(StatusCode::try_from(msg.status).unwrap())
111    }
112
113    /// Requests and updates the client's parameter tree from the server.
114    ///
115    /// # Returns:
116    /// * `Ok(StatusCode)` - The status code indicating the result of the request.
117    /// * `Err(String)` - An error message if the request fails.
118    pub fn request_parameter_tree(&mut self) -> Result<StatusCode, String> {
119        match self.get_parameter_tree() {
120            Ok((status_code, parameter_tree)) => {
121                self.parameter_tree = parameter_tree;
122                Ok(status_code)
123            }
124            Err(e) => Err(e),
125        }
126    }
127
128    /// Updates a specific parameter on the server with the provided value.
129    ///
130    /// # Arguments:
131    /// * `path` - The hierarchical path to the parameter being updated.
132    /// * `value` - The new value for the parameter. Must implement `SetParameterValue`.
133    ///
134    /// # Returns:
135    /// * `Ok(StatusCode)` - The status code after setting the parameter.
136    /// * `Err(String)` - An error message if the update fails.
137    pub fn set_parameter<V>(&self, path: &str, value: V) -> Result<StatusCode, String>
138    where
139        V: SetParameterValue + Default + PartialEq,
140    {
141        let data_type = self
142            .parameter_tree
143            .get_parameter_data_type(&path)
144            .ok_or((
145                StatusCode::WrongParameterPath,
146                format!("Parameter data type not found for path: {}", path),
147            ))
148            .unwrap();
149
150        let msg = SetParameterMsg {
151            header: None,
152            offset: None,
153            path: path.to_string(),
154            value: encode_parameter_value(data_type, &value),
155        };
156
157        let buffer = Self::encode_with_hash(&msg)
158            .map_err(|e| format!("Failed to encode SetParameter: {:?}", e))?;
159        self.send_message(&buffer)?;
160
161        let buf = receive_message(self.connection_data.sock.as_ref().unwrap())
162            .map_err(|_| "Failed to receive status message".to_string())?;
163        let msg = Self::decode_status_msg(buf.as_slice())
164            .map_err(|e| format!("Failed to decode status message: {:?}", e))?;
165
166        Ok(StatusCode::try_from(msg.status).unwrap())
167    }
168
169    pub fn set_parameters<T>(&self, paths: Vec<&str>, values: T) -> Result<StatusCode, String>
170    where
171        T: SetParameterTuple,
172    {
173        let mut msg = SetParameterListMsg {
174            header: None,
175            params: Vec::new(),
176        };
177
178        let mut i = 0;
179        for path in paths {
180            let data_type = self
181                .parameter_tree
182                .get_parameter_data_type(&path)
183                .ok_or((
184                    StatusCode::WrongParameterPath,
185                    format!("Parameter data type not found for path: {}", path),
186                ))
187                .unwrap();
188
189            msg.params.push(SetParameterMsg {
190                header: None,
191                offset: None,
192                path: path.to_string(),
193                value: T::get_tuple_element(&values, i, data_type)?,
194            });
195            i = i + 1
196        }
197
198        let buffer = Self::encode_with_hash(&msg)
199            .map_err(|e| format!("Failed to encode SetParameterList: {:?}", e))?;
200        self.send_message(&buffer)?;
201
202        let buf = receive_message(self.connection_data.sock.as_ref().unwrap())
203            .map_err(|_| "Failed to receive status message".to_string())?;
204        let msg = Self::decode_status_msg(buf.as_slice())
205            .map_err(|e| format!("Failed to decode status message: {:?}", e))?;
206
207        Ok(StatusCode::try_from(msg.status).unwrap())
208    }
209
210    /// Retrieves the value of a parameter from the server for the given path.
211    ///
212    /// # Arguments:
213    /// * `path` - The hierarchical path of the parameter to retrieve.
214    ///
215    /// # Type Parameters:
216    /// * `V` - The expected value type of the parameter. This type must implement the `GetParameterValue` trait
217    ///         to properly decode the parameter's value from the server response.
218    ///
219    /// # Returns:
220    /// * `Ok(V)` - The parameter value successfully retrieved and decoded into the type `V`.
221    /// * `Err(String)` - An error message if the retrieval or decoding fails.
222    ///
223    /// # Errors:
224    /// This function will return an error if:
225    /// * The parameter's data type cannot be identified.
226    /// * The path to the parameter is invalid or non-existent.
227    /// * There is an issue encoding the request or decoding the response from the server.
228    ///
229    pub fn get_parameter<V>(&self, path: &str) -> Result<V, String>
230    where
231        V: GetParameterValue + Default,
232    {
233        let data_type = self
234            .parameter_tree
235            .get_parameter_data_type(&path)
236            .ok_or((
237                StatusCode::WrongParameterPath,
238                format!("Parameter data type not found for path: {}", path),
239            ))
240            .unwrap();
241
242        let msg = GetParameterMsg {
243            header: None,
244            path: path.to_string(),
245        };
246
247        let buffer = Self::encode_with_hash(&msg)
248            .map_err(|e| format!("Failed to encode GetParameter: {:?}", e))?;
249        self.send_message(&buffer)?;
250
251        let buf = receive_message(self.connection_data.sock.as_ref().unwrap())
252            .map_err(|_| "Failed to receive parameter message".to_string())?;
253        let msg = Self::decode_parameter_msg(buf.as_slice())
254            .map_err(|e| format!("Failed to decode parameter message: {:?}", e))?;
255
256        Ok(decode_parameter_value(data_type, &msg.value))
257    }
258
259    pub fn get_parameters<T>(&self, paths: Vec<&str>) -> Result<T, String>
260    where
261        T: GetParameterTuple,
262    {
263        let mut msg = GetParameterListMsg {
264            header: None,
265            params: Vec::new(),
266        };
267
268        for path in paths {
269            // data_types.push(
270            //     self.parameter_tree
271            //         .get_parameter_data_type(&path)
272            //         .ok_or((
273            //             StatusCode::WrongParameterPath,
274            //             format!("Parameter data type not found for path: {}", path),
275            //         ))
276            //         .unwrap(),
277            // );
278            msg.params.push(GetParameterMsg {
279                header: None,
280                path: path.to_string(),
281            })
282        }
283
284        let buffer = Self::encode_with_hash(&msg)
285            .map_err(|e| format!("Failed to encode GetParameterList: {:?}", e))?;
286        self.send_message(&buffer)?;
287
288        let buf = receive_message(self.connection_data.sock.as_ref().unwrap())
289            .map_err(|_| "Failed to receive parameter list message".to_string())?;
290        let msg = Self::decode_message::<ParameterListMsg>(buf.as_slice())
291            .map_err(|e| format!("Failed to decode parameter list message: {:?}", e))?;
292
293        let combined_iterator = msg.params.iter().map(|parameter| {
294            (
295                &parameter.info.as_ref().unwrap().data_type,
296                parameter.value.as_slice(),
297            )
298        });
299
300        T::get_parameters(combined_iterator)
301    }
302
303    /// Retrieves the server's parameter tree.
304    ///
305    /// This function sends a `GetParameterTreeMsg` request to the server and decodes the response
306    /// into a `ParameterTree` object along with a status code indicating the result.
307    ///
308    /// # Returns:
309    /// * `Ok((StatusCode, ParameterTree))` - A tuple containing the status code and the retrieved `ParameterTree`.
310    /// * `Err(String)` - An error message if retrieving or decoding the parameter tree fails.
311    pub fn get_parameter_tree(&self) -> Result<(StatusCode, ParameterTree), String> {
312        let get_parameter_tree = GetParameterTreeMsg { header: None };
313        let buffer = Self::encode_with_hash(&get_parameter_tree)
314            .map_err(|e| format!("Failed to encode GetParameterTreeMsg: {:?}", e))?;
315        self.send_message(&buffer)?;
316
317        let buf = receive_message(self.connection_data.sock.as_ref().unwrap())
318            .map_err(|_| "Failed to receive parameter tree message".to_string())?;
319        let msg = Self::decode_parameter_tree_msg(buf.as_slice())
320            .map_err(|e| format!("Failed to decode parameter tree message: {:?}", e))?;
321
322        match ParameterTree::from_message(msg) {
323            Some(parameter_tree) => Ok((StatusCode::Ok, parameter_tree)),
324            None => Err("Failed to create ParameterTree: Invalid status code.".to_string()),
325        }
326    }
327
328    pub fn get_parameter_tree_hash(&self) -> Result<u32, String> {
329        let get_parameter_tree_hash = GetParameterTreeHashMsg { header: None };
330        let buffer = Self::encode_with_hash(&get_parameter_tree_hash)
331            .map_err(|e| format!("Failed to encode GetParameterTreeHashMsg: {:?}", e))?;
332        self.send_message(&buffer)?;
333
334        let buf = receive_message(self.connection_data.sock.as_ref().unwrap())
335            .map_err(|_| "Failed to receive parameter tree hash message".to_string())?;
336        let msg = Self::decode_message::<ParameterTreeMsg>(buf.as_slice())
337            .map_err(|e| format!("Failed to decode parameter tree message: {:?}", e))?;
338
339        Ok(msg.hash)
340    }
341
342    pub fn create_group<I>(
343        &self,
344        parameters: I,
345        group_name: &str,
346        frequency_divider: u32,
347    ) -> Result<GroupStatusMsg, String>
348    where
349        I: Parameters,
350    {
351        let create_group_msg = CreateGroupMsg {
352            header: None,
353            frq_divider: frequency_divider,
354            alias: group_name.to_string(),
355            paths: parameters.into_vec(),
356        };
357        let buffer = Self::encode_with_hash(&create_group_msg)
358            .map_err(|e| format!("Failed to encode CreateGroupMsg: {:?}", e))?;
359        self.send_message(&buffer)?;
360
361        let buf = receive_message(self.connection_data.sock.as_ref().unwrap())
362            .map_err(|_| "Failed to receive group status message".to_string())?;
363        let msg = Self::decode_message::<GroupStatusMsg>(buf.as_slice())
364            .map_err(|e| format!("Failed to decode group status message: {:?}", e))?;
365
366        Ok(msg)
367    }
368
369    pub fn remove_group(&self, group_name: &str) -> Result<StatusCode, String> {
370        let remove_group_msg = RemoveGroupMsg {
371            header: None,
372            alias: group_name.to_string(),
373        };
374        let buffer = Self::encode_with_hash(&remove_group_msg)
375            .map_err(|e| format!("Failed to encode RemoveGroupMsg: {:?}", e))?;
376        self.send_message(&buffer)?;
377
378        let buf = receive_message(self.connection_data.sock.as_ref().unwrap())
379            .map_err(|_| "Failed to receive status message".to_string())?;
380        let msg = Self::decode_status_msg(buf.as_slice())
381            .map_err(|e| format!("Failed to decode status message: {:?}", e))?;
382
383        if msg.status == StatusCode::Ok as i32 {
384            Ok(StatusCode::Ok)
385        } else {
386            Err("Failed to remove group".to_string())
387        }
388    }
389
390    /// Encodes a message with its associated hash into a byte buffer for transport.
391    ///
392    /// # Arguments:
393    /// * `message` - The message to encode. Must implement `prost::Message` and `Hash`.
394    ///
395    /// # Returns:
396    /// * `Ok(Vec<u8>)` - The encoded message as a byte buffer.
397    /// * `Err(String)` - An error message if encoding fails.
398    fn encode_with_hash<M: Message + Hash>(message: &M) -> Result<Vec<u8>, String> {
399        let mut buffer: Vec<u8> = Vec::new();
400        buffer.extend(get_hash::<M>().to_le_bytes());
401        message
402            .encode(&mut buffer)
403            .map_err(|e| format!("Failed to encode message: {:?}", e))?;
404
405        Ok(buffer)
406    }
407
408    /// Decodes a byte slice into a specific Protobuf message type.
409    ///
410    /// # Arguments:
411    /// * `reply_slice` - The received byte slice containing the message data.
412    ///
413    /// # Returns:
414    /// * `Ok(T)` - The decoded message of type `T`.
415    /// * `Err(DecodeError)` - An error if decoding fails.
416    pub fn decode_message<T: Message + Default + Hash>(
417        reply_slice: &[u8],
418    ) -> Result<T, DecodeError> {
419        let hash_size = get_hash_size();
420
421        // Verify the provided slice is large enough to contain the hash
422        if hash_size > reply_slice.len() {
423            return Err(DecodeError::new("Invalid message length, hash missing"));
424        }
425
426        // Extract the hash from the slice
427        let provided_hash = u32::from_le_bytes(
428            reply_slice[..hash_size]
429                .try_into()
430                .map_err(|_| DecodeError::new("Failed to extract hash"))?,
431        );
432
433        // Validate the provided hash against the expected one for the generic type T
434        if provided_hash != get_hash::<T>() {
435            return Err(DecodeError::new("Invalid message hash"));
436        }
437
438        // Decode the rest of the slice into a Protobuf message
439        let decode_slice = &reply_slice[hash_size..];
440        T::decode(decode_slice)
441    }
442
443    /// Decodes a `ParameterTreeMsg` from a byte slice.
444    ///
445    /// This is a helper function for decoding parameter tree messages sent from the server.
446    ///
447    /// # Arguments:
448    /// * `reply_slice` - The received buffer containing the encoded `ParameterTreeMsg`.
449    ///
450    /// # Returns:
451    /// * `Ok(ParameterTreeMsg)` - The decoded parameter tree message.
452    /// * `Err(DecodeError)` - An error if the decoding fails.
453    fn decode_parameter_tree_msg(reply_slice: &[u8]) -> Result<ParameterTreeMsg, DecodeError> {
454        Self::decode_message::<ParameterTreeMsg>(reply_slice)
455    }
456
457    /// Decodes a `StatusMsg` from a received response slice.
458    ///
459    /// This is a helper function for decoding `StatusMsg` responses from the server.
460    ///
461    /// # Arguments:
462    /// * `reply_slice` - The received buffer containing the encoded `StatusMsg`.
463    ///
464    /// # Returns:
465    /// * `Ok(StatusMsg)` - The decoded status message.
466    /// * `Err(DecodeError)` - An error if decoding the `StatusMsg` fails.
467    fn decode_status_msg(reply_slice: &[u8]) -> Result<StatusMsg, DecodeError> {
468        Self::decode_message::<StatusMsg>(reply_slice)
469    }
470
471    fn decode_parameter_msg(reply_slice: &[u8]) -> Result<ParameterMsg, DecodeError> {
472        Self::decode_message::<ParameterMsg>(reply_slice)
473    }
474
475    /// Sends the provided data buffer to the server using the NNG socket.
476    ///
477    /// This function sends a message buffer over the active NNG socket connection.
478    ///
479    /// # Arguments:
480    /// * `buffer` - A byte slice containing the message to send.
481    ///
482    /// # Returns:
483    /// * `Ok(())` - If the message was successfully sent.
484    /// * `Err(String)` - If sending the message fails.
485    fn send_message(&self, buffer: &[u8]) -> Result<(), String> {
486        unsafe {
487            // Create raw pointer for the buffer
488            let data_ptr = buffer.as_ptr() as *mut std::ffi::c_void;
489            let data_len = buffer.len();
490
491            // Use nng_send API
492            let sock = self
493                .connection_data
494                .sock
495                .ok_or("Socket is not available. Connect first.")?;
496            let rv = nng_c_sys::nng_send(sock, data_ptr, data_len, 0);
497
498            // Check if the send operation was successful
499            if rv != 0 {
500                return Err(format!(
501                    "Failed to send message via NNG. Error code: {}",
502                    rv
503                ));
504            }
505        }
506
507        Ok(())
508    }
509}