use crate::atom::Atom;
use crate::integrator::Step;
use crate::simulation::Plugin;
use nalgebra::Vector3;
use specs::{Component, Entities, Entity, Join, ReadExpect, ReadStorage, System};
use std::fmt::Display;
use std::fs::File;
use std::io;
use std::io::BufWriter;
use std::io::Write;
use std::marker::PhantomData;
use std::path::Path;
extern crate byteorder;
use byteorder::{LittleEndian, WriteBytesExt};
pub struct OutputSystem<C: Component + Clone, W: Write, F: Format<C, W>, A = Atom> {
interval: u64,
atom_flag: PhantomData<A>,
stream: W,
formatter: PhantomData<F>,
marker: PhantomData<C>,
}
pub struct FileOutputPlugin<C,F,A>
where C: Component + Clone,
F: Format<C, BufWriter<File>>
{
file_name: String,
interval: u64,
phantom_c: PhantomData<C>,
phantom_f: PhantomData<F>,
phantom_a: PhantomData<A>
}
impl<C,F,A> FileOutputPlugin<C,F,A>
where
C: Component + Clone,
A: Component,
F: Format<C, BufWriter<File>>
{
pub fn new(file_name: String, interval: u64) -> FileOutputPlugin<C,F,A>
{
FileOutputPlugin {
file_name,
interval,
phantom_a: PhantomData,
phantom_c: PhantomData,
phantom_f: PhantomData
}
}
}
impl<C,F,A> Plugin for FileOutputPlugin<C,F,A>
where
C: Component + Clone + Sync + Send + 'static,
A: Component + Sync + Send + 'static,
F: Format<C, BufWriter<File>> + Sync + Send + 'static
{
fn build(&self, builder: &mut crate::simulation::SimulationBuilder) {
builder.dispatcher_builder.add(
new_with_filter::<C, F, A>(self.file_name.clone(), self.interval),
"",
&[],
);
}
fn deps(&self) -> Vec::<Box<dyn Plugin>> {
Vec::new()
}
}
fn new_with_filter<C, F, A>(
file_name: String,
interval: u64,
) -> OutputSystem<C, BufWriter<File>, F, A>
where
C: Component + Clone,
A: Component,
F: Format<C, BufWriter<File>>,
{
let path = Path::new(&file_name);
let display = path.display();
let file = match File::create(&path) {
Err(why) => panic!("couldn't open {}: {}", display, why),
Ok(file) => file,
};
let writer = BufWriter::new(file);
OutputSystem {
interval,
atom_flag: PhantomData,
stream: writer,
formatter: PhantomData,
marker: PhantomData,
}
}
impl<'a, C, A, W, F> System<'a> for OutputSystem<C, W, F, A>
where
C: Component + Clone,
A: Component,
W: Write,
F: Format<C, W>,
{
type SystemData = (
Entities<'a>,
ReadStorage<'a, C>,
ReadStorage<'a, A>,
ReadExpect<'a, Step>,
);
fn run(&mut self, (entities, data, atom_flags, step): Self::SystemData) {
if step.n % self.interval == 0 {
let atom_number = (&atom_flags).join().count();
F::write_frame_header(&mut self.stream, step.n, atom_number).expect("Could not write.");
for (data, _, ent) in (&data, &atom_flags, &entities).join() {
F::write_atom(&mut self.stream, ent, data.clone()).expect("Could not write.");
}
}
}
}
pub trait Format<C, W>
where
C: Component + Clone,
W: Write,
{
fn write_frame_header(writer: &mut W, step: u64, atom_number: usize) -> Result<(), io::Error>;
fn write_atom(writer: &mut W, atom: Entity, data: C) -> Result<(), io::Error>;
}
pub struct Text {}
impl<C, W> Format<C, W> for Text
where
C: Component + Clone + Display,
W: Write,
{
fn write_frame_header(writer: &mut W, step: u64, atom_number: usize) -> Result<(), io::Error> {
writeln!(writer, "step-{:?}, {:?}", step, atom_number)?;
Ok(())
}
fn write_atom(writer: &mut W, atom: Entity, data: C) -> Result<(), io::Error> {
writeln!(writer, "{:?},{:?}: {}", atom.gen().id(), atom.id(), data)?;
Ok(())
}
}
pub struct SerdeJson {}
impl<C, W> Format<C, W> for SerdeJson
where
C: Component + serde::Serialize + Clone,
W: Write,
{
fn write_frame_header(writer: &mut W, step: u64, atom_number: usize) -> Result<(), io::Error> {
writeln!(writer, "step-{:?}, {:?}", step, atom_number)?;
Ok(())
}
fn write_atom(writer: &mut W, atom: Entity, data: C) -> Result<(), io::Error> {
let serialized = serde_json::to_string(&data).unwrap();
writeln!(
writer,
"{:?},{:?}, {}",
atom.gen().id(),
atom.id(),
serialized
)?;
Ok(())
}
}
pub trait XYZPosition {
fn pos(&self) -> Vector3<f64>;
}
pub struct XYZ {}
impl<C, W> Format<C, W> for XYZ
where
C: Component + Clone + XYZPosition,
W: Write,
{
fn write_frame_header(writer: &mut W, _step: u64, atom_number: usize) -> Result<(), io::Error> {
write!(writer, "{:?}\n\n", atom_number)?;
Ok(())
}
fn write_atom(writer: &mut W, _atom: Entity, data: C) -> Result<(), io::Error> {
let position = 20000.0 * data.pos();
writeln!(
writer,
"H\t{}\t{}\t{}",
position[0], position[1], position[2]
)?;
Ok(())
}
}
type Endianness = LittleEndian;
pub trait BinaryConversion {
fn data(&self) -> Vec<f64>;
}
pub struct Binary {}
impl<C, W> Format<C, W> for Binary
where
C: Component + Clone + BinaryConversion,
W: Write,
{
fn write_frame_header(writer: &mut W, step: u64, atom_number: usize) -> Result<(), io::Error> {
writer.write_u64::<Endianness>(step)?;
writer.write_u64::<Endianness>(atom_number as u64)?;
Ok(())
}
fn write_atom(writer: &mut W, atom: Entity, data: C) -> Result<(), io::Error> {
writer.write_i32::<Endianness>(atom.gen().id())?;
writer.write_u32::<Endianness>(atom.id())?;
for element in data.data() {
writer.write_f64::<Endianness>(element)?;
}
Ok(())
}
}