@operato/ops-contract 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1379,17 +1379,43 @@ export interface EquipmentMotion {
1379
1379
  * → OEE=(run/planned)×quality. 세 인자가 손실 위치(셋업·기아·불량)를 분해해 드러낸다.
1380
1380
  */
1381
1381
  export interface OeeMetrics {
1382
- availability: number;
1383
- performance: number;
1384
- quality: number;
1385
- overall: number;
1382
+ /**
1383
+ * ── 세 비율은 **없을 수 있다** (2026-08-30) ──────────────────────────────
1384
+ *
1385
+ * 예전에는 근거가 없을 때 `1` 을 냈다. 잰 구간이 없는데 「가동률 100%」가 되고, 만든 것이 하나도
1386
+ * 없는데 「양품률 100%」가 된다. **없는 근거로 좋은 숫자를 만드는 것**이고, 사용자는 그것으로
1387
+ * 판단한다.
1388
+ *
1389
+ * 없으면 답하지 않는다. 무엇이 없어서인지는 `missing` 이 말한다.
1390
+ */
1391
+ availability?: number;
1392
+ performance?: number;
1393
+ quality?: number;
1394
+ overall?: number;
1395
+ /** 값이 없는 이유 — 비면 셋 다 나왔다는 뜻이다. */
1396
+ missing?: OeeMissing[];
1386
1397
  runMs: number;
1387
1398
  setupMs: number;
1388
1399
  downMs: number;
1389
- idleMs: number;
1400
+ delayMs: number;
1390
1401
  goodCount: number;
1391
1402
  scrapCount: number;
1392
1403
  }
1404
+ /**
1405
+ * OEE 값이 없는 이유.
1406
+ *
1407
+ * 「모름」과 「0」은 다르다. 계획 조업 시간을 모르면 가동률을 낼 수 없고, 그때 0 을 내면 설비가
1408
+ * 놀고 있는 것처럼 보이고 1 을 내면 완벽한 것처럼 보인다. 둘 다 거짓이다.
1409
+ */
1410
+ export type OeeMissing =
1411
+ /** 계획 조업 시간(PBT)을 모른다 — 아직 아무것도 재지 않았거나 근무 캘린더가 없다. */
1412
+ 'planned-busy-time'
1413
+ /** 품목당 표준 시간(PRI)이 선언되지 않았다 — 성능률의 분자를 만들 수 없다. */
1414
+ | 'planned-run-time-per-item'
1415
+ /** 만든 것이 없다 — 양품률을 낼 대상이 없다. */
1416
+ | 'produced-quantity'
1417
+ /** 실 생산 시간이 0 이다 — 성능률의 분모가 없다. */
1418
+ | 'actual-production-time';
1393
1419
  export interface EquipmentState extends EffectivePeriod {
1394
1420
  id: string;
1395
1421
  kind: string;
package/dist/index.d.ts CHANGED
@@ -18,3 +18,4 @@ export * from './wms-profile.ts';
18
18
  export * from './yms-profile.ts';
19
19
  export * from './canonical-record.ts';
20
20
  export * from './version.ts';
21
+ export * from './oee.ts';
package/dist/index.js CHANGED
@@ -39,3 +39,4 @@ export * from "./wms-profile.js";
39
39
  export * from "./yms-profile.js";
40
40
  export * from "./canonical-record.js";
41
41
  export * from "./version.js";
42
+ export * from "./oee.js";
package/dist/oee.d.ts ADDED
@@ -0,0 +1,54 @@
1
+ import type { OeeMetrics } from './contract.ts';
2
+ /**
3
+ * OEE 계측 카운터 — 시뮬은 틱으로, 라이브는 텔레메트리나 사건 누적기가 채운다.
4
+ *
5
+ * 이름은 ISO 22400-2 의 시간 항목을 따른다.
6
+ */
7
+ export interface OeeCounters {
8
+ /** 실 생산 시간(APT) — 실제로 만들고 있던 시간. 준비·대기·고장은 여기 들어가지 않는다. */
9
+ runMs: number;
10
+ /** 준비(AUST). */
11
+ setupMs: number;
12
+ /** 고장(ADOT). */
13
+ downMs: number;
14
+ goodCount: number;
15
+ scrapCount: number;
16
+ /** 계획정지 — 계획 조업 시간에서 뺀다(점심·예방보전·교대). */
17
+ holdMs?: number;
18
+ /** 언제부터 쟀나. 없으면 잰 구간이 없는 것이고, 그때는 가동률을 낼 수 없다. */
19
+ metricsSinceMs?: number;
20
+ /**
21
+ * **품목당 표준 시간**(PRI) — 성능률의 분자를 만든다.
22
+ *
23
+ * 없으면 성능률을 내지 않는다. 이 값이 없는데 실 가동 시간으로 대신 나누면 「얼마나 빨리
24
+ * 만들었나」가 아니라 「얼마나 쉬지 않았나」가 되고, 이름만 성능률이 된다.
25
+ */
26
+ plannedRunTimePerItemMs?: number;
27
+ }
28
+ /**
29
+ * **OEE** — ISO 22400-2 의 정의를 따른다.
30
+ *
31
+ * ```
32
+ * PBT 계획 조업 시간 = 잰 구간 − 계획정지
33
+ * APT 실 생산 시간 = runMs
34
+ * availability APT / PBT
35
+ * effectiveness PRI × PQ / APT PQ = 양품 + 불량
36
+ * quality GQ / PQ
37
+ * overall 셋의 곱
38
+ * ```
39
+ *
40
+ * ── 예전 식과 무엇이 달랐나 (2026-08-30 대조) ────────────────────────────
41
+ * 예전에는 가동률의 분자가 `PBT − 준비 − 고장` 이었다. **대기가 분자에 남아 있었다.** 표준은
42
+ * `AOET = APT + AUST + ADET + ADOT` 로 대기도 실 생산 시간에서 뺀다.
43
+ *
44
+ * 총합은 같았다(가동률 × 성능률이 양쪽 다 `runMs / PBT` 로 떨어진다). 다른 것은 두 값을 따로 볼
45
+ * 때다. 자재를 기다린 시간이 성능률로 넘어가 있어서, **자재 문제를 설비 성능 문제로 읽게 했다.**
46
+ * 공장이 손 쓰는 방법이 그 둘에서 갈린다 — 가동률이 낮으면 보전·자재를 보고, 성능률이 낮으면
47
+ * 사이클 타임을 본다.
48
+ *
49
+ * 성능률도 달랐다. 표준은 품목당 표준 시간으로 재는데 우리는 실 가동 시간의 비율을 냈다.
50
+ *
51
+ * 대조는 원문이 아니라 그 표준을 구현한 규격에서 했다(OPC Foundation MachineTool §C.2). ISO 22400-2
52
+ * 원문은 유료라 읽지 못했다 — 이 주석이 근거의 한계를 함께 말한다.
53
+ */
54
+ export declare function computeOee(c: OeeCounters, nowMs: number): OeeMetrics;
package/dist/oee.js ADDED
@@ -0,0 +1,62 @@
1
+ /**
2
+ * **OEE** — ISO 22400-2 의 정의를 따른다.
3
+ *
4
+ * ```
5
+ * PBT 계획 조업 시간 = 잰 구간 − 계획정지
6
+ * APT 실 생산 시간 = runMs
7
+ * availability APT / PBT
8
+ * effectiveness PRI × PQ / APT PQ = 양품 + 불량
9
+ * quality GQ / PQ
10
+ * overall 셋의 곱
11
+ * ```
12
+ *
13
+ * ── 예전 식과 무엇이 달랐나 (2026-08-30 대조) ────────────────────────────
14
+ * 예전에는 가동률의 분자가 `PBT − 준비 − 고장` 이었다. **대기가 분자에 남아 있었다.** 표준은
15
+ * `AOET = APT + AUST + ADET + ADOT` 로 대기도 실 생산 시간에서 뺀다.
16
+ *
17
+ * 총합은 같았다(가동률 × 성능률이 양쪽 다 `runMs / PBT` 로 떨어진다). 다른 것은 두 값을 따로 볼
18
+ * 때다. 자재를 기다린 시간이 성능률로 넘어가 있어서, **자재 문제를 설비 성능 문제로 읽게 했다.**
19
+ * 공장이 손 쓰는 방법이 그 둘에서 갈린다 — 가동률이 낮으면 보전·자재를 보고, 성능률이 낮으면
20
+ * 사이클 타임을 본다.
21
+ *
22
+ * 성능률도 달랐다. 표준은 품목당 표준 시간으로 재는데 우리는 실 가동 시간의 비율을 냈다.
23
+ *
24
+ * 대조는 원문이 아니라 그 표준을 구현한 규격에서 했다(OPC Foundation MachineTool §C.2). ISO 22400-2
25
+ * 원문은 유료라 읽지 못했다 — 이 주석이 근거의 한계를 함께 말한다.
26
+ */
27
+ export function computeOee(c, nowMs) {
28
+ const missing = [];
29
+ /* 계획 조업 시간 — 잰 구간을 모르면 없다. 0 으로 메우면 「1970년부터 재고 있었다」가 된다. */
30
+ const planned = c.metricsSinceMs == null ? 0 : Math.max(0, nowMs - c.metricsSinceMs - (c.holdMs ?? 0));
31
+ if (planned <= 0)
32
+ missing.push('planned-busy-time');
33
+ /* 대기는 남는 시간이다 — 계획 조업에서 생산·준비·고장을 뺀 것. 따로 재지 않아도 나온다. */
34
+ const delayMs = Math.max(0, planned - c.runMs - c.setupMs - c.downMs);
35
+ const availability = planned > 0 ? Math.min(1, c.runMs / planned) : undefined;
36
+ const produced = c.goodCount + c.scrapCount;
37
+ if (produced <= 0)
38
+ missing.push('produced-quantity');
39
+ const quality = produced > 0 ? c.goodCount / produced : undefined;
40
+ /* 성능률 — 표준 시간이 없으면 내지 않는다. 실 생산 시간이 0 이어도 나눌 수 없다. */
41
+ if (c.plannedRunTimePerItemMs == null)
42
+ missing.push('planned-run-time-per-item');
43
+ else if (c.runMs <= 0)
44
+ missing.push('actual-production-time');
45
+ const performance = c.plannedRunTimePerItemMs != null && c.runMs > 0
46
+ ? Math.min(1, (c.plannedRunTimePerItemMs * produced) / c.runMs)
47
+ : undefined;
48
+ const overall = availability != null && performance != null && quality != null ? availability * performance * quality : undefined;
49
+ return {
50
+ ...(availability != null ? { availability } : {}),
51
+ ...(performance != null ? { performance } : {}),
52
+ ...(quality != null ? { quality } : {}),
53
+ ...(overall != null ? { overall } : {}),
54
+ ...(missing.length ? { missing } : {}),
55
+ runMs: c.runMs,
56
+ setupMs: c.setupMs,
57
+ downMs: c.downMs,
58
+ delayMs,
59
+ goodCount: c.goodCount,
60
+ scrapCount: c.scrapCount
61
+ };
62
+ }
package/dist/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const CONTRACT_VERSION = "0.4.0";
1
+ export declare const CONTRACT_VERSION = "0.5.0";
package/dist/version.js CHANGED
@@ -11,4 +11,4 @@
11
11
  *
12
12
  * **정본은 `package.json` 이다.** 이 값이 그것과 같은지는 시험이 지킨다(§`version-matches`).
13
13
  */
14
- export const CONTRACT_VERSION = '0.4.0';
14
+ export const CONTRACT_VERSION = '0.5.0';
@@ -80,6 +80,7 @@ __export(index_exports, {
80
80
  classClosure: () => classClosure,
81
81
  classIdentifierViolation: () => classIdentifierViolation,
82
82
  commandsOf: () => commandsOf,
83
+ computeOee: () => computeOee,
83
84
  conversionFactorOf: () => conversionFactorOf,
84
85
  criterionSaysNothing: () => criterionSaysNothing,
85
86
  documentPath: () => documentPath,
@@ -3733,7 +3734,36 @@ function retiredVocabularyIn(line) {
3733
3734
  }
3734
3735
 
3735
3736
  // src/version.ts
3736
- var CONTRACT_VERSION = "0.4.0";
3737
+ var CONTRACT_VERSION = "0.5.0";
3738
+
3739
+ // src/oee.ts
3740
+ function computeOee(c, nowMs) {
3741
+ const missing = [];
3742
+ const planned = c.metricsSinceMs == null ? 0 : Math.max(0, nowMs - c.metricsSinceMs - (c.holdMs ?? 0));
3743
+ if (planned <= 0) missing.push("planned-busy-time");
3744
+ const delayMs = Math.max(0, planned - c.runMs - c.setupMs - c.downMs);
3745
+ const availability = planned > 0 ? Math.min(1, c.runMs / planned) : void 0;
3746
+ const produced = c.goodCount + c.scrapCount;
3747
+ if (produced <= 0) missing.push("produced-quantity");
3748
+ const quality = produced > 0 ? c.goodCount / produced : void 0;
3749
+ if (c.plannedRunTimePerItemMs == null) missing.push("planned-run-time-per-item");
3750
+ else if (c.runMs <= 0) missing.push("actual-production-time");
3751
+ const performance = c.plannedRunTimePerItemMs != null && c.runMs > 0 ? Math.min(1, c.plannedRunTimePerItemMs * produced / c.runMs) : void 0;
3752
+ const overall = availability != null && performance != null && quality != null ? availability * performance * quality : void 0;
3753
+ return {
3754
+ ...availability != null ? { availability } : {},
3755
+ ...performance != null ? { performance } : {},
3756
+ ...quality != null ? { quality } : {},
3757
+ ...overall != null ? { overall } : {},
3758
+ ...missing.length ? { missing } : {},
3759
+ runMs: c.runMs,
3760
+ setupMs: c.setupMs,
3761
+ downMs: c.downMs,
3762
+ delayMs,
3763
+ goodCount: c.goodCount,
3764
+ scrapCount: c.scrapCount
3765
+ };
3766
+ }
3737
3767
  // Annotate the CommonJS export names for ESM import in node:
3738
3768
  0 && (module.exports = {
3739
3769
  BIZSTEP,
@@ -3797,6 +3827,7 @@ var CONTRACT_VERSION = "0.4.0";
3797
3827
  classClosure,
3798
3828
  classIdentifierViolation,
3799
3829
  commandsOf,
3830
+ computeOee,
3800
3831
  conversionFactorOf,
3801
3832
  criterionSaysNothing,
3802
3833
  documentPath,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@operato/ops-contract",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "Operations domain contract — the standard vocabulary that producers and readers agree on (EPCIS 2.0/GS1, ISA-95, IEC 61850/ISO 50001). Types, guards, validation. No state, no engine.",
5
5
  "type": "module",
6
6
  "main": "./dist-cjs/index.cjs",