use crate::activity;
use crate::activity::Activity;
use crate::activity_builder::ActivityBuilder;
use crate::{id, Error, Network, Result};
use crate::{DurationType, StartType};
use petgraph::graphmap::DiGraphMap;
use petgraph::Direction;
use std::collections::HashMap;
#[derive(Default)]
pub struct NetworkBuilder {
activities: HashMap<u64, ActivityBuilder>,
connections: DiGraphMap<u64, ()>,
}
impl NetworkBuilder {
pub(crate) fn new(
activities: HashMap<u64, ActivityBuilder>,
connections: DiGraphMap<u64, ()>,
) -> NetworkBuilder {
NetworkBuilder {
activities,
connections,
}
}
pub fn add_activity(&mut self, reference: &str, duration: f64) -> Result<()> {
self.add_extended_activity(
reference,
reference,
duration,
duration,
duration,
StartType::Earliest,
DurationType::Expected,
)
}
pub fn remove_activity(&mut self, reference: &str) -> Result<()> {
self.activities
.remove(&id(&reference))
.ok_or_else(|| Error::UnknownReference(reference.to_string()))?;
if !self.connections.remove_node(id(&reference)) {
return Err(Error::UnknownReference(reference.to_string()));
};
Ok(())
}
pub fn remove_edge(&mut self, origin_reference: &str, target_reference: &str) -> Result<()> {
let origin = self.activity(id(&origin_reference))?;
let target = self.activity(id(&target_reference))?;
if self
.connections
.remove_edge(id(&origin), id(&target))
.is_none()
{
return Err(Error::UnknownEdge(
origin_reference.to_string(),
target_reference.to_string(),
));
}
Ok(())
}
pub fn add_extended_activity(
&mut self,
reference: &str,
description: &str,
minimum_duration: f64,
expected_duration: f64,
maximum_duration: f64,
start_type: StartType,
duration_type: DurationType,
) -> Result<()> {
let mut activity_builder = ActivityBuilder::new(
reference,
description,
expected_duration,
minimum_duration,
maximum_duration,
);
activity_builder.start_type = start_type;
activity_builder.duration_type = duration_type;
self.activities
.insert(id(&reference), activity_builder.clone());
self.connections.add_node(id(&activity_builder));
Ok(())
}
pub fn connect(&mut self, origin_reference: &str, target_reference: &str) -> Result<()> {
let origin = self
.activities
.get(&id(&origin_reference))
.ok_or_else(|| Error::UnknownReference(origin_reference.to_string()))?;
let target = self
.activities
.get(&id(&target_reference))
.ok_or_else(|| Error::UnknownReference(target_reference.to_string()))?;
self.connections.add_edge(id(&origin), id(&target), ());
Ok(())
}
fn mut_activity(&mut self, id: u64) -> Result<&mut ActivityBuilder> {
self.activities.get_mut(&id).ok_or(Error::UnknownId(id))
}
fn activity(&self, id: u64) -> Result<&ActivityBuilder> {
self.activities.get(&id).ok_or(Error::UnknownId(id))
}
fn previous_activities(&self, id: u64) -> Result<Vec<&ActivityBuilder>> {
self.connections
.neighbors_directed(id, Direction::Incoming)
.map(|ref id| self.activities.get(id).ok_or(Error::UnknownId(*id)))
.collect::<Result<Vec<&ActivityBuilder>>>()
}
fn next_activities(&self, id: u64) -> Result<Vec<&ActivityBuilder>> {
self.connections
.neighbors_directed(id, Direction::Outgoing)
.map(|ref id| self.activities.get(id).ok_or(Error::UnknownId(*id)))
.collect::<Result<Vec<&ActivityBuilder>>>()
}
fn forward_pass(&mut self) -> Result<()> {
let mut activity_ids: Vec<u64> = Vec::new();
for id in self
.connections
.neighbors_directed(id(&activity::START_REFERENCE), Direction::Outgoing)
{
activity_ids.push(id);
}
while !activity_ids.is_empty() {
let current_activity_id = activity_ids.remove(0);
let prev_activities = self.previous_activities(current_activity_id)?;
if prev_activities
.iter()
.all(|activity| activity.earliest_finish.is_some())
{
let earliest_start = prev_activities
.iter()
.map(|a| a.earliest_finish.ok_or(Error::FieldNotSet))
.collect::<Result<Vec<f64>>>()?
.iter()
.copied()
.fold(0.0, f64::max);
let depth = prev_activities
.iter()
.map(|a| a.depth)
.max()
.ok_or(Error::FieldNotSet)?;
let activity = self.mut_activity(current_activity_id)?;
activity.earliest_start(earliest_start);
activity.earliest_finish(earliest_start + activity.duration()?);
activity.depth = depth + 1;
for next_activity in self.next_activities(current_activity_id)?.iter() {
activity_ids.push(id(&next_activity.reference));
}
} else {
activity_ids.push(current_activity_id)
}
}
Ok(())
}
fn backward_pass(&mut self) -> Result<()> {
let mut activity_ids: Vec<u64> = Vec::new();
for id in self
.connections
.neighbors_directed(id(&activity::FINISH_REFERENCE), Direction::Incoming)
{
activity_ids.push(id);
}
while !activity_ids.is_empty() {
let current_activity_id = activity_ids.remove(0);
let next_activities = self.next_activities(current_activity_id)?;
if next_activities
.iter()
.all(|activity| activity.latest_start.is_some())
{
let latest_finish = next_activities
.iter()
.map(|a| a.latest_start.ok_or(Error::FieldNotSet))
.collect::<Result<Vec<f64>>>()?
.iter()
.copied()
.fold(f64::MAX, f64::min);
let activity = self.mut_activity(current_activity_id)?;
activity.latest_finish(latest_finish);
activity.latest_start(latest_finish - activity.duration()?);
for prev_activity in self.previous_activities(current_activity_id)?.iter() {
activity_ids.push(id(&prev_activity.reference));
}
} else {
activity_ids.push(current_activity_id)
}
}
Ok(())
}
pub(crate) fn build(&mut self) -> Result<Network> {
for activity in self.activities.values_mut() {
activity.earliest_start = None;
activity.latest_start = None;
activity.earliest_finish = None;
activity.latest_finish = None;
}
let mut start_activity = ActivityBuilder::new(
activity::START_REFERENCE,
activity::START_REFERENCE,
0.0,
0.0,
0.0,
);
start_activity.earliest_start(0.0);
start_activity.earliest_finish(0.0);
start_activity.latest_start(0.0);
start_activity.latest_finish(0.0);
let start_id = id(&activity::START_REFERENCE);
self.connections.add_node(start_id);
for &id in self.activities.keys() {
if self
.connections
.edges_directed(id, Direction::Incoming)
.count()
== 0
{
self.connections.add_edge(start_id, id, ());
}
}
self.activities.insert(start_id, start_activity);
let finish_id = id(&activity::FINISH_REFERENCE);
let finish_activity = ActivityBuilder::new(
activity::FINISH_REFERENCE,
activity::FINISH_REFERENCE,
0.0,
0.0,
0.0,
);
self.connections.add_node(finish_id);
for &id in self.activities.keys() {
if id == finish_id {
continue;
}
if self
.connections
.edges_directed(id, Direction::Outgoing)
.count()
== 0
{
self.connections.add_edge(id, finish_id, ());
}
}
self.activities.insert(finish_id, finish_activity);
self.forward_pass()?;
let finish_activity = self.mut_activity(id(&activity::FINISH_REFERENCE))?;
let latest_finish = finish_activity.earliest_finish.ok_or(Error::FieldNotSet)?;
let latest_start = latest_finish - finish_activity.duration()?;
finish_activity.latest_finish(latest_finish);
finish_activity.latest_start(latest_start);
self.backward_pass()?;
let mut activities = HashMap::<u64, Activity>::new();
let mut connections = DiGraphMap::<u64, ()>::new();
for &id in self.activities.keys() {
let activity = self.activity(id)?.try_into()?;
activities.insert(id, activity);
connections.add_node(id);
for next_neighboud_id in self.connections.neighbors_directed(id, Direction::Outgoing) {
connections.add_edge(id, next_neighboud_id, ());
}
}
activities.remove(&start_id);
activities.remove(&finish_id);
self.activities.remove(&start_id);
self.activities.remove(&finish_id);
connections.remove_node(start_id);
connections.remove_node(finish_id);
self.connections.remove_node(start_id);
self.connections.remove_node(finish_id);
let network = Network::new(activities, connections, latest_start);
Ok(network)
}
pub fn update_activity<F>(&mut self, reference: &str, with_activity_builder: F) -> Result<()>
where
F: FnOnce(&mut ActivityBuilder),
{
let activity_builder = self.mut_activity(id(&reference))?;
with_activity_builder(activity_builder);
Ok(())
}
}
impl From<Network> for NetworkBuilder {
fn from(network: Network) -> NetworkBuilder {
network.to_builder()
}
}
impl From<&Network> for NetworkBuilder {
fn from(network: &Network) -> NetworkBuilder {
network.to_builder()
}
}