use crate::analog::Analog;
use crate::forces::Forces;
use crate::manufacturer::Manufacturer;
use crate::parameters::Parameters;
use crate::points::Points;
use crate::seg::Seg;
use crate::events::Events;
use crate::processor::Processor;
use crate::{C3d, C3dParseError};
use std::fs::File;
use std::io::{Read, Seek, SeekFrom};
use std::path::PathBuf;
impl PartialEq for C3d {
fn eq(&self, other: &Self) -> bool {
self.parameters == other.parameters && self.events == other.events
}
}
impl C3d {
pub fn load(file_name: &str) -> Result<C3d, C3dParseError> {
let c3d = C3d::new();
let (c3d, mut file) = c3d.open_file(file_name)?;
let (c3d, header_bytes, parameter_bytes, _) = c3d.parse_basic_info(&mut file)?;
Ok(c3d
.parse_header(&header_bytes)?
.parse_parameters(&header_bytes, ¶meter_bytes)?
.parse_data(file)?)
}
pub fn from_bytes(bytes: &[u8]) -> Result<C3d, C3dParseError> {
let (c3d, header_bytes, parameter_bytes, data_start_block_index) =
C3d::new().parse_basic_info_from_bytes(bytes)?;
Ok(c3d
.parse_header(&header_bytes)?
.parse_parameters(&header_bytes, ¶meter_bytes)?
.parse_data_from_bytes(bytes, data_start_block_index)?)
}
pub fn load_header(file_name: &str) -> Result<C3d, C3dParseError> {
let c3d = C3d::new();
let (c3d, mut file) = c3d.open_file(file_name)?;
let (c3d, header_bytes, _, _) = c3d.parse_basic_info(&mut file)?;
Ok(c3d.parse_header(&header_bytes)?)
}
pub fn load_parameters(file_name: &str) -> Result<C3d, C3dParseError> {
let c3d = C3d::new();
let (c3d, mut file) = c3d.open_file(file_name)?;
let (c3d, header_bytes, parameter_bytes, _) = c3d.parse_basic_info(&mut file)?;
Ok(c3d
.parse_header(&header_bytes)?
.parse_parameters(&header_bytes, ¶meter_bytes)?)
}
pub fn new() -> C3d {
C3d::default()
}
fn force_analog_data(&self, force_plate: usize, frame: usize) -> Option<[f32; 8]> {
let channel = self.forces.channel.as_ref()?;
if self.forces.used as usize <= force_plate || channel.cols() <= force_plate {
return None;
}
if let Some(channels) = &self.forces.channel {
if !(channels.rows() == 8 || channels.rows() == 6) {
return None;
}
let mut analog = [0f32; 8];
for i in 0..channels.rows() {
let channel_index = channels[i][force_plate];
dbg!(self.analog.size());
if self.analog.rows() <= channel_index as usize
|| self.analog.cols()
<= frame * self.analog.analog_samples_per_channel_per_frame as usize
{
return None;
}
analog[i] = self.analog
[frame * self.analog.analog_samples_per_channel_per_frame as usize]
[channel_index as usize];
}
return Some(analog);
}
None
}
pub fn force(&self, force_plate: usize, frame: usize) -> Option<[f32; 3]> {
let analog = self.force_analog_data(force_plate, frame)?;
self.forces.force_from_analog(analog, force_plate)
}
pub fn center_of_pressure(&self, force_plate: usize, frame: usize) -> Option<[f32; 2]> {
let analog = self.force_analog_data(force_plate, frame)?;
self.forces
.center_of_pressure_from_analog(analog, force_plate)
}
fn open_file(mut self, file_name: &str) -> Result<(C3d, File), C3dParseError> {
self.file_path = Some(PathBuf::from(file_name));
let file =
File::open(self.file_path.clone().unwrap()).map_err(|e| C3dParseError::ReadError(e))?;
Ok((self, file))
}
fn parse_basic_info(
mut self,
file: &mut File,
) -> Result<(C3d, [u8; 512], Vec<u8>, usize), C3dParseError> {
let header_bytes = read_header_bytes(file)?;
let (processor, parameter_bytes, data_start_block_index) =
read_parameter_bytes(file, &header_bytes)?;
self.processor = processor;
Ok((self, header_bytes, parameter_bytes, data_start_block_index))
}
fn parse_basic_info_from_bytes(
mut self,
bytes: &[u8],
) -> Result<(C3d, [u8; 512], Vec<u8>, usize), C3dParseError> {
if bytes.len() < 512 {
return Err(C3dParseError::InsufficientBlocks("header".to_string()));
}
let header_bytes: [u8; 512] = bytes[0..512].try_into().unwrap();
let parameter_start_block_index = header_bytes[0] as usize;
if bytes.len() < 512 * (parameter_start_block_index) {
return Err(C3dParseError::InsufficientBlocks("parameter".to_string()));
}
let blocks_to_skip = 512 * (parameter_start_block_index - 1);
let parameter_start_block: [u8; 512] = bytes[blocks_to_skip..(blocks_to_skip + 512)]
.try_into()
.unwrap();
self.processor =
Processor::from_parameter_start_block(parameter_start_block.try_into().unwrap())?;
let data_start_block_index =
self.processor.u16([header_bytes[16], header_bytes[17]]) as usize;
if bytes.len() < 512 * (data_start_block_index) {
return Err(C3dParseError::InsufficientBlocks("data".to_string()));
}
let parameter_bytes: Vec<u8> = bytes[512..(512 * (data_start_block_index - 1))]
.try_into()
.unwrap();
Ok((self, header_bytes, parameter_bytes, data_start_block_index))
}
fn parse_header(mut self, header_bytes: &[u8; 512]) -> Result<C3d, C3dParseError> {
self.points = Points::parse_header(&header_bytes, &self.processor);
self.analog = Analog::parse_header(&header_bytes, &self.processor);
Ok(self)
}
fn parse_parameters(
mut self,
header_bytes: &[u8; 512],
parameter_bytes: &Vec<u8>,
) -> Result<C3d, C3dParseError> {
self.parameters = Parameters::parse_parameter_blocks(parameter_bytes, &self.processor)?;
self.events = Events::from_header_and_raw_parameters(
&header_bytes,
&self.parameters.raw_parameters,
&self.processor,
)?;
self.manufacturer = Manufacturer::from_raw(&self.parameters.raw_parameters);
self.seg = Seg::from_raw(&self.parameters.raw_parameters);
self.forces = Forces::from_raw(&self.parameters.raw_parameters)?;
Ok(self)
}
fn parse_data(self, file: File) -> Result<C3d, C3dParseError> {
let data_bytes = read_data_bytes(file)?;
self.parse_data_bytes(data_bytes)
}
fn parse_data_from_bytes(
self,
bytes: &[u8],
data_start_block_index: usize,
) -> Result<C3d, C3dParseError> {
let data_start_byte = 512 * (data_start_block_index - 1);
if bytes.len() < data_start_byte {
return Err(C3dParseError::InsufficientBlocks("data".to_string()));
}
self.parse_data_bytes(bytes[data_start_byte..].to_vec())
}
fn parse_data_bytes(mut self, data_bytes: Vec<u8>) -> Result<C3d, C3dParseError> {
self.points.parse(
&data_bytes,
&self.parameters.raw_parameters,
&self.processor,
self.analog.analog_samples_per_frame,
)?;
self.analog.parse(
&data_bytes,
&self.parameters.raw_parameters,
&self.processor,
self.points.num_frames,
&self.points.format,
self.points.points_per_frame,
)?;
Ok(self)
}
}
#[derive(Debug, PartialEq)]
pub enum ProcessStep {
LoadFile,
ParseBasicInfo,
ParseHeader,
ParseParameters,
ParseData,
Complete,
}
pub fn test_load_file(file_name: &str) -> ProcessStep {
let c3d = C3d::new();
let (c3d, mut file) = match c3d.open_file(file_name) {
Ok(c3d) => c3d,
Err(_) => return ProcessStep::LoadFile,
};
let (c3d, header_bytes, parameter_bytes, _) = match c3d.parse_basic_info(&mut file) {
Ok((c3d, header_bytes, parameter_bytes, data_start_block_index)) => {
(c3d, header_bytes, parameter_bytes, data_start_block_index)
}
Err(_) => return ProcessStep::ParseBasicInfo,
};
let c3d = match c3d.parse_header(&header_bytes) {
Ok(c3d) => c3d,
Err(_) => return ProcessStep::ParseHeader,
};
let c3d = match c3d.parse_parameters(&header_bytes, ¶meter_bytes) {
Ok(c3d) => c3d,
Err(_) => return ProcessStep::ParseParameters,
};
match c3d.parse_data(file) {
Ok(c3d) => c3d,
Err(_) => return ProcessStep::ParseData,
};
ProcessStep::Complete
}
fn read_header_bytes(file: &mut File) -> Result<[u8; 512], C3dParseError> {
let mut header_bytes = [0u8; 512];
file.read_exact(&mut header_bytes)
.map_err(|e| C3dParseError::ReadError(e))?;
Ok(header_bytes)
}
fn read_parameter_bytes(
file: &mut File,
header_bytes: &[u8; 512],
) -> Result<(Processor, Vec<u8>, usize), C3dParseError> {
let parameter_start_block_index = header_bytes[0] as usize;
let blocks_to_skip = parameter_start_block_index - 2;
file.seek(SeekFrom::Current((512 * blocks_to_skip) as i64))
.map_err(|e| C3dParseError::ReadError(e))?;
let mut parameter_start_block = [0u8; 512];
file.read_exact(&mut parameter_start_block)
.map_err(|e| C3dParseError::ReadError(e))?;
let processor = Processor::from_parameter_start_block(parameter_start_block)?;
let data_start_block_index = processor.u16([header_bytes[16], header_bytes[17]]) as usize;
let mut parameter_bytes_tail =
Vec::with_capacity((data_start_block_index - parameter_start_block_index - 1) * 512)
as Vec<u8>;
for _ in 0..(data_start_block_index - parameter_start_block_index - 1) {
let mut block = [0u8; 512];
file.read_exact(&mut block)
.map_err(|e| C3dParseError::ReadError(e))?;
parameter_bytes_tail.extend(block.iter());
}
let parameter_bytes = [
parameter_start_block.as_slice(),
parameter_bytes_tail.as_slice(),
]
.concat();
Ok((processor, parameter_bytes, data_start_block_index))
}
fn read_data_bytes(mut file: File) -> Result<Vec<u8>, C3dParseError> {
let mut data: Vec<u8> = Vec::new();
file.read_to_end(&mut data)
.map_err(|e| C3dParseError::ReadError(e))?;
Ok(data)
}