use crate::{CanError, CanMessage};
use std::time::Duration;
pub const MIN_INTERVAL_MS: u64 = 1;
pub const MAX_INTERVAL_MS: u64 = 10_000;
#[derive(Debug, Clone)]
pub struct PeriodicMessage {
id: u32,
message: CanMessage,
interval: Duration,
enabled: bool,
}
impl PeriodicMessage {
pub fn new(message: CanMessage, interval: Duration) -> Result<Self, CanError> {
Self::validate_interval(interval)?;
Ok(Self {
id: 0, message,
interval,
enabled: true,
})
}
fn validate_interval(interval: Duration) -> Result<(), CanError> {
let ms_u128 = interval.as_millis();
let ms = u64::try_from(ms_u128).map_err(|_| CanError::InvalidParameter {
parameter: "interval".to_string(),
reason: format!(
"interval must be between {MIN_INTERVAL_MS}ms and {MAX_INTERVAL_MS}ms, got {ms_u128}ms"
),
})?;
if !(MIN_INTERVAL_MS..=MAX_INTERVAL_MS).contains(&ms) {
return Err(CanError::InvalidParameter {
parameter: "interval".to_string(),
reason: format!(
"interval must be between {MIN_INTERVAL_MS}ms and {MAX_INTERVAL_MS}ms, got {ms}ms"
),
});
}
Ok(())
}
#[must_use]
pub fn id(&self) -> u32 {
self.id
}
pub(crate) fn set_id(&mut self, id: u32) {
self.id = id;
}
#[must_use]
pub fn message(&self) -> &CanMessage {
&self.message
}
#[must_use]
pub fn interval(&self) -> Duration {
self.interval
}
#[must_use]
pub fn is_enabled(&self) -> bool {
self.enabled
}
#[allow(clippy::needless_pass_by_value)]
pub fn update_data(&mut self, data: Vec<u8>) -> Result<(), CanError> {
use crate::message::MessageFlags;
use crate::CanId;
let new_message = if self.message.flags().contains(MessageFlags::FD) {
CanMessage::new_fd(self.message.id(), &data)?
} else {
match self.message.id() {
CanId::Standard(id) => CanMessage::new_standard(id, &data)?,
CanId::Extended(id) => CanMessage::new_extended(id, &data)?,
}
};
self.message = new_message;
Ok(())
}
pub fn set_interval(&mut self, interval: Duration) -> Result<(), CanError> {
Self::validate_interval(interval)?;
self.interval = interval;
Ok(())
}
pub fn set_enabled(&mut self, enabled: bool) {
self.enabled = enabled;
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::CanId;
fn create_test_message() -> CanMessage {
CanMessage::new_standard(0x123, &[0x01, 0x02, 0x03, 0x04]).unwrap()
}
#[test]
fn test_new_periodic_message() {
let msg = create_test_message();
let periodic = PeriodicMessage::new(msg, Duration::from_millis(100)).unwrap();
assert_eq!(periodic.interval(), Duration::from_millis(100));
assert!(periodic.is_enabled());
assert_eq!(periodic.message().id(), CanId::Standard(0x123));
}
#[test]
fn test_interval_validation_min() {
let msg = create_test_message();
let result = PeriodicMessage::new(msg, Duration::from_millis(0));
assert!(result.is_err());
}
#[test]
fn test_interval_validation_max() {
let msg = create_test_message();
let result = PeriodicMessage::new(msg, Duration::from_millis(10_001));
assert!(result.is_err());
}
#[test]
fn test_interval_validation_valid_min() {
let msg = create_test_message();
let result = PeriodicMessage::new(msg, Duration::from_millis(1));
assert!(result.is_ok());
}
#[test]
fn test_interval_validation_valid_max() {
let msg = create_test_message();
let result = PeriodicMessage::new(msg, Duration::from_millis(10_000));
assert!(result.is_ok());
}
#[test]
fn test_update_data() {
let msg = create_test_message();
let mut periodic = PeriodicMessage::new(msg, Duration::from_millis(100)).unwrap();
periodic.update_data(vec![0xAA, 0xBB]).unwrap();
assert_eq!(periodic.message().data(), &[0xAA, 0xBB]);
}
#[test]
fn test_set_interval() {
let msg = create_test_message();
let mut periodic = PeriodicMessage::new(msg, Duration::from_millis(100)).unwrap();
periodic.set_interval(Duration::from_millis(200)).unwrap();
assert_eq!(periodic.interval(), Duration::from_millis(200));
}
#[test]
fn test_set_enabled() {
let msg = create_test_message();
let mut periodic = PeriodicMessage::new(msg, Duration::from_millis(100)).unwrap();
assert!(periodic.is_enabled());
periodic.set_enabled(false);
assert!(!periodic.is_enabled());
periodic.set_enabled(true);
assert!(periodic.is_enabled());
}
}