darra-ethercat-master 2.7.0

Commercial EtherCAT master protocol stack, real-time kernel driver integration, Windows and Linux support, multi-language SDKs, complex topology and hot-plug support.
Documentation

use crate::slave::coe::EcDataType;
use std::sync::Mutex;

#[derive(Debug, Clone)]
pub enum EcValue {

    Bool(bool),

    I8(i8),

    U8(u8),

    I16(i16),

    U16(u16),

    I32(i32),

    U32(u32),

    I64(i64),

    U64(u64),

    F32(f32),

    F64(f64),

    String(String),

    Bytes(Vec<u8>),
}

impl std::fmt::Display for EcValue {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            EcValue::Bool(v) => write!(f, "{}", v),
            EcValue::I8(v) => write!(f, "{}", v),
            EcValue::U8(v) => write!(f, "{}", v),
            EcValue::I16(v) => write!(f, "{}", v),
            EcValue::U16(v) => write!(f, "{}", v),
            EcValue::I32(v) => write!(f, "{}", v),
            EcValue::U32(v) => write!(f, "{}", v),
            EcValue::I64(v) => write!(f, "{}", v),
            EcValue::U64(v) => write!(f, "{}", v),
            EcValue::F32(v) => write!(f, "{}", v),
            EcValue::F64(v) => write!(f, "{}", v),
            EcValue::String(v) => write!(f, "{}", v),
            EcValue::Bytes(v) => write!(f, "[{} bytes]", v.len()),
        }
    }
}

pub struct BaseData {

    data_type: EcDataType,

    raw_data: Mutex<Vec<u8>>,
}

impl BaseData {

    pub fn new(data_type: EcDataType) -> Self {
        let size = type_size(data_type);
        Self {
            data_type,
            raw_data: Mutex::new(vec![0u8; size]),
        }
    }

    pub fn with_value(data_type: EcDataType, value: &EcValue) -> Self {
        let bd = Self::new(data_type);
        let _ = bd.write(value);
        bd
    }

    pub fn data_type(&self) -> EcDataType {
        self.data_type
    }

    pub fn read(&self) -> EcValue {
        let guard = self.raw_data.lock().unwrap();
        convert_from_bytes(&guard, self.data_type)
    }

    pub fn write(&self, value: &EcValue) -> bool {
        match convert_to_bytes(value, self.data_type) {
            Some(bytes) => {
                let mut guard = self.raw_data.lock().unwrap();
                *guard = bytes;
                true
            }
            None => false,
        }
    }

    pub fn write_raw(&self, data: &[u8]) -> bool {

        match self.data_type {
            EcDataType::OctetString | EcDataType::UnicodeString
            | EcDataType::VisibleString | EcDataType::Domain => {
                let mut guard = self.raw_data.lock().unwrap();
                guard.clear();
                guard.extend_from_slice(data);
                return true;
            }
            _ => {}
        }

        let expected = type_size(self.data_type);
        if data.len() != expected {
            return false;
        }
        let mut guard = self.raw_data.lock().unwrap();
        guard.clear();
        guard.extend_from_slice(data);
        true
    }

    pub fn raw_data(&self) -> Vec<u8> {
        self.raw_data.lock().unwrap().clone()
    }

    pub fn get_bit(&self, bit_index: usize) -> Option<bool> {
        let guard = self.raw_data.lock().unwrap();
        let byte_index = bit_index / 8;
        let bit_offset = bit_index % 8;
        if byte_index >= guard.len() {
            return None;
        }
        Some((guard[byte_index] & (1 << bit_offset)) != 0)
    }

    pub fn set_bit(&self, bit_index: usize, value: bool) -> bool {
        let mut guard = self.raw_data.lock().unwrap();
        let byte_index = bit_index / 8;
        let bit_offset = bit_index % 8;
        if byte_index >= guard.len() {
            return false;
        }
        if value {
            guard[byte_index] |= 1 << bit_offset;
        } else {
            guard[byte_index] &= !(1 << bit_offset);
        }
        true
    }
}

impl std::fmt::Display for BaseData {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self.read())
    }
}

impl std::fmt::Debug for BaseData {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("BaseData")
            .field("type", &self.data_type)
            .field("value", &self.read())
            .finish()
    }
}

