Functional Weave
Code in Python

manufacturing.capacity@1.0.0

impl/python.py

3,167 bytes · the Python 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.

from typing import Dict, List, Sequence

from .manufacturing_capacity_types import CapacityLoad, RoutedLoad, WorkCentre
from .math_rational import compare_rational, divide_rational, multiply_rational, rational, rational_to_integer  ← from math.rational ^1.0.0 · built alongside by fune

MAX_SAFE = 2**53 - 1


def _is_whole(value: int) -> bool:
    return not isinstance(value, bool) and isinstance(value, int) and -MAX_SAFE <= value <= MAX_SAFE


def _minutes(what: str, value: int) -> None:
    if not _is_whole(value) or value < 0:
        raise ValueError("%s must be a whole number of minutes, not negative, received %r" % (what, value))


def work_centre_load(work_centres: Sequence[WorkCentre], loads: Sequence[RoutedLoad]) -> List[CapacityLoad]:
    """Capacity, load and overload per work centre. Capacity is kept as an
    exact fraction, so a load a fraction of a minute over it is still an
    overload.
    """
    totals: Dict[str, int] = {}
    for wc in work_centres:
        if wc.id in totals:
            raise ValueError('duplicate work centre "%s"' % (wc.id,))
        _minutes('availableMinutes of "%s"' % (wc.id,), wc.available_minutes)
        if not _is_whole(wc.efficiency_basis_points) or wc.efficiency_basis_points < 0:
            raise ValueError(
                'efficiencyBasisPoints of "%s" must be a whole number, not negative, received %r'
                % (wc.id, wc.efficiency_basis_points)
            )
        if not _is_whole(wc.utilisation_basis_points) or not 0 <= wc.utilisation_basis_points <= 10000:
            raise ValueError(
                'utilisationBasisPoints of "%s" must be a whole number from 0 to 10000, received %r'
                % (wc.id, wc.utilisation_basis_points)
            )
        totals[wc.id] = 0
    for load in loads:
        if load.work_centre not in totals:
            raise ValueError('a load names work centre "%s", which is not in the list' % (load.work_centre,))
        _minutes('a load on "%s"' % (load.work_centre,), load.minutes)
        total = totals[load.work_centre] + load.minutes
        if total > MAX_SAFE:
            raise ValueError('the load on "%s" exceeds 2^53 - 1 minutes' % (load.work_centre,))
        totals[load.work_centre] = total
    result: List[CapacityLoad] = []
    for wc in work_centres:
        capacity = multiply_rational(
            rational(wc.available_minutes, 1),
            multiply_rational(rational(wc.efficiency_basis_points, 10000), rational(wc.utilisation_basis_points, 10000)),
        )
        load_minutes = totals[wc.id]
        load = rational(load_minutes, 1)
        capacity_minutes = rational_to_integer(capacity, "down")
        result.append(
            CapacityLoad(
                work_centre=wc.id,
                capacity_minutes=capacity_minutes,
                load_minutes=load_minutes,
                spare_minutes=capacity_minutes - load_minutes,
                load_basis_points=None
                if capacity.numerator == 0
                else rational_to_integer(multiply_rational(divide_rational(load, capacity), rational(10000, 1)), "half-up"),
                overloaded=compare_rational(load, capacity) > 0,
            )
        )
    return result