dvrip_rs/commands/
upgrade.rs1use 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 async fn get_upgrade_info(&mut self) -> Result<Value>;
18
19 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 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 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 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 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 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}