use heapless::LinearMap;
use crate::datatypes::Value;
use crate::{Error, Result};
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Address {
pub index: u16,
pub subindex: u8,
}
impl Address {
pub const fn new(index: u16, subindex: u8) -> Self {
Self { index, subindex }
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AccessType {
Ro,
Wo,
Rw,
Const,
}
impl AccessType {
pub const fn is_readable(self) -> bool {
matches!(self, AccessType::Ro | AccessType::Rw | AccessType::Const)
}
pub const fn is_writable(self) -> bool {
matches!(self, AccessType::Wo | AccessType::Rw)
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Entry {
pub value: Value,
pub access: AccessType,
}
impl Entry {
pub const fn rw(value: Value) -> Self {
Self {
value,
access: AccessType::Rw,
}
}
pub const fn ro(value: Value) -> Self {
Self {
value,
access: AccessType::Ro,
}
}
pub const fn constant(value: Value) -> Self {
Self {
value,
access: AccessType::Const,
}
}
}
#[derive(Debug)]
pub struct ObjectDictionary<const N: usize> {
entries: LinearMap<Address, Entry, N>,
}
impl<const N: usize> Default for ObjectDictionary<N> {
fn default() -> Self {
Self::new()
}
}
impl<const N: usize> ObjectDictionary<N> {
pub const fn new() -> Self {
Self {
entries: LinearMap::new(),
}
}
pub fn insert(&mut self, addr: Address, entry: Entry) -> Result<()> {
self.entries
.insert(addr, entry)
.map(|_| ())
.map_err(|_| Error::DictionaryFull)
}
pub fn entry(&self, addr: Address) -> Option<&Entry> {
self.entries.get(&addr)
}
pub fn read(&self, addr: Address) -> Result<Value> {
let entry = self.entries.get(&addr).ok_or(Error::ObjectNotFound)?;
if !entry.access.is_readable() {
return Err(Error::WriteOnly);
}
Ok(entry.value)
}
pub fn write(&mut self, addr: Address, value: Value) -> Result<()> {
let entry = self.entries.get_mut(&addr).ok_or(Error::ObjectNotFound)?;
if !entry.access.is_writable() {
return Err(Error::ReadOnly);
}
if entry.value.data_type() != value.data_type() {
return Err(Error::TypeMismatch);
}
entry.value = value;
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::datatypes::Value;
fn od() -> ObjectDictionary<8> {
let mut od = ObjectDictionary::new();
od.insert(
Address::new(0x1000, 0),
Entry::constant(Value::Unsigned32(0x0000_0192)),
)
.unwrap();
od.insert(Address::new(0x1017, 0), Entry::rw(Value::Unsigned16(1000)))
.unwrap();
od
}
#[test]
fn read_returns_stored_value() {
assert_eq!(
od().read(Address::new(0x1000, 0)).unwrap(),
Value::Unsigned32(0x192)
);
}
#[test]
fn read_missing_object_errors() {
assert_eq!(
od().read(Address::new(0x9999, 0)),
Err(Error::ObjectNotFound)
);
}
#[test]
fn write_updates_value() {
let mut od = od();
od.write(Address::new(0x1017, 0), Value::Unsigned16(500))
.unwrap();
assert_eq!(
od.read(Address::new(0x1017, 0)).unwrap(),
Value::Unsigned16(500)
);
}
#[test]
fn write_read_only_object_errors() {
let mut od = od();
assert_eq!(
od.write(Address::new(0x1000, 0), Value::Unsigned32(1)),
Err(Error::ReadOnly)
);
}
#[test]
fn write_wrong_type_errors() {
let mut od = od();
assert_eq!(
od.write(Address::new(0x1017, 0), Value::Unsigned32(1)),
Err(Error::TypeMismatch)
);
}
#[test]
fn dictionary_full_errors() {
let mut od: ObjectDictionary<1> = ObjectDictionary::new();
od.insert(Address::new(0x2000, 0), Entry::rw(Value::Unsigned8(1)))
.unwrap();
assert_eq!(
od.insert(Address::new(0x2001, 0), Entry::rw(Value::Unsigned8(2))),
Err(Error::DictionaryFull)
);
}
}