1use std::{cell::RefCell, clone::Clone, fmt, rc::Rc};
4
5use chrono::{DateTime, TimeZone, Utc};
6
7use crate::{
8 data::Data,
9 error::Result,
10 packet::{PacketFieldId, PacketId},
11 specification_file::{
12 Language, PacketTemplateFieldPart, SpecificationFile, Type, Unit, UnitFamily, UnitId,
13 },
14};
15
16#[derive(Debug)]
33pub struct DeviceSpec {
34 pub device_id: String,
36
37 pub channel: u8,
39
40 pub self_address: u16,
42
43 pub peer_address: Option<u16>,
45
46 pub name: String,
48}
49
50#[derive(Debug)]
70pub struct PacketSpec {
71 pub packet_id: String,
73
74 pub channel: u8,
76
77 pub destination_address: u16,
79
80 pub source_address: u16,
82
83 pub command: u16,
85
86 pub destination_device: Rc<DeviceSpec>,
88
89 pub source_device: Rc<DeviceSpec>,
91
92 pub name: String,
94
95 pub fields: Vec<PacketFieldSpec>,
97}
98
99#[derive(Debug, PartialEq)]
123pub struct PacketFieldSpec {
124 pub field_id: String,
126
127 pub packet_field_id: String,
129
130 pub name: String,
132
133 pub unit_id: UnitId,
135
136 pub unit_family: UnitFamily,
138
139 pub unit_code: String,
141
142 pub unit_text: String,
144
145 pub precision: i32,
147
148 pub typ: Type,
150
151 pub parts: Vec<PacketTemplateFieldPart>,
153
154 pub language: Language,
156}
157
158#[derive(Debug)]
160pub struct RawValueFormatter<'a> {
161 language: Language,
162 typ: Type,
163 precision: i32,
164 raw_value: i64,
165 unit_text: &'a str,
166}
167
168#[derive(Debug)]
170pub struct PacketFieldFormatter<'a> {
171 language: Language,
172 typ: Type,
173 precision: i32,
174 raw_value: Option<i64>,
175 unit_text: &'a str,
176}
177
178#[derive(Debug)]
195pub struct Specification {
196 file: SpecificationFile,
197 language: Language,
198 devices: RefCell<Vec<Rc<DeviceSpec>>>,
199 packets: RefCell<Vec<Rc<PacketSpec>>>,
200}
201
202#[derive(Debug)]
225pub struct DataSetPacketFieldIterator<'a, T: AsRef<[Data]>> {
226 spec: &'a Specification,
227 data_set: &'a T,
228 data_index: usize,
229 field_index: usize,
230}
231
232#[derive(Debug)]
234pub struct DataSetPacketField<'a, T: AsRef<[Data]>> {
235 data_set: &'a T,
236 data_index: usize,
237 packet_spec: Rc<PacketSpec>,
238 field_index: usize,
239 raw_value: Option<i64>,
240}
241
242fn get_cached_device_spec(
243 devices: &[Rc<DeviceSpec>],
244 channel: u8,
245 self_address: u16,
246 peer_address: u16,
247) -> Option<Rc<DeviceSpec>> {
248 let result = devices.iter().find(|&device| {
249 if device.channel != channel {
250 false
251 } else if device.self_address != self_address {
252 false
253 } else if device.peer_address.is_some() && device.peer_address.unwrap() != peer_address {
254 false
255 } else {
256 true
257 }
258 });
259
260 result.cloned()
261}
262
263fn get_or_create_cached_device_spec(
264 devices: &mut Vec<Rc<DeviceSpec>>,
265 channel: u8,
266 self_address: u16,
267 peer_address: u16,
268 file: &SpecificationFile,
269 language: Language,
270) -> Rc<DeviceSpec> {
271 if let Some(device) = get_cached_device_spec(devices, channel, self_address, peer_address) {
272 return device;
273 }
274
275 let device_template = file.find_device_template(self_address, peer_address);
276
277 let peer_address_option = match device_template {
278 None => None,
279 Some(device_template) => {
280 if device_template.peer_mask == 0 {
281 None
282 } else {
283 Some(peer_address)
284 }
285 }
286 };
287
288 let device_id = match peer_address_option {
289 None => format!("{:02X}_{:04X}", channel, self_address),
290 Some(peer_address) => format!("{:02X}_{:04X}_{:04X}", channel, self_address, peer_address),
291 };
292
293 let name = match device_template {
294 None => {
295 match language {
296 Language::En => format!("Unknown device 0x{:04X}", self_address),
297 Language::De => format!("Unbekanntes Gerät 0x{:04X}", self_address),
298 Language::Fr => format!("Unknown device 0x{:04X}", self_address), }
300 }
301 Some(device_template) => file
302 .localized_text_by_index(&device_template.name_localized_text_index, language)
303 .to_owned(),
304 };
305
306 let name = match channel {
307 0 => name,
308 _ => format!("VBus {}: {}", channel, name),
309 };
310
311 let device = DeviceSpec {
312 device_id,
313 channel,
314 self_address,
315 peer_address: peer_address_option,
316 name,
317 };
318
319 devices.push(Rc::new(device));
320
321 get_cached_device_spec(devices, channel, self_address, peer_address).unwrap()
322}
323
324fn get_cached_packet_spec(
325 packets: &[Rc<PacketSpec>],
326 packet_id: PacketId,
327) -> Option<Rc<PacketSpec>> {
328 let PacketId(channel, destination_address, source_address, command) = packet_id;
329
330 let result = packets.iter().find(|&packet| {
331 if packet.channel != channel {
332 false
333 } else if packet.destination_address != destination_address {
334 false
335 } else if packet.source_address != source_address {
336 false
337 } else if packet.command != command {
338 false
339 } else {
340 true
341 }
342 });
343
344 result.cloned()
345}
346
347fn get_or_create_cached_packet_spec(
348 packets: &mut Vec<Rc<PacketSpec>>,
349 packet_id: PacketId,
350 devices: &mut Vec<Rc<DeviceSpec>>,
351 file: &SpecificationFile,
352 language: Language,
353) -> Rc<PacketSpec> {
354 let PacketId(channel, destination_address, source_address, command) = packet_id;
355
356 if let Some(packet) = get_cached_packet_spec(packets, packet_id) {
357 return packet;
358 }
359
360 let destination_device = get_or_create_cached_device_spec(
361 devices,
362 channel,
363 destination_address,
364 source_address,
365 file,
366 language,
367 );
368 let source_device = get_or_create_cached_device_spec(
369 devices,
370 channel,
371 source_address,
372 destination_address,
373 file,
374 language,
375 );
376
377 let packet_id_string = packet_id.packet_id_string();
378
379 let packet_name = match destination_address {
380 0x0010 => source_device.name.clone(),
381 _ => format!("{} => {}", source_device.name, destination_device.name),
382 };
383
384 let fields = match file.find_packet_template(destination_address, source_address, command) {
385 None => Vec::new(),
386 Some(packet_template) => packet_template
387 .fields
388 .iter()
389 .map(|field| {
390 let field_id = file.text_by_index(&field.id_text_index).to_string();
391
392 let packet_field_id = format!("{}_{}", packet_id_string, field_id);
393
394 let field_name = file
395 .localized_text_by_index(&field.name_localized_text_index, language)
396 .to_string();
397
398 let unit = file.unit_by_id(&field.unit_id);
399
400 let unit_family = file.unit_family_by_id(&unit.unit_family_id);
401 let unit_code = file.text_by_index(&unit.unit_code_text_index).to_string();
402 let unit_text = file.text_by_index(&unit.unit_text_text_index).to_string();
403
404 let typ = file.type_by_id(&field.type_id);
405
406 PacketFieldSpec {
407 field_id,
408 packet_field_id,
409 name: field_name,
410 unit_id: field.unit_id,
411 unit_family,
412 unit_code,
413 unit_text,
414 precision: field.precision,
415 typ,
416 parts: field.parts.clone(),
417 language,
418 }
419 })
420 .collect(),
421 };
422
423 let packet = PacketSpec {
424 packet_id: packet_id_string,
425 channel,
426 destination_address,
427 source_address,
428 command,
429 destination_device,
430 source_device,
431 name: packet_name,
432 fields,
433 };
434
435 packets.push(Rc::new(packet));
436
437 get_cached_packet_spec(packets, packet_id).unwrap()
438}
439
440pub fn power_of_ten_i64(n: u32) -> i64 {
442 match n {
443 0 => 1,
444 1 => 10,
445 2 => 100,
446 3 => 1_000,
447 4 => 10_000,
448 5 => 100_000,
449 6 => 1_000_000,
450 7 => 10_000_000,
451 8 => 100_000_000,
452 9 => 1_000_000_000,
453 _ => 10i64.pow(n),
454 }
455}
456
457pub fn power_of_ten_f64(n: i32) -> f64 {
459 match n {
460 -9 => 0.000_000_001,
461 -8 => 0.000_000_01,
462 -7 => 0.000_000_1,
463 -6 => 0.000_001,
464 -5 => 0.000_01,
465 -4 => 0.000_1,
466 -3 => 0.001,
467 -2 => 0.01,
468 -1 => 0.1,
469 0 => 1.0,
470 1 => 10.0,
471 2 => 100.0,
472 3 => 1_000.0,
473 4 => 10_000.0,
474 5 => 100_000.0,
475 6 => 1_000_000.0,
476 7 => 10_000_000.0,
477 8 => 100_000_000.0,
478 9 => 1_000_000_000.0,
479 _ => 10.0f64.powf(f64::from(n)),
480 }
481}
482
483impl Specification {
484 pub fn from_file(file: SpecificationFile, language: Language) -> Specification {
497 let devices = RefCell::new(Vec::new());
498 let packets = RefCell::new(Vec::new());
499
500 Specification {
501 file,
502 language,
503 devices,
504 packets,
505 }
506 }
507
508 pub fn specification_file(&self) -> &SpecificationFile {
510 &self.file
511 }
512
513 pub fn language(&self) -> Language {
515 self.language
516 }
517
518 pub fn get_device_spec(
535 &self,
536 channel: u8,
537 self_address: u16,
538 peer_address: u16,
539 ) -> Rc<DeviceSpec> {
540 let mut devices = self.devices.borrow_mut();
541 get_or_create_cached_device_spec(
542 &mut devices,
543 channel,
544 self_address,
545 peer_address,
546 &self.file,
547 self.language,
548 )
549 }
550
551 pub fn get_packet_spec(
571 &self,
572 channel: u8,
573 destination_address: u16,
574 source_address: u16,
575 command: u16,
576 ) -> Rc<PacketSpec> {
577 let mut devices = self.devices.borrow_mut();
578 let mut packets = self.packets.borrow_mut();
579 let packet_id = PacketId(channel, destination_address, source_address, command);
580 get_or_create_cached_packet_spec(
581 &mut packets,
582 packet_id,
583 &mut devices,
584 &self.file,
585 self.language,
586 )
587 }
588
589 pub fn get_packet_spec_by_id(&self, packet_id: PacketId) -> Rc<PacketSpec> {
609 self.get_packet_spec(packet_id.0, packet_id.1, packet_id.2, packet_id.3)
610 }
611
612 pub fn fields_in_data_set<'a, T: AsRef<[Data]> + 'a>(
633 &'a self,
634 data_set: &'a T,
635 ) -> DataSetPacketFieldIterator<'a, T> {
636 DataSetPacketFieldIterator {
637 spec: self,
638 data_set,
639 data_index: 0,
640 field_index: 0,
641 }
642 }
643
644 pub fn fmt_timestamp<Tz: TimeZone>(&self, timestamp: &DateTime<Tz>) -> RawValueFormatter<'_> {
663 RawValueFormatter {
664 language: self.language,
665 typ: Type::DateTime,
666 precision: 0,
667 raw_value: timestamp.naive_local().timestamp() - 978_307_200,
668 unit_text: "",
669 }
670 }
671
672 pub fn unit_by_unit_code(&self, unit_code: &str) -> Option<&Unit> {
686 self.file.unit_by_unit_code(unit_code)
687 }
688
689 pub fn convert_value(&self, value: f64, src_unit: &Unit, dst_unit: &Unit) -> Result<f64> {
703 self.file.convert_value(value, src_unit, dst_unit)
704 }
705}
706
707impl PacketSpec {
708 pub fn get_field_spec_position(&self, id: &str) -> Option<usize> {
710 self.fields
711 .iter()
712 .position(|field_spec| field_spec.field_id == id)
713 }
714
715 pub fn get_field_spec_by_position(&self, pos: usize) -> &PacketFieldSpec {
717 &self.fields[pos]
718 }
719
720 pub fn get_field_spec(&self, id: &str) -> Option<&PacketFieldSpec> {
722 self.fields
723 .iter()
724 .find(|field_spec| field_spec.field_id == id)
725 }
726}
727
728impl PacketFieldSpec {
729 pub fn raw_value_i64(&self, buf: &[u8]) -> Option<i64> {
731 let length = buf.len();
732
733 let mut valid = false;
734 let mut raw_value = 0;
735
736 for part in &self.parts {
737 let offset = part.offset as usize;
738
739 if offset < length {
740 let mut part_value = if part.is_signed {
741 i64::from(buf[offset] as i8)
742 } else {
743 i64::from(buf[offset])
744 };
745 if part.mask != 0xFF {
746 part_value &= i64::from(part.mask);
747 }
748 if part.bit_pos > 0 {
749 part_value >>= part.bit_pos;
750 }
751 raw_value += part_value * part.factor;
752 valid = true;
753 }
754 }
755
756 if valid {
757 Some(raw_value)
758 } else {
759 None
760 }
761 }
762
763 pub fn raw_value_f64(&self, buf: &[u8]) -> Option<f64> {
765 self.raw_value_i64(buf)
766 .map(|raw_value| raw_value as f64 * power_of_ten_f64(-self.precision))
767 }
768
769 pub fn fmt_raw_value(
771 &self,
772 raw_value: Option<i64>,
773 append_unit: bool,
774 ) -> PacketFieldFormatter<'_> {
775 let unit_text = if append_unit { &self.unit_text } else { "" };
776 PacketFieldFormatter {
777 language: self.language,
778 typ: self.typ,
779 precision: self.precision,
780 raw_value,
781 unit_text,
782 }
783 }
784}
785
786const WEEKDAYS_EN: [&str; 7] = ["Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"];
787
788const WEEKDAYS_DE: [&str; 7] = ["Mo", "Di", "Mi", "Do", "Fr", "Sa", "So"];
789
790const WEEKDAYS_FR: [&str; 7] = ["Lu", "Ma", "Me", "Je", "Ve", "Sa", "Di"];
791
792impl<'a> RawValueFormatter<'a> {
793 pub fn new(
795 language: Language,
796 typ: Type,
797 precision: i32,
798 raw_value: i64,
799 unit_text: &'a str,
800 ) -> RawValueFormatter<'a> {
801 RawValueFormatter {
802 language,
803 typ,
804 precision,
805 raw_value,
806 unit_text,
807 }
808 }
809}
810
811impl<'a> fmt::Display for RawValueFormatter<'a> {
812 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
813 match self.typ {
814 Type::Number => {
815 if self.precision > 0 {
816 let sign = if self.raw_value < 0 { "-" } else { "" };
817 let raw_value = self.raw_value.abs();
818 let factor = power_of_ten_i64(self.precision as u32);
819 let left_part = raw_value / factor;
820 let right_part = raw_value % factor;
821 let separator = match self.language {
822 Language::En => ".",
823 Language::De | Language::Fr => ",",
824 };
825
826 write!(f, "{}{}{}", sign, left_part, separator)?;
827 match self.precision {
828 1 => write!(f, "{:01}", right_part)?,
829 2 => write!(f, "{:02}", right_part)?,
830 3 => write!(f, "{:03}", right_part)?,
831 4 => write!(f, "{:04}", right_part)?,
832 5 => write!(f, "{:05}", right_part)?,
833 6 => write!(f, "{:06}", right_part)?,
834 7 => write!(f, "{:07}", right_part)?,
835 8 => write!(f, "{:08}", right_part)?,
836 9 => write!(f, "{:09}", right_part)?,
837 _ => {
838 let s = format!("{}", right_part + factor);
839 write!(f, "{}", &s[1..])?;
840 }
841 };
842 write!(f, "{}", self.unit_text)
843 } else {
844 write!(f, "{}{}", self.raw_value, self.unit_text)
845 }
846 }
847 Type::Time => {
848 let hours = self.raw_value / 60;
849 let minutes = self.raw_value % 60;
850 write!(f, "{:02}:{:02}", hours, minutes)
851 }
852 Type::WeekTime => {
853 let weekday_idx = ((self.raw_value / 1440) % 7) as usize;
854 let hours = (self.raw_value / 60) % 24;
855 let minutes = self.raw_value % 60;
856 match self.language {
857 Language::En => write!(
858 f,
859 "{},{:02}:{:02}",
860 WEEKDAYS_EN[weekday_idx], hours, minutes
861 ),
862 Language::De => write!(
863 f,
864 "{},{:02}:{:02}",
865 WEEKDAYS_DE[weekday_idx], hours, minutes
866 ),
867 Language::Fr => write!(
868 f,
869 "{},{:02}:{:02}",
870 WEEKDAYS_FR[weekday_idx], hours, minutes
871 ),
872 }
873 }
874 Type::DateTime => {
875 let timestamp = Utc.timestamp(self.raw_value + 978_307_200, 0);
876 match self.language {
877 Language::En | Language::Fr => {
878 write!(f, "{}", timestamp.format("%d/%m/%Y %H:%M:%S"))
879 }
880 Language::De => write!(f, "{}", timestamp.format("%d.%m.%Y %H:%M:%S")),
881 }
882 }
883 }
884 }
885}
886
887impl<'a> fmt::Display for PacketFieldFormatter<'a> {
888 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
889 if let Some(raw_value) = self.raw_value {
890 let formatter = RawValueFormatter::new(
891 self.language,
892 self.typ,
893 self.precision,
894 raw_value,
895 self.unit_text,
896 );
897 formatter.fmt(f)
898 } else {
899 Ok(())
900 }
901 }
902}
903
904impl<'a, T: AsRef<[Data]> + 'a> Iterator for DataSetPacketFieldIterator<'a, T> {
905 type Item = DataSetPacketField<'a, T>;
906
907 fn next(&mut self) -> Option<Self::Item> {
908 let data_slice = self.data_set.as_ref();
909 let data_slice_len = data_slice.len();
910
911 while self.data_index < data_slice_len {
912 let data = &data_slice[self.data_index];
913 if let Data::Packet(ref packet) = *data {
914 let packet_spec = self.spec.get_packet_spec(
915 packet.header.channel,
916 packet.header.destination_address,
917 packet.header.source_address,
918 packet.command,
919 );
920 if self.field_index < packet_spec.fields.len() {
921 let field_index = self.field_index;
922 self.field_index += 1;
923
924 let frame_data = &packet.frame_data[0..packet.frame_count as usize * 4];
925
926 let field_spec = &packet_spec.fields[field_index];
927 let raw_value = field_spec.raw_value_i64(frame_data);
928
929 return Some(DataSetPacketField {
930 data_set: self.data_set,
931 data_index: self.data_index,
932 packet_spec: packet_spec.clone(),
933 field_index,
934 raw_value,
935 });
936 }
937 }
938
939 self.data_index += 1;
940 self.field_index = 0;
941 }
942
943 None
944 }
945}
946
947impl<'a, T: AsRef<[Data]>> DataSetPacketField<'a, T> {
948 pub fn new(
950 data_set: &'a T,
951 data_index: usize,
952 packet_spec: Rc<PacketSpec>,
953 field_index: usize,
954 raw_value: Option<i64>,
955 ) -> DataSetPacketField<'a, T> {
956 DataSetPacketField {
957 data_set,
958 data_index,
959 packet_spec,
960 field_index,
961 raw_value,
962 }
963 }
964
965 pub fn data_set(&self) -> &[Data] {
967 self.data_set.as_ref()
968 }
969
970 pub fn data_index(&self) -> usize {
972 self.data_index
973 }
974
975 pub fn data(&self) -> &Data {
977 &self.data_set.as_ref()[self.data_index]
978 }
979
980 pub fn packet_spec(&self) -> &PacketSpec {
982 self.packet_spec.as_ref()
983 }
984
985 pub fn field_index(&self) -> usize {
987 self.field_index
988 }
989
990 pub fn field_spec(&self) -> &PacketFieldSpec {
992 &self.packet_spec.fields[self.field_index]
993 }
994
995 pub fn packet_id(&self) -> PacketId {
997 self.data().as_packet().packet_id()
998 }
999
1000 pub fn field_id(&self) -> &str {
1002 &self.field_spec().field_id
1003 }
1004
1005 pub fn packet_field_id(&self) -> PacketFieldId<'_> {
1007 PacketFieldId(
1008 self.data().as_packet().packet_id(),
1009 &self.field_spec().field_id,
1010 )
1011 }
1012
1013 pub fn raw_value_i64(&self) -> &Option<i64> {
1015 &self.raw_value
1016 }
1017
1018 pub fn raw_value_f64(&self) -> Option<f64> {
1020 self.raw_value
1021 .map(|v| v as f64 * power_of_ten_f64(-self.field_spec().precision))
1022 }
1023
1024 pub fn fmt_raw_value(&self, append_unit: bool) -> PacketFieldFormatter<'_> {
1026 self.field_spec().fmt_raw_value(self.raw_value, append_unit)
1027 }
1028}
1029
1030#[cfg(test)]
1031mod tests {
1032 use super::*;
1033
1034 use crate::{
1035 recording_reader::RecordingReader,
1036 test_data::{RECORDING_2, SPEC_FILE_1},
1037 };
1038
1039 #[test]
1040 fn test_power_of_ten_i64() {
1041 for n in 0..19 {
1042 assert_eq!(10i64.pow(n), power_of_ten_i64(n));
1043 }
1044 }
1045
1046 #[test]
1047 fn test_power_of_ten_f64() {
1048 for n in -20..20 {
1049 assert_eq!(10.0f64.powf(n as f64), power_of_ten_f64(n));
1050 }
1051 }
1052
1053 #[test]
1054 fn test_raw_value_formatter() {
1055 use crate::specification_file::{Language::*, Type::*};
1056
1057 let fmt_to_string = |language, typ, prec, value, unit| {
1058 let formatter = RawValueFormatter::new(language, typ, prec, value, unit);
1059 format!("{}", formatter)
1060 };
1061
1062 assert_eq!("12346", fmt_to_string(En, Number, 0, 12346, ""));
1063 assert_eq!("12346 unit", fmt_to_string(En, Number, 0, 12346, " unit"));
1064 assert_eq!("12345.7", fmt_to_string(En, Number, 1, 123457, ""));
1065 assert_eq!("12345.68", fmt_to_string(En, Number, 2, 1234568, ""));
1066 assert_eq!("12345.679", fmt_to_string(En, Number, 3, 12345679, ""));
1067 assert_eq!("12345.6789", fmt_to_string(En, Number, 4, 123456789, ""));
1068 assert_eq!(
1069 "1.2345678900",
1070 fmt_to_string(En, Number, 10, 12345678900, "")
1071 );
1072 assert_eq!(
1073 "1,2345678900",
1074 fmt_to_string(De, Number, 10, 12345678900, "")
1075 );
1076 assert_eq!(
1077 "1,2345678900",
1078 fmt_to_string(Fr, Number, 10, 12345678900, "")
1079 );
1080
1081 assert_eq!(
1082 "12:01",
1083 fmt_to_string(En, Time, 10, 721, " ignore this unit")
1084 );
1085 assert_eq!(
1086 "12:01",
1087 fmt_to_string(De, Time, 10, 721, " ignore this unit")
1088 );
1089 assert_eq!(
1090 "12:01",
1091 fmt_to_string(Fr, Time, 10, 721, " ignore this unit")
1092 );
1093
1094 assert_eq!(
1095 "Th,12:01",
1096 fmt_to_string(En, WeekTime, 10, 3 * 1440 + 721, " ignore this unit")
1097 );
1098 assert_eq!(
1099 "Do,12:01",
1100 fmt_to_string(De, WeekTime, 10, 3 * 1440 + 721, " ignore this unit")
1101 );
1102 assert_eq!(
1103 "Je,12:01",
1104 fmt_to_string(Fr, WeekTime, 10, 3 * 1440 + 721, " ignore this unit")
1105 );
1106
1107 assert_eq!(
1108 "22/12/2013 15:17:42",
1109 fmt_to_string(En, DateTime, 10, 409418262, " ignore this unit")
1110 );
1111 assert_eq!(
1112 "22.12.2013 15:17:42",
1113 fmt_to_string(De, DateTime, 10, 409418262, " ignore this unit")
1114 );
1115 assert_eq!(
1116 "22/12/2013 15:17:42",
1117 fmt_to_string(Fr, DateTime, 10, 409418262, " ignore this unit")
1118 );
1119 }
1120
1121 #[test]
1122 fn test_from_file() {
1123 let spec_file = SpecificationFile::from_bytes(SPEC_FILE_1).unwrap();
1124
1125 let spec = Specification::from_file(spec_file, Language::En);
1126
1127 assert_eq!(0, spec.devices.borrow().len());
1128 assert_eq!(0, spec.packets.borrow().len());
1129 }
1130
1131 #[test]
1132 fn test_get_device_spec() {
1133 let spec_file = SpecificationFile::from_bytes(SPEC_FILE_1).unwrap();
1134
1135 let spec = Specification::from_file(spec_file, Language::En);
1136
1137 assert_eq!(0, spec.devices.borrow().len());
1138
1139 let device_spec = spec.get_device_spec(0x01, 0x7E31, 0x0010);
1140
1141 assert_eq!(1, spec.devices.borrow().len());
1142 assert_eq!("01_7E31", device_spec.device_id);
1143 assert_eq!(0x01, device_spec.channel);
1144 assert_eq!(0x7E31, device_spec.self_address);
1145 assert_eq!(None, device_spec.peer_address);
1146 assert_eq!("VBus 1: DeltaSol MX [WMZ #1]", device_spec.name);
1147
1148 let device_spec = spec.get_device_spec(0x01, 0x7E31, 0x0010);
1149
1150 assert_eq!(1, spec.devices.borrow().len());
1151 assert_eq!("01_7E31", device_spec.device_id);
1152
1153 let device_spec = spec.get_device_spec(0x00, 0x7E31, 0x0010);
1154
1155 assert_eq!(2, spec.devices.borrow().len());
1156 assert_eq!("00_7E31", device_spec.device_id);
1157 assert_eq!(0x00, device_spec.channel);
1158 assert_eq!(0x7E31, device_spec.self_address);
1159 assert_eq!(None, device_spec.peer_address);
1160 assert_eq!("DeltaSol MX [WMZ #1]", device_spec.name);
1161
1162 let device_spec = spec.get_device_spec(0x00, 0x7E11, 0x0010);
1163
1164 assert_eq!(3, spec.devices.borrow().len());
1165 assert_eq!("00_7E11", device_spec.device_id);
1166 assert_eq!(0x00, device_spec.channel);
1167 assert_eq!(0x7E11, device_spec.self_address);
1168 assert_eq!(None, device_spec.peer_address);
1169 assert_eq!("Unknown device 0x7E11", device_spec.name);
1170 }
1171
1172 #[test]
1173 fn test_get_packet_spec() {
1174 let spec_file = SpecificationFile::from_bytes(SPEC_FILE_1).unwrap();
1175
1176 let spec = Specification::from_file(spec_file, Language::En);
1177
1178 assert_eq!(0, spec.packets.borrow().len());
1179
1180 let packet_spec = spec.get_packet_spec(0x01, 0x0010, 0x7E31, 0x0100);
1181
1182 assert_eq!(1, spec.packets.borrow().len());
1183 assert_eq!("01_0010_7E31_10_0100", packet_spec.packet_id);
1184 assert_eq!(0x01, packet_spec.channel);
1185 assert_eq!(0x0010, packet_spec.destination_address);
1186 assert_eq!(0x7E31, packet_spec.source_address);
1187 assert_eq!(0x0100, packet_spec.command);
1188 assert_eq!("VBus 1: DFA", packet_spec.destination_device.name);
1189 assert_eq!(
1190 "VBus 1: DeltaSol MX [WMZ #1]",
1191 packet_spec.source_device.name
1192 );
1193 assert_eq!("VBus 1: DeltaSol MX [WMZ #1]", packet_spec.name);
1194 assert_eq!(8, packet_spec.fields.len());
1195
1196 let field_spec = &packet_spec.fields[0];
1197 assert_eq!("000_4_0", field_spec.field_id);
1198 assert_eq!("01_0010_7E31_10_0100_000_4_0", field_spec.packet_field_id);
1199 assert_eq!("Heat quantity", field_spec.name);
1200 assert_eq!(18, field_spec.unit_id.0);
1201 assert_eq!(UnitFamily::Energy, field_spec.unit_family);
1202 assert_eq!("WattHours", field_spec.unit_code);
1203 assert_eq!(" Wh", field_spec.unit_text);
1204 assert_eq!(0, field_spec.precision);
1205 assert_eq!(Type::Number, field_spec.typ);
1206 assert_eq!(8, field_spec.parts.len());
1207
1208 let packet_spec = spec.get_packet_spec(0x01, 0x0010, 0x7E31, 0x0100);
1209
1210 assert_eq!(1, spec.packets.borrow().len());
1211 assert_eq!("01_0010_7E31_10_0100", packet_spec.packet_id);
1212
1213 let packet_spec = spec.get_packet_spec(0x00, 0x0010, 0x7E31, 0x0100);
1214
1215 assert_eq!(2, spec.packets.borrow().len());
1216 assert_eq!("00_0010_7E31_10_0100", packet_spec.packet_id);
1217 assert_eq!(0x00, packet_spec.channel);
1218 assert_eq!(0x0010, packet_spec.destination_address);
1219 assert_eq!(0x7E31, packet_spec.source_address);
1220 assert_eq!(0x0100, packet_spec.command);
1221 assert_eq!("DFA", packet_spec.destination_device.name);
1222 assert_eq!("DeltaSol MX [WMZ #1]", packet_spec.source_device.name);
1223 assert_eq!("DeltaSol MX [WMZ #1]", packet_spec.name);
1224 assert_eq!(8, packet_spec.fields.len());
1225
1226 let packet_spec = spec.get_packet_spec(0x00, 0x0010, 0x7E11, 0x0100);
1227
1228 assert_eq!(3, spec.packets.borrow().len());
1229 assert_eq!("00_0010_7E11_10_0100", packet_spec.packet_id);
1230 assert_eq!(0x00, packet_spec.channel);
1231 assert_eq!(0x0010, packet_spec.destination_address);
1232 assert_eq!(0x7E11, packet_spec.source_address);
1233 assert_eq!(0x0100, packet_spec.command);
1234 assert_eq!("DFA", packet_spec.destination_device.name);
1235 assert_eq!("Unknown device 0x7E11", packet_spec.source_device.name);
1236 assert_eq!("Unknown device 0x7E11", packet_spec.name);
1237 assert_eq!(0, packet_spec.fields.len());
1238 }
1239
1240 #[test]
1241 fn test_get_field_spec() {
1242 let spec_file = SpecificationFile::from_bytes(SPEC_FILE_1).unwrap();
1243
1244 let spec = Specification::from_file(spec_file, Language::En);
1245
1246 assert_eq!(0, spec.packets.borrow().len());
1247
1248 let packet_spec = spec.get_packet_spec(0x01, 0x0010, 0x7E31, 0x0100);
1249
1250 let field_spec = packet_spec.get_field_spec("000_4_0").unwrap();
1251 assert_eq!("000_4_0", field_spec.field_id);
1252 assert_eq!("01_0010_7E31_10_0100_000_4_0", field_spec.packet_field_id);
1253 assert_eq!("Heat quantity", field_spec.name);
1254 assert_eq!(18, field_spec.unit_id.0);
1255 assert_eq!(UnitFamily::Energy, field_spec.unit_family);
1256 assert_eq!("WattHours", field_spec.unit_code);
1257 assert_eq!(" Wh", field_spec.unit_text);
1258 assert_eq!(0, field_spec.precision);
1259 assert_eq!(Type::Number, field_spec.typ);
1260 assert_eq!(8, field_spec.parts.len());
1261
1262 assert_eq!(None, packet_spec.get_field_spec("000_2_0"));
1263 }
1264
1265 #[test]
1266 fn test_raw_value_i64() {
1267 let spec_file = SpecificationFile::from_bytes(SPEC_FILE_1).unwrap();
1268
1269 let spec = Specification::from_file(spec_file, Language::En);
1270
1271 assert_eq!(0, spec.packets.borrow().len());
1272
1273 let packet_spec = spec.get_packet_spec(0x01, 0x0010, 0x7F61, 0x0100);
1274
1275 let buf = &[
1276 0x78, 0x56, 0x34, 0x12, 0xB8, 0x22, 0x00, 0x00, 0x48, 0xDD, 0xFF, 0xFF,
1277 ];
1278
1279 assert_eq!(
1280 Some(0x12345678),
1281 packet_spec
1282 .get_field_spec("000_4_0")
1283 .unwrap()
1284 .raw_value_i64(buf)
1285 );
1286 assert_eq!(
1287 Some(8888),
1288 packet_spec
1289 .get_field_spec("004_4_0")
1290 .unwrap()
1291 .raw_value_i64(buf)
1292 );
1293 assert_eq!(
1294 Some(-8888),
1295 packet_spec
1296 .get_field_spec("008_4_0")
1297 .unwrap()
1298 .raw_value_i64(buf)
1299 );
1300 assert_eq!(
1301 Some(0x345678),
1302 packet_spec
1303 .get_field_spec("000_4_0")
1304 .unwrap()
1305 .raw_value_i64(&buf[0..3])
1306 );
1307 assert_eq!(
1308 Some(0x5678),
1309 packet_spec
1310 .get_field_spec("000_4_0")
1311 .unwrap()
1312 .raw_value_i64(&buf[0..2])
1313 );
1314 assert_eq!(
1315 Some(0x78),
1316 packet_spec
1317 .get_field_spec("000_4_0")
1318 .unwrap()
1319 .raw_value_i64(&buf[0..1])
1320 );
1321 assert_eq!(
1322 None,
1323 packet_spec
1324 .get_field_spec("000_4_0")
1325 .unwrap()
1326 .raw_value_i64(&buf[0..0])
1327 );
1328 }
1329
1330 #[test]
1331 fn test_raw_value_f64() {
1332 let spec_file = SpecificationFile::from_bytes(SPEC_FILE_1).unwrap();
1333
1334 let spec = Specification::from_file(spec_file, Language::En);
1335
1336 assert_eq!(0, spec.packets.borrow().len());
1337
1338 let packet_spec = spec.get_packet_spec(0x01, 0x0010, 0x7F61, 0x0100);
1339
1340 let buf = &[
1341 0x78, 0x56, 0x34, 0x12, 0xB8, 0x22, 0x00, 0x00, 0x48, 0xDD, 0xFF, 0xFF,
1342 ];
1343
1344 assert_eq!(
1345 Some(0x12345678 as f64),
1346 packet_spec
1347 .get_field_spec("000_4_0")
1348 .unwrap()
1349 .raw_value_f64(buf)
1350 );
1351 assert_eq!(
1352 Some(888.8000000000001),
1353 packet_spec
1354 .get_field_spec("004_4_0")
1355 .unwrap()
1356 .raw_value_f64(buf)
1357 );
1358 assert_eq!(
1359 Some(-888.8000000000001),
1360 packet_spec
1361 .get_field_spec("008_4_0")
1362 .unwrap()
1363 .raw_value_f64(buf)
1364 );
1365 assert_eq!(
1366 Some(0x345678 as f64),
1367 packet_spec
1368 .get_field_spec("000_4_0")
1369 .unwrap()
1370 .raw_value_f64(&buf[0..3])
1371 );
1372 assert_eq!(
1373 Some(0x5678 as f64),
1374 packet_spec
1375 .get_field_spec("000_4_0")
1376 .unwrap()
1377 .raw_value_f64(&buf[0..2])
1378 );
1379 assert_eq!(
1380 Some(0x78 as f64),
1381 packet_spec
1382 .get_field_spec("000_4_0")
1383 .unwrap()
1384 .raw_value_f64(&buf[0..1])
1385 );
1386 assert_eq!(
1387 None,
1388 packet_spec
1389 .get_field_spec("000_4_0")
1390 .unwrap()
1391 .raw_value_f64(&buf[0..0])
1392 );
1393 }
1394
1395 #[test]
1396 fn test_fmt_raw_value() {
1397 let fake_field_spec = |precision, typ, unit_text: &str| PacketFieldSpec {
1398 field_id: "".to_string(),
1399 packet_field_id: "".to_string(),
1400 name: "".to_string(),
1401 unit_id: UnitId(0),
1402 unit_family: UnitFamily::None,
1403 unit_code: "unit code".to_string(),
1404 unit_text: unit_text.to_string(),
1405 precision,
1406 typ,
1407 parts: Vec::new(),
1408 language: Language::En,
1409 };
1410
1411 let fmt_raw_value = |field_spec: &PacketFieldSpec, raw_value, append_unit| {
1412 let test_value = field_spec.fmt_raw_value(Some(raw_value), append_unit);
1413 format!("{}", test_value)
1414 };
1415
1416 let field_spec = fake_field_spec(0, Type::Number, "don't append unit");
1417 assert_eq!("12346", fmt_raw_value(&field_spec, 12346, false));
1418
1419 let field_spec = fake_field_spec(0, Type::Number, " unit");
1420 assert_eq!("12346 unit", fmt_raw_value(&field_spec, 12346, true));
1421
1422 let field_spec = fake_field_spec(1, Type::Number, "don't append unit");
1423 assert_eq!("12345.7", fmt_raw_value(&field_spec, 123457, false));
1424
1425 let field_spec = fake_field_spec(2, Type::Number, "don't append unit");
1426 assert_eq!("12345.68", fmt_raw_value(&field_spec, 1234568, false));
1427
1428 let field_spec = fake_field_spec(3, Type::Number, "don't append unit");
1429 assert_eq!("12345.679", fmt_raw_value(&field_spec, 12345679, false));
1430
1431 let field_spec = fake_field_spec(4, Type::Number, "don't append unit");
1432 assert_eq!("12345.6789", fmt_raw_value(&field_spec, 123456789, false));
1433
1434 let field_spec = fake_field_spec(4, Type::Number, "don't append unit");
1435 assert_eq!("12345.0009", fmt_raw_value(&field_spec, 123450009, false));
1436
1437 let field_spec = fake_field_spec(10, Type::Number, "don't append unit");
1438 assert_eq!(
1439 "1.2345678900",
1440 fmt_raw_value(&field_spec, 12345678900, false)
1441 );
1442
1443 let field_spec = fake_field_spec(10, Type::Time, "don't append unit");
1444 assert_eq!("12:01", fmt_raw_value(&field_spec, 721, true));
1445
1446 let field_spec = fake_field_spec(10, Type::WeekTime, "don't append unit");
1447 assert_eq!("Th,12:01", fmt_raw_value(&field_spec, 3 * 1440 + 721, true));
1448
1449 let field_spec = fake_field_spec(10, Type::DateTime, "don't append unit");
1450 assert_eq!(
1451 "22/12/2013 15:17:42",
1452 fmt_raw_value(&field_spec, 409418262, true)
1453 );
1454 }
1455
1456 #[test]
1457 fn test_fields_in_data_set() {
1458 let mut rr = RecordingReader::new(RECORDING_2);
1459
1460 let data_set = rr.read_data_set().unwrap().unwrap();
1461
1462 let spec_file = SpecificationFile::from_bytes(SPEC_FILE_1).unwrap();
1463
1464 let spec = Specification::from_file(spec_file, Language::En);
1465
1466 let fields = spec.fields_in_data_set(&data_set).collect::<Vec<_>>();
1467
1468 assert_eq!(8, fields.len());
1469
1470 let field = &fields[0];
1471 assert_eq!(1, field.data_index());
1472 assert_eq!(&data_set.as_data_slice()[1], field.data());
1473 assert_eq!("00_0010_7E31_10_0100", field.packet_spec().packet_id);
1474 assert_eq!(0, field.field_index());
1475 assert_eq!("000_4_0", field.field_spec().field_id);
1476 assert_eq!(Some(0f64), field.raw_value_f64());
1477 assert_eq!("0", format!("{}", field.fmt_raw_value(false)));
1478 assert_eq!("0 Wh", format!("{}", field.fmt_raw_value(true)));
1479
1480 let field = &fields[1];
1481 assert_eq!(1, field.data_index());
1482 assert_eq!(&data_set.as_data_slice()[1], field.data());
1483 assert_eq!("00_0010_7E31_10_0100", field.packet_spec().packet_id);
1484 assert_eq!(1, field.field_index());
1485 assert_eq!("008_4_0", field.field_spec().field_id);
1486 assert_eq!(Some(0f64), field.raw_value_f64());
1487 assert_eq!("0", format!("{}", field.fmt_raw_value(false)));
1488 assert_eq!("0 Wh", format!("{}", field.fmt_raw_value(true)));
1489
1490 let field = &fields[2];
1491 assert_eq!(1, field.data_index());
1492 assert_eq!(&data_set.as_data_slice()[1], field.data());
1493 assert_eq!("00_0010_7E31_10_0100", field.packet_spec().packet_id);
1494 assert_eq!(2, field.field_index());
1495 assert_eq!("012_4_0", field.field_spec().field_id);
1496 assert_eq!(Some(0f64), field.raw_value_f64());
1497 assert_eq!("0", format!("{}", field.fmt_raw_value(false)));
1498 assert_eq!("0 Wh", format!("{}", field.fmt_raw_value(true)));
1499
1500 let field = &fields[3];
1501 assert_eq!(1, field.data_index());
1502 assert_eq!(&data_set.as_data_slice()[1], field.data());
1503 assert_eq!("00_0010_7E31_10_0100", field.packet_spec().packet_id);
1504 assert_eq!(3, field.field_index());
1505 assert_eq!("020_4_0", field.field_spec().field_id);
1506 assert_eq!(Some(0f64), field.raw_value_f64());
1507 assert_eq!("0", format!("{}", field.fmt_raw_value(false)));
1508 assert_eq!("0 Wh", format!("{}", field.fmt_raw_value(true)));
1509
1510 let field = &fields[4];
1511 assert_eq!(1, field.data_index());
1512 assert_eq!(&data_set.as_data_slice()[1], field.data());
1513 assert_eq!("00_0010_7E31_10_0100", field.packet_spec().packet_id);
1514 assert_eq!(4, field.field_index());
1515 assert_eq!("016_4_0", field.field_spec().field_id);
1516 assert_eq!(Some(0f64), field.raw_value_f64());
1517 assert_eq!("0", format!("{}", field.fmt_raw_value(false)));
1518 assert_eq!("0 l", format!("{}", field.fmt_raw_value(true)));
1519
1520 let field = &fields[5];
1521 assert_eq!(1, field.data_index());
1522 assert_eq!(&data_set.as_data_slice()[1], field.data());
1523 assert_eq!("00_0010_7E31_10_0100", field.packet_spec().packet_id);
1524 assert_eq!(5, field.field_index());
1525 assert_eq!("024_4_0", field.field_spec().field_id);
1526 assert_eq!(Some(0f64), field.raw_value_f64());
1527 assert_eq!("0", format!("{}", field.fmt_raw_value(false)));
1528 assert_eq!("0 l", format!("{}", field.fmt_raw_value(true)));
1529
1530 let field = &fields[6];
1531 assert_eq!(1, field.data_index());
1532 assert_eq!(&data_set.as_data_slice()[1], field.data());
1533 assert_eq!("00_0010_7E31_10_0100", field.packet_spec().packet_id);
1534 assert_eq!(6, field.field_index());
1535 assert_eq!("028_4_0", field.field_spec().field_id);
1536 assert_eq!(Some(0f64), field.raw_value_f64());
1537 assert_eq!("0", format!("{}", field.fmt_raw_value(false)));
1538 assert_eq!("0 l", format!("{}", field.fmt_raw_value(true)));
1539 }
1540}