use std::ffi::{CStr, CString, OsString};
use std::fmt;
use std::fs::{metadata, remove_file};
use std::iter::{Iterator, IntoIterator};
use std::path::Path;
use std::ptr;
use libc::c_char;
use Result;
use error::Error;
use ffi::{fwifc_close, fwifc_file, fwifc_get_calib, fwifc_get_info, fwifc_open, fwifc_read,
fwifc_reindex, fwifc_sbl_t, fwifc_seek, fwifc_seek_time, fwifc_seek_time_external,
fwifc_tell, fwifc_set_sosbl_relative};
#[derive(Debug)]
pub struct File {
handle: fwifc_file,
index_path: OsString,
}
impl File {
pub fn open<T: Into<Vec<u8>>>(path: T) -> Result<File> {
unsafe {
let path = try!(CString::new(path));
let mut file: fwifc_file = ptr::null_mut();
sdftry!(fwifc_open(path.as_ptr(), &mut file));
let index_path = Path::new(try!(path.to_str())).with_extension("idx").into_os_string();
Ok(File {
handle: file,
index_path: index_path,
})
}
}
pub fn reindex(&mut self) -> Result<()> {
if !self.indexed() {
info!("Reindexing");
unsafe { sdftry!(fwifc_reindex(self.handle)) }
}
Ok(())
}
pub fn remove_index(&self) -> Result<()> {
remove_file(&self.index_path).map_err(|e| Error::from(e))
}
pub fn set_sosbl_mode(&mut self, mode: SosblMode) -> Result<()> {
unsafe {
let value = match mode {
SosblMode::Absolute => 0,
SosblMode::Relative => 1,
};
Ok(sdftry!(fwifc_set_sosbl_relative(self.handle, value)))
}
}
pub fn info(&mut self) -> Result<FileInfo> {
unsafe {
let mut instrument: *const c_char = ptr::null_mut();
let mut serial: *const c_char = ptr::null_mut();
let mut epoch: *const c_char = ptr::null_mut();
let mut v_group = 0f64;
let mut sampling_time = 0f64;
let mut flags = 0u16;
let mut num_facets = 0u16;
sdftry!(fwifc_get_info(self.handle,
&mut instrument,
&mut serial,
&mut epoch,
&mut v_group,
&mut sampling_time,
&mut flags,
&mut num_facets));
Ok(FileInfo {
instrument: try!(CStr::from_ptr(instrument).to_str()).to_string(),
serial: try!(CStr::from_ptr(serial).to_str()).to_string(),
epoch: try!(CStr::from_ptr(epoch).to_str()).to_string(),
v_group: v_group,
sampling_time: sampling_time,
gps_synchronized: flags & 0x01 == 1,
num_facets: num_facets,
})
}
}
pub fn calibration(&mut self, kind: CalibrationTableKind) -> Result<Calibration> {
unsafe {
let mut count = 0u32;
let mut abscissa: *const f64 = ptr::null_mut();
let mut ordinate: *const f64 = ptr::null_mut();
sdftry!(fwifc_get_calib(self.handle,
try!(kind.as_u16()),
&mut count,
&mut abscissa,
&mut ordinate));
let mut abscissa_vec = Vec::with_capacity(count as usize);
let mut ordinate_vec = Vec::with_capacity(count as usize);
for i in 0..count {
abscissa_vec.push(*abscissa.offset(i as isize));
ordinate_vec.push(*ordinate.offset(i as isize));
}
Ok(Calibration {
abscissa: abscissa_vec,
ordinate: ordinate_vec,
})
}
}
pub fn read(&mut self) -> Result<Record> {
unsafe {
let mut time_sorg = 0.0;
let mut time_external = 0.0;
let mut origin = [0.0f64; 3];
let mut direction = [0.0f64; 3];
let mut flags = 0;
let mut facet = 0;
let mut sbl_count = 0;
let mut sbl_size = 0;
let mut sbl: *mut fwifc_sbl_t = ptr::null_mut();
sdftry!(fwifc_read(self.handle,
&mut time_sorg,
&mut time_external,
origin.as_mut_ptr(),
direction.as_mut_ptr(),
&mut flags,
&mut facet,
&mut sbl_count,
&mut sbl_size,
&mut sbl));
let mut blocks = Vec::with_capacity(sbl_count as usize);
for i in 0..sbl_count {
let ref block = *sbl.offset(i as isize);
let mut samples = Vec::with_capacity(block.sample_count as usize);
for j in 0..block.sample_count {
samples.push(*block.sample.offset(j as isize));
}
let channel = try!(Channel::from_u32(block.channel));
blocks.push(Block {
time_sosbl: block.time_sosbl,
channel: channel,
samples: samples,
});
}
Ok(Record {
time_sorg: time_sorg,
time_external: time_external,
origin: origin,
direction: direction,
synchronized: flags & 0x01 == 1,
sync_lastsec: flags & 0x02 == 2,
housekeeping: flags & 0x04 == 4,
facet: facet,
blocks: blocks,
})
}
}
pub fn seek(&mut self, index: u32) -> Result<()> {
unsafe { Ok(sdftry!(fwifc_seek(self.handle, index))) }
}
pub fn seek_time(&mut self, time: f64) -> Result<()> {
unsafe { Ok(sdftry!(fwifc_seek_time(self.handle, time))) }
}
pub fn seek_time_external(&mut self, time: f64) -> Result<()> {
unsafe { Ok(sdftry!(fwifc_seek_time_external(self.handle, time))) }
}
pub fn tell(&mut self) -> Result<u32> {
let mut index = 0u32;
unsafe { sdftry!(fwifc_tell(self.handle, &mut index)) }
Ok(index)
}
pub fn indexed(&self) -> bool {
metadata(&self.index_path).map(|m| m.is_file()).unwrap_or(false)
}
}
impl Drop for File {
fn drop(&mut self) {
unsafe {
let result = fwifc_close(self.handle);
if result != 0 {
panic!("Error when closing file: {}", Error::from_i32(result));
}
}
}
}
impl IntoIterator for File {
type Item = Record;
type IntoIter = FileIterator;
fn into_iter(mut self) -> Self::IntoIter {
self.reindex().unwrap();
FileIterator { file: self }
}
}
#[derive(Debug)]
pub struct FileIterator {
file: File,
}
impl Iterator for FileIterator {
type Item = Record;
fn next(&mut self) -> Option<Self::Item> {
match self.file.read() {
Ok(record) => Some(record),
Err(Error::EndOfFile(_)) => None,
Err(err) => panic!("Error when iterating through the file: {}", err),
}
}
}
#[derive(Clone, Copy, Debug)]
pub enum SosblMode {
Relative,
Absolute,
}
#[derive(Debug)]
pub struct FileInfo {
pub instrument: String,
pub serial: String,
pub epoch: String,
pub v_group: f64,
pub sampling_time: f64,
pub gps_synchronized: bool,
pub num_facets: u16,
}
#[derive(Debug)]
pub struct Calibration {
pub abscissa: Vec<f64>,
pub ordinate: Vec<f64>,
}
#[derive(Clone, Copy, Debug)]
pub enum CalibrationTableKind {
Amplitude(Channel),
Range(Channel),
}
impl CalibrationTableKind {
pub fn as_u16(&self) -> Result<u16> {
match *self {
CalibrationTableKind::Amplitude(channel) => {
match channel {
Channel::High => Ok(0),
Channel::Low => Ok(1),
_ => Err(Error::NoCalibrationTableForChannel(channel)),
}
}
CalibrationTableKind::Range(channel) => {
match channel {
Channel::High => Ok(2),
Channel::Low => Ok(3),
_ => Err(Error::NoCalibrationTableForChannel(channel)),
}
}
}
}
}
#[derive(Debug)]
pub struct Record {
pub time_sorg: f64,
pub time_external: f64,
pub origin: [f64; 3],
pub direction: [f64; 3],
pub synchronized: bool,
pub sync_lastsec: bool,
pub housekeeping: bool,
pub facet: u16,
pub blocks: Vec<Block>,
}
impl fmt::Display for Record {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f,
"time_sorg: {}\ntime_external: {}\norigin: {} {} {}\ndirection: {} {} \
{}\nsynchronized: {}\nsync_lastsec: {}\nhousekeeping: {}\nfacet: {}\nnblocks: {}",
self.time_sorg,
self.time_external,
self.origin[0],
self.origin[1],
self.origin[2],
self.direction[0],
self.direction[1],
self.direction[2],
self.synchronized,
self.sync_lastsec,
self.housekeeping,
self.facet,
self.blocks.len())
}
}
#[derive(Debug)]
pub struct Block {
pub time_sosbl: f64,
pub channel: Channel,
pub samples: Vec<u16>,
}
impl fmt::Display for Block {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
try!(write!(f,
"time_sosbl: {}\nchannel: {}\nsamples: ",
self.time_sosbl,
self.channel));
for (i, ref sample) in self.samples.iter().enumerate() {
let mut seperator = ", ";
if i == self.samples.len() - 1 {
seperator = ""
}
try!(write!(f, "{}{}", sample, seperator));
}
Ok(())
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum Channel {
High,
Low,
Saturation,
Reference,
}
impl Channel {
pub fn from_u32(n: u32) -> Result<Channel> {
match n {
0 => Ok(Channel::High),
1 => Ok(Channel::Low),
2 => Ok(Channel::Saturation),
3 => Ok(Channel::Reference),
_ => Err(Error::InvalidChannel(n)),
}
}
}
impl fmt::Display for Channel {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let name = match *self {
Channel::High => "high",
Channel::Low => "low",
Channel::Saturation => "saturation",
Channel::Reference => "reference",
};
write!(f, "{}", name)
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::fs::remove_file;
#[test]
fn open_throws_on_bad_filename() {
assert!(File::open("notafile.sdf").is_err());
}
#[test]
fn file_info() {
let mut file = File::open("data/110630_174316.sdf").unwrap();
let info = file.info().unwrap();
assert_eq!("Q680I", info.instrument);
assert_eq!("9998212", info.serial);
assert_eq!("UNKNOWN", info.epoch);
assert_eq!(299707502.1266937, info.v_group);
assert_eq!(0.000000001, info.sampling_time);
assert!(info.gps_synchronized);
assert_eq!(4, info.num_facets);
}
#[test]
fn file_calibration() {
let mut file = File::open("data/110630_174316.sdf").unwrap();
let calib = file.calibration(CalibrationTableKind::Amplitude(Channel::High)).unwrap();
assert_eq!(256, calib.abscissa.len());
assert_eq!(calib.ordinate.len(), calib.abscissa.len());
}
#[test]
fn smart_index() {
remove_file("data/110630_174316.idx").unwrap_or(());
{
let mut file = File::open("data/110630_174316.sdf").unwrap();
assert!(!file.indexed());
file.reindex().unwrap();
}
let file = File::open("data/110630_174316.sdf").unwrap();
assert!(file.indexed());
}
}