pub fn type_size(dt: EcDataType) -> usize {
    match dt {
        EcDataType::Boolean | EcDataType::Integer8 | EcDataType::Unsigned8 => 1,
        EcDataType::Bit1 | EcDataType::Bit2 | EcDataType::Bit3 | EcDataType::Bit4 => 1,
        EcDataType::Bit5 | EcDataType::Bit6 | EcDataType::Bit7 | EcDataType::Bit8 => 1,
        EcDataType::Integer16 | EcDataType::Unsigned16 => 2,
        EcDataType::Integer24 | EcDataType::Unsigned24 => 3,
        EcDataType::Integer32 | EcDataType::Unsigned32 | EcDataType::Real32 => 4,
        EcDataType::Integer64 | EcDataType::Unsigned64 | EcDataType::Real64 => 8,
        EcDataType::TimeOfDay | EcDataType::TimeDifference => 8,
        EcDataType::VisibleString | EcDataType::OctetString | EcDataType::Domain => 256,
        EcDataType::UnicodeString => 512,
        _ => 1,
    }
}

pub fn convert_from_bytes(data: &[u8], dt: EcDataType) -> EcValue {
    if data.is_empty() {
        return default_value(dt);
    }
    match dt {

        EcDataType::Boolean => EcValue::Bool(data[0] != 0),
        EcDataType::Bit1 | EcDataType::Bit2 | EcDataType::Bit3
        | EcDataType::Bit4 | EcDataType::Bit5 | EcDataType::Bit6 | EcDataType::Bit7
        | EcDataType::Bit8 => EcValue::Bool((data[0] & 1) != 0),

        EcDataType::Integer8 => EcValue::I8(data[0] as i8),
        EcDataType::Unsigned8 => EcValue::U8(data[0]),

        EcDataType::Integer16 if data.len() >= 2 => {
            EcValue::I16(i16::from_le_bytes([data[0], data[1]]))
        }
        EcDataType::Unsigned16 if data.len() >= 2 => {
            EcValue::U16(u16::from_le_bytes([data[0], data[1]]))
        }

        EcDataType::Integer24 if data.len() >= 3 => {
            let v = (data[0] as i32) | ((data[1] as i32) << 8) | ((data[2] as i32) << 16);

            let v = if v & 0x800000 != 0 { v | !0xFFFFFF_i32 } else { v };
            EcValue::I32(v)
        }
        EcDataType::Unsigned24 if data.len() >= 3 => {
            let v = (data[0] as u32) | ((data[1] as u32) << 8) | ((data[2] as u32) << 16);
            EcValue::U32(v)
        }

        EcDataType::Integer32 if data.len() >= 4 => {
            EcValue::I32(i32::from_le_bytes([data[0], data[1], data[2], data[3]]))
        }
        EcDataType::Unsigned32 if data.len() >= 4 => {
            EcValue::U32(u32::from_le_bytes([data[0], data[1], data[2], data[3]]))
        }

        EcDataType::Integer64 if data.len() >= 8 => {
            let mut buf = [0u8; 8];
            buf.copy_from_slice(&data[..8]);
            EcValue::I64(i64::from_le_bytes(buf))
        }
        EcDataType::Unsigned64 if data.len() >= 8 => {
            let mut buf = [0u8; 8];
            buf.copy_from_slice(&data[..8]);
            EcValue::U64(u64::from_le_bytes(buf))
        }

        EcDataType::Real32 if data.len() >= 4 => {
            EcValue::F32(f32::from_le_bytes([data[0], data[1], data[2], data[3]]))
        }
        EcDataType::Real64 if data.len() >= 8 => {
            let mut buf = [0u8; 8];
            buf.copy_from_slice(&data[..8]);
            EcValue::F64(f64::from_le_bytes(buf))
        }

        EcDataType::VisibleString => {
            let null_pos = data.iter().position(|&b| b == 0).unwrap_or(data.len());

            EcValue::String(crate::utils::help::decode_ethercat_string(&data[..null_pos]))
        }
        EcDataType::UnicodeString => {

            let null_pos = data.chunks(2)
                .position(|c| c.len() == 2 && c[0] == 0 && c[1] == 0)
                .unwrap_or(data.len() / 2);
            let u16s: Vec<u16> = data[..null_pos * 2].chunks(2)
                .filter(|c| c.len() == 2)
                .map(|c| u16::from_le_bytes([c[0], c[1]]))
                .collect();
            EcValue::String(String::from_utf16_lossy(&u16s))
        }

        EcDataType::OctetString | EcDataType::Domain => {
            EcValue::Bytes(data.to_vec())
        }

        EcDataType::TimeOfDay | EcDataType::TimeDifference if data.len() >= 8 => {
            let mut buf = [0u8; 8];
            buf.copy_from_slice(&data[..8]);
            EcValue::I64(i64::from_le_bytes(buf))
        }

        _ => default_value(dt),
    }
}

