Functional Weave
Code in TypeScript

manufacturing.oee@1.0.0

impl/typescript.ts

2,585 bytes · the TypeScript 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.

import { type RoundingMode, roundDiv } from "./math_round_div.ts";  ← from math.round-div ^1.0.0 · built alongside by fune
import { type Oee } from "./manufacturing_oee_types.ts";

const MAX_PLANNED = 100_000_000_000;

function whole(name: string, value: number): void {
  if (!Number.isSafeInteger(value)) {
    throw new RangeError(`${name} must be a whole number, received ${value}`);
  }
}

/**
 * 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.
 */
export function overallEquipmentEffectiveness(
  plannedSeconds: number,
  downtimeSeconds: number,
  idealCycleMillis: number,
  totalCount: number,
  goodCount: number,
  mode: RoundingMode,
): Oee {
  whole("plannedSeconds", plannedSeconds);
  whole("downtimeSeconds", downtimeSeconds);
  whole("idealCycleMillis", idealCycleMillis);
  whole("totalCount", totalCount);
  whole("goodCount", goodCount);
  if (plannedSeconds <= 0 || plannedSeconds > MAX_PLANNED) {
    throw new RangeError(`plannedSeconds must be from 1 to 10^11, received ${plannedSeconds}`);
  }
  if (downtimeSeconds < 0 || downtimeSeconds > plannedSeconds) {
    throw new RangeError(`downtimeSeconds must be from 0 to plannedSeconds, received ${downtimeSeconds}`);
  }
  if (idealCycleMillis <= 0) {
    throw new RangeError(`idealCycleMillis must be greater than zero, received ${idealCycleMillis}`);
  }
  if (totalCount < 0) {
    throw new RangeError(`totalCount must not be negative, received ${totalCount}`);
  }
  if (goodCount < 0 || goodCount > totalCount) {
    throw new RangeError(`goodCount must be from 0 to totalCount, received ${goodCount}`);
  }
  if (BigInt(totalCount) * BigInt(idealCycleMillis) * 10000n > BigInt(Number.MAX_SAFE_INTEGER)) {
    throw new RangeError("totalCount and idealCycleMillis are too large: total x ideal cycle x 10000 must stay within 2^53 - 1");
  }
  const runSeconds = plannedSeconds - downtimeSeconds;
  if (runSeconds === 0 && totalCount > 0) {
    throw new RangeError(`no units can be made with no run time, received totalCount ${totalCount}`);
  }
  const availability = roundDiv(runSeconds * 10000, plannedSeconds, mode);
  const performance = runSeconds === 0 ? 0 : roundDiv(idealCycleMillis * totalCount * 10000, runSeconds * 1000, mode);
  const quality = totalCount === 0 ? 0 : roundDiv(goodCount * 10000, totalCount, mode);
  const oee = roundDiv(goodCount * idealCycleMillis * 10000, plannedSeconds * 1000, mode);
  return { availability, performance, quality, oee, runSeconds };
}