use super::funejson::Value; use super::math_round_div::round_div; const MAX_SAFE: i128 = (1i128 << 53) - 1; const MAX_PLANNED: i64 = 100_000_000_000; /// OEE and its three factors in basis points. OEE is rounded once from the /// exact product (good count x ideal cycle / planned time), never from the /// three already-rounded factors, which is off by one surprisingly often. /// /// # Panics /// Panics on times or counts out of range (see the README), or an unknown mode. pub fn overall_equipment_effectiveness( planned_seconds: i64, downtime_seconds: i64, ideal_cycle_millis: i64, total_count: i64, good_count: i64, mode: &str, ) -> Oee { if planned_seconds <= 0 || planned_seconds > MAX_PLANNED { panic!("plannedSeconds must be from 1 to 10^11, received {}", planned_seconds); } if downtime_seconds < 0 || downtime_seconds > planned_seconds { panic!("downtimeSeconds must be from 0 to plannedSeconds, received {}", downtime_seconds); } if ideal_cycle_millis <= 0 { panic!("idealCycleMillis must be greater than zero, received {}", ideal_cycle_millis); } if total_count < 0 { panic!("totalCount must not be negative, received {}", total_count); } if good_count < 0 || good_count > total_count { panic!("goodCount must be from 0 to totalCount, received {}", good_count); } if (total_count as i128) * (ideal_cycle_millis as i128) * 10000 > MAX_SAFE { panic!("totalCount and idealCycleMillis are too large: total x ideal cycle x 10000 must stay within 2^53 - 1"); } let run_seconds = planned_seconds - downtime_seconds; if run_seconds == 0 && total_count > 0 { panic!("no units can be made with no run time, received totalCount {}", total_count); } let availability = round_div(run_seconds * 10000, planned_seconds, mode); let performance = if run_seconds == 0 { 0 } else { round_div(ideal_cycle_millis * total_count * 10000, run_seconds * 1000, mode) }; let quality = if total_count == 0 { 0 } else { round_div(good_count * 10000, total_count, mode) }; let oee = round_div(good_count * ideal_cycle_millis * 10000, planned_seconds * 1000, mode); Oee { availability, performance, quality, oee, run_seconds, } } pub fn oee_to_value(result: &Oee) -> Value { Value::obj(vec![ ("availability", Value::Int(result.availability)), ("performance", Value::Int(result.performance)), ("quality", Value::Int(result.quality)), ("oee", Value::Int(result.oee)), ("runSeconds", Value::Int(result.run_seconds)), ]) } pub fn fune_vector(args: &[Value]) -> Value { let names = ["plannedSeconds", "downtimeSeconds", "idealCycleMillis", "totalCount", "goodCount"]; for (i, name) in names.iter().enumerate() { if let Value::Float(f) = &args[i] { panic!("{} must be a whole number, received {}", name, f); } } oee_to_value(&overall_equipment_effectiveness( args[0].as_i64(), args[1].as_i64(), args[2].as_i64(), args[3].as_i64(), args[4].as_i64(), args[5].as_str(), )) }