use crate::activity::Activity;
use crate::activity_builder::ActivityBuilder;
use crate::{id, Error, NetworkBuilder, Result};
use petgraph::graphmap::DiGraphMap;
use petgraph::Direction;
use std::collections::HashMap;
use std::fmt::Write;
#[derive(Default, Clone, Debug)]
pub struct Network {
activities: HashMap<u64, Activity>,
connections: DiGraphMap<u64, ()>,
finish_earliest_start: f64,
}
impl Network {
pub fn builder() -> NetworkBuilder {
NetworkBuilder::default()
}
pub(crate) fn new(
activities: HashMap<u64, Activity>,
connections: DiGraphMap<u64, ()>,
finish_earliest_start: f64,
) -> Self {
Self {
activities,
connections,
finish_earliest_start,
}
}
fn id_activity(&self, id: u64) -> Result<&Activity> {
self.activities.get(&id).ok_or(Error::UnknownId(id))
}
fn activity(&self, reference: &str) -> Result<&Activity> {
self.activities
.get(&id(&reference))
.ok_or_else(|| Error::UnknownReference(reference.to_string()))
}
pub fn activities(&self) -> Result<Vec<&str>> {
let mut depth_and_references: Vec<(usize, &str)> = self
.activities
.values()
.map(|activity| (activity.depth, activity.reference.as_str()))
.collect();
depth_and_references.sort_by_key(|&(depth, _)| depth);
Ok(depth_and_references
.iter()
.map(|(_, reference)| *reference)
.collect())
}
pub fn edges(&self) -> Result<Vec<(&str, &str)>> {
self.connections
.all_edges()
.map(|(origin_id, target_id, _)| {
match (self.id_activity(origin_id), self.id_activity(target_id)) {
(Ok(origin), Ok(target)) => {
Ok((origin.reference.as_str(), target.reference.as_str()))
}
(Err(error), _) => Err(error),
(_, Err(error)) => Err(error),
}
})
.collect::<Result<Vec<(&str, &str)>>>()
}
pub fn connected(&self, origin_reference: &str, target_reference: &str) -> Result<bool> {
let origin = self.activity(origin_reference)?;
let target = self.activity(target_reference)?;
match self.connections.edge_weight(id(origin), id(target)) {
Some(_) => Ok(true),
None => Ok(false),
}
}
pub fn start_activities(&self) -> Result<Vec<&str>> {
let mut start_activities = Vec::new();
for id in self.activities.keys() {
if self
.connections
.edges_directed(*id, Direction::Incoming)
.count()
== 0
{
start_activities.push(self.id_activity(*id)?.reference.as_str());
}
}
Ok(start_activities)
}
pub fn finish_activities(&self) -> Result<Vec<&str>> {
let mut finish_activities = Vec::new();
for id in self.activities.keys() {
if self
.connections
.edges_directed(*id, Direction::Outgoing)
.count()
== 0
{
finish_activities.push(self.id_activity(*id)?.reference.as_str());
}
}
Ok(finish_activities)
}
fn ids_to_references(&self, ids: Vec<u64>) -> Result<Vec<&str>> {
Ok(ids
.iter()
.map(|id| self.id_activity(*id))
.collect::<Result<Vec<&Activity>>>()?
.iter()
.map(|activity| activity.reference.as_str())
.collect())
}
pub fn next_activities(&self, reference: &str) -> Result<Vec<&str>> {
self.ids_to_references(
self.connections
.neighbors_directed(id(&reference), Direction::Outgoing)
.collect(),
)
}
pub fn previous_activities(&self, reference: &str) -> Result<Vec<&str>> {
self.ids_to_references(
self.connections
.neighbors_directed(id(&reference), Direction::Incoming)
.collect(),
)
}
pub fn earliest_finish(&self, reference: &str) -> Result<f64> {
Ok(self.activity(reference)?.earliest_finish)
}
pub fn latest_finish(&self, reference: &str) -> Result<f64> {
Ok(self.activity(reference)?.latest_finish)
}
pub fn latest_start(&self, reference: &str) -> Result<f64> {
Ok(self.activity(reference)?.latest_start)
}
pub fn earliest_start(&self, reference: &str) -> Result<f64> {
Ok(self.activity(reference)?.earliest_start)
}
pub fn on_critical_path(&self, reference: &str) -> Result<bool> {
let activity = self.activity(reference)?;
Ok(activity.on_critical_path())
}
pub fn critical_path_activities(&self) -> Result<Vec<&str>> {
Ok(self
.activities
.values()
.filter(|activity| activity.on_critical_path())
.map(|activity| activity.reference.as_str())
.collect())
}
pub fn total_float(&self, reference: &str) -> Result<f64> {
Ok(self.activity(reference)?.total_float())
}
pub fn free_float(&self, reference: &str) -> Result<f64> {
let activity = self.activity(reference)?;
let min_earliest_start = self
.next_activities(reference)?
.iter()
.map(|reference| self.activity(reference))
.collect::<Result<Vec<&Activity>>>()?
.iter()
.map(|activity| activity.earliest_start)
.fold(self.finish_earliest_start, f64::min);
let max_earliest_finish = self
.previous_activities(reference)?
.iter()
.map(|reference| self.activity(reference))
.collect::<Result<Vec<&Activity>>>()?
.iter()
.map(|activity| activity.earliest_finish)
.fold(0.0, f64::max);
Ok(min_earliest_start - max_earliest_finish - activity.duration())
}
pub fn start(&self, reference: &str) -> Result<f64> {
Ok(self.activity(reference)?.start())
}
pub fn finish(&self, reference: &str) -> Result<f64> {
Ok(self.activity(reference)?.finish())
}
pub fn active_at(&self, reference: &str, time: f64) -> Result<bool> {
Ok(self.activity(reference)?.active_on(time))
}
pub fn active_during(&self, reference: &str, range: std::ops::Range<f64>) -> Result<bool> {
Ok(self.activity(reference)?.active_during(range))
}
pub fn mean_duration(&self, reference: &str) -> Result<f64> {
Ok(self.activity(reference)?.mean_duration())
}
pub fn variance(&self, reference: &str) -> Result<f64> {
Ok(self.activity(reference)?.variance())
}
pub fn standard_deviation(&self, reference: &str) -> Result<f64> {
Ok(self.activity(reference)?.standard_deviation())
}
pub fn depth(&self, reference: &str) -> Result<usize> {
Ok(self.activity(reference)?.depth)
}
pub fn to_dot(&self) -> Result<String> {
let mut s = String::new();
writeln!(&mut s, "digraph {{")?;
writeln!(&mut s, "rankdir=LR;")?;
writeln!(&mut s, "node [shape=Mrecord];")?;
for activity in self.activities.values() {
write!(&mut s, "\"{}\" ", activity.reference)?;
write!(&mut s, "[label=\"{}|", activity.description)?;
write!(&mut s, "{{")?;
write!(
&mut s,
"{{{}|{}}}",
activity.earliest_start, activity.latest_start,
)?;
write!(
&mut s,
"|{{{}|{{{}|{}}}}}",
activity.reference,
activity.total_float(),
self.free_float(&activity.reference)?
)?;
write!(
&mut s,
"|{{{}|{}}}}}",
activity.earliest_finish, activity.latest_finish
)?;
writeln!(&mut s, "|{}\"];", activity.duration())?;
}
for (from, to) in self.edges()?.iter() {
write!(&mut s, "\"{}\" -> \"{}\"", from, to)?;
if self.on_critical_path(from)? && self.on_critical_path(to)? {
write!(&mut s, "[style=bold]")?;
}
writeln!(&mut s, ";")?;
}
write!(&mut s, "}}")?;
Ok(s)
}
pub(crate) fn to_builder(&self) -> NetworkBuilder {
let mut activities = HashMap::<u64, ActivityBuilder>::new();
for (&id, activity) in self.activities.iter() {
activities.insert(id, activity.into());
}
NetworkBuilder::new(activities, self.connections.clone())
}
pub fn update_activity<F>(self, reference: &str, with_activity_builder: F) -> Result<Network>
where
F: FnOnce(&mut ActivityBuilder),
{
let mut network_builder = self.to_builder();
network_builder.update_activity(reference, with_activity_builder)?;
Self::try_from(network_builder)
}
}
impl TryFrom<NetworkBuilder> for Network {
type Error = crate::Error;
fn try_from(mut network_builder: NetworkBuilder) -> std::result::Result<Network, Self::Error> {
network_builder.build()
}
}
impl TryFrom<&mut NetworkBuilder> for Network {
type Error = crate::Error;
fn try_from(network_builder: &mut NetworkBuilder) -> std::result::Result<Network, Self::Error> {
network_builder.build()
}
}