ipmi_rs/storage/sdr/record/
mod.rs

1mod full_sensor_record;
2pub use full_sensor_record::FullSensorRecord;
3
4mod compact_sensor_record;
5mod event_only_sensor_record;
6mod fru_device_locator;
7mod mc_device_locator;
8
9pub mod traits;
10pub use traits::*;
11
12pub use compact_sensor_record::CompactSensorRecord;
13
14use nonmax::NonMaxU8;
15
16use crate::storage::sdr::record::event_only_sensor_record::EventOnlySensorRecord;
17use crate::storage::sdr::record::fru_device_locator::FruDeviceLocator;
18use crate::storage::sdr::record::mc_device_locator::McDeviceLocatorRecord;
19use crate::{
20    connection::{Channel, LogicalUnit},
21    Loggable,
22};
23
24use super::{event_reading_type_code::EventReadingTypeCodes, RecordId, SensorType, Unit};
25
26#[derive(Debug)]
27pub struct Value {
28    units: SensorUnits,
29    value: f32,
30}
31
32impl Value {
33    pub fn new(units: SensorUnits, value: f32) -> Self {
34        Self { units, value }
35    }
36
37    pub fn display(&self, short: bool) -> String {
38        if self.units.is_percentage {
39            format!("{:.2} %", self.value)
40        } else {
41            // TODO: use Modifier unit and rate units
42            // somehow here
43            self.units.base_unit.display(short, self.value)
44        }
45    }
46}
47
48#[derive(Debug, Clone, Copy, PartialEq)]
49pub struct SensorKey {
50    pub owner_id: SensorOwner,
51    pub owner_channel: Channel,
52    pub fru_inv_device_owner_lun: LogicalUnit,
53    pub owner_lun: LogicalUnit,
54    pub sensor_number: SensorNumber,
55}
56
57impl SensorKey {
58    pub fn parse(record_data: &[u8]) -> Result<Self, ParseError> {
59        if record_data.len() != 3 {
60            return Err(ParseError::NotEnoughData);
61        }
62
63        let owner_id = SensorOwner::from(record_data[0]);
64        let owner_channel_fru_lun = record_data[1];
65
66        let owner_channel_value = (owner_channel_fru_lun & 0xF0) >> 4;
67        let owner_channel =
68            Channel::new(owner_channel_value).ok_or(ParseError::InvalidOwnerChannel)?;
69
70        let fru_inv_device_owner_lun = LogicalUnit::from_low_bits(owner_channel_fru_lun >> 2);
71        let owner_lun = LogicalUnit::from_low_bits(owner_channel_fru_lun & 0b11);
72
73        let sensor_number =
74            SensorNumber(NonMaxU8::new(record_data[2]).ok_or(ParseError::InvalidSensorNumber)?);
75
76        Ok(Self {
77            owner_id,
78            owner_channel,
79            fru_inv_device_owner_lun,
80            owner_lun,
81            sensor_number,
82        })
83    }
84}
85
86impl SensorKey {
87    fn log_into(&self, level: usize, log: &mut Vec<crate::fmt::LogItem>) {
88        let sensor_owner = match self.owner_id {
89            SensorOwner::I2C(addr) => format!("I2C @ 0x{:02X}", addr),
90            SensorOwner::System(addr) => format!("System @ 0x{:02X}", addr),
91        };
92
93        log.push((level, "Sensor owner", sensor_owner).into());
94        log.push((level, "Owner channel", self.owner_channel).into());
95        log.push((level, "Owner LUN", self.owner_lun.value()).into());
96        log.push((level, "Sensor number", self.sensor_number.get()).into());
97    }
98}
99
100#[derive(Debug, Clone, Copy, PartialEq)]
101pub enum SensorOwner {
102    I2C(u8),
103    System(u8),
104}
105
106impl From<u8> for SensorOwner {
107    fn from(value: u8) -> Self {
108        let id = (value & 0xFE) >> 1;
109
110        if (value & 1) == 1 {
111            Self::System(id)
112        } else {
113            Self::I2C(id)
114        }
115    }
116}
117
118impl From<SensorOwner> for u8 {
119    fn from(value: SensorOwner) -> u8 {
120        match value {
121            SensorOwner::I2C(id) => (id << 1) & 0xFE,
122            SensorOwner::System(id) => ((id << 1) & 0xFE) | 1,
123        }
124    }
125}
126
127#[derive(Debug, Clone, Copy)]
128pub enum EntityRelativeTo {
129    System,
130    Device,
131}
132
133#[derive(Debug, Clone, Copy)]
134
135pub enum EntityInstance {
136    Physical {
137        relative: EntityRelativeTo,
138        instance_number: u8,
139    },
140    LogicalContainer {
141        relative: EntityRelativeTo,
142        instance_number: u8,
143    },
144}
145
146impl From<u8> for EntityInstance {
147    fn from(value: u8) -> Self {
148        let instance_number = value & 0x7F;
149        let relative = match instance_number {
150            0x00..=0x5F => EntityRelativeTo::System,
151            0x60..=0x7F => EntityRelativeTo::Device,
152            _ => unreachable!(),
153        };
154
155        if (value & 0x80) == 0x80 {
156            Self::LogicalContainer {
157                relative,
158                instance_number,
159            }
160        } else {
161            Self::Physical {
162                relative,
163                instance_number,
164            }
165        }
166    }
167}
168
169#[derive(Clone, Copy, Debug, PartialEq)]
170pub struct SensorInitialization {
171    pub settable: bool,
172    pub scanning: bool,
173    pub events: bool,
174    pub thresholds: bool,
175    pub hysteresis: bool,
176    pub sensor_type: bool,
177    pub event_generation_enabled_on_startup: bool,
178    pub sensor_scanning_enabled_on_startup: bool,
179}
180
181impl From<u8> for SensorInitialization {
182    fn from(value: u8) -> Self {
183        bitflags::bitflags! {
184            pub struct Flags: u8 {
185                const SETTABLE = 1 << 7;
186                const SCANNING = 1 << 6;
187                const EVENTS = 1 << 5;
188                const THRESHOLDS = 1 << 4;
189                const HYSTERESIS = 1 << 3;
190                const TYPE = 1 << 2;
191                const EVENTGEN_ON_STARTUP = 1 << 1;
192                const SCANNING_ON_STARTUP = 1 << 0;
193            }
194        }
195
196        let flags = Flags::from_bits_truncate(value);
197
198        Self {
199            settable: flags.contains(Flags::SETTABLE),
200            scanning: flags.contains(Flags::SCANNING),
201            events: flags.contains(Flags::EVENTS),
202            thresholds: flags.contains(Flags::THRESHOLDS),
203            hysteresis: flags.contains(Flags::THRESHOLDS),
204            sensor_type: flags.contains(Flags::TYPE),
205            event_generation_enabled_on_startup: flags.contains(Flags::EVENTGEN_ON_STARTUP),
206            sensor_scanning_enabled_on_startup: flags.contains(Flags::SCANNING_ON_STARTUP),
207        }
208    }
209}
210
211#[derive(Debug, Clone, Copy)]
212pub enum HysteresisCapability {
213    NoneOrUnspecified,
214    Readable,
215    ReadableAndSettable,
216    FixedAndUnreadable,
217}
218
219bitflags::bitflags! {
220    pub struct ThresholdAssertEventMask: u16 {
221        const UPPER_NON_RECOVERABLE_GOING_HIGH = 1 << 11;
222        const UPPER_NON_RECOVERABLE_GOING_LOW = 1 << 10;
223        const UPPER_CRITICAL_GOING_HIGH = 1 << 9;
224        const UPPER_CRITICAL_GOING_LOW = 1 << 8;
225        const UPPER_NON_CRITICAL_GOING_HIGH = 1 << 7;
226        const UPPER_NON_CRITICAL_GOING_LOW = 1 << 6;
227        const LOWER_NON_RECOVERABLE_GOING_HIGH = 1 << 5;
228        const LOWER_NON_RECOVERABLE_GOING_LOW = 1 << 4;
229        const LOWER_CRITICAL_GOING_HIGH = 1 << 3;
230        const LOWER_CRITICAL_GOING_LOW = 1 << 2;
231        const LOWER_NON_CRITICAL_GOING_HIGH = 1 << 1;
232        const LOWER_NON_CRITICAL_GOING_LOW = 1 << 0;
233
234    }
235}
236
237impl ThresholdAssertEventMask {
238    pub fn for_kind(&self, kind: ThresholdKind) -> &[EventKind] {
239        static BOTH: [EventKind; 2] = [EventKind::GoingHigh, EventKind::GoingLow];
240        static HIGH: [EventKind; 1] = [EventKind::GoingHigh];
241        static LOW: [EventKind; 1] = [EventKind::GoingLow];
242        static NONE: [EventKind; 0] = [];
243
244        let (low, high) = match kind {
245            ThresholdKind::LowerNonCritical => (
246                self.contains(Self::LOWER_NON_CRITICAL_GOING_LOW),
247                self.contains(Self::LOWER_NON_CRITICAL_GOING_HIGH),
248            ),
249            ThresholdKind::LowerCritical => (
250                self.contains(Self::LOWER_CRITICAL_GOING_LOW),
251                self.contains(Self::LOWER_CRITICAL_GOING_HIGH),
252            ),
253            ThresholdKind::LowerNonRecoverable => (
254                self.contains(Self::LOWER_NON_RECOVERABLE_GOING_LOW),
255                self.contains(Self::LOWER_NON_RECOVERABLE_GOING_HIGH),
256            ),
257            ThresholdKind::UpperNonCritical => (
258                self.contains(Self::UPPER_NON_CRITICAL_GOING_LOW),
259                self.contains(Self::UPPER_NON_CRITICAL_GOING_HIGH),
260            ),
261            ThresholdKind::UpperCritical => (
262                self.contains(Self::UPPER_CRITICAL_GOING_LOW),
263                self.contains(Self::UPPER_CRITICAL_GOING_HIGH),
264            ),
265            ThresholdKind::UpperNonRecoverable => (
266                self.contains(Self::UPPER_NON_RECOVERABLE_GOING_LOW),
267                self.contains(Self::UPPER_NON_RECOVERABLE_GOING_HIGH),
268            ),
269        };
270
271        if low && high {
272            &BOTH
273        } else if low {
274            &LOW
275        } else if high {
276            &HIGH
277        } else {
278            &NONE
279        }
280    }
281}
282
283#[derive(Debug, Clone, Copy, PartialEq)]
284pub enum EventKind {
285    GoingHigh,
286    GoingLow,
287}
288
289#[derive(Debug, Clone, Copy)]
290
291pub struct Thresholds {
292    pub lower_non_recoverable: bool,
293    pub lower_critical: bool,
294    pub lower_non_critical: bool,
295    pub upper_non_recoverable: bool,
296    pub upper_critical: bool,
297    pub upper_non_critical: bool,
298}
299
300impl Thresholds {
301    pub fn for_kind(&self, kind: ThresholdKind) -> bool {
302        match kind {
303            ThresholdKind::LowerNonCritical => self.lower_non_critical,
304            ThresholdKind::LowerCritical => self.lower_critical,
305            ThresholdKind::LowerNonRecoverable => self.lower_non_recoverable,
306            ThresholdKind::UpperNonCritical => self.upper_non_critical,
307            ThresholdKind::UpperCritical => self.upper_critical,
308            ThresholdKind::UpperNonRecoverable => self.upper_non_recoverable,
309        }
310    }
311}
312
313#[derive(Debug, Clone, Copy, PartialEq)]
314pub enum ThresholdKind {
315    LowerNonCritical,
316    LowerCritical,
317    LowerNonRecoverable,
318    UpperNonCritical,
319    UpperCritical,
320    UpperNonRecoverable,
321}
322
323impl ThresholdKind {
324    pub fn variants() -> impl Iterator<Item = Self> {
325        [
326            Self::LowerNonCritical,
327            Self::LowerCritical,
328            Self::LowerNonRecoverable,
329            Self::UpperNonCritical,
330            Self::UpperCritical,
331            Self::UpperNonRecoverable,
332        ]
333        .into_iter()
334    }
335}
336
337#[derive(Debug, Clone, Copy)]
338pub struct Threshold {
339    pub kind: ThresholdKind,
340    pub readable: bool,
341    pub settable: bool,
342    pub event_assert_going_high: bool,
343    pub event_assert_going_low: bool,
344    pub event_deassert_going_high: bool,
345    pub event_deassert_going_low: bool,
346}
347
348#[derive(Debug, Clone, Copy)]
349pub enum ThresholdAccessCapability {
350    None,
351    Readable {
352        readable: Thresholds,
353        values: Thresholds,
354    },
355    ReadableAndSettable {
356        readable: Thresholds,
357        values: Thresholds,
358        settable: Thresholds,
359    },
360    FixedAndUnreadable {
361        supported: Thresholds,
362    },
363}
364
365impl ThresholdAccessCapability {
366    pub fn readable(&self, kind: ThresholdKind) -> bool {
367        match self {
368            ThresholdAccessCapability::Readable { readable, .. } => readable.for_kind(kind),
369            ThresholdAccessCapability::ReadableAndSettable { readable, .. } => {
370                readable.for_kind(kind)
371            }
372            _ => false,
373        }
374    }
375
376    pub fn settable(&self, kind: ThresholdKind) -> bool {
377        match self {
378            ThresholdAccessCapability::ReadableAndSettable { settable, .. } => {
379                settable.for_kind(kind)
380            }
381            _ => false,
382        }
383    }
384}
385
386#[derive(Debug, Clone, Copy)]
387pub struct SensorCapabilities {
388    pub ignore: bool,
389    pub auto_rearm: bool,
390    // TODO: make a type
391    pub event_message_control: u8,
392    pub hysteresis: HysteresisCapability,
393    pub threshold_access: ThresholdAccessCapability,
394    pub assertion_threshold_events: ThresholdAssertEventMask,
395    pub deassertion_threshold_events: ThresholdAssertEventMask,
396}
397
398impl SensorCapabilities {
399    pub fn new(
400        caps: u8,
401        assert_lower_thrsd: u16,
402        deassert_upper_thrshd: u16,
403        discrete_rd_thrsd_set_thrshd_read: u16,
404    ) -> Self {
405        let ignore = (caps & 0x80) == 0x80;
406        let auto_rearm = (caps & 0x40) == 0x40;
407        let hysteresis = match caps & 0x30 >> 4 {
408            0b00 => HysteresisCapability::NoneOrUnspecified,
409            0b01 => HysteresisCapability::Readable,
410            0b10 => HysteresisCapability::ReadableAndSettable,
411            0b11 => HysteresisCapability::FixedAndUnreadable,
412            _ => unreachable!(),
413        };
414        let event_message_control = caps & 0b11;
415
416        let assertion_event_mask = ThresholdAssertEventMask::from_bits_truncate(assert_lower_thrsd);
417        let deassertion_event_mask =
418            ThresholdAssertEventMask::from_bits_truncate(deassert_upper_thrshd);
419
420        let threshold_read_value_mask = Thresholds {
421            lower_non_recoverable: ((assert_lower_thrsd >> 14) & 0x1) == 1,
422            lower_critical: ((assert_lower_thrsd >> 13) & 0x1) == 1,
423            lower_non_critical: ((assert_lower_thrsd >> 12) & 0x1) == 1,
424            upper_non_recoverable: ((deassert_upper_thrshd >> 14) & 0x1) == 1,
425            upper_critical: ((deassert_upper_thrshd >> 14) & 0x1) == 1,
426            upper_non_critical: ((deassert_upper_thrshd >> 14) & 0x1) == 1,
427        };
428
429        let threshold_set_mask = Thresholds {
430            upper_non_recoverable: ((discrete_rd_thrsd_set_thrshd_read >> 13) & 0x1) == 1,
431            upper_critical: ((discrete_rd_thrsd_set_thrshd_read >> 12) & 0x1) == 1,
432            upper_non_critical: ((discrete_rd_thrsd_set_thrshd_read >> 11) & 0x1) == 1,
433            lower_non_recoverable: ((discrete_rd_thrsd_set_thrshd_read >> 10) & 0x1) == 1,
434            lower_critical: ((discrete_rd_thrsd_set_thrshd_read >> 9) & 0x1) == 1,
435            lower_non_critical: ((discrete_rd_thrsd_set_thrshd_read >> 8) & 0x1) == 1,
436        };
437
438        let threshold_read_mask = Thresholds {
439            upper_non_recoverable: ((discrete_rd_thrsd_set_thrshd_read >> 5) & 0x1) == 1,
440            upper_critical: ((discrete_rd_thrsd_set_thrshd_read >> 4) & 0x1) == 1,
441            upper_non_critical: ((discrete_rd_thrsd_set_thrshd_read >> 3) & 0x1) == 1,
442            lower_non_recoverable: ((discrete_rd_thrsd_set_thrshd_read >> 2) & 0x1) == 1,
443            lower_critical: ((discrete_rd_thrsd_set_thrshd_read >> 1) & 0x1) == 1,
444            lower_non_critical: (discrete_rd_thrsd_set_thrshd_read & 0x1) == 1,
445        };
446
447        let threshold_access_support = match (caps & 0xC) >> 2 {
448            0b00 => ThresholdAccessCapability::None,
449            0b01 => ThresholdAccessCapability::Readable {
450                readable: threshold_read_mask,
451                values: threshold_read_value_mask,
452            },
453            0b10 => ThresholdAccessCapability::ReadableAndSettable {
454                readable: threshold_read_mask,
455                values: threshold_read_value_mask,
456                settable: threshold_set_mask,
457            },
458            0b11 => ThresholdAccessCapability::FixedAndUnreadable {
459                supported: threshold_read_mask,
460            },
461            _ => unreachable!(),
462        };
463
464        Self {
465            ignore,
466            auto_rearm,
467            hysteresis,
468            event_message_control,
469            threshold_access: threshold_access_support,
470            assertion_threshold_events: assertion_event_mask,
471            deassertion_threshold_events: deassertion_event_mask,
472        }
473    }
474}
475
476#[derive(Debug, Clone, Copy, PartialEq)]
477pub enum DataFormat {
478    Unsigned,
479    OnesComplement,
480    TwosComplement,
481}
482
483#[derive(Debug, Clone, Copy, PartialEq)]
484pub enum RateUnit {
485    Microsecond,
486    Millisecond,
487    Second,
488    Minute,
489    Hour,
490    Day,
491}
492
493#[derive(Debug, Clone, Copy, PartialEq)]
494pub enum ModifierUnit {
495    BasUnitDivByModifier(Unit),
496    BaseUnitMulByModifier(Unit),
497}
498
499#[derive(Debug, Clone, Copy)]
500pub struct SensorUnits {
501    pub rate: Option<RateUnit>,
502    pub modifier: Option<ModifierUnit>,
503    pub is_percentage: bool,
504    pub base_unit: Unit,
505}
506
507impl SensorUnits {
508    pub fn from(sensor_units_1: u8, base_unit: u8, modifier_unit: u8) -> Self {
509        let rate = match (sensor_units_1 >> 3) & 0b111 {
510            0b000 => None,
511            0b001 => Some(RateUnit::Microsecond),
512            0b010 => Some(RateUnit::Millisecond),
513            0b011 => Some(RateUnit::Second),
514            0b100 => Some(RateUnit::Minute),
515            0b101 => Some(RateUnit::Hour),
516            0b110 => Some(RateUnit::Day),
517            0b111 => None,
518            _ => unreachable!(),
519        };
520
521        let base_unit = Unit::from(base_unit);
522
523        let modifier_unit = Unit::from(modifier_unit);
524
525        let modifier = match (sensor_units_1 >> 1) & 0b11 {
526            0b00 => None,
527            0b01 => Some(ModifierUnit::BasUnitDivByModifier(modifier_unit)),
528            0b10 => Some(ModifierUnit::BaseUnitMulByModifier(modifier_unit)),
529            0b11 => None,
530            _ => unreachable!(),
531        };
532
533        let is_percentage = (sensor_units_1 & 0x1) == 0x1;
534
535        Self {
536            rate,
537            modifier,
538            base_unit,
539            is_percentage,
540        }
541    }
542}
543
544#[derive(Debug, Clone, Copy)]
545
546pub enum Linearization {
547    Linear,
548    Ln,
549    Log10,
550    Log2,
551    E,
552    Exp10,
553    Exp2,
554    OneOverX,
555    Sqr,
556    Cube,
557    Sqrt,
558    CubeRoot,
559    Oem(u8),
560    Unknown(u8),
561}
562
563impl From<u8> for Linearization {
564    fn from(value: u8) -> Self {
565        match value {
566            0 => Self::Linear,
567            1 => Self::Ln,
568            2 => Self::Log10,
569            3 => Self::Log2,
570            4 => Self::E,
571            5 => Self::Exp10,
572            6 => Self::Exp2,
573            7 => Self::OneOverX,
574            8 => Self::Sqr,
575            9 => Self::Sqrt,
576            10 => Self::Cube,
577            11 => Self::Sqrt,
578            12 => Self::CubeRoot,
579            0x71..=0x7F => Self::Oem(value),
580            v => Self::Unknown(v),
581        }
582    }
583}
584
585#[derive(Clone, Copy, Debug, PartialEq)]
586pub enum Direction {
587    UnspecifiedNotApplicable,
588    Input,
589    Output,
590}
591
592impl TryFrom<u8> for Direction {
593    type Error = ParseError;
594
595    fn try_from(value: u8) -> Result<Self, Self::Error> {
596        let dir = match value {
597            0b00 => Self::UnspecifiedNotApplicable,
598            0b01 => Self::Input,
599            0b10 => Self::Output,
600            _ => return Err(ParseError::InvalidSensorDirection),
601        };
602        Ok(dir)
603    }
604}
605
606#[derive(Debug)]
607pub enum ParseError {
608    NotEnoughData,
609    IncorrectRecordLength,
610    InvalidSensorId,
611    InvalidIdStringModifier(u8),
612    InvalidSensorNumber,
613    InvalidSensorDirection,
614    InvalidOwnerChannel,
615}
616
617#[derive(Debug, Clone, PartialEq)]
618pub struct TypeLengthRaw<'a>(u8, &'a [u8]);
619
620impl<'a> TypeLengthRaw<'a> {
621    pub fn new(value: u8, other_data: &'a [u8]) -> Self {
622        Self(value, other_data)
623    }
624}
625
626impl<'a> TryFrom<TypeLengthRaw<'a>> for SensorId {
627    type Error = ParseError;
628
629    fn try_from(value: TypeLengthRaw<'a>) -> Result<Self, Self::Error> {
630        let TypeLengthRaw(value, data) = value;
631        let type_code = (value >> 6) & 0b11;
632
633        let length = value & 0x1F;
634
635        let data = &data[..(length as usize).min(data.len())];
636
637        let id = match type_code {
638            0b00 => SensorId::Unicode(
639                core::str::from_utf8(data)
640                    .map_err(|_| ParseError::InvalidSensorId)
641                    .map(str::to_string)?,
642            ),
643            0b01 => SensorId::BCDPlus(data.to_vec()),
644            0b10 => SensorId::Ascii6BPacked(data.to_vec()),
645            0b11 => SensorId::Ascii8BAndLatin1(data.iter().map(|v| *v as char).collect()),
646            _ => unreachable!(),
647        };
648
649        Ok(id)
650    }
651}
652
653#[derive(Debug, Clone, PartialEq)]
654pub enum SensorId {
655    Unicode(String),
656    BCDPlus(Vec<u8>),
657    Ascii6BPacked(Vec<u8>),
658    Ascii8BAndLatin1(String),
659}
660
661impl core::fmt::Display for SensorId {
662    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
663        match self {
664            SensorId::Unicode(v) => write!(f, "{}", v),
665            SensorId::Ascii8BAndLatin1(v) => write!(f, "{}", v),
666            SensorId::Ascii6BPacked(data) => write!(f, "{:02X?}", data),
667            SensorId::BCDPlus(data) => write!(f, "{:02X?}", data),
668        }
669    }
670}
671
672impl Default for SensorId {
673    fn default() -> Self {
674        Self::Unicode("".into())
675    }
676}
677
678#[derive(Debug, Clone, Copy, PartialEq)]
679pub struct SensorNumber(pub NonMaxU8);
680
681impl SensorNumber {
682    pub fn new(value: NonMaxU8) -> Self {
683        Self(value)
684    }
685
686    pub fn get(&self) -> u8 {
687        self.0.get()
688    }
689}
690
691#[derive(Debug, Clone)]
692pub struct RecordHeader {
693    pub id: RecordId,
694
695    pub sdr_version_major: u8,
696    pub sdr_version_minor: u8,
697}
698
699#[derive(Debug, Clone)]
700pub struct Record {
701    pub header: RecordHeader,
702    pub contents: RecordContents,
703}
704
705#[derive(Debug, Clone)]
706pub enum RecordContents {
707    FullSensor(FullSensorRecord),
708    CompactSensor(CompactSensorRecord),
709    EventOnlySensor(EventOnlySensorRecord),
710    FruDeviceLocator(FruDeviceLocator),
711    McDeviceLocator(McDeviceLocatorRecord),
712    Unknown { ty: u8, data: Vec<u8> },
713}
714
715impl Record {
716    pub fn common_data(&self) -> Option<&SensorRecordCommon> {
717        self.contents.common_data()
718    }
719
720    pub fn full_sensor(&self) -> Option<&FullSensorRecord> {
721        self.contents.full_sensor()
722    }
723
724    pub fn compact_sensor(&self) -> Option<&CompactSensorRecord> {
725        self.contents.compact_sensor()
726    }
727
728    pub fn event_only(&self) -> Option<&EventOnlySensorRecord> {
729        self.contents.event_only()
730    }
731
732    pub fn parse(data: &[u8]) -> Result<Self, ParseError> {
733        if data.len() < 5 {
734            return Err(ParseError::NotEnoughData);
735        }
736
737        let record_id = RecordId::new_raw(u16::from_le_bytes([data[0], data[1]]));
738        let sdr_version_min = (data[2] & 0xF0) >> 4;
739        let sdr_version_maj = data[2] & 0x0F;
740        let record_type = data[3];
741        let record_length = data[4];
742
743        let record_data = &data[5..];
744        if record_data.len() != record_length as usize {
745            return Err(ParseError::IncorrectRecordLength);
746        }
747
748        let contents = if record_type == 0x01 {
749            RecordContents::FullSensor(FullSensorRecord::parse(record_data)?)
750        } else if record_type == 0x02 {
751            RecordContents::CompactSensor(CompactSensorRecord::parse(record_data)?)
752        } else if record_type == 0x03 {
753            RecordContents::EventOnlySensor(EventOnlySensorRecord::parse(record_data)?)
754        } else if record_type == 0x11 {
755            RecordContents::FruDeviceLocator(FruDeviceLocator::parse(record_data)?)
756        } else if record_type == 0x12 {
757            RecordContents::McDeviceLocator(McDeviceLocatorRecord::parse(record_data)?)
758        } else {
759            RecordContents::Unknown {
760                ty: record_type,
761                data: record_data.to_vec(),
762            }
763        };
764
765        Ok(Self {
766            header: RecordHeader {
767                id: record_id,
768                sdr_version_minor: sdr_version_min,
769                sdr_version_major: sdr_version_maj,
770            },
771            contents,
772        })
773    }
774
775    pub fn id(&self) -> Option<&SensorId> {
776        self.contents.id()
777    }
778
779    pub fn sensor_number(&self) -> Option<SensorNumber> {
780        self.contents.sensor_number()
781    }
782}
783
784impl RecordContents {
785    pub fn common_data(&self) -> Option<&SensorRecordCommon> {
786        match self {
787            RecordContents::FullSensor(s) => Some(s.common()),
788            RecordContents::CompactSensor(s) => Some(s.common()),
789            RecordContents::EventOnlySensor(_) => None,
790            RecordContents::FruDeviceLocator(_) => None,
791            RecordContents::McDeviceLocator(_) => None,
792            RecordContents::Unknown { .. } => None,
793        }
794    }
795
796    pub fn full_sensor(&self) -> Option<&FullSensorRecord> {
797        if let RecordContents::FullSensor(full_sensor) = self {
798            Some(full_sensor)
799        } else {
800            None
801        }
802    }
803
804    pub fn compact_sensor(&self) -> Option<&CompactSensorRecord> {
805        if let RecordContents::CompactSensor(compact_sensor) = self {
806            Some(compact_sensor)
807        } else {
808            None
809        }
810    }
811
812    pub fn event_only(&self) -> Option<&EventOnlySensorRecord> {
813        if let RecordContents::EventOnlySensor(event) = self {
814            Some(event)
815        } else {
816            None
817        }
818    }
819
820    pub fn id(&self) -> Option<&SensorId> {
821        match self {
822            RecordContents::FullSensor(full) => Some(full.id_string()),
823            RecordContents::CompactSensor(compact) => Some(compact.id_string()),
824            RecordContents::EventOnlySensor(event) => Some(&event.id_string),
825            RecordContents::FruDeviceLocator(fru) => Some(&fru.id_string),
826            RecordContents::McDeviceLocator(mc) => Some(&mc.id_string),
827            RecordContents::Unknown { .. } => None,
828        }
829    }
830
831    pub fn sensor_number(&self) -> Option<SensorNumber> {
832        match self {
833            RecordContents::FullSensor(full) => Some(full.sensor_number()),
834            RecordContents::CompactSensor(compact) => Some(compact.sensor_number()),
835            RecordContents::EventOnlySensor(event) => Some(event.key.sensor_number),
836            RecordContents::FruDeviceLocator(_) | RecordContents::McDeviceLocator(_) => None,
837            RecordContents::Unknown { .. } => None,
838        }
839    }
840}
841
842#[derive(Debug, Clone)]
843pub struct SensorRecordCommon {
844    pub key: SensorKey,
845    // TODO: make a type EntityId
846    pub entity_id: u8,
847    pub entity_instance: EntityInstance,
848    pub initialization: SensorInitialization,
849    pub capabilities: SensorCapabilities,
850    pub ty: SensorType,
851    pub event_reading_type_code: EventReadingTypeCodes,
852    pub sensor_units: SensorUnits,
853    pub sensor_id: SensorId,
854}
855
856impl SensorRecordCommon {
857    /// Parse common sensor record data, but set the SensorID to an empty UTF-8 String.
858    ///
859    /// You _must_ remember to [`SensorRecordCommon::set_id`] once the ID of the
860    /// record has been parsed.
861    pub(crate) fn parse_without_id(record_data: &[u8]) -> Result<(Self, &[u8]), ParseError> {
862        if record_data.len() < 17 {
863            return Err(ParseError::NotEnoughData);
864        }
865
866        let sensor_key = SensorKey::parse(&record_data[..3])?;
867
868        let entity_id = record_data[3];
869
870        let entity_instance = record_data[4];
871        let entity_instance = EntityInstance::from(entity_instance);
872
873        let initialization = record_data[5];
874        let initialization = SensorInitialization::from(initialization);
875
876        let sensor_capabilities = record_data[6];
877
878        let sensor_type = record_data[7].into();
879        let event_reading_type_code = record_data[8].into();
880
881        let assertion_event_mask_lower_thrsd_reading_mask =
882            u16::from_le_bytes([record_data[9], record_data[10]]);
883        let deassertion_event_mask_upper_thrsd_reading_mask =
884            u16::from_le_bytes([record_data[11], record_data[12]]);
885        let settable_thrsd_readable_thrsd_mask =
886            u16::from_le_bytes([record_data[13], record_data[14]]);
887
888        let capabilities = SensorCapabilities::new(
889            sensor_capabilities,
890            assertion_event_mask_lower_thrsd_reading_mask,
891            deassertion_event_mask_upper_thrsd_reading_mask,
892            settable_thrsd_readable_thrsd_mask,
893        );
894
895        let sensor_units_1 = record_data[15];
896        let base_unit = record_data[16];
897        let modifier_unit = record_data[17];
898
899        let sensor_units = SensorUnits::from(sensor_units_1, base_unit, modifier_unit);
900
901        Ok((
902            Self {
903                key: sensor_key,
904                entity_id,
905                entity_instance,
906                initialization,
907                capabilities,
908                ty: sensor_type,
909                event_reading_type_code,
910                sensor_units,
911                sensor_id: Default::default(),
912            },
913            &record_data[18..],
914        ))
915    }
916
917    pub(crate) fn set_id(&mut self, id: SensorId) {
918        self.sensor_id = id;
919    }
920}
921
922impl Loggable for Record {
923    fn as_log(&self) -> Vec<crate::fmt::LogItem> {
924        let full = self.full_sensor();
925        let compact = self.compact_sensor();
926        let event_only = self.event_only();
927
928        let mut log = Vec::new();
929
930        if full.is_some() {
931            log.push((0, "SDR Record (Full)").into());
932        } else if compact.is_some() {
933            log.push((0, "SDR Record (Compact)").into());
934        } else if event_only.is_some() {
935            log.push((0, "SDR Record (Event-only)").into())
936        } else {
937            log.push((0, "Cannot log unknown sensor type").into());
938            return log;
939        }
940
941        let RecordHeader {
942            id,
943            sdr_version_major: sdr_v_maj,
944            sdr_version_minor: sdr_v_min,
945        } = &self.header;
946
947        log.push((1, "Record ID", format!("0x{:04X}", id.0)).into());
948        log.push((1, "SDR Version", format!("{sdr_v_maj}.{sdr_v_min}")).into());
949
950        if let Some(common) = self.common_data() {
951            log.push((1, "Sensor Type", format!("{:?}", common.ty)).into());
952        }
953
954        if let Some(full) = full {
955            full.key_data().log_into(1, &mut log);
956
957            let display = |v: Value| v.display(true);
958
959            let nominal_reading = full
960                .nominal_value()
961                .map(display)
962                .unwrap_or("Unknown".into());
963
964            let max_reading = full.max_reading().map(display).unwrap_or("Unknown".into());
965            let min_reading = full.min_reading().map(display).unwrap_or("Unknown".into());
966
967            log.push((1, "Sensor ID", full.id_string()).into());
968            log.push((1, "Entity ID", full.entity_id()).into());
969            log.push((1, "Nominal reading", nominal_reading).into());
970            log.push((1, "Max reading", max_reading).into());
971            log.push((1, "Min reading", min_reading).into());
972        } else if let Some(compact) = compact {
973            compact.key_data().log_into(1, &mut log);
974            log.push((1, "Sensor ID", compact.id_string()).into());
975        } else if let Some(event_only) = event_only {
976            event_only.key_data().log_into(1, &mut log);
977            log.push((1, "Sensor ID", event_only.id_string()).into());
978        }
979
980        log
981    }
982}
983
984#[cfg(test)]
985mod tests {
986    use super::*;
987
988    #[test]
989    fn test_sensor_owner_round_trip() {
990        for x in 0u8..=255u8 {
991            let o = SensorOwner::from(x);
992            let value: u8 = o.into();
993            assert_eq!(x, value);
994        }
995    }
996}