From 26695a2509675a4548074e1e3ed32b9bd5922ea8 Mon Sep 17 00:00:00 2001 From: Tiago Amorim Date: Wed, 29 May 2024 21:26:37 +0100 Subject: [PATCH] tweaked blueprint scheduling algorithm --- src/scheduler/handlers/blueprint_handler.rs | 48 +++--------------- src/scheduler/mod.rs | 56 +++++++++++++-------- 2 files changed, 42 insertions(+), 62 deletions(-) diff --git a/src/scheduler/handlers/blueprint_handler.rs b/src/scheduler/handlers/blueprint_handler.rs index 54989c2..59e47d4 100644 --- a/src/scheduler/handlers/blueprint_handler.rs +++ b/src/scheduler/handlers/blueprint_handler.rs @@ -1,7 +1,4 @@ -use crate::{ - db_api::{Item, Recipe}, - scheduler::TIME_IN_DAY, -}; +use crate::db_api::{Item, Recipe}; use super::item_handler::{self}; @@ -25,40 +22,12 @@ impl ItemBlueprint { Ok(()) } - pub fn schedule( - &mut self, - due_date: i32, - current_date: i32, - ) -> anyhow::Result { - let mut schedule_day = due_date; - let mut duration_acc = 0; - - // walk back from the due date and find the - // last day that can accommodate each step in - // the process - for step in self.process.iter_mut() { - duration_acc += step.recipe.operation_time; - if duration_acc > TIME_IN_DAY { - schedule_day -= 1; - duration_acc = 0; - } - - if schedule_day < current_date { - return Err(anyhow::anyhow!( - "Cannot schedule item, due date is in the past" - )); - } - - step.transf.set_date(schedule_day); - - tracing::debug!( - "Scheduled transformation {:?} for day {}", - step.transf, - schedule_day - ); - } - - Ok(schedule_day) + pub fn set_start(&mut self, starting_date: i64) { + self.process + .last_mut() + .expect("empty process") + .transf + .set_date(starting_date as i32); } pub fn generate( @@ -66,8 +35,7 @@ impl ItemBlueprint { full_recipe: &[Recipe], ) -> anyhow::Result { // for each item generate its components and - // transformations. Then schedule everything - // and update the item status to "Scheduled" + // transformations. let process = match item_handler::describe_process(full_recipe, item.clone()) { Ok(proc) => proc, diff --git a/src/scheduler/mod.rs b/src/scheduler/mod.rs index 740e085..4004bb3 100644 --- a/src/scheduler/mod.rs +++ b/src/scheduler/mod.rs @@ -48,24 +48,15 @@ impl Scheduler { let current_date = { let mut con = pool.acquire().await?; db_api::get_date(&mut con).await? - }; + } as i64; + // earliest start is the next day so that materials can be prepared + let earliest_start = current_date + 1; - let blueprints = order_items + let mut blueprints = order_items .iter() .filter_map(|item| { - let mut bp = match ItemBlueprint::generate( - (*item).clone(), - &full_recipe, - ) { - Ok(bp) => bp, - Err(e) => { - tracing::error!("{:?}", e); - return None; - } - }; - - match bp.schedule(order.due_date(), current_date as i32) { - Ok(_) => Some(bp), + match ItemBlueprint::generate((*item).clone(), &full_recipe) { + Ok(bp) => Some(bp), Err(e) => { tracing::error!("{:?}", e); None @@ -73,16 +64,37 @@ impl Scheduler { } }) .collect::>(); - - if blueprints.len() < order.quantity() as usize { - anyhow::bail!( - "Cannot fullfill order {:?}, can only schedule {:?}/{:?} parts", - order.id(), - blueprints.len(), - order.quantity() + assert_eq!(order_items.len(), blueprints.len()); + + //TODO: schedule the blueprints for production taking into account + //maximum factory parallelism capacity + let bp_time: i64 = full_recipe.iter().map(|r| r.operation_time).sum(); + + //TODO: make these constants config parameteres + // + // Assume that the factory can handle 3 blueprints in parallel at most + const PARALLELISM_CAPACITY: i64 = 3; + // assume a % of the needed time is spent on logistics instead of production + const LOGISTICS_TIME_FACTOR: i64 = 25; + + // the last day that the order can be completed in order to be able to + // be delivered on time + let completion_date = order.due_date() as i64 - 1; + let bp_time = bp_time + bp_time * LOGISTICS_TIME_FACTOR / 100; + let total_time = bp_time * order.quantity() as i64; + let days_needed = total_time / (TIME_IN_DAY * PARALLELISM_CAPACITY); + if days_needed > completion_date - earliest_start { + tracing::warn!( + "Order {} cannot be completed on time, not enough capacity", + order.id() ); } + let starting_date = earliest_start.max(completion_date - days_needed); + blueprints + .iter_mut() + .for_each(|bp| bp.set_start(starting_date)); + let mut tx = pool.begin().await?; for mut bp in blueprints { bp.insert_to_db(&mut tx).await?;