sts3215-controller 0.1.0

A Rust library for controlling ST3215 servos
Documentation
use crate::protocol_packet_handler::ProtocolPacketHandler;
use crate::values::*;
use std::collections::HashMap;

pub struct GroupSyncRead {
    start_address: u8,
    data_length: usize,
    last_result: bool,
    is_param_changed: bool,
    param: Vec<u8>,
    data_dict: HashMap<u8, Vec<u8>>,
}

impl GroupSyncRead {
    pub fn new(start_address: u8, data_length: usize) -> Self {
        Self {
            start_address,
            data_length,
            last_result: false,
            is_param_changed: false,
            param: Vec::new(),
            data_dict: HashMap::new(),
        }
    }

    fn make_param(&mut self) {
        if self.data_dict.is_empty() {
            return;
        }

        self.param.clear();
        for scs_id in self.data_dict.keys() {
            self.param.push(*scs_id);
        }
    }

    pub fn add_param(&mut self, sts_id: u8) -> bool {
        if self.data_dict.contains_key(&sts_id) {
            return false;
        }

        self.data_dict.insert(sts_id, Vec::new());
        self.is_param_changed = true;
        true
    }

    pub fn remove_param(&mut self, sts_id: u8) {
        if self.data_dict.remove(&sts_id).is_some() {
            self.is_param_changed = true;
        }
    }

    pub fn clear_param(&mut self) {
        self.data_dict.clear();
    }

    pub fn tx_packet(&mut self, ph: &mut ProtocolPacketHandler) -> CommResult {
        if self.data_dict.is_empty() {
            return CommResult::NotAvailable;
        }

        if self.is_param_changed || self.param.is_empty() {
            self.make_param();
        }

        ph.sync_read_tx(self.start_address, self.data_length as u8, &self.param)
    }

    pub fn rx_packet(&mut self, ph: &mut ProtocolPacketHandler) -> CommResult {
        self.last_result = true;

        if self.data_dict.is_empty() {
            return CommResult::NotAvailable;
        }

        let (result, rxpacket) = ph.sync_read_rx(self.data_length, self.data_dict.len());

        if rxpacket.len() >= (self.data_length + 6) {
            for sts_id in self.data_dict.keys().copied().collect::<Vec<_>>() {
                match self.read_rx(&rxpacket, sts_id, self.data_length) {
                    (Some(data), CommResult::Success) => {
                        self.data_dict.insert(sts_id, data);
                    }
                    _ => {
                        self.last_result = false;
                    }
                }
            }
        } else {
            self.last_result = false;
        }

        result
    }

    pub fn tx_rx_packet(&mut self, ph: &mut ProtocolPacketHandler) -> CommResult {
        let result = self.tx_packet(ph);
        if !result.is_success() {
            return result;
        }
        self.rx_packet(ph)
    }

    fn read_rx(&self, rxpacket: &[u8], sts_id: u8, data_length: usize) -> (Option<Vec<u8>>, CommResult) {
        let rx_length = rxpacket.len();
        let mut rx_index = 0;

        while (rx_index + 6 + data_length) <= rx_length {
            let mut headpacket = [0u8; 3];
            
            while rx_index < rx_length {
                headpacket[2] = headpacket[1];
                headpacket[1] = headpacket[0];
                headpacket[0] = rxpacket[rx_index];
                rx_index += 1;
                
                if headpacket[2] == 0xFF && headpacket[1] == 0xFF && headpacket[0] == sts_id {
                    break;
                }
            }

            if (rx_index + 3 + data_length) > rx_length {
                break;
            }

            if rxpacket[rx_index] != (data_length + 2) as u8 {
                rx_index += 1;
                continue;
            }
            rx_index += 1;

            let error = rxpacket[rx_index];
            rx_index += 1;

            let mut cal_sum = sts_id.wrapping_add((data_length + 2) as u8).wrapping_add(error);
            let mut data = vec![error];
            
            for i in 0..data_length {
                if rx_index + i >= rx_length {
                    return (None, CommResult::RxCorrupt);
                }
                data.push(rxpacket[rx_index + i]);
                cal_sum = cal_sum.wrapping_add(rxpacket[rx_index + i]);
            }
            rx_index += data_length;

            cal_sum = !cal_sum;

            if rx_index >= rx_length {
                return (None, CommResult::RxCorrupt);
            }

            if cal_sum != rxpacket[rx_index] {
                return (None, CommResult::RxCorrupt);
            }

            return (Some(data), CommResult::Success);
        }

        (None, CommResult::RxCorrupt)
    }

    pub fn is_available(&self, sts_id: u8, address: u8, data_length: usize) -> (bool, u8) {
        if !self.data_dict.contains_key(&sts_id) {
            return (false, 0);
        }

        if address < self.start_address
            || self.start_address + self.data_length as u8 - (data_length as u8) < address
        {
            return (false, 0);
        }

        if let Some(data) = self.data_dict.get(&sts_id) {
            if data.is_empty() || data.len() < (data_length + 1) {
                return (false, 0);
            }
            return (true, data[0]);
        }

        (false, 0)
    }

    pub fn get_data(&self, ph: &ProtocolPacketHandler, sts_id: u8, address: u8, data_length: usize) -> u32 {
        if let Some(data) = self.data_dict.get(&sts_id) {
            let offset = (address - self.start_address) as usize + 1;
            
            if data.len() <= offset {
                return 0;
            }

            match data_length {
                1 => data[offset] as u32,
                2 => {
                    if data.len() <= offset + 1 {
                        return 0;
                    }
                    ph.sts_makeword(data[offset], data[offset + 1]) as u32
                }
                4 => {
                    if data.len() <= offset + 3 {
                        return 0;
                    }
                    ph.sts_makedword(
                        ph.sts_makeword(data[offset], data[offset + 1]),
                        ph.sts_makeword(data[offset + 2], data[offset + 3]),
                    )
                }
                _ => 0,
            }
        } else {
            0
        }
    }
}