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