use crate::node_id::NodeId;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct LwwRegister<T> {
value: T,
timestamp: u64,
node_id: NodeId,
}
impl<T> LwwRegister<T> {
pub const fn new(value: T, node_id: NodeId, timestamp: u64) -> Self {
Self {
value,
timestamp,
node_id,
}
}
#[inline]
pub const fn value(&self) -> &T {
&self.value
}
#[inline]
pub const fn timestamp(&self) -> u64 {
self.timestamp
}
#[inline]
pub const fn node_id(&self) -> NodeId {
self.node_id
}
pub fn update(&mut self, value: T, node_id: NodeId, timestamp: u64) -> bool {
if self.should_accept(timestamp, node_id) {
self.value = value;
self.timestamp = timestamp;
self.node_id = node_id;
true
} else {
false
}
}
pub fn merge(&mut self, other: Self) {
if self.should_accept(other.timestamp, other.node_id) {
*self = other;
}
}
fn should_accept(&self, timestamp: u64, node_id: NodeId) -> bool {
timestamp > self.timestamp
|| (timestamp == self.timestamp && node_id.as_u32() > self.node_id.as_u32())
}
}
impl<T: Default> Default for LwwRegister<T> {
fn default() -> Self {
Self {
value: T::default(),
timestamp: 0,
node_id: NodeId::NULL,
}
}
}
impl<T: Clone> LwwRegister<T> {
pub fn value_cloned(&self) -> T {
self.value.clone()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct Position {
pub lat_microdeg: i32,
pub lon_microdeg: i32,
pub alt_cm: i32,
}
impl Position {
#[cfg(feature = "std")]
pub fn from_degrees(lat: f64, lon: f64, alt_m: f32) -> Self {
Self {
lat_microdeg: (lat * 1_000_000.0) as i32,
lon_microdeg: (lon * 1_000_000.0) as i32,
alt_cm: (alt_m * 100.0) as i32,
}
}
#[cfg(feature = "std")]
pub fn to_degrees(&self) -> (f64, f64, f32) {
(
self.lat_microdeg as f64 / 1_000_000.0,
self.lon_microdeg as f64 / 1_000_000.0,
self.alt_cm as f32 / 100.0,
)
}
pub fn encode(&self) -> [u8; 12] {
let mut buf = [0u8; 12];
buf[0..4].copy_from_slice(&self.lat_microdeg.to_le_bytes());
buf[4..8].copy_from_slice(&self.lon_microdeg.to_le_bytes());
buf[8..12].copy_from_slice(&self.alt_cm.to_le_bytes());
buf
}
pub fn decode(data: &[u8; 12]) -> Self {
Self {
lat_microdeg: i32::from_le_bytes([data[0], data[1], data[2], data[3]]),
lon_microdeg: i32::from_le_bytes([data[4], data[5], data[6], data[7]]),
alt_cm: i32::from_le_bytes([data[8], data[9], data[10], data[11]]),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_lww_basic() {
let mut reg = LwwRegister::new(10, NodeId::new(1), 100);
assert_eq!(reg.value(), &10);
assert!(reg.update(20, NodeId::new(2), 200));
assert_eq!(reg.value(), &20);
assert!(!reg.update(30, NodeId::new(3), 150));
assert_eq!(reg.value(), &20);
}
#[test]
fn test_lww_tiebreaker() {
let mut reg = LwwRegister::new(10, NodeId::new(1), 100);
assert!(reg.update(20, NodeId::new(5), 100));
assert_eq!(reg.value(), &20);
assert_eq!(reg.node_id(), NodeId::new(5));
assert!(!reg.update(30, NodeId::new(3), 100));
assert_eq!(reg.value(), &20);
}
#[test]
fn test_lww_merge() {
let mut reg1 = LwwRegister::new(10, NodeId::new(1), 100);
let reg2 = LwwRegister::new(20, NodeId::new(2), 200);
reg1.merge(reg2);
assert_eq!(reg1.value(), &20);
}
#[test]
fn test_position_encode_decode() {
let pos = Position {
lat_microdeg: 37_774_929, lon_microdeg: -122_419_416, alt_cm: 1000, };
let encoded = pos.encode();
let decoded = Position::decode(&encoded);
assert_eq!(pos, decoded);
}
#[cfg(feature = "std")]
#[test]
fn test_position_degrees() {
let pos = Position::from_degrees(37.774929, -122.419416, 10.0);
let (lat, lon, alt) = pos.to_degrees();
assert!((lat - 37.774929).abs() < 0.000001);
assert!((lon - (-122.419416)).abs() < 0.000001);
assert!((alt - 10.0).abs() < 0.01);
}
}