use std::cell::RefCell;
use std::collections::{HashMap, HashSet};
use std::rc::Rc;
use simcore::cast;
use crate::events::{MessageReceived, TimerFired};
use crate::logger::LogEntry;
use crate::System;
use crate::mc::network::DeliveryOptions;
use crate::mc::{
McEvent, McNetwork, McNode, McResult, McState, McStats, McSystem, McTime, PendingEvents, Strategy, StrategyConfig,
TraceHandler,
};
pub struct ModelChecker {
system: McSystem,
}
impl ModelChecker {
pub fn new(sys: &System) -> Self {
let sim = sys.sim();
let mc_net = McNetwork::new(sys.network());
let trace = sys.logger().trace().clone();
let trace_handler = Rc::new(RefCell::new(TraceHandler::new(trace)));
let mut nodes: HashMap<String, McNode> = HashMap::new();
for node in sys.nodes() {
let node = sys.get_node(&node).unwrap();
nodes.insert(
node.name.clone(),
McNode::new(
node.name.clone(),
node.processes(),
trace_handler.clone(),
node.clock_skew(),
),
);
}
let mut events = PendingEvents::new();
for event in sim.dump_events() {
cast!(match event.data {
MessageReceived { msg, src, dst, .. } => {
events.push(McEvent::MessageReceived {
msg,
src,
dst,
options: DeliveryOptions::NoFailures(McTime::from(mc_net.max_delay())),
});
}
TimerFired { proc, timer } => {
events.push(McEvent::TimerFired {
proc,
timer,
timer_delay: McTime::from(0.0),
});
}
});
}
Self {
system: McSystem::new(nodes, mc_net, events, trace_handler),
}
}
fn run_impl<S>(&mut self, strategy: &mut S, preliminary_callback: impl FnOnce(&mut McSystem)) -> McResult
where
S: Strategy,
{
let initial_state = self.system.get_state();
self.system.trace_handler.borrow_mut().push(LogEntry::McStarted {});
preliminary_callback(&mut self.system);
strategy.mark_visited(self.system.get_state());
let res = strategy.run(&mut self.system);
strategy.reset();
self.system.set_state(initial_state);
res
}
pub fn run<S: Strategy>(&mut self, strategy_config: StrategyConfig) -> McResult {
self.run_with_change::<S>(strategy_config, |_| {})
}
pub fn run_with_change<S>(
&mut self,
strategy_config: StrategyConfig,
preliminary_callback: impl FnOnce(&mut McSystem),
) -> McResult
where
S: Strategy,
{
let mut strategy = S::build(strategy_config);
self.run_impl(&mut strategy, preliminary_callback)
}
pub fn run_from_states<S: Strategy>(
&mut self,
strategy_config: StrategyConfig,
states: HashSet<McState>,
) -> McResult {
self.run_from_states_with_change::<S>(strategy_config, states, |_| {})
}
pub fn run_from_states_with_change<S>(
&mut self,
strategy_config: StrategyConfig,
states: HashSet<McState>,
preliminary_callback: impl Fn(&mut McSystem),
) -> McResult
where
S: Strategy,
{
let mut total_stats = McStats::default();
let mut states = Vec::from_iter(states);
let mut strategy = S::build(strategy_config);
states.sort_by_key(|x| x.depth);
for state in states {
self.system.set_state(state);
let stats = self.run_impl(&mut strategy, &preliminary_callback)?;
total_stats.combine(stats);
}
Ok(total_stats)
}
}