1use std::fmt;
2use std::str;
3
4use super::super::*;
5use super::*;
6
7impl Sexagesimal {
8 fn format_indi_style(&self, precision: usize) -> String {
9 let is_negative = self.hour < 0.0;
10 let hour = self.hour.abs();
11
12 let minute = self.minute.unwrap_or_else(|| (hour - hour.trunc()) * 60.0);
14
15 let second = self
17 .second
18 .unwrap_or_else(|| (minute - minute.trunc()) * 60.0);
19
20 let result = match precision {
22 9 => format!(
23 "{:02}:{:02}.{:02}",
24 minute.trunc() as i64,
25 second.trunc() as i64,
26 ((second % 1.0) * 100.0).round() as i64
27 ),
28 8 => format!(
29 "{:02}:{:02}.{:01}",
30 minute.trunc() as i64,
31 second.trunc() as i64,
32 ((second % 1.0) * 10.0).round() as i64
33 ),
34 6 => format!("{:02}:{:02}", minute.trunc() as i64, second.round() as i64),
35 5 => format!(
36 "{:02}.{:01}",
37 minute.trunc() as i64,
38 ((minute % 1.0) * 10.0).round() as i64
39 ),
40 3 => format!("{:02}", minute.round() as i64),
41 _ => format!("{:02}:{:02}", minute.trunc() as i64, second.round() as i64),
42 };
43
44 let mut final_result = String::new();
46 if is_negative {
47 final_result.push('-');
48 }
49 if hour >= 1.0 || hour.trunc() != 0.0 {
50 final_result.push_str(&format!("{}:", hour.trunc() as i64));
51 }
52 final_result.push_str(&result);
53
54 final_result
55 }
56
57 fn format_double(&self) -> f64 {
58 let mut value = self.hour;
59 if let Some(min) = self.minute {
60 value += min / 60.0;
61 if let Some(sec) = self.second {
62 value += sec / 3600.0;
63 }
64 }
65 value
66 }
67}
68
69fn parse_m_format(format: &str) -> Option<usize> {
70 let parts: Vec<&str> = format.split('.').collect();
72 if parts.len() != 2 {
73 return None;
74 }
75
76 if !parts[1].ends_with('m') {
77 return None;
78 }
79
80 let precision_str = &parts[1][..parts[1].len() - 1];
82 precision_str.parse().ok()
83}
84
85impl fmt::Display for Number {
86 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
87 if let Some(m_precision) = parse_m_format(&self.format) {
89 let formatted = self.value.format_indi_style(m_precision);
91 if let Some(width) = f.width() {
93 if f.sign_aware_zero_pad() {
94 write!(f, "{:0>width$}", formatted, width = width)
95 } else if f.align() == Some(fmt::Alignment::Left) {
96 write!(f, "{:<width$}", formatted, width = width)
97 } else {
98 write!(f, "{:>width$}", formatted, width = width)
99 }
100 } else {
101 write!(f, "{}", formatted)
102 }
103 } else {
104 write!(f, "{}", self.value.format_double())
106 }
107 }
108}
109
110impl<'de> Deserialize<'de> for Sexagesimal {
111 fn deserialize<D>(deserializer: D) -> Result<Sexagesimal, D::Error>
112 where
113 D: serde::Deserializer<'de>,
114 {
115 let s: String = Deserialize::deserialize(deserializer)?;
116 let mut components = s.split([' ', ':']);
117
118 let hour = components
119 .next()
120 .map(str::parse)
121 .transpose()
122 .unwrap()
123 .unwrap();
124 let minute = components.next().map(str::parse).transpose().unwrap();
125 let second = components.next().map(str::parse).transpose().unwrap();
126
127 Ok(Sexagesimal {
128 hour,
129 minute,
130 second,
131 })
132 }
133}
134
135impl Serialize for Sexagesimal {
136 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
137 where
138 S: serde::Serializer,
139 {
140 serializer.serialize_str(format!("{}", self).as_str())
141 }
142}
143
144impl std::fmt::Display for Sexagesimal {
145 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
146 write!(f, "{}", self.hour)?;
147 if let Some(minute) = self.minute {
148 write!(f, ":{}", minute)?;
149 }
150 if let Some(second) = self.second {
151 write!(f, ":{}", second)?;
152 }
153
154 Ok(())
155 }
156}
157
158impl From<f64> for Sexagesimal {
159 fn from(value: f64) -> Self {
160 Self {
161 hour: value as f64,
162 minute: None,
163 second: None,
164 }
165 }
166}
167
168impl From<usize> for Sexagesimal {
169 fn from(value: usize) -> Self {
170 Self {
171 hour: value as f64,
172 minute: None,
173 second: None,
174 }
175 }
176}
177
178impl From<u64> for Sexagesimal {
179 fn from(value: u64) -> Self {
180 Self {
181 hour: value as f64,
182 minute: None,
183 second: None,
184 }
185 }
186}
187
188impl From<i32> for Sexagesimal {
189 fn from(value: i32) -> Self {
190 Self {
191 hour: value as f64,
192 minute: None,
193 second: None,
194 }
195 }
196}
197
198impl From<u32> for Sexagesimal {
199 fn from(value: u32) -> Self {
200 Self {
201 hour: value as f64,
202 minute: None,
203 second: None,
204 }
205 }
206}
207
208impl From<u16> for Sexagesimal {
209 fn from(value: u16) -> Self {
210 Self {
211 hour: value as f64,
212 minute: None,
213 second: None,
214 }
215 }
216}
217
218impl From<u8> for Sexagesimal {
219 fn from(value: u8) -> Self {
220 Self {
221 hour: value as f64,
222 minute: None,
223 second: None,
224 }
225 }
226}
227
228impl From<Sexagesimal> for f64 {
229 fn from(value: Sexagesimal) -> Self {
230 let mut val = value.hour;
231
232 let sign = value.hour.signum();
233 let div = 60.0;
234
235 if let Some(minute) = value.minute {
236 val += sign * minute / div;
237 }
238 if let Some(second) = value.second {
239 val += sign * second / (div * div);
240 }
241
242 val
243 }
244}
245
246impl CommandtoParam for DefNumberVector {
247 fn get_name(&self) -> &String {
248 &self.name
249 }
250 fn get_group(&self) -> &Option<String> {
251 &self.group
252 }
253 fn to_param(self) -> Parameter {
254 Parameter::NumberVector(NumberVector {
255 name: self.name,
256 group: self.group,
257 label: self.label,
258 state: self.state,
259 perm: self.perm,
260 timeout: self.timeout,
261 timestamp: self.timestamp.map(Timestamp::into_inner),
262 values: self
263 .numbers
264 .into_iter()
265 .map(|i| {
266 (
267 i.name,
268 Number {
269 label: i.label,
270 format: i.format,
271 min: i.min,
272 max: i.max,
273 step: i.step,
274 value: i.value,
275 },
276 )
277 })
278 .collect(),
279 })
280 }
281}
282
283impl CommandToUpdate for SetNumberVector {
284 fn get_name(&self) -> &String {
285 &self.name
286 }
287
288 fn update_param(self, param: &mut Parameter) -> Result<String, UpdateError> {
289 match param {
290 Parameter::NumberVector(number_vector) => {
291 number_vector.state = self.state;
292 number_vector.timeout = self.timeout;
293 number_vector.timestamp = self.timestamp.map(Timestamp::into_inner);
294 for number in self.numbers {
295 if let Some(existing) = number_vector.values.get_mut(&number.name) {
296 existing.min = number.min.unwrap_or(existing.min);
297 existing.max = number.max.unwrap_or(existing.max);
298 existing.step = number.step.unwrap_or(existing.step);
299 existing.value = number.value;
300 }
301 }
302 Ok(self.name)
303 }
304 _ => Err(UpdateError::ParameterTypeMismatch(self.name.clone())),
305 }
306 }
307}
308
309#[cfg(test)]
310mod tests {
311 use super::*;
312 #[test]
315 fn test_def_number() {
316 let xml = r#"
317 <defNumber name="SIM_XRES" label="CCD X resolution" format="%4.0f" min="512" max="8192" step="512">
318 1280
319 </defNumber>
320 "#;
321 let command: Result<DefNumber, _> = quick_xml::de::from_str(xml);
322
323 match command {
324 Ok(param) => {
325 assert_eq!(param.name, "SIM_XRES");
326 assert_eq!(param.label, Some(String::from("CCD X resolution")));
327 assert_eq!(param.value, 1280.0.into());
328 }
329 Err(e) => {
330 panic!("Unexpected: {:?}", e);
331 }
332 }
333 }
334
335 #[test]
336 fn test_def_number_vector() {
337 let xml = r#"
338 <defNumberVector device="CCD Simulator" name="SIMULATOR_SETTINGS" label="Settings" group="Simulator Config" state="Idle" perm="rw" timeout="60" timestamp="2022-08-12T05:52:27">
339 <defNumber name="SIM_XRES" label="CCD X resolution" format="%4.0f" min="512" max="8192" step="512">
340 1280
341 </defNumber>
342 <defNumber name="SIM_YRES" label="CCD Y resolution" format="%4.0f" min="512" max="8192" step="512">
343 1024
344 </defNumber>
345 <defNumber name="SIM_XSIZE" label="CCD X Pixel Size" format="%4.2f" min="1" max="30" step="5">
346 5.2000000000000001776
347 </defNumber>
348 </defNumberVector>
349 "#;
350 let command: Result<DefNumberVector, _> = quick_xml::de::from_str(xml);
351
352 match command {
353 Ok(param) => {
354 assert_eq!(param.device, "CCD Simulator");
355 assert_eq!(param.name, "SIMULATOR_SETTINGS");
356 assert_eq!(param.numbers.len(), 3)
357 }
358 e => {
359 panic!("Unexpected: {:?}", e)
360 }
361 }
362 }
363
364 #[test]
365 fn test_set_number_vector() {
366 let xml = r#"
367 <setNumberVector device="CCD Simulator" name="SIM_FOCUSING" state="Ok" timeout="60" timestamp="2022-10-01T21:21:10">
368 <oneNumber name="SIM_FOCUS_POSITION">
369 7340
370 </oneNumber>
371 <oneNumber name="SIM_FOCUS_MAX">
372 100000
373 </oneNumber>
374 <oneNumber name="SIM_SEEING">
375 3.5
376 </oneNumber>
377 </setNumberVector>
378"#;
379
380 let command: Result<SetNumberVector, _> = quick_xml::de::from_str(xml);
381
382 match command {
383 Ok(param) => {
384 assert_eq!(param.device, "CCD Simulator");
385 assert_eq!(param.name, "SIM_FOCUSING");
386 assert_eq!(param.numbers.len(), 3)
387 }
388 e => {
389 panic!("Unexpected: {:?}", e)
390 }
391 }
392 }
393
394 #[test]
395 fn test_new_number_vector() {
396 let xml = r#"
397 <newNumberVector device="CCD Simulator" name="SIM_FOCUSING" timestamp="2022-10-01T21:21:10">
398 <oneNumber name="SIM_FOCUS_POSITION">
399 7340
400 </oneNumber>
401 <oneNumber name="SIM_FOCUS_MAX">
402 100000
403 </oneNumber>
404 <oneNumber name="SIM_SEEING">
405 3.5
406 </oneNumber>
407 </newNumberVector>
408 "#;
409
410 let command: Result<NewNumberVector, _> = quick_xml::de::from_str(xml);
411
412 match command {
413 Ok(param) => {
414 assert_eq!(param.device, "CCD Simulator");
415 assert_eq!(param.name, "SIM_FOCUSING");
416 assert_eq!(param.numbers.len(), 3)
417 }
418 e => {
419 panic!("Unexpected: {:?}", e)
420 }
421 }
422 }
423
424 #[test]
425 fn test_parse_number_normal() {
426 let xml = r#"-10.505"#;
427
428 let event: Result<Sexagesimal, _> = quick_xml::de::from_str(xml);
429
430 if let Ok(e) = event {
431 assert_eq!(Into::<Sexagesimal>::into(-10.505), e);
432 } else {
433 panic!("Unexpected");
434 }
435 }
436
437 #[test]
438 fn test_parse_number_sexagesimal_1() {
439 let xml = r#"-10 30.3"#;
440
441 let event: Result<Sexagesimal, _> = quick_xml::de::from_str(xml);
442
443 if let Ok(e) = event {
444 assert_eq!(
445 Sexagesimal {
446 hour: -10.,
447 minute: Some(30.3),
448 second: None
449 },
450 e.into()
451 );
452 } else {
453 panic!("Unexpected");
454 }
455 }
456
457 #[test]
458 fn test_parse_number_sexagesimal_2() {
459 let xml = r#"-10:30:18"#;
460
461 let event: Result<Sexagesimal, _> = quick_xml::de::from_str(xml);
462
463 if let Ok(e) = event {
464 assert_eq!(
465 Sexagesimal {
466 hour: -10.0,
467 minute: Some(30.),
468 second: Some(18.)
469 },
470 e.into()
471 );
472 } else {
473 panic!("Unexpected");
474 }
475 }
476
477 #[test]
478 fn test_send_new_number_vector() {
479 let timestamp = DateTime::from_str("2022-10-13T07:41:56.301Z")
480 .unwrap()
481 .into();
482
483 let command = NewNumberVector {
484 device: String::from_str("CCD Simulator").unwrap(),
485 name: String::from_str("Exposure").unwrap(),
486 timestamp: Some(timestamp),
487 numbers: vec![OneNumber {
488 name: String::from_str("seconds").unwrap(),
489 value: 3.0.into(),
490 }],
491 };
492
493 let result = quick_xml::se::to_string(&command).unwrap();
497 assert_eq!(
498 result,
499 String::from_str("<newNumberVector device=\"CCD Simulator\" name=\"Exposure\" timestamp=\"2022-10-13T07:41:56.301\"><oneNumber name=\"seconds\">3</oneNumber></newNumberVector>").unwrap()
500 );
501 }
502}