pub fn convert_to_bytes(value: &EcValue, dt: EcDataType) -> Option<Vec<u8>> {
    match (value, dt) {
        (EcValue::Bool(v), EcDataType::Boolean)
        | (EcValue::Bool(v), EcDataType::Bit1)
        | (EcValue::Bool(v), EcDataType::Bit2)
        | (EcValue::Bool(v), EcDataType::Bit3)
        | (EcValue::Bool(v), EcDataType::Bit4)
        | (EcValue::Bool(v), EcDataType::Bit5)
        | (EcValue::Bool(v), EcDataType::Bit6)
        | (EcValue::Bool(v), EcDataType::Bit7)
        | (EcValue::Bool(v), EcDataType::Bit8) => {
            Some(vec![if *v { 1 } else { 0 }])
        }

        (EcValue::I8(v), EcDataType::Integer8) => Some(vec![*v as u8]),
        (EcValue::U8(v), EcDataType::Unsigned8) => Some(vec![*v]),

        (EcValue::I16(v), EcDataType::Integer16) => Some(v.to_le_bytes().to_vec()),
        (EcValue::U16(v), EcDataType::Unsigned16) => Some(v.to_le_bytes().to_vec()),

        (EcValue::I32(v), EcDataType::Integer24) => {
            let bytes = v.to_le_bytes();
            Some(vec![bytes[0], bytes[1], bytes[2]])
        }
        (EcValue::U32(v), EcDataType::Unsigned24) => {
            let bytes = v.to_le_bytes();
            Some(vec![bytes[0], bytes[1], bytes[2]])
        }

        (EcValue::I32(v), EcDataType::Integer32) => Some(v.to_le_bytes().to_vec()),
        (EcValue::U32(v), EcDataType::Unsigned32) => Some(v.to_le_bytes().to_vec()),

        (EcValue::I64(v), EcDataType::Integer64) => Some(v.to_le_bytes().to_vec()),
        (EcValue::U64(v), EcDataType::Unsigned64) => Some(v.to_le_bytes().to_vec()),

        (EcValue::F32(v), EcDataType::Real32) => Some(v.to_le_bytes().to_vec()),
        (EcValue::F64(v), EcDataType::Real64) => Some(v.to_le_bytes().to_vec()),

        (EcValue::String(v), EcDataType::VisibleString) => {
            let mut buf = vec![0u8; 256];
            let bytes = v.as_bytes();
            let len = bytes.len().min(255);
            buf[..len].copy_from_slice(&bytes[..len]);
            Some(buf)
        }
        (EcValue::String(v), EcDataType::UnicodeString) => {
            let mut buf = vec![0u8; 512];
            let u16s: Vec<u16> = v.encode_utf16().collect();
            let len = u16s.len().min(255);
            for (i, &c) in u16s[..len].iter().enumerate() {
                let bytes = c.to_le_bytes();
                buf[i * 2] = bytes[0];
                buf[i * 2 + 1] = bytes[1];
            }
            Some(buf)
        }

        (EcValue::Bytes(v), EcDataType::OctetString)
        | (EcValue::Bytes(v), EcDataType::Domain) => Some(v.clone()),

        (EcValue::I64(v), EcDataType::TimeOfDay)
        | (EcValue::I64(v), EcDataType::TimeDifference) => Some(v.to_le_bytes().to_vec()),

        _ => None,
    }
}

pub fn default_value(dt: EcDataType) -> EcValue {
    match dt {
        EcDataType::Boolean | EcDataType::Bit1 | EcDataType::Bit2 | EcDataType::Bit3
        | EcDataType::Bit4 | EcDataType::Bit5 | EcDataType::Bit6 | EcDataType::Bit7
        | EcDataType::Bit8 => EcValue::Bool(false),
        EcDataType::Integer8 => EcValue::I8(0),
        EcDataType::Unsigned8 => EcValue::U8(0),
        EcDataType::Integer16 => EcValue::I16(0),
        EcDataType::Unsigned16 => EcValue::U16(0),
        EcDataType::Integer24 | EcDataType::Integer32 => EcValue::I32(0),
        EcDataType::Unsigned24 | EcDataType::Unsigned32 => EcValue::U32(0),
        EcDataType::Integer64 | EcDataType::TimeOfDay | EcDataType::TimeDifference => EcValue::I64(0),
        EcDataType::Unsigned64 => EcValue::U64(0),
        EcDataType::Real32 => EcValue::F32(0.0),
        EcDataType::Real64 => EcValue::F64(0.0),
        EcDataType::VisibleString | EcDataType::UnicodeString => EcValue::String(String::new()),
        EcDataType::OctetString | EcDataType::Domain => EcValue::Bytes(Vec::new()),
        _ => EcValue::U8(0),
    }
}