1use chrono::{Duration, Local, Utc};
2
3use pyrinas_shared::ota::v2::{OTAImageData, OTAImageType, OTAPackage, OtaUpdate};
5use pyrinas_shared::{
6 ManagementData, ManagmentDataType, OtaGroupListResponse, OtaImageListResponse,
7};
8
9use serde_cbor;
11
12use std::fs::File;
14use std::io::{self, prelude::*};
15use std::net::TcpStream;
16
17use tungstenite::{protocol::WebSocket, stream::MaybeTlsStream, Message};
19
20use thiserror::Error;
22
23use crate::{git, OtaLink, OtaSubCommand};
24
25#[derive(Debug, Error)]
26pub enum Error {
27 #[error("file error: {source}")]
28 FileError {
29 #[from]
30 source: io::Error,
31 },
32
33 #[error("serde_cbor error: {source}")]
35 CborError {
36 #[from]
37 source: serde_cbor::Error,
38 },
39
40 #[error("websocket error: {source}")]
42 WebsocketError {
43 #[from]
44 source: tungstenite::Error,
45 },
46
47 #[error("repository is dirty. Run --force to override")]
49 DirtyError,
50
51 #[error("{source}")]
53 GitError {
54 #[from]
55 source: git::GitError,
56 },
57}
58
59pub fn process(
61 socket: &mut WebSocket<MaybeTlsStream<TcpStream>>,
62 cmd: &OtaSubCommand,
63) -> Result<(), Error> {
64 match cmd {
65 OtaSubCommand::Add(a) => {
66 let image_id = crate::ota::add_ota(socket, a.force)?;
67
68 println!("{} image successfully uploaded!", &image_id);
69
70 match &a.device_id {
72 Some(device_id) => {
73 let a = OtaLink {
74 device_id: Some(device_id.clone()),
75 group_id: Some(device_id.to_string()),
76 image_id: Some(image_id),
77 ota_version: a.ota_version,
78 };
79
80 crate::ota::link(socket, &a)?;
81
82 println!("OTA Linked! {:?}", &a);
83 }
84 None => (),
85 };
86 }
87 OtaSubCommand::Remove(r) => {
88 crate::ota::remove_ota(socket, &r.image_id)?;
89
90 println!("{} successfully removed!", &r.image_id);
91 }
92 OtaSubCommand::Unlink(a) => {
93 crate::ota::unlink(socket, a)?;
94
95 println!("OTA Unlinked! {:?}", a);
96 }
97 OtaSubCommand::Link(a) => {
98 crate::ota::link(socket, a)?;
99
100 println!("OTA Linked! {:?}", &a);
101 }
102 OtaSubCommand::ListGroups => {
103 crate::ota::get_ota_group_list(socket)?;
104
105 let start = Utc::now();
106
107 loop {
109 if Utc::now() > start + Duration::seconds(10) {
110 eprintln!("No response from server!");
111 break;
112 }
113
114 match socket.read_message() {
115 Ok(msg) => {
116 let data = match msg {
117 tungstenite::Message::Binary(b) => b,
118 _ => {
119 eprintln!("Unexpected WS message!");
120 break;
121 }
122 };
123
124 let list: OtaGroupListResponse = match serde_cbor::from_slice(&data) {
125 Ok(m) => m,
126 Err(e) => {
127 eprintln!("Unable to get image list! Error: {}", e);
128 break;
129 }
130 };
131
132 for name in list.groups.iter() {
133 println!("{}", name);
135 }
136
137 break;
138 }
139 Err(_) => continue,
140 };
141 }
142 }
143 OtaSubCommand::ListImages => {
144 crate::ota::get_ota_image_list(socket)?;
145
146 let start = Utc::now();
147
148 loop {
150 if Utc::now() > start + Duration::seconds(10) {
151 eprintln!("No response from server!");
152 break;
153 }
154
155 match socket.read_message() {
156 Ok(msg) => {
157 let data = match msg {
158 tungstenite::Message::Binary(b) => b,
159 _ => {
160 eprintln!("Unexpected WS message!");
161 break;
162 }
163 };
164
165 let list: OtaImageListResponse = match serde_cbor::from_slice(&data) {
166 Ok(m) => m,
167 Err(e) => {
168 eprintln!("Unable to get image list! Error: {}", e);
169 break;
170 }
171 };
172
173 for (name, package) in list.images.iter() {
174 let date = match package.date_added {
176 Some(d) => d.with_timezone(&Local).to_string(),
177 None => "".to_string(),
178 };
179
180 println!("{} {}", name, date);
182 }
183
184 break;
185 }
186 Err(_) => continue,
187 };
188 }
189 }
190 };
191
192 Ok(())
193}
194
195pub fn add_ota(
197 stream: &mut WebSocket<MaybeTlsStream<TcpStream>>,
198 force: bool,
199) -> Result<String, Error> {
200 let ver = crate::git::get_git_describe()?;
202
203 let (package_version, dirty) = crate::git::get_ota_package_version(&ver)?;
205
206 if dirty && !force {
208 return Err(Error::DirtyError);
209 }
210
211 let path = "./build/zephyr/app_update.bin";
213
214 let mut buf: Vec<u8> = Vec::new();
216 let mut file = File::open(&path)?;
217 let size = file.read_to_end(&mut buf)?;
218
219 println!("Reading {} bytes from firmware update binary.", size);
220
221 let new = OtaUpdate {
223 uid: None,
224 package: Some(OTAPackage {
225 version: package_version.clone(),
226 files: Vec::new(),
227 date_added: Some(Utc::now()),
228 }),
229 images: Some(
230 [OTAImageData {
231 data: buf,
232 image_type: OTAImageType::Primary,
233 }]
234 .to_vec(),
235 ),
236 };
237
238 let data = serde_cbor::to_vec(&new)?;
240
241 let msg = ManagementData {
243 cmd: ManagmentDataType::AddOta,
244 target: None,
245 msg: data,
246 };
247
248 let data = serde_cbor::to_vec(&msg)?;
250
251 stream.write_message(Message::binary(data))?;
253
254 Ok(package_version.to_string())
255}
256
257pub fn unlink(
258 stream: &mut WebSocket<MaybeTlsStream<TcpStream>>,
259 link: &OtaLink,
260) -> Result<(), Error> {
261 let msg = ManagementData {
263 cmd: ManagmentDataType::UnlinkOta,
264 target: None,
265 msg: serde_cbor::to_vec(link)?,
266 };
267
268 let data = serde_cbor::to_vec(&msg)?;
270
271 stream.write_message(Message::binary(data))?;
273
274 Ok(())
275}
276
277pub fn link(
278 stream: &mut WebSocket<MaybeTlsStream<TcpStream>>,
279 link: &OtaLink,
280) -> Result<(), Error> {
281 let msg = ManagementData {
283 cmd: ManagmentDataType::LinkOta,
284 target: None,
285 msg: serde_cbor::to_vec(link)?,
286 };
287
288 let data = serde_cbor::to_vec(&msg)?;
290
291 stream.write_message(Message::binary(data))?;
293
294 Ok(())
295}
296
297pub fn remove_ota(
299 stream: &mut WebSocket<MaybeTlsStream<TcpStream>>,
300 image_id: &str,
301) -> Result<(), Error> {
302 let msg = ManagementData {
304 cmd: ManagmentDataType::RemoveOta,
305 target: None,
306 msg: image_id.as_bytes().to_vec(),
307 };
308
309 let data = serde_cbor::to_vec(&msg)?;
311
312 stream.write_message(Message::binary(data))?;
314
315 Ok(())
316}
317
318pub fn get_ota_group_list(stream: &mut WebSocket<MaybeTlsStream<TcpStream>>) -> Result<(), Error> {
319 let msg = ManagementData {
321 cmd: ManagmentDataType::GetGroupList,
322 target: None,
323 msg: [].to_vec(),
324 };
325
326 let data = serde_cbor::to_vec(&msg)?;
328
329 stream.write_message(Message::binary(data))?;
331
332 Ok(())
333}
334
335pub fn get_ota_image_list(stream: &mut WebSocket<MaybeTlsStream<TcpStream>>) -> Result<(), Error> {
336 let msg = ManagementData {
338 cmd: ManagmentDataType::GetImageList,
339 target: None,
340 msg: [].to_vec(),
341 };
342
343 let data = serde_cbor::to_vec(&msg)?;
345
346 stream.write_message(Message::binary(data))?;
348
349 Ok(())
350}