///|
pub struct ScheduledUnit {
id : String
duration_h : Double
predecessors : Array[String]
} derive(Debug)
///|
pub struct ScheduledEntry {
id : String
start_h : Double
finish_h : Double
slack_h : Double
} derive(Debug)
///|
pub struct ScheduleResult {
entries : Array[ScheduledEntry]
makespan_h : Double
critical_unit_id : String
feasible : Bool
} derive(Debug)
///|
pub fn ScheduledUnit::ScheduledUnit(
id~ : String,
duration_h~ : Double,
predecessors~ : Array[String],
) -> ScheduledUnit {
{ id, duration_h: duration_h.max(0.0), predecessors }
}
///|
fn schedule_entry_for(
entries : Array[ScheduledEntry],
id : String,
) -> ScheduledEntry? {
for entry in entries {
if entry.id == id {
return Some(entry)
}
}
None
}
///|
fn schedule_has_id(ids : Array[String], id : String) -> Bool {
for item in ids {
if item == id {
return true
}
}
false
}
///|
pub fn schedule_network(units : Array[ScheduledUnit]) -> ScheduleResult {
let entries : Array[ScheduledEntry] = []
let mut feasible = true
for unit in units {
let mut start = 0.0
for predecessor in unit.predecessors {
match schedule_entry_for(entries, predecessor) {
Some(entry) => start = start.max(entry.finish_h)
None => feasible = false
}
}
entries.push({
id: unit.id,
start_h: start,
finish_h: start + unit.duration_h,
slack_h: 0.0,
})
}
let mut makespan = 0.0
let mut critical = ""
for entry in entries {
if entry.finish_h >= makespan {
makespan = entry.finish_h
critical = entry.id
}
}
let updated : Array[ScheduledEntry] = []
for entry in entries {
updated.push({ ..entry, slack_h: makespan - entry.finish_h })
}
{
entries: updated,
makespan_h: makespan,
critical_unit_id: critical,
feasible,
}
}
///|
pub fn critical_entries(schedule : ScheduleResult) -> Array[ScheduledEntry] {
let critical : Array[ScheduledEntry] = []
for entry in schedule.entries {
if entry.slack_h <= 1.0e-12 {
critical.push(entry)
}
}
critical
}
///|
pub fn schedule_duration(schedule : ScheduleResult, id : String) -> Double {
match schedule_entry_for(schedule.entries, id) {
Some(entry) => entry.finish_h - entry.start_h
None => 0.0
}
}
///|
pub fn schedule_contains_cycle(units : Array[ScheduledUnit]) -> Bool {
let ids : Array[String] = []
for unit in units {
ids.push(unit.id)
}
for unit in units {
for predecessor in unit.predecessors {
if predecessor == unit.id || !schedule_has_id(ids, predecessor) {
return true
}
}
}
false
}
///|
pub fn schedule_resource_hours(
schedule : ScheduleResult,
resource_count : Int,
) -> Double {
if resource_count <= 0 {
0.0
} else {
let mut total = 0.0
for entry in schedule.entries {
total += entry.finish_h - entry.start_h
}
total / resource_count.to_double()
}
}
///|
pub fn schedule_entry_count(schedule : ScheduleResult) -> Int {
schedule.entries.length()
}
///|
pub fn schedule_is_valid(schedule : ScheduleResult) -> Bool {
schedule.feasible && !schedule_contains_cycle([])
}