Skip to main content

dvrip_rs/commands/
upgrade.rs

1use crate::Authentication;
2use crate::constants::OK_CODES;
3use crate::dvrip::DVRIPCam;
4use crate::error::Result;
5use crate::protocol::{receive_json, receive_packet_header, send_packet};
6use async_trait::async_trait;
7use serde_json::{Value, json};
8use std::sync::Arc;
9use tokio::fs::File;
10use tokio::io::AsyncReadExt;
11
12pub type UpgradeProgressCallback = Box<dyn Fn(String) + Send + Sync>;
13
14#[async_trait]
15pub trait Upgrade: Send + Sync {
16    /// Get upgrade information
17    async fn get_upgrade_info(&mut self) -> Result<Value>;
18
19    /// Perform system upgrade
20    async fn upgrade(
21        &mut self,
22        filename: &str,
23        packet_size: usize,
24        progress_callback: Option<UpgradeProgressCallback>,
25    ) -> Result<Value>;
26}
27
28#[async_trait]
29impl Upgrade for DVRIPCam {
30    async fn get_upgrade_info(&mut self) -> Result<Value> {
31        self.get_command("OPSystemUpgrade", None).await
32    }
33
34    async fn upgrade(
35        &mut self,
36        filename: &str,
37        packet_size: usize,
38        progress_callback: Option<UpgradeProgressCallback>,
39    ) -> Result<Value> {
40        // Iniciar upgrade
41        let start_data = json!({
42            "Action": "Start",
43            "Type": "System",
44        });
45
46        let reply = self
47            .set_command("OPSystemUpgrade", start_data, Some(0x5F0))
48            .await?;
49
50        if let Some(ret) = reply.get("Ret").and_then(|r| r.as_u64())
51            && !OK_CODES.contains(&(ret as u32))
52        {
53            return Ok(reply);
54        }
55
56        let callback = progress_callback.map(Arc::new);
57
58        // Send file
59        let mut file = File::open(filename).await?;
60        let mut blocknum = 0u32;
61        let file_metadata = file.metadata().await?;
62        let file_size = file_metadata.len() as usize;
63        let mut sent_bytes = 0usize;
64
65        let mut stream_guard = self.stream.lock().await;
66        if let Some(s) = stream_guard.as_mut() {
67            let (mut reader, mut writer) = s.split();
68            let session = self.session_id();
69
70            loop {
71                let mut buffer = vec![0u8; packet_size];
72                let bytes_read = file.read(&mut buffer).await?;
73
74                if bytes_read == 0 {
75                    break;
76                }
77
78                buffer.truncate(bytes_read);
79
80                send_packet(&mut writer, session, blocknum, 0x5F2, &buffer, 0).await?;
81
82                blocknum += 1;
83                sent_bytes += bytes_read;
84
85                // Verificar resposta
86                let reply_header = receive_packet_header(&mut reader).await?;
87                if reply_header.msg_id == 0x5F2 {
88                    let reply_data =
89                        receive_json(&mut reader, reply_header.data_len as usize, self.timeout)
90                            .await?;
91                    if let Some(ret) = reply_data.get("Ret").and_then(|r| r.as_u64())
92                        && ret != 100
93                    {
94                        if let Some(cb) = &callback {
95                            cb("Upgrade failed".to_string());
96                        }
97                        return Ok(reply_data);
98                    }
99                }
100
101                // Progress
102                if let Some(cb) = &callback {
103                    let progress = (sent_bytes as f64 / file_size as f64) * 100.0;
104                    cb(format!("Uploading: {:.1}%", progress));
105                }
106            }
107
108            let final_packet = vec![0u8; 0];
109            send_packet(&mut writer, session, blocknum, 0x5F2, &final_packet, 0).await?;
110
111            // Wait for upgrade start confirmation
112            loop {
113                let reply_header = receive_packet_header(&mut reader).await?;
114                let reply_data =
115                    receive_json(&mut reader, reply_header.data_len as usize, self.timeout).await?;
116
117                if let Some(ret) = reply_data.get("Ret").and_then(|r| r.as_u64()) {
118                    if ret == 515 {
119                        if let Some(cb) = &callback {
120                            cb("Upgrade successful".to_string());
121                        }
122                        return Ok(reply_data);
123                    } else if [512, 513, 514].contains(&(ret as u32)) {
124                        if let Some(cb) = &callback {
125                            cb("Upgrade failed".to_string());
126                        }
127                        return Ok(reply_data);
128                    } else if ret <= 100
129                        && let Some(cb) = &callback
130                    {
131                        cb(format!("Upgrading: {}%", ret));
132                    }
133                }
134            }
135        }
136
137        Err(crate::error::DVRIPError::ConnectionError(
138            "Stream not available".to_string(),
139        ))
140    }
141}