use std::time::Duration;
use crate::app::types::Timestamp;
use crate::app::variations::{Group34Var1, Group34Var2, Group34Var3};
use crate::util::bit::bits;
use crate::util::bit::BitMask;
use crate::util::bit::Bitfield;
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum DoubleBit {
Intermediate,
DeterminedOff,
DeterminedOn,
Indeterminate,
}
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum Time {
Synchronized(Timestamp),
Unsynchronized(Timestamp),
}
impl Time {
pub fn is_synchronized(&self) -> bool {
std::matches!(self, Self::Synchronized(_))
}
pub fn synchronized(ts: u64) -> Time {
Self::Synchronized(Timestamp::new(ts))
}
pub fn unsynchronized(ts: u64) -> Time {
Self::Unsynchronized(Timestamp::new(ts))
}
}
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub struct Flags {
pub value: u8,
}
impl Flags {
pub const ONLINE: Flags = Flags::new(bits::BIT_0.value);
pub const RESTART: Flags = Flags::new(bits::BIT_1.value);
pub const COMM_LOST: Flags = Flags::new(bits::BIT_2.value);
pub const REMOTE_FORCED: Flags = Flags::new(bits::BIT_3.value);
pub const LOCAL_FORCED: Flags = Flags::new(bits::BIT_4.value);
pub const CHATTER_FILTER: Flags = Flags::new(bits::BIT_5.value);
pub const OVER_RANGE: Flags = Flags::new(bits::BIT_5.value);
pub const ROLL_OVER: Flags = Flags::new(bits::BIT_5.value);
pub const DISCONTINUITY: Flags = Flags::new(bits::BIT_6.value);
pub const REFERENCE_ERR: Flags = Flags::new(bits::BIT_6.value);
pub const fn new(value: u8) -> Self {
Self { value }
}
pub fn is_set(&self, other: Flags) -> bool {
(self.value & other.value) == other.value
}
}
pub(crate) trait ToVariation<V> {
fn to_variation(&self) -> V;
}
pub(crate) trait WireFlags {
fn get_wire_flags(&self) -> u8;
}
impl From<Option<Time>> for Time {
fn from(x: Option<Time>) -> Self {
x.unwrap_or_else(|| Time::Unsynchronized(Timestamp::new(0)))
}
}
impl From<Option<Time>> for Timestamp {
fn from(x: Option<Time>) -> Self {
Time::from(x).timestamp()
}
}
impl Time {
pub(crate) fn checked_add(self, x: u16) -> Option<Self> {
match self {
Time::Synchronized(ts) => ts
.checked_add(Duration::from_millis(x as u64))
.map(Time::Synchronized),
Time::Unsynchronized(ts) => ts
.checked_add(Duration::from_millis(x as u64))
.map(Time::Unsynchronized),
}
}
pub fn timestamp(&self) -> Timestamp {
match self {
Time::Synchronized(ts) => *ts,
Time::Unsynchronized(ts) => *ts,
}
}
}
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub struct BinaryInput {
pub value: bool,
pub flags: Flags,
pub time: Option<Time>,
}
impl BinaryInput {
pub const fn new(value: bool, flags: Flags, time: Time) -> Self {
Self {
value,
flags,
time: Some(time),
}
}
}
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub struct DoubleBitBinaryInput {
pub value: DoubleBit,
pub flags: Flags,
pub time: Option<Time>,
}
impl DoubleBitBinaryInput {
pub const fn new(value: DoubleBit, flags: Flags, time: Time) -> Self {
Self {
value,
flags,
time: Some(time),
}
}
}
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub struct BinaryOutputStatus {
pub value: bool,
pub flags: Flags,
pub time: Option<Time>,
}
impl BinaryOutputStatus {
pub const fn new(value: bool, flags: Flags, time: Time) -> Self {
Self {
value,
flags,
time: Some(time),
}
}
}
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub struct Counter {
pub value: u32,
pub flags: Flags,
pub time: Option<Time>,
}
impl Counter {
pub const fn new(value: u32, flags: Flags, time: Time) -> Self {
Self {
value,
flags,
time: Some(time),
}
}
}
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub struct FrozenCounter {
pub value: u32,
pub flags: Flags,
pub time: Option<Time>,
}
impl FrozenCounter {
pub const fn new(value: u32, flags: Flags, time: Time) -> Self {
Self {
value,
flags,
time: Some(time),
}
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct AnalogInput {
pub value: f64,
pub flags: Flags,
pub time: Option<Time>,
}
impl AnalogInput {
pub const fn new(value: f64, flags: Flags, time: Time) -> Self {
Self {
value,
flags,
time: Some(time),
}
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct FrozenAnalogInput {
pub value: f64,
pub flags: Flags,
pub time: Option<Time>,
}
impl FrozenAnalogInput {
pub const fn new(value: f64, flags: Flags, time: Time) -> Self {
Self {
value,
flags,
time: Some(time),
}
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum AnalogInputDeadBand {
U16(u16),
U32(u32),
F32(f32),
}
impl From<Group34Var1> for AnalogInputDeadBand {
fn from(value: Group34Var1) -> Self {
Self::U16(value.value)
}
}
impl From<Group34Var2> for AnalogInputDeadBand {
fn from(value: Group34Var2) -> Self {
AnalogInputDeadBand::U32(value.value)
}
}
impl From<Group34Var3> for AnalogInputDeadBand {
fn from(value: Group34Var3) -> Self {
AnalogInputDeadBand::F32(value.value)
}
}
impl std::ops::BitOr<Flags> for Flags {
type Output = Flags;
fn bitor(self, rhs: Flags) -> Self::Output {
Flags::new(self.value | rhs.value)
}
}
impl std::ops::BitOrAssign<Flags> for Flags {
fn bitor_assign(&mut self, rhs: Flags) {
self.value |= rhs.value
}
}
impl Flags {
pub(crate) fn state(self) -> bool {
self.value.bit_7()
}
pub(crate) fn double_bit_state(self) -> DoubleBit {
DoubleBit::from(self.value.bit_7(), self.value.bit_6())
}
pub(crate) fn with_bits_set_to(&self, mask: BitMask, value: bool) -> Flags {
if value {
self.with_bits_set(mask)
} else {
self.with_bits_cleared(mask)
}
}
pub(crate) fn with_bits_cleared(&self, mask: BitMask) -> Flags {
Flags::new(self.value & !mask.value)
}
pub(crate) fn with_bits_set(&self, mask: BitMask) -> Flags {
Flags::new(self.value | mask.value)
}
pub(crate) fn without(&self, mask: BitMask) -> Flags {
Flags::new(self.value & !mask.value)
}
}
struct FlagFormatter {
prev: bool,
}
impl FlagFormatter {
fn new() -> Self {
Self { prev: false }
}
fn push(
&mut self,
is_set: bool,
text: &'static str,
f: &mut std::fmt::Formatter,
) -> std::fmt::Result {
if is_set {
if self.prev {
f.write_str(", ")?;
}
self.prev = true;
f.write_str(text)?;
}
Ok(())
}
fn begin(flags: Flags, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "0x{:02X} [", flags.value)
}
fn end(f: &mut std::fmt::Formatter) -> std::fmt::Result {
f.write_str("]")
}
fn format_binary_flags_0_to_4(
&mut self,
flags: Flags,
f: &mut std::fmt::Formatter,
) -> std::fmt::Result {
self.push(flags.is_set(Flags::ONLINE), "ONLINE", f)?;
self.push(flags.is_set(Flags::RESTART), "RESTART", f)?;
self.push(flags.is_set(Flags::COMM_LOST), "COMM_LOST", f)?;
self.push(flags.is_set(Flags::REMOTE_FORCED), "REMOTE_FORCED", f)?;
self.push(flags.is_set(Flags::LOCAL_FORCED), "LOCAL_FORCED", f)?;
Ok(())
}
fn format_binary_flags_0_to_5(
&mut self,
flags: Flags,
f: &mut std::fmt::Formatter,
) -> std::fmt::Result {
self.format_binary_flags_0_to_4(flags, f)?;
self.push(flags.is_set(Flags::CHATTER_FILTER), "CHATTER_FILTER", f)?;
Ok(())
}
fn push_debug_item<T>(
&mut self,
name: &'static str,
item: T,
f: &mut std::fmt::Formatter,
) -> std::fmt::Result
where
T: std::fmt::Debug,
{
if self.prev {
f.write_str(", ")?;
}
self.prev = true;
write!(f, "{name} = {item:?}")
}
}
pub(crate) struct BinaryFlagFormatter {
flags: Flags,
}
impl BinaryFlagFormatter {
pub(crate) fn new(value: u8) -> Self {
Self {
flags: Flags::new(value),
}
}
}
impl std::fmt::Display for BinaryFlagFormatter {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
let mut formatter = FlagFormatter::new();
FlagFormatter::begin(self.flags, f)?;
formatter.format_binary_flags_0_to_5(self.flags, f)?;
formatter.push(self.flags.value.bit_6(), "RESERVED(6)", f)?;
formatter.push(self.flags.value.bit_7(), "STATE", f)?;
FlagFormatter::end(f)
}
}
pub(crate) struct DoubleBitBinaryFlagFormatter {
flags: Flags,
}
impl DoubleBitBinaryFlagFormatter {
pub(crate) fn new(value: u8) -> Self {
Self {
flags: Flags::new(value),
}
}
}
impl std::fmt::Display for DoubleBitBinaryFlagFormatter {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
let mut formatter = FlagFormatter::new();
FlagFormatter::begin(self.flags, f)?;
formatter.format_binary_flags_0_to_5(self.flags, f)?;
formatter.push_debug_item("state", self.flags.double_bit_state(), f)?;
FlagFormatter::end(f)
}
}
pub(crate) struct BinaryOutputStatusFlagFormatter {
flags: Flags,
}
impl BinaryOutputStatusFlagFormatter {
pub(crate) fn new(value: u8) -> Self {
Self {
flags: Flags::new(value),
}
}
}
impl std::fmt::Display for BinaryOutputStatusFlagFormatter {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
let mut formatter = FlagFormatter::new();
FlagFormatter::begin(self.flags, f)?;
formatter.format_binary_flags_0_to_4(self.flags, f)?;
formatter.push(self.flags.value.bit_5(), "RESERVED(5)", f)?;
formatter.push(self.flags.value.bit_6(), "RESERVED(6)", f)?;
formatter.push(self.flags.value.bit_7(), "STATE", f)?;
FlagFormatter::end(f)
}
}
pub(crate) struct CounterFlagFormatter {
flags: Flags,
}
impl CounterFlagFormatter {
pub(crate) fn new(value: u8) -> Self {
Self {
flags: Flags::new(value),
}
}
}
impl std::fmt::Display for CounterFlagFormatter {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
let mut formatter = FlagFormatter::new();
FlagFormatter::begin(self.flags, f)?;
formatter.format_binary_flags_0_to_4(self.flags, f)?;
formatter.push(self.flags.value.bit_5(), "ROLLOVER", f)?;
formatter.push(self.flags.value.bit_6(), "DISCONTINUITY", f)?;
formatter.push(self.flags.value.bit_7(), "RESERVED(7)", f)?;
FlagFormatter::end(f)
}
}
pub(crate) struct AnalogFlagFormatter {
flags: Flags,
}
impl AnalogFlagFormatter {
pub(crate) fn new(value: u8) -> Self {
Self {
flags: Flags::new(value),
}
}
}
impl std::fmt::Display for AnalogFlagFormatter {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
let mut formatter = FlagFormatter::new();
FlagFormatter::begin(self.flags, f)?;
formatter.format_binary_flags_0_to_4(self.flags, f)?;
formatter.push(self.flags.value.bit_5(), "OVER_RANGE", f)?;
formatter.push(self.flags.value.bit_6(), "REFERENCE_ERR", f)?;
formatter.push(self.flags.value.bit_7(), "RESERVED(7)", f)?;
FlagFormatter::end(f)
}
}
pub(crate) trait AnalogConversions {
const OVER_RANGE: BitMask = bits::BIT_5;
fn get_value(&self) -> f64;
fn get_flags(&self) -> Flags;
fn to_i16(&self) -> (Flags, i16) {
if self.get_value() < i16::MIN.into() {
return (self.get_flags().with_bits_set(Self::OVER_RANGE), i16::MIN);
}
if self.get_value() > i16::MAX.into() {
return (self.get_flags().with_bits_set(Self::OVER_RANGE), i16::MAX);
}
(self.get_flags(), self.get_value() as i16)
}
fn to_i32(&self) -> (Flags, i32) {
if self.get_value() < i32::MIN.into() {
return (self.get_flags().with_bits_set(Self::OVER_RANGE), i32::MIN);
}
if self.get_value() > i32::MAX.into() {
return (self.get_flags().with_bits_set(Self::OVER_RANGE), i32::MAX);
}
(self.get_flags(), self.get_value() as i32)
}
fn to_f32(&self) -> (Flags, f32) {
if self.get_value() < f32::MIN.into() {
return (self.get_flags().with_bits_set(Self::OVER_RANGE), f32::MIN);
}
if self.get_value() > f32::MAX.into() {
return (self.get_flags().with_bits_set(Self::OVER_RANGE), f32::MAX);
}
(self.get_flags(), self.get_value() as f32)
}
}
#[derive(Copy, Clone, PartialEq, Debug)]
pub struct AnalogOutputStatus {
pub value: f64,
pub flags: Flags,
pub time: Option<Time>,
}
impl AnalogOutputStatus {
pub fn new(value: f64, flags: Flags, time: Time) -> Self {
Self {
value,
flags,
time: Some(time),
}
}
}
#[allow(missing_copy_implementations)]
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct OctetString {
value: [u8; Self::MAX_SIZE],
len: u8,
}
#[allow(clippy::len_without_is_empty)]
impl OctetString {
const MAX_SIZE: usize = 255;
pub fn new(value: &[u8]) -> Result<Self, OctetStringLengthError> {
let len = value.len();
if len == 0 {
return Err(OctetStringLengthError::ZeroLength);
}
if len > 255 {
return Err(OctetStringLengthError::MoreThan255Octets);
}
let mut result = Self {
value: [0u8; 255],
len: len as u8,
};
result.value[..len].copy_from_slice(value);
Ok(result)
}
pub fn value(&self) -> &[u8] {
&self.value[..self.len() as usize]
}
pub fn len(&self) -> u8 {
self.len
}
pub(crate) fn as_boxed_slice(&self) -> Box<[u8]> {
self.value().into()
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum OctetStringLengthError {
ZeroLength,
MoreThan255Octets,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn octet_string_methods() {
let octet_string = OctetString::new(&[0, 1, 2, 3, 4]).unwrap();
assert_eq!(5, octet_string.len());
assert_eq!(&[0, 1, 2, 3, 4], octet_string.value());
assert_eq!(&[0, 1, 2, 3, 4], &*octet_string.as_boxed_slice());
}
#[test]
fn new_octet_string_zero_length() {
assert_eq!(
Err(OctetStringLengthError::ZeroLength),
OctetString::new(&[])
);
}
#[test]
fn new_octet_string_greater_size() {
assert_eq!(
Err(OctetStringLengthError::MoreThan255Octets),
OctetString::new(&[0; 500])
);
}
#[test]
fn octet_string_default_value() {
assert_eq!(&[0x00], OctetString::default().value());
}
#[test]
fn flag_bit_or_works() {
let flags = Flags::ONLINE | Flags::LOCAL_FORCED;
assert_eq!(flags.value, 0b0001_0001);
}
#[test]
fn flag_bit_or_assign_works() {
let mut flags = Flags::ONLINE;
flags |= Flags::LOCAL_FORCED;
assert_eq!(flags.value, 0b0001_0001);
}
#[test]
fn formats_binary_flags() {
assert_eq!(format!("{}", BinaryFlagFormatter::new(0)), "0x00 []");
assert_eq!(
format!("{}", BinaryFlagFormatter::new(0b1100_0001)),
"0xC1 [ONLINE, RESERVED(6), STATE]"
);
}
#[test]
fn formats_double_flags() {
assert_eq!(
format!("{}", DoubleBitBinaryFlagFormatter::new(0)),
"0x00 [state = Intermediate]"
);
assert_eq!(
format!("{}", DoubleBitBinaryFlagFormatter::new(0b1100_0001)),
"0xC1 [ONLINE, state = Indeterminate]"
);
}
}