from .manufacturing_oee_types import Oee from .math_round_div import RoundingMode, round_div MAX_SAFE = 2**53 - 1 MAX_PLANNED = 10**11 def _whole(name: str, value: int) -> None: if isinstance(value, bool) or not isinstance(value, int) or value > MAX_SAFE or value < -MAX_SAFE: raise ValueError("%s must be a whole number, received %r" % (name, value)) def overall_equipment_effectiveness( planned_seconds: int, downtime_seconds: int, ideal_cycle_millis: int, total_count: int, good_count: int, mode: RoundingMode, ) -> Oee: """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. """ _whole("plannedSeconds", planned_seconds) _whole("downtimeSeconds", downtime_seconds) _whole("idealCycleMillis", ideal_cycle_millis) _whole("totalCount", total_count) _whole("goodCount", good_count) if planned_seconds <= 0 or planned_seconds > MAX_PLANNED: raise ValueError("plannedSeconds must be from 1 to 10^11, received %d" % planned_seconds) if downtime_seconds < 0 or downtime_seconds > planned_seconds: raise ValueError("downtimeSeconds must be from 0 to plannedSeconds, received %d" % downtime_seconds) if ideal_cycle_millis <= 0: raise ValueError("idealCycleMillis must be greater than zero, received %d" % ideal_cycle_millis) if total_count < 0: raise ValueError("totalCount must not be negative, received %d" % total_count) if good_count < 0 or good_count > total_count: raise ValueError("goodCount must be from 0 to totalCount, received %d" % good_count) if total_count * ideal_cycle_millis * 10000 > MAX_SAFE: raise ValueError( "totalCount and idealCycleMillis are too large: total x ideal cycle x 10000 must stay within 2^53 - 1" ) run_seconds = planned_seconds - downtime_seconds if run_seconds == 0 and total_count > 0: raise ValueError("no units can be made with no run time, received totalCount %d" % total_count) availability = round_div(run_seconds * 10000, planned_seconds, mode) performance = 0 if run_seconds == 0 else round_div(ideal_cycle_millis * total_count * 10000, run_seconds * 1000, mode) quality = 0 if total_count == 0 else round_div(good_count * 10000, total_count, mode) oee = round_div(good_count * ideal_cycle_millis * 10000, planned_seconds * 1000, mode) return Oee(availability=availability, performance=performance, quality=quality, oee=oee, run_seconds=run_seconds)