1use std::cmp::Ordering;
2use std::fs;
3use std::string::ToString;
4use bytemuck::{cast_slice, pod_collect_to_vec};
5use serde::{Deserialize, Serialize};
6use serde_with::{skip_serializing_none};
7use hdf5_metno::{Extent, File, H5Type};
8use num_complex::Complex;
9use crate::consts::{ARCHIVE_FILE_CHUNK_SIZE, DATA_ARCHIVE_DIR, DATA_CHUNK_SIZE};
10use crate::plugins::radar_packet::NetType::Archiver;
11
12pub static VERSION:&str = env!("CARGO_PKG_VERSION");
14
15#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
17pub enum NetType {
18 Server,
19 Client,
20 Archiver,
21}
22
23#[derive(Serialize,Deserialize,Debug, PartialEq,Clone)]
25#[serde(untagged)]
26pub enum SettingType{
27 SettingData(SettingData),
28 SettingInfo(Vec<SettingInfo>),
29}
30#[derive(Serialize,Deserialize,Debug,Clone)]
32pub struct Identity {
33 pub(crate) net_type:NetType,
34 pub(crate) version:String,
35}
36
37#[derive(H5Type,Clone,Copy,Serialize,Deserialize,Debug)]
39#[repr(C)]
40pub struct State {
41 pub(crate) angle:f64,
42 pub(crate) antenna:u8,
43 pub(crate) enabled:bool,
44 pub(crate) samples:u64,
45 pub(crate) rotation_rate:f64,
46}
47
48#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
50pub struct SettingData {
51 pub samples:Option<u16>,
52 pub sample_rate:Option<f64>,
53 pub playback_delay:Option<u16>,
54}
55
56
57#[derive(Serialize,Deserialize,Debug,Clone)]
59pub struct SettingsPacket {
60 pub(crate) identity:Identity,
61 pub(crate) controls:SettingType,
62}
63#[derive(Serialize, Deserialize, Debug, Clone)]
67pub struct ComPacket {
68 pub(crate) identity:Identity,
69 pub(crate) timestamp:f64,
70 pub(crate) state:State,
71 #[serde(with = "serde_bytes")]
72 pub(crate) data:Vec<u8>,
73}
74
75#[derive(H5Type,Serialize,Clone,Copy)]
77#[repr(C)]
78pub struct HDF5Packet {
79 timestamp:f64,
80 state:State,
81}
82
83impl From<&ComPacket> for HDF5Packet {
85 fn from(packet: &ComPacket) -> Self {
86 Self {
87 timestamp: packet.timestamp,
88 state: packet.state,
89 }
90 }
91}
92
93impl HDF5Packet {
96 #[expect(dead_code)] pub fn find_index_in_file(timestamp:f64, file: File) -> Option<usize> {
98 let timestamps_ds = match file.dataset("timestamps"){
99 Ok(ds) => ds,
100 Err(_) => return None,
101 };
102 let timestamps = match timestamps_ds.read_raw::<f64>(){
103 Ok(ts) => ts,
104 Err(_) => return None,
105 };
106
107 match timestamps.binary_search_by(|&ts| ts.partial_cmp(×tamp).unwrap_or(Ordering::Equal)) {
108 Ok(idx) => Some(idx), Err(idx) => { if idx == 0 {
111 Some(idx)
112 } else if idx == timestamps.len(){
113 Some(idx-1)
114 } else {
115 let previous_diff = (timestamp - timestamps[idx-1]).abs();
116 let next_diff = (timestamps[idx] - timestamp).abs();
117 if previous_diff <= next_diff {
118 Some(idx-1)
119 } else {
120 Some(idx)
121 }
122 }
123 }
124 }
125 }
126
127 #[expect(dead_code)] pub fn find_file_in_data_dir(timestamp:f64) -> File {
129 let mut data_path = dirs::home_dir().expect("Failed to get home directory");
130 data_path.push(DATA_ARCHIVE_DIR);
131 let epoch_secs = timestamp as i64;
132 let name = [
133 data_path.to_str().unwrap(),
135 (epoch_secs - (epoch_secs % ARCHIVE_FILE_CHUNK_SIZE)).to_string().as_str()
137 ,".h5"
139 ].concat();
140 File::open(&name).unwrap_or_else(|_| {
141 fs::create_dir_all(data_path.to_str().unwrap()).expect("Failed to create data dir"); File::create(&name).expect("Couldn't create radar-data.h5 file")
143 })
144 }
145}
146
147
148#[skip_serializing_none]
150#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
151pub struct SettingInfo {
152 id: String,
153 name:String,
154 min:Option<String>,
155 max:Option<String>,
156 step:Option<String>,
157 unit:Option<String>,
158 values:Option<Vec<SettingInfo>>,
159}
160
161impl SettingInfo {
163 pub fn get_archiver_settings() -> Vec<SettingInfo> {
164 let mut vals = Vec::<SettingInfo>::new();
165 vals.push(SettingInfo {
166 id: "playback_delay".to_string(),
167 name: "Playback Delay".to_string(),
168 min: Some("0".to_string()),
169 max: Some(u16::MAX.to_string()),
170 step: Some("1".to_string()),
171 unit: Some("Ms".to_string()),
172 values: None,
173 });
174 vals
175 }
176 pub fn get_server_settings() -> Vec<SettingInfo> {
177 let mut vals = Vec::<SettingInfo>::new();
178 vals.push(SettingInfo {
179 id: "samples".to_string(),
180 name: "Samples".to_string(),
181 min: Some("0".to_string()),
182 max: Some(u16::MAX.to_string()),
183 step: Some("1".to_string()),
184 unit: None,
185 values: None,
186 });
187 vals.push(SettingInfo {
188 id: "sample_rate".to_string(),
189 name: "Sample Rate".to_string(),
190 min: Some("0".to_string()),
191 max: Some(u16::MAX.to_string()),
192 step: None,
193 unit: Some("Samples/Sec".to_string()),
194 values: None,
195 });
196 vals
197 }
198}
199
200pub trait Hdf5Object{
202 fn to_hdf5(&self, file:&mut File) -> hdf5_metno::Result<()>;
204
205 fn from_hdf5(idx:usize, file: &File) -> hdf5_metno::Result<Self> where Self: Sized;
207}
208
209impl Hdf5Object for ComPacket {
211 fn to_hdf5(&self, file: &mut File) -> hdf5_metno::Result<()> {
212 let timestamps = match file.dataset("timestamps") {
213 Ok(ds) => ds,
214 Err(_) => {
215 println!("creating new timestamps dataset");
216 file.new_dataset::<f64>()
217 .chunk((1,))
218 .shape(Extent::resizable(0))
219 .create("timestamps").expect("failed to create new dataset for timestamps")
220 }
221 };
222
223 let metadata = match file.dataset("metadata") {
224 Ok(ds) => ds,
225 Err(_) => {
226 println!("creating new metadata dataset");
227 file.new_dataset::<HDF5Packet>()
228 .chunk((1,))
229 .shape(Extent::resizable(0))
230 .create("metadata").expect("failed to create new dataset for timestamps")
231 }
232 };
233
234 let data = match file.dataset("data") {
235 Ok(ds) => ds,
236 Err(_) => {
237 println!("creating new data dataset");
238 file.new_dataset::<Complex<i32>>()
239 .chunk((1,DATA_CHUNK_SIZE))
240 .shape((1..,DATA_CHUNK_SIZE))
241 .create("data").expect("failed to create new dataset for timestamps")
242 }
243 };
244
245 let ds:&[Complex<i32>] = cast_slice(self.data.as_slice());
246
247 let mut idx = timestamps.size();
248 let mut written = 0usize;
249 let mut writes = 0;
250
251 while written < ds.len() {
252 let slice = &ds[written..DATA_CHUNK_SIZE + (DATA_CHUNK_SIZE * writes)];
253 timestamps.resize(idx + 1)?;
254 metadata.resize(idx + 1)?;
255 data.resize((idx+1,DATA_CHUNK_SIZE))?;
256 timestamps.write_slice(&[self.timestamp],idx..idx+1)?;
257 metadata.write_slice(&[HDF5Packet::from(self)],idx..idx+1)?;
258 data.write_slice(slice, (idx,..))?;
259 written += DATA_CHUNK_SIZE;
260 writes += 1;
261 idx += 1;
262 }
263
264 file.flush()?;
265 Ok(())
266 }
267
268 fn from_hdf5(idx:usize,file: &File) -> hdf5_metno::Result<Self> {
269 let metadata = match file.dataset("metadata") {
270 Ok(ds) => ds,
271 Err(e) => return Err(e),
272 };
273
274 let data = match file.dataset("data") {
275 Ok(ds) => ds,
276 Err(e) => return Err(e)
277 };
278 let meta:HDF5Packet = metadata.read_slice(idx..).expect("Failed to read metadata").to_vec()[0];
279 let data:Vec<Complex<i32>> = data.read_slice((idx,..)).expect("failed to read data").to_vec();
280
281 Ok(ComPacket{
282 identity:Identity{
283 net_type:Archiver,
284 version: VERSION.to_string()
285 },
286 timestamp: meta.timestamp,
287 state:meta.state,
288 data:pod_collect_to_vec(data.as_slice()) })
290 }
291}