use std::{
collections::HashMap,
fs::File,
io::{self, BufRead, BufReader, Read, Write},
path::Path,
process::ExitCode,
str::FromStr,
};
use event_simulation::{CallState, PlayDirection, Simulation, SimulationState};
use petri_net_simulation::{MultiPetriNetSimulation, PetriNetSimulationBorrowMut};
use pns::{Net, Tid};
fn main() -> ExitCode {
let mut save = false;
let mut load = false;
let args = std::env::args().skip(1);
for arg in args {
match arg.as_ref() {
"save" => save = true,
"load" => load = true,
_ => eprintln!("Ignore invalid argument '{arg}'"),
}
}
let Ok(net) = Net::load("examples/example.pn".as_ref()) else {
eprintln!("Failed to load example net");
return ExitCode::FAILURE;
};
let mut net = MultiPetriNetSimulation::new(net);
let mut names = HashMap::new();
let Ok(file) = File::open("examples/example.pnk") else {
eprintln!("Failed to load example keys");
return ExitCode::FAILURE;
};
for (tid, line) in net.transition_ids().zip(BufReader::new(file).lines()) {
let Ok(line) = line else {
eprintln!("Failed to read key");
return ExitCode::FAILURE;
};
names.insert(tid, line);
}
if save && net.save("examples/example_copy.pns".as_ref()).is_err() {
eprintln!("Failed to save example net");
}
if load {
fn read_values(filename: &Path) -> std::io::Result<Vec<u32>> {
let size = std::fs::metadata(filename)?.len().div_ceil(4);
let mut file = File::open(filename)?;
(0..size)
.map(|_| {
let mut value = [0; 4];
file.read_exact(&mut value)?;
Ok(u32::from_le_bytes(value))
})
.collect()
}
let Ok(data) = read_values("examples/example.pns".as_ref()) else {
eprintln!("Reading state data failed");
return ExitCode::FAILURE;
};
if net.add_simulation_from_data(data).is_err() {
eprintln!("State initialization failed");
return ExitCode::FAILURE;
}
} else {
net.add_simulation();
}
enum StepAction {
Continue,
Reverse,
Save,
Quit,
}
let mut forward = true;
for mut simulation in &mut net {
fn step<D: PlayDirection>(
fire: CallState<PetriNetSimulationBorrowMut, D>,
names: &HashMap<Tid, String>,
) -> StepAction {
if fire.callables.is_empty() {
return StepAction::Reverse;
}
println!("Choose a transition:");
for (i, tid) in fire.callables.iter().enumerate() {
println!("{}: {}", i + 1, names[tid]);
}
print!("> ");
let _ = io::stdout().flush();
let stdin = io::stdin();
let mut string = String::new();
let Ok(size) = stdin.read_line(&mut string) else {
eprintln!("Input error");
return StepAction::Continue;
};
println!();
if size <= 1 {
return StepAction::Continue;
}
match unsafe { string.chars().next().unwrap_unchecked() } {
'r' => return StepAction::Reverse,
'q' => return StepAction::Quit,
's' => return StepAction::Save,
_ => (),
}
match usize::from_str(&string[..(string.len() - 1)]) {
Ok(num) if num != 0 && num <= fire.callables.len() => {
fire.call(num - 1);
}
_ => {
println!(
"You have to input a valid number from 1 to {}",
fire.callables.len()
);
println!();
println!("Other options:");
println!("q: Quit");
println!("r: Reverse play direction");
println!("s: Save");
println!();
return StepAction::Continue;
}
}
StepAction::Continue
}
loop {
let step_action = if forward {
step(simulation.prepare_call(), &names)
} else {
step(simulation.prepare_revert(), &names)
};
use StepAction::*;
match step_action {
Continue => (),
Reverse => {
println!("Reverse play direction!");
println!();
forward = !forward;
}
Save => {
fn save(data: &[usize], filename: &Path) -> std::io::Result<()> {
let mut file = File::create(filename)?;
for &count in data {
file.write_all(&(count as u32).to_le_bytes())?;
}
Ok(())
}
let data = simulation.data();
let result = if save(data, "examples/example.pns".as_ref()).is_ok() {
"successful"
} else {
"failed"
};
println!("Saving state {result}");
println!();
}
Quit => break,
}
}
}
ExitCode::SUCCESS
}