Skip to content

Commit 3510543

Browse files
committed
Implemented CSPTP server functionality.
1 parent af73547 commit 3510543

5 files changed

Lines changed: 353 additions & 14 deletions

File tree

statime-csptp/src/lib.rs

Lines changed: 35 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -138,16 +138,41 @@ use statime_wire::{ClockIdentity, ClockQuality};
138138
#[cfg(feature = "std")]
139139
extern crate std;
140140

141+
mod manager;
141142
mod messages;
143+
mod platform;
144+
mod server;
142145

143-
// TODO: Figure out how to actually deal with the state.
144-
struct CsptpState {
145-
grandmaster_identity: ClockIdentity,
146-
grandmaster_priority_1: u8,
147-
grandmaster_priority_2: u8,
148-
grandmaster_clock_quality: ClockQuality,
149-
steps_removed: u16,
150-
ptp_timescale: bool,
151-
time_traceable: bool,
152-
frequency_traceable: bool,
146+
pub use manager::{CsptpConfig, CsptpManager};
147+
pub use platform::{InternalState, StateMutex};
148+
pub use server::{ServerRecvResult, ServerSocket, serve};
149+
150+
/// Observable CSPTP state
151+
#[derive(Debug, Copy, Clone)]
152+
pub struct CsptpState {
153+
/// Clock identity of grandmaster currently in use. This will be the local
154+
/// clock identity if there is no upstream time source or the if the
155+
/// upstream time source does not use CSPTP
156+
pub grandmaster_identity: ClockIdentity,
157+
/// Priority 1 for the grandmaster currently in use. This will be the local
158+
/// value if there is no upstream time source or the if the upstream time
159+
/// source does not use CSPTP
160+
pub grandmaster_priority_1: u8,
161+
/// Priority 2 for the grandmaster currently in use. This will be the local
162+
/// value if there is no upstream time source or the if the upstream time
163+
/// source does not use CSPTP
164+
pub grandmaster_priority_2: u8,
165+
/// Clock quality for the grandmaster currently in use. This will be the
166+
/// local value if there is no upstream time source or the if the upstream
167+
/// time source does not use CSPTP
168+
pub grandmaster_clock_quality: ClockQuality,
169+
/// Steps removed from the current grandmaster. Will be 0 if there is no
170+
/// upstream time source or the upstream time source does not use CSPTP.
171+
pub steps_removed: u16,
172+
/// Whether the ptp timescale is in use.
173+
pub ptp_timescale: bool,
174+
/// Whether the current time is traceable.
175+
pub time_traceable: bool,
176+
/// Whether the current frequency is traceable.
177+
pub frequency_traceable: bool,
153178
}

statime-csptp/src/manager.rs

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
use ntp_proto::{ClockId, SourceType, TimeSnapshot};
2+
use statime_wire::{ClockIdentity, ClockQuality};
3+
4+
use crate::{
5+
CsptpState,
6+
platform::{InternalState, StateMutex},
7+
};
8+
9+
/// General configuration for the CSPTP protocol
10+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11+
pub struct CsptpConfig {
12+
/// Identity of the local instance.
13+
pub identity: ClockIdentity,
14+
/// Priority 1 value to use for the local clock.
15+
pub priority_1: u8,
16+
/// Priority 2 value to use for the local clock.
17+
pub priority_2: u8,
18+
/// Quality of the local clock.
19+
pub clock_quality: ClockQuality,
20+
/// Whether the local time uses the ptp timescale.
21+
pub ptp_timescale: bool,
22+
/// Whether the local time is traceable.
23+
pub time_traceable: bool,
24+
/// Whether the local frequency is traceable.
25+
pub frequency_traceable: bool,
26+
}
27+
28+
impl Default for CsptpConfig {
29+
fn default() -> Self {
30+
Self {
31+
identity: ClockIdentity::default(),
32+
priority_1: 255,
33+
priority_2: 255,
34+
clock_quality: ClockQuality::default(),
35+
ptp_timescale: true,
36+
time_traceable: false,
37+
frequency_traceable: false,
38+
}
39+
}
40+
}
41+
42+
/// Manager for the CSPTP general protocol state.
43+
pub struct CsptpManager<Mutex> {
44+
pub(crate) config: CsptpConfig,
45+
pub(crate) state: Mutex,
46+
}
47+
48+
impl<Mutex: StateMutex> CsptpManager<Mutex> {
49+
/// Create a new CSPTP protocol manager.
50+
#[must_use]
51+
pub fn new(config: CsptpConfig) -> Self {
52+
Self {
53+
config,
54+
state: Mutex::new(InternalState {
55+
csptp_state: CsptpState {
56+
grandmaster_identity: config.identity,
57+
grandmaster_priority_1: config.priority_1,
58+
grandmaster_priority_2: config.priority_2,
59+
grandmaster_clock_quality: config.clock_quality,
60+
steps_removed: 0,
61+
ptp_timescale: config.ptp_timescale,
62+
time_traceable: config.time_traceable,
63+
frequency_traceable: config.frequency_traceable,
64+
},
65+
time_snapshot: TimeSnapshot::default(),
66+
active_source: None,
67+
}),
68+
}
69+
}
70+
71+
/// Update which sources are used for time synchronization.
72+
pub fn update_used_sources(&self, mut sources: impl Iterator<Item = (ClockId, SourceType)>) {
73+
let active_source = sources.next().map(|(clock_id, _)| clock_id);
74+
self.state.with_mut(move |state| {
75+
if state.active_source != active_source {
76+
state.active_source = active_source;
77+
state.csptp_state.grandmaster_identity = self.config.identity;
78+
state.csptp_state.grandmaster_priority_1 = self.config.priority_1;
79+
state.csptp_state.grandmaster_priority_2 = self.config.priority_2;
80+
state.csptp_state.grandmaster_clock_quality = self.config.clock_quality;
81+
state.csptp_state.ptp_timescale = self.config.ptp_timescale;
82+
state.csptp_state.time_traceable = self.config.time_traceable;
83+
state.csptp_state.frequency_traceable = self.config.frequency_traceable;
84+
}
85+
});
86+
}
87+
88+
/// Observe the system state.
89+
#[must_use]
90+
pub fn observe(&self) -> CsptpState {
91+
self.state.with_ref(|state| state.csptp_state)
92+
}
93+
}

statime-csptp/src/messages.rs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ use crate::{
1313

1414
mod tlvs;
1515

16+
pub(crate) const MAX_MESSAGE_SIZE: usize = 512;
17+
1618
/// A message with additional restrictions to ensure it is a valid CSPTP Message.
1719
pub(crate) struct CsptpMessage<'a> {
1820
message: Message<'a>,
@@ -51,7 +53,6 @@ impl<'a> CsptpMessage<'a> {
5153
/// # Errors
5254
/// This returns an error when the provided buffer does not contain a valid
5355
/// PTP message, or when the provided message is incomplete.
54-
#[expect(unused)]
5556
pub(crate) fn deserialize(buffer: &'a [u8]) -> Result<Self, statime_wire::Error> {
5657
let message = Message::deserialize(buffer)?;
5758
if message.header.sdo_id != SdoId::try_from(0x300).unwrap()
@@ -96,7 +97,6 @@ impl<'a> CsptpMessage<'a> {
9697
Ok(CsptpMessage { message })
9798
}
9899

99-
#[expect(unused)]
100100
pub(crate) fn is_request(&self) -> bool {
101101
matches!(self.message.body, MessageBody::Sync(_))
102102
&& self
@@ -141,7 +141,6 @@ impl<'a> CsptpMessage<'a> {
141141
}
142142

143143
/// Generate a response to a request. A buffer of 30 bytes will always be able to contain the response
144-
#[expect(unused)]
145144
pub(crate) fn new_response(
146145
buffer: &'a mut [u8],
147146
request: &CsptpMessage<'_>,
@@ -202,7 +201,6 @@ impl<'a> CsptpMessage<'a> {
202201
})
203202
}
204203

205-
#[expect(unused)]
206204
pub(crate) fn new_follow_up(
207205
response: &CsptpMessage<'_>,
208206
send_timestamp: Timestamp,

statime-csptp/src/platform.rs

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
use core::cell::RefCell;
2+
3+
use ntp_proto::{ClockId, TimeSnapshot};
4+
5+
use crate::CsptpState;
6+
7+
/// Internal state used by CSPTP.
8+
///
9+
/// This is publicly nameable and visible to make [`StateMutex`] implementable
10+
/// outside of this crate.
11+
#[derive(Clone, Copy)]
12+
pub struct InternalState {
13+
pub(crate) csptp_state: CsptpState,
14+
pub(crate) time_snapshot: TimeSnapshot,
15+
pub(crate) active_source: Option<ClockId>,
16+
}
17+
18+
/// A mutex over a [`InternalState`]
19+
///
20+
/// This provides an abstraction for locking state in various environments.
21+
/// Implementations are provided for [`core::cell::RefCell`] and
22+
/// [`std::sync::RwLock`].
23+
pub trait StateMutex {
24+
/// Creates a new instance of the mutex
25+
fn new(state: InternalState) -> Self;
26+
27+
/// Takes a shared reference to the contained state and calls `f` with it
28+
fn with_ref<R, F: FnOnce(&InternalState) -> R>(&self, f: F) -> R;
29+
30+
/// Takes a mutable reference to the contained state and calls `f` with it
31+
fn with_mut<R, F: FnOnce(&mut InternalState) -> R>(&self, f: F) -> R;
32+
}
33+
34+
impl StateMutex for RefCell<InternalState> {
35+
fn new(state: InternalState) -> Self {
36+
RefCell::new(state)
37+
}
38+
39+
fn with_ref<R, F: FnOnce(&InternalState) -> R>(&self, f: F) -> R {
40+
f(&RefCell::borrow(self))
41+
}
42+
43+
fn with_mut<R, F: FnOnce(&mut InternalState) -> R>(&self, f: F) -> R {
44+
f(&mut RefCell::borrow_mut(self))
45+
}
46+
}
47+
48+
#[cfg(feature = "std")]
49+
impl StateMutex for std::sync::RwLock<InternalState> {
50+
fn new(state: InternalState) -> Self {
51+
std::sync::RwLock::new(state)
52+
}
53+
54+
fn with_ref<R, F: FnOnce(&InternalState) -> R>(&self, f: F) -> R {
55+
f(&self.read().unwrap())
56+
}
57+
58+
fn with_mut<R, F: FnOnce(&mut InternalState) -> R>(&self, f: F) -> R {
59+
f(&mut self.write().unwrap())
60+
}
61+
}

0 commit comments

Comments
 (0)