Functional Weave
Code in Rust

manufacturing.oee@1.0.0

impl/rust.rs

3,247 bytes · the Rust implementation · view raw

Imports name this capability’s declared dependencies, which fune builds next to it in your project; each one links to its page.

use super::funejson::Value;  ← the fune runtime: the JSON value the test vectors use; fune build keeps it only where a signature takes one
use super::math_round_div::round_div;  ← from math.round-div ^1.0.0 · built alongside by fune

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(),
    ))
}