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