Skip to content

Commit 3435f81

Browse files
committed
Expose port dataset for observability.
1 parent 397dc39 commit 3435f81

File tree

8 files changed

+200
-3
lines changed

8 files changed

+200
-3
lines changed

statime-linux/src/main.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,7 @@ async fn actual_main() {
298298
parent_ds: instance.parent_ds(),
299299
time_properties_ds: instance.time_properties_ds(),
300300
path_trace_ds: instance.path_trace_ds(),
301+
port_ds: vec![],
301302
});
302303
statime_linux::observer::spawn(&config, instance_state_receiver).await;
303304

@@ -505,6 +506,7 @@ async fn run(
505506
parent_ds: instance.parent_ds(),
506507
time_properties_ds: instance.time_properties_ds(),
507508
path_trace_ds: instance.path_trace_ds(),
509+
port_ds: mut_bmca_ports.iter().map(|v| v.port_ds()).collect(),
508510
});
509511

510512
let mut clock_states = vec![ClockSyncMode::FromSystem; internal_sync_senders.len()];

statime-linux/src/metrics/format.rs

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,57 @@ fn format_path_trace_ds(
352352
Ok(())
353353
}
354354

355+
fn format_port_ds(
356+
w: &mut impl Write,
357+
port_ds: &[statime::observability::port::PortDS],
358+
labels: Vec<(&'static str, String)>,
359+
) -> std::fmt::Result {
360+
format_metric(
361+
w,
362+
"port_state",
363+
"The current state of the port",
364+
MetricType::Gauge,
365+
None,
366+
port_ds
367+
.iter()
368+
.map(|port_ds| {
369+
let mut labels = labels.clone();
370+
labels.push(("port", format!("{}", port_ds.port_identity.port_number)));
371+
Measurement {
372+
labels,
373+
value: port_ds.port_state as u8,
374+
}
375+
})
376+
.collect(),
377+
)?;
378+
379+
format_metric(
380+
w,
381+
"mean_link_delay",
382+
"The current mean link delay of the port",
383+
MetricType::Gauge,
384+
Some(Unit::Nanoseconds),
385+
port_ds
386+
.iter()
387+
.filter_map(|port_ds| match port_ds.delay_mechanism {
388+
statime::observability::port::DelayMechanism::P2P {
389+
mean_link_delay, ..
390+
} => {
391+
let mut labels = labels.clone();
392+
labels.push(("port", format!("{}", port_ds.port_identity.port_number)));
393+
Some(Measurement {
394+
labels,
395+
value: mean_link_delay.to_nanos(),
396+
})
397+
}
398+
_ => None,
399+
})
400+
.collect(),
401+
)?;
402+
403+
Ok(())
404+
}
405+
355406
pub fn format_state(w: &mut impl std::fmt::Write, state: &ObservableState) -> std::fmt::Result {
356407
format_metric(
357408
w,
@@ -379,6 +430,7 @@ pub fn format_state(w: &mut impl std::fmt::Write, state: &ObservableState) -> st
379430
format_parent_ds(w, &state.instance.parent_ds, labels.clone())?;
380431
format_time_properties_ds(w, &state.instance.time_properties_ds, labels.clone())?;
381432
format_path_trace_ds(w, &state.instance.path_trace_ds, labels.clone())?;
433+
format_port_ds(w, &state.instance.port_ds, labels.clone())?;
382434

383435
w.write_str("# EOF\n")?;
384436
Ok(())

statime-linux/src/observer.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ use std::{fs::Permissions, os::unix::prelude::PermissionsExt, path::Path, time::
22

33
use statime::{
44
config::TimePropertiesDS,
5-
observability::{current::CurrentDS, default::DefaultDS, parent::ParentDS, PathTraceDS},
5+
observability::{
6+
current::CurrentDS, default::DefaultDS, parent::ParentDS, port::PortDS, PathTraceDS,
7+
},
68
};
79
use tokio::{io::AsyncWriteExt, net::UnixStream, task::JoinHandle};
810

@@ -29,6 +31,8 @@ pub struct ObservableInstanceState {
2931
/// A concrete implementation of the PTP Path Trace dataset (IEEE1588-2019
3032
/// section 16.2.2)
3133
pub path_trace_ds: PathTraceDS,
34+
/// Port datasets for all the ports.
35+
pub port_ds: Vec<PortDS>,
3236
}
3337

3438
pub async fn spawn(

statime/src/datastructures/common/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ pub use clock_identity::*;
1515
pub use clock_quality::*;
1616
pub use leap_indicator::*;
1717
pub(crate) use port_identity::*;
18-
pub(crate) use time_interval::*;
18+
pub use time_interval::*;
1919
pub use time_source::*;
2020
pub use timestamp::*;
2121
pub use tlv::*;

statime/src/datastructures/common/time_interval.rs

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use core::ops::{Deref, DerefMut};
22

3+
use az::Cast;
34
use fixed::types::I48F16;
45

56
use crate::{
@@ -9,7 +10,29 @@ use crate::{
910

1011
/// Represents time intervals in nanoseconds
1112
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
12-
pub(crate) struct TimeInterval(pub I48F16);
13+
pub struct TimeInterval(pub I48F16);
14+
15+
#[cfg(feature = "serde")]
16+
impl<'de> serde::Deserialize<'de> for TimeInterval {
17+
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
18+
where
19+
D: serde::Deserializer<'de>,
20+
{
21+
Ok(TimeInterval(I48F16::from_bits(i64::deserialize(
22+
deserializer,
23+
)?)))
24+
}
25+
}
26+
27+
#[cfg(feature = "serde")]
28+
impl serde::Serialize for TimeInterval {
29+
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
30+
where
31+
S: serde::Serializer,
32+
{
33+
serializer.serialize_i64(self.0.to_bits())
34+
}
35+
}
1336

1437
impl Deref for TimeInterval {
1538
type Target = I48F16;
@@ -45,6 +68,12 @@ impl From<Duration> for TimeInterval {
4568
}
4669
}
4770

71+
impl TimeInterval {
72+
pub fn to_nanos(self) -> f64 {
73+
self.0.cast()
74+
}
75+
}
76+
4877
#[cfg(test)]
4978
mod tests {
5079
use super::*;

statime/src/observability/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,8 @@ pub mod default;
88
/// A concrete implementation of the PTP Parent dataset (IEEE1588-2019 section
99
/// 8.2.3)
1010
pub mod parent;
11+
/// A concrete implementation of the PTP Port dataset (IEEE1588-2019 section
12+
/// 8.2.15)
13+
pub mod port;
1114

1215
pub use crate::datastructures::datasets::PathTraceDS;

statime/src/observability/port.rs

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
use crate::datastructures::common::{PortIdentity, TimeInterval};
2+
3+
/// Type for `[PortDS].port_state`, see also *IEEE1588-2019 section 8.2.15.3.1
4+
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
5+
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
6+
#[repr(u8)]
7+
#[allow(missing_docs)]
8+
pub enum PortState {
9+
Initializing = 1,
10+
Faulty = 2,
11+
Disabled = 3,
12+
Listening = 4,
13+
PreMaster = 5,
14+
Master = 6,
15+
Passive = 7,
16+
Uncalibrated = 8,
17+
Slave = 9,
18+
}
19+
20+
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
21+
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
22+
#[repr(u8)]
23+
#[allow(missing_docs)]
24+
pub enum DelayMechanism {
25+
E2E {
26+
/// See *IEEE1588-2019 section 8.2.15.3.2*
27+
log_min_delay_req_interval: i8,
28+
} = 1,
29+
P2P {
30+
/// See *IEEE1588-2019 section 8.2.15.4.5*
31+
log_min_p_delay_req_interval: i8,
32+
/// See *IEEE1588-2019 section 8.2.15.3.3*
33+
mean_link_delay: TimeInterval,
34+
} = 2,
35+
NoMechanism = 0xfe,
36+
CommonP2P {
37+
/// See *IEEE1588-2019 section 8.2.15.3.3*
38+
mean_link_delay: TimeInterval,
39+
} = 3,
40+
Special = 4,
41+
}
42+
43+
/// A concrete implementation of the PTP Port dataset (IEEE1588-2019 section
44+
/// 8.2.15)
45+
///
46+
/// meanLinkDelay, logMinDelayReqInterval and logMinPDelayReqInterval are
47+
/// exposed through the delay mechanism type when the relevant delay mechanism
48+
/// is in use.
49+
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
50+
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
51+
pub struct PortDS {
52+
/// See *IEEE1588-2019 section 8.2.15.2.1*
53+
pub port_identity: PortIdentity,
54+
/// See *IEEE1588-2019 section 8.2.15.3.1*
55+
pub port_state: PortState,
56+
/// See *IEEE1588-2019 section 8.2.15.4.1*
57+
pub log_announce_interval: i8,
58+
/// See *IEEE1588-2019 section 8.2.15.4.2*
59+
pub announce_receipt_timeout: u8,
60+
/// See *IEEE1588-2019 section 8.2.15.4.3*
61+
pub log_sync_interval: i8,
62+
/// See *IEEE1588-2019 section 8.2.15.4.4*
63+
pub delay_mechanism: DelayMechanism,
64+
/// See *IEEE1588-2019 section 8.2.15.4.6*
65+
pub version_number: u8,
66+
/// See *IEEE1588-2019 section 8.2.15.4.7*
67+
pub minor_version_number: u8,
68+
/// See *IEEE1588-2019 section 8.2.15.4.8*
69+
pub delay_asymmetry: TimeInterval,
70+
/// See *IEEE1588-2019 section 8.2.15.5.2*
71+
pub master_only: bool,
72+
}

statime/src/port/mod.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ use crate::{
3030
messages::{Message, MessageBody},
3131
},
3232
filters::Filter,
33+
observability::{self, port::PortDS},
3334
ptp_instance::{PtpInstanceState, PtpInstanceStateMutex},
3435
time::{Duration, Time},
3536
};
@@ -589,6 +590,40 @@ impl<L, A, R, C, F: Filter, S> Port<'_, L, A, R, C, F, S> {
589590
pub(crate) fn number(&self) -> u16 {
590591
self.port_identity.port_number
591592
}
593+
594+
/// Get a copy of the port dataset of the port
595+
pub fn port_ds(&self) -> PortDS {
596+
PortDS {
597+
port_identity: self.port_identity,
598+
port_state: match self.port_state {
599+
PortState::Faulty => observability::port::PortState::Faulty,
600+
PortState::Listening => observability::port::PortState::Listening,
601+
PortState::Master => observability::port::PortState::Master,
602+
PortState::Passive => observability::port::PortState::Passive,
603+
PortState::Slave(_) => observability::port::PortState::Slave,
604+
},
605+
log_announce_interval: self.config.announce_interval.as_log_2(),
606+
announce_receipt_timeout: self.config.announce_receipt_timeout,
607+
log_sync_interval: self.config.sync_interval.as_log_2(),
608+
delay_mechanism: match self.config.delay_mechanism {
609+
crate::config::DelayMechanism::E2E { interval } => {
610+
observability::port::DelayMechanism::E2E {
611+
log_min_delay_req_interval: interval.as_log_2(),
612+
}
613+
}
614+
crate::config::DelayMechanism::P2P { interval } => {
615+
observability::port::DelayMechanism::P2P {
616+
log_min_p_delay_req_interval: interval.as_log_2(),
617+
mean_link_delay: self.mean_delay.map(|v| v.into()).unwrap_or_default(),
618+
}
619+
}
620+
},
621+
version_number: 2,
622+
minor_version_number: self.config.minor_ptp_version as u8,
623+
delay_asymmetry: self.config.delay_asymmetry.into(),
624+
master_only: self.config.master_only,
625+
}
626+
}
592627
}
593628

594629
impl<'a, A, C, F: Filter, R: Rng, S: PtpInstanceStateMutex> Port<'a, InBmca, A, R, C, F, S> {

0 commit comments

Comments
 (0)