use zencan_common::{
objects::{AccessType, DataType, ObjectCode, SubInfo},
sdo::AbortCode,
AtomicCell,
};
use super::{ObjectFlagAccess, SubObjectAccess};
pub trait ObjectAccess: Sync + Send {
fn read(&self, sub: u8, offset: usize, buf: &mut [u8]) -> Result<usize, AbortCode>;
fn read_size(&self, sub: u8) -> Result<usize, AbortCode>;
fn write(&self, sub: u8, data: &[u8]) -> Result<(), AbortCode>;
fn begin_partial(&self, sub: u8) -> Result<(), AbortCode> {
if let Ok(sub_info) = self.sub_info(sub) {
if sub_info.access_type.is_writable() {
Err(AbortCode::UnsupportedAccess)
} else {
Err(AbortCode::ReadOnly)
}
} else {
Err(AbortCode::NoSuchSubIndex)
}
}
fn write_partial(&self, _sub: u8, _buf: &[u8]) -> Result<(), AbortCode> {
Err(AbortCode::GeneralError)
}
fn end_partial(&self, _sub: u8) -> Result<(), AbortCode> {
Err(AbortCode::GeneralError)
}
fn object_code(&self) -> ObjectCode;
fn sub_info(&self, sub: u8) -> Result<SubInfo, AbortCode>;
fn max_sub_number(&self) -> u8 {
match self.object_code() {
ObjectCode::Null => 0,
ObjectCode::Domain => 0,
ObjectCode::DefType => 0,
ObjectCode::DefStruct => 0,
ObjectCode::Var => 0,
ObjectCode::Array => self.read_u8(0).unwrap(),
ObjectCode::Record => self.read_u8(0).unwrap(),
}
}
fn set_event_flag(&self, _sub: u8) -> Result<(), AbortCode> {
Err(AbortCode::UnsupportedAccess)
}
fn read_event_flag(&self, _sub: u8) -> bool {
false
}
fn clear_events(&self) {}
fn access_type(&self, sub: u8) -> Result<AccessType, AbortCode> {
Ok(self.sub_info(sub)?.access_type)
}
fn data_type(&self, sub: u8) -> Result<DataType, AbortCode> {
Ok(self.sub_info(sub)?.data_type)
}
fn size(&self, sub: u8) -> Result<usize, AbortCode> {
Ok(self.sub_info(sub)?.size)
}
fn current_size(&self, sub: u8) -> Result<usize, AbortCode> {
const CHUNK_SIZE: usize = 8;
let size = self.size(sub)?;
if self.data_type(sub)?.is_str() {
let mut chunk = 0;
let mut buf = [0; CHUNK_SIZE];
while chunk < size / CHUNK_SIZE + 1 {
let offset = chunk * CHUNK_SIZE;
let bytes_to_read = (size - offset).min(CHUNK_SIZE);
self.read(sub, offset, &mut buf[0..bytes_to_read])?;
if let Some(zero_pos) = buf[0..bytes_to_read].iter().position(|b| *b == 0) {
return Ok(zero_pos + chunk * CHUNK_SIZE);
}
chunk += 1;
}
}
Ok(size)
}
fn read_u32(&self, sub: u8) -> Result<u32, AbortCode> {
let mut buf = [0; 4];
self.read(sub, 0, &mut buf)?;
Ok(u32::from_le_bytes(buf))
}
fn read_u16(&self, sub: u8) -> Result<u16, AbortCode> {
let mut buf = [0; 2];
self.read(sub, 0, &mut buf)?;
Ok(u16::from_le_bytes(buf))
}
fn read_u8(&self, sub: u8) -> Result<u8, AbortCode> {
let mut buf = [0; 1];
self.read(sub, 0, &mut buf)?;
Ok(buf[0])
}
fn read_i32(&self, sub: u8) -> Result<i32, AbortCode> {
let mut buf = [0; 4];
self.read(sub, 0, &mut buf)?;
Ok(i32::from_le_bytes(buf))
}
fn read_i16(&self, sub: u8) -> Result<i16, AbortCode> {
let mut buf = [0; 2];
self.read(sub, 0, &mut buf)?;
Ok(i16::from_le_bytes(buf))
}
fn read_i8(&self, sub: u8) -> Result<i8, AbortCode> {
let mut buf = [0; 1];
self.read(sub, 0, &mut buf)?;
Ok(buf[0] as i8)
}
}
pub trait ProvidesSubObjects {
fn get_sub_object(&self, sub: u8) -> Option<(SubInfo, &dyn SubObjectAccess)>;
fn flags(&self) -> Option<&dyn ObjectFlagAccess> {
None
}
fn object_code(&self) -> ObjectCode;
}
impl<T: ProvidesSubObjects + Sync + Send> ObjectAccess for T {
fn read(&self, sub: u8, offset: usize, buf: &mut [u8]) -> Result<usize, AbortCode> {
if let Some((info, access)) = self.get_sub_object(sub) {
if info.access_type.is_readable() {
access.read(offset, buf)
} else {
Err(AbortCode::WriteOnly)
}
} else {
Err(AbortCode::NoSuchSubIndex)
}
}
fn read_size(&self, sub: u8) -> Result<usize, AbortCode> {
if let Some((_info, access)) = self.get_sub_object(sub) {
Ok(access.read_size())
} else {
Err(AbortCode::NoSuchSubIndex)
}
}
fn write(&self, sub: u8, data: &[u8]) -> Result<(), AbortCode> {
if let Some((info, access)) = self.get_sub_object(sub) {
if info.access_type.is_writable() {
access.write(data)
} else {
Err(AbortCode::ReadOnly)
}
} else {
Err(AbortCode::NoSuchSubIndex)
}
}
fn begin_partial(&self, sub: u8) -> Result<(), AbortCode> {
if let Some((info, access)) = self.get_sub_object(sub) {
if info.access_type.is_writable() {
access.begin_partial()
} else {
Err(AbortCode::ReadOnly)
}
} else {
Err(AbortCode::NoSuchSubIndex)
}
}
fn write_partial(&self, sub: u8, buf: &[u8]) -> Result<(), AbortCode> {
if let Some((_, access)) = self.get_sub_object(sub) {
access.write_partial(buf)
} else {
Err(AbortCode::NoSuchSubIndex)
}
}
fn end_partial(&self, sub: u8) -> Result<(), AbortCode> {
if let Some((_, access)) = self.get_sub_object(sub) {
access.end_partial()
} else {
Err(AbortCode::NoSuchSubIndex)
}
}
fn set_event_flag(&self, sub: u8) -> Result<(), AbortCode> {
if let Some(flags) = self.flags() {
flags.set_flag(sub);
Ok(())
} else {
Err(AbortCode::UnsupportedAccess)
}
}
fn read_event_flag(&self, sub: u8) -> bool {
if let Some(flags) = self.flags() {
flags.get_flag(sub)
} else {
false
}
}
fn clear_events(&self) {
if let Some(flags) = self.flags() {
flags.clear();
}
}
fn object_code(&self) -> ObjectCode {
self.object_code()
}
fn sub_info(&self, sub: u8) -> Result<SubInfo, AbortCode> {
if let Some((info, _access)) = self.get_sub_object(sub) {
Ok(info)
} else {
Err(AbortCode::NoSuchSubIndex)
}
}
}
#[allow(missing_debug_implementations)]
pub struct CallbackObject<'a> {
obj: AtomicCell<Option<&'a dyn ObjectAccess>>,
object_code: ObjectCode,
}
impl<'a> CallbackObject<'a> {
pub const fn new(object_code: ObjectCode) -> Self {
Self {
obj: AtomicCell::new(None),
object_code,
}
}
pub fn register_handler(&self, handler: &'a dyn ObjectAccess) {
self.obj.store(Some(handler))
}
}
impl ObjectAccess for CallbackObject<'_> {
fn read(&self, sub: u8, offset: usize, buf: &mut [u8]) -> Result<usize, AbortCode> {
if let Some(obj) = self.obj.load() {
obj.read(sub, offset, buf)
} else {
Err(AbortCode::ResourceNotAvailable)
}
}
fn read_size(&self, sub: u8) -> Result<usize, AbortCode> {
if let Some(obj) = self.obj.load() {
obj.read_size(sub)
} else {
Err(AbortCode::ResourceNotAvailable)
}
}
fn write(&self, sub: u8, data: &[u8]) -> Result<(), AbortCode> {
if let Some(obj) = self.obj.load() {
obj.write(sub, data)
} else {
Err(AbortCode::ResourceNotAvailable)
}
}
fn write_partial(&self, sub: u8, buf: &[u8]) -> Result<(), AbortCode> {
if let Some(obj) = self.obj.load() {
obj.write_partial(sub, buf)
} else {
Err(AbortCode::ResourceNotAvailable)
}
}
fn end_partial(&self, sub: u8) -> Result<(), AbortCode> {
if let Some(obj) = self.obj.load() {
obj.end_partial(sub)
} else {
Err(AbortCode::ResourceNotAvailable)
}
}
fn object_code(&self) -> ObjectCode {
self.object_code
}
fn sub_info(&self, sub: u8) -> Result<SubInfo, AbortCode> {
if let Some(obj) = self.obj.load() {
obj.sub_info(sub)
} else {
Err(AbortCode::ResourceNotAvailable)
}
}
}
#[allow(missing_debug_implementations)]
pub struct ODEntry<'a> {
pub index: u16,
pub data: &'a dyn ObjectAccess,
}
pub fn find_object<'a>(table: &[ODEntry<'a>], index: u16) -> Option<&'a dyn ObjectAccess> {
find_object_entry(table, index).map(|entry| entry.data)
}
pub fn find_object_entry<'a, 'b>(table: &'b [ODEntry<'a>], index: u16) -> Option<&'b ODEntry<'a>> {
table
.binary_search_by_key(&index, |e| e.index)
.ok()
.map(|i| &table[i])
}