ping_viewer_next/device/manager/
continuous_mode.rs

1use serde_json::json;
2use tracing::{error, trace, warn};
3use uuid::Uuid;
4
5use crate::device::{
6    manager::{Answer, DeviceAnswer, ManagerError},
7    manager::{DeviceManager, DeviceSelection},
8};
9
10impl DeviceManager {
11    // Call the helpers specifically for each device type
12    pub async fn continuous_mode_start(
13        &mut self,
14        mut subscriber: tokio::sync::broadcast::Receiver<
15            bluerobotics_ping::message::ProtocolMessage,
16        >,
17        device_id: Uuid,
18        device_type: DeviceSelection,
19    ) -> Option<tokio::task::JoinHandle<()>> {
20        let raw_handler = match self.get_device_handler(device_id).await {
21            Ok(handler) => handler,
22            Err(err) => {
23                trace!("Error during start_continuous_mode: Failed to get device handler: {err:?}");
24                return None;
25            }
26        };
27
28        let handler = match self.extract_handler(raw_handler) {
29            Ok(handler) => handler,
30            Err(err) => {
31                trace!("Error during start_continuous_mode: Failed to extract handler: {err:?}");
32                return None;
33            }
34        };
35
36        match device_type {
37            DeviceSelection::Ping1D => Some(tokio::spawn(async move {
38                loop {
39                    match subscriber.recv().await {
40                        Ok(msg) => {
41                            Self::ping1d_continuous_mode_helper(msg, device_id);
42                        }
43                        Err(err) => {
44                            Self::handle_error_continuous_mode(err, device_id);
45                            break;
46                        }
47                    }
48                }
49            })),
50            DeviceSelection::Ping360 => {
51                Some(tokio::spawn(async move {
52                    // Attempt to send the Ping360 request and handle the result
53                    let device_data = match handler
54                        .send(crate::device::devices::PingRequest::Ping360(
55                            crate::device::devices::Ping360Request::DeviceData,
56                        ))
57                        .await
58                    {
59                        Ok(response) => match response {
60                            crate::device::devices::PingAnswer::PingMessage(
61                                bluerobotics_ping::Messages::Ping360(
62                                    bluerobotics_ping::ping360::Messages::DeviceData(msg),
63                                ),
64                            ) => msg,
65                            msg => {
66                                error!("Error during start_continuous_mode: unexpected message: {msg:?}");
67                                return;
68                            }
69                        },
70                        Err(err) => {
71                            error!("Error during start_continuous_mode: Device Error: {err:?}");
72                            return;
73                        }
74                    };
75
76                    loop {
77                        for n in 0..=399 {
78                            // Handle timeout and errors
79                            let result = tokio::time::timeout(
80                                std::time::Duration::from_millis(1000),
81                                handler.send(crate::device::devices::PingRequest::Ping360(
82                                    crate::device::devices::Ping360Request::Transducer(
83                                        bluerobotics_ping::ping360::TransducerStruct {
84                                            mode: device_data.mode,
85                                            gain_setting: device_data.gain_setting,
86                                            transmit_duration: device_data.transmit_duration,
87                                            sample_period: device_data.sample_period,
88                                            transmit_frequency: device_data.transmit_frequency,
89                                            number_of_samples: device_data.number_of_samples,
90                                            angle: n,
91                                            transmit: 1,
92                                            reserved: 0,
93                                        },
94                                    ),
95                                )),
96                            )
97                            .await;
98
99                            match result {
100                                Ok(Ok(answer)) => match answer {
101                                    crate::device::devices::PingAnswer::PingMessage(msg) => {
102                                        Self::ping360_continuous_mode_helper(msg, device_id)
103                                    }
104                                    msg => {
105                                        error!("Error during continuous_mode: Unexpected Message: {msg:?}");
106                                        return;
107                                    }
108                                },
109                                Ok(Err(err)) => {
110                                    error!("Error during continuous_mode: Device Error: {err:?}");
111                                    return;
112                                }
113                                Err(_err) => {
114                                    warn!("Error during continuous_mode: Answer delayed more than 1 s");
115                                }
116                            }
117                        }
118                    }
119                }))
120            }
121            DeviceSelection::Common | DeviceSelection::Auto => None,
122        }
123    }
124
125    // Execute some especial commands required for device enter in auto_send mode
126    pub async fn continuous_mode_startup_routine(
127        &self,
128        device_id: Uuid,
129        device_type: DeviceSelection,
130    ) -> Result<(), ManagerError> {
131        if device_type == DeviceSelection::Ping1D {
132            let handler_request = self.get_device_handler(device_id).await?;
133            let handler = self.extract_handler(handler_request)?;
134
135            let id = <bluerobotics_ping::ping1d::ProfileStruct as bluerobotics_ping::message::MessageInfo>::id();
136            let _ = handler
137                .send(crate::device::devices::PingRequest::Ping1D(
138                    crate::device::devices::Ping1DRequest::ContinuousStart(
139                        bluerobotics_ping::ping1d::ContinuousStartStruct { id },
140                    ),
141                ))
142                .await
143                .map_err(|err| {trace!("Something went wrong while executing continuous_mode_startup, details: {err:?}"); ManagerError::DeviceError(err)})?;
144        }
145        Ok(())
146    }
147
148    // Execute some especial commands required for device stop auto_send mode
149    pub async fn continuous_mode_shutdown_routine(
150        &self,
151        device_id: Uuid,
152        device_type: DeviceSelection,
153    ) -> Result<(), ManagerError> {
154        let handler_request = self.get_device_handler(device_id).await?;
155        let handler = self.extract_handler(handler_request)?;
156
157        if device_type == DeviceSelection::Ping1D {
158            let id = <bluerobotics_ping::ping1d::ProfileStruct as bluerobotics_ping::message::MessageInfo>::id();
159            let _ = handler
160                .send(crate::device::devices::PingRequest::Ping1D(
161                    crate::device::devices::Ping1DRequest::ContinuousStop(
162                        bluerobotics_ping::ping1d::ContinuousStopStruct { id },
163                    ),
164                ))
165                .await
166                .map_err(|err| {trace!("Something went wrong while executing broadcast_startup_routine, details: {err:?}"); ManagerError::DeviceError(err)})?;
167        }
168        Ok(())
169    }
170
171    // An inner helper focused on Ping1D, which uses Profile message to plot graphs
172    pub fn ping1d_continuous_mode_helper(
173        msg: bluerobotics_ping::message::ProtocolMessage,
174        device_id: Uuid,
175    ) {
176        if msg.message_id == <bluerobotics_ping::ping1d::ProfileStruct as bluerobotics_ping::message::MessageInfo>::id() {
177            if let Ok(bluerobotics_ping::Messages::Ping1D(bluerobotics_ping::ping1d::Messages::Profile(_answer))) = bluerobotics_ping::Messages::try_from(&msg) {
178                let answer = Answer::DeviceMessage(DeviceAnswer {
179                    answer: crate::device::devices::PingAnswer::PingMessage(
180                        bluerobotics_ping::Messages::try_from(&msg).unwrap(),
181                    ),
182                    device_id,
183                });
184                crate::server::protocols::v1::websocket::send_to_websockets(json!(answer), Some(device_id));
185            }
186        }
187    }
188
189    // An inner helper focused on Ping360, which uses DeviceData message to plot graphs
190    pub fn ping360_continuous_mode_helper(msg: bluerobotics_ping::Messages, device_id: Uuid) {
191        let answer = Answer::DeviceMessage(DeviceAnswer {
192            answer: crate::device::devices::PingAnswer::PingMessage(msg),
193            device_id,
194        });
195        crate::server::protocols::v1::websocket::send_to_websockets(json!(answer), Some(device_id));
196    }
197
198    // An inner helper that returns error to requester
199    pub fn handle_error_continuous_mode(
200        error: tokio::sync::broadcast::error::RecvError,
201        device_id: Uuid,
202    ) {
203        let error = ManagerError::DeviceError(crate::device::devices::DeviceError::PingError(
204            bluerobotics_ping::error::PingError::TokioBroadcastError(error.to_string()),
205        ));
206        crate::server::protocols::v1::websocket::send_to_websockets(json!(error), Some(device_id));
207    }
208}