use core::fmt::{Display, Formatter};
use bytes::{Buf, BufMut};
use crate::{
Error,
contrib::{Percentage, Watts},
protocol::{
Address,
address,
codec::{BitSize, Decode, Encode},
},
};
pub type BlockStride = address::Stride<48010, Block>;
pub const N_ENTRIES_PER_BLOCK: usize = 12;
pub const N_BLOCKS: usize = 8;
pub type Full = [Entry; Entry::N_TOTAL];
pub type Block = [Entry; N_ENTRIES_PER_BLOCK];
#[must_use]
#[derive(Copy, Clone)]
pub struct BlockIndex(pub u16);
impl BlockIndex {
#[expect(clippy::cast_possible_truncation)]
pub const MAX: u16 = (N_BLOCKS - 1) as u16;
}
impl Address for BlockIndex {}
impl Encode for BlockIndex {
fn encode(&self, to: &mut impl BufMut) {
BlockStride::from(self.0).encode(to);
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
#[repr(u16)]
#[must_use]
pub enum WorkingMode {
SelfUse = 1_u16,
FeedInPriority = 2_u16,
BackUp = 3_u16,
PeakShaving = 4_u16,
ForceCharge = 6_u16,
ForceDischarge = 7_u16,
Unknown(u16),
}
impl Encode for WorkingMode {
fn encode(&self, to: &mut impl BufMut) {
to.put_u16(match self {
Self::SelfUse => 1,
Self::FeedInPriority => 2,
Self::BackUp => 3,
Self::PeakShaving => 4,
Self::ForceCharge => 6,
Self::ForceDischarge => 7,
Self::Unknown(working_mode) => *working_mode,
});
}
}
impl Decode for WorkingMode {
fn decode(from: &mut impl Buf) -> Result<Self, Error> {
Ok(match from.try_get_u16()? {
1 => Self::SelfUse,
2 => Self::FeedInPriority,
3 => Self::BackUp,
4 => Self::PeakShaving,
6 => Self::ForceCharge,
7 => Self::ForceDischarge,
working_mode => Self::Unknown(working_mode),
})
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
#[must_use]
pub struct NaiveTime {
pub hour: u8,
pub minute: u8,
}
impl Display for NaiveTime {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
write!(f, "{:02}:{:02}", self.hour, self.minute)
}
}
impl NaiveTime {
pub const MIN: Self = Self { hour: 0, minute: 0 };
pub const MAX: Self = Self { hour: 23, minute: 59 };
}
impl Encode for NaiveTime {
fn encode(&self, to: &mut impl BufMut) {
to.put_u8(self.hour);
to.put_u8(self.minute);
}
}
impl Decode for NaiveTime {
fn decode(from: &mut impl Buf) -> Result<Self, Error> {
Ok(Self { hour: from.try_get_u8()?, minute: from.try_get_u8()? })
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
#[must_use]
pub struct Entry {
pub is_enabled: bool,
pub start_time: NaiveTime,
pub end_time: NaiveTime,
pub working_mode: WorkingMode,
pub maximum_state_of_charge: Percentage<u8>,
pub minimum_state_of_charge: Percentage<u8>,
#[allow(clippy::doc_markdown)]
pub target_state_of_charge: Percentage<u16>,
pub power: Watts<u16>,
pub reserved_1: u16,
pub reserved_2: u16,
pub reserved_3: u16,
}
impl Entry {
pub const N_TOTAL: usize = N_BLOCKS * N_ENTRIES_PER_BLOCK;
}
impl BitSize for Entry {
const N_BITS: u16 = 20 * 8;
}
impl Encode for Entry {
fn encode(&self, to: &mut impl BufMut) {
to.put_u16(u16::from(self.is_enabled));
self.start_time.encode(to);
self.end_time.encode(to);
self.working_mode.encode(to);
to.put_u8(self.maximum_state_of_charge.0);
to.put_u8(self.minimum_state_of_charge.0);
self.target_state_of_charge.encode(to);
self.power.encode(to);
self.reserved_1.encode(to);
self.reserved_2.encode(to);
self.reserved_3.encode(to);
}
}
impl Decode for Entry {
fn decode(from: &mut impl Buf) -> Result<Self, Error> {
Ok(Self {
is_enabled: from.try_get_u16()? != 0,
start_time: NaiveTime::decode(from)?,
end_time: NaiveTime::decode(from)?,
working_mode: WorkingMode::decode(from)?,
maximum_state_of_charge: Percentage(from.try_get_u8()?),
minimum_state_of_charge: Percentage(from.try_get_u8()?),
target_state_of_charge: Percentage::decode(from)?,
power: Watts::decode(from)?,
reserved_1: u16::decode(from)?,
reserved_2: u16::decode(from)?,
reserved_3: u16::decode(from)?,
})
}
}