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 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 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 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 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}