@things-factory/station 10.1.247 → 10.1.248

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.
Files changed (177) hide show
  1. package/package.json +4 -4
  2. package/dist-client/index.d.ts +0 -11
  3. package/dist-client/index.js +0 -24
  4. package/dist-client/index.js.map +0 -1
  5. package/dist-client/station/scene/index.d.ts +0 -11
  6. package/dist-client/station/scene/index.js +0 -12
  7. package/dist-client/station/scene/index.js.map +0 -1
  8. package/dist-client/station/scene/scene-kit.d.ts +0 -9
  9. package/dist-client/station/scene/scene-kit.js +0 -10
  10. package/dist-client/station/scene/scene-kit.js.map +0 -1
  11. package/dist-client/station/scene/station-context-binding.d.ts +0 -25
  12. package/dist-client/station/scene/station-context-binding.js +0 -39
  13. package/dist-client/station/scene/station-context-binding.js.map +0 -1
  14. package/dist-client/station/scene/station-context-scene.d.ts +0 -17
  15. package/dist-client/station/scene/station-context-scene.js +0 -127
  16. package/dist-client/station/scene/station-context-scene.js.map +0 -1
  17. package/dist-client/station/scene/station-counts-scene.d.ts +0 -6
  18. package/dist-client/station/scene/station-counts-scene.js +0 -51
  19. package/dist-client/station/scene/station-counts-scene.js.map +0 -1
  20. package/dist-client/station/scene/station-field-nature.d.ts +0 -2
  21. package/dist-client/station/scene/station-field-nature.js +0 -42
  22. package/dist-client/station/scene/station-field-nature.js.map +0 -1
  23. package/dist-client/station/scene/station-field-scene.d.ts +0 -26
  24. package/dist-client/station/scene/station-field-scene.js +0 -73
  25. package/dist-client/station/scene/station-field-scene.js.map +0 -1
  26. package/dist-client/station/scene/station-icons.d.ts +0 -82
  27. package/dist-client/station/scene/station-icons.js +0 -131
  28. package/dist-client/station/scene/station-icons.js.map +0 -1
  29. package/dist-client/station/scene/station-numpad-scene.d.ts +0 -9
  30. package/dist-client/station/scene/station-numpad-scene.js +0 -144
  31. package/dist-client/station/scene/station-numpad-scene.js.map +0 -1
  32. package/dist-client/station/scene/station-scene-style.d.ts +0 -51
  33. package/dist-client/station/scene/station-scene-style.js +0 -108
  34. package/dist-client/station/scene/station-scene-style.js.map +0 -1
  35. package/dist-client/station/scene/station-scene.d.ts +0 -294
  36. package/dist-client/station/scene/station-scene.js +0 -68
  37. package/dist-client/station/scene/station-scene.js.map +0 -1
  38. package/dist-client/station/scene/station-who-scene.d.ts +0 -6
  39. package/dist-client/station/scene/station-who-scene.js +0 -48
  40. package/dist-client/station/scene/station-who-scene.js.map +0 -1
  41. package/dist-client/station/scene/station-work-panel-scene.d.ts +0 -28
  42. package/dist-client/station/scene/station-work-panel-scene.js +0 -37
  43. package/dist-client/station/scene/station-work-panel-scene.js.map +0 -1
  44. package/dist-client/station/station-board-binding.d.ts +0 -26
  45. package/dist-client/station/station-board-binding.js +0 -60
  46. package/dist-client/station/station-board-binding.js.map +0 -1
  47. package/dist-client/station/station-context.d.ts +0 -178
  48. package/dist-client/station/station-context.js +0 -30
  49. package/dist-client/station/station-context.js.map +0 -1
  50. package/dist-client/station/station-palette.d.ts +0 -34
  51. package/dist-client/station/station-palette.js +0 -119
  52. package/dist-client/station/station-palette.js.map +0 -1
  53. package/dist-client/station/station-refresh-policy.d.ts +0 -17
  54. package/dist-client/station/station-refresh-policy.js +0 -18
  55. package/dist-client/station/station-refresh-policy.js.map +0 -1
  56. package/dist-client/station/station-runtime.d.ts +0 -47
  57. package/dist-client/station/station-runtime.js +0 -47
  58. package/dist-client/station/station-runtime.js.map +0 -1
  59. package/dist-client/station/station-scene-context.d.ts +0 -68
  60. package/dist-client/station/station-scene-context.js +0 -62
  61. package/dist-client/station/station-scene-context.js.map +0 -1
  62. package/dist-client/station/station-screen-flow.d.ts +0 -16
  63. package/dist-client/station/station-screen-flow.js +0 -58
  64. package/dist-client/station/station-screen-flow.js.map +0 -1
  65. package/dist-client/station/station-tile-registry.d.ts +0 -89
  66. package/dist-client/station/station-tile-registry.js +0 -86
  67. package/dist-client/station/station-tile-registry.js.map +0 -1
  68. package/dist-client/station/station-work-panel-view.d.ts +0 -23
  69. package/dist-client/station/station-work-panel-view.js +0 -125
  70. package/dist-client/station/station-work-panel-view.js.map +0 -1
  71. package/dist-client/station/station-work-runtime.d.ts +0 -47
  72. package/dist-client/station/station-work-runtime.js +0 -161
  73. package/dist-client/station/station-work-runtime.js.map +0 -1
  74. package/dist-client/station/station-work-scenarios.d.ts +0 -41
  75. package/dist-client/station/station-work-scenarios.js +0 -59
  76. package/dist-client/station/station-work-scenarios.js.map +0 -1
  77. package/dist-client/station/task-counts.d.ts +0 -32
  78. package/dist-client/station/task-counts.js +0 -26
  79. package/dist-client/station/task-counts.js.map +0 -1
  80. package/dist-client/tsconfig.tsbuildinfo +0 -1
  81. package/dist-server/activities/activity-station-screen-control-withdrawal.d.ts +0 -62
  82. package/dist-server/activities/activity-station-screen-control-withdrawal.js +0 -143
  83. package/dist-server/activities/activity-station-screen-control-withdrawal.js.map +0 -1
  84. package/dist-server/activities/activity-station-screen-release.d.ts +0 -81
  85. package/dist-server/activities/activity-station-screen-release.js +0 -147
  86. package/dist-server/activities/activity-station-screen-release.js.map +0 -1
  87. package/dist-server/activities/index.d.ts +0 -49
  88. package/dist-server/activities/index.js +0 -21
  89. package/dist-server/activities/index.js.map +0 -1
  90. package/dist-server/index.d.ts +0 -3
  91. package/dist-server/index.js +0 -8
  92. package/dist-server/index.js.map +0 -1
  93. package/dist-server/service/index.d.ts +0 -22
  94. package/dist-server/service/index.js +0 -40
  95. package/dist-server/service/index.js.map +0 -1
  96. package/dist-server/service/station/index.d.ts +0 -13
  97. package/dist-server/service/station/index.js +0 -25
  98. package/dist-server/service/station/index.js.map +0 -1
  99. package/dist-server/service/station/screen-model-digest.d.ts +0 -60
  100. package/dist-server/service/station/screen-model-digest.js +0 -97
  101. package/dist-server/service/station/screen-model-digest.js.map +0 -1
  102. package/dist-server/service/station/staged-material.d.ts +0 -62
  103. package/dist-server/service/station/staged-material.js +0 -44
  104. package/dist-server/service/station/staged-material.js.map +0 -1
  105. package/dist-server/service/station/station-argument-sources.d.ts +0 -31
  106. package/dist-server/service/station/station-argument-sources.js +0 -66
  107. package/dist-server/service/station/station-argument-sources.js.map +0 -1
  108. package/dist-server/service/station/station-assignment-choice.d.ts +0 -81
  109. package/dist-server/service/station/station-assignment-choice.js +0 -105
  110. package/dist-server/service/station/station-assignment-choice.js.map +0 -1
  111. package/dist-server/service/station/station-assignment-resolver.d.ts +0 -104
  112. package/dist-server/service/station/station-assignment-resolver.js +0 -426
  113. package/dist-server/service/station/station-assignment-resolver.js.map +0 -1
  114. package/dist-server/service/station/station-assignment-service.d.ts +0 -33
  115. package/dist-server/service/station/station-assignment-service.js +0 -64
  116. package/dist-server/service/station/station-assignment-service.js.map +0 -1
  117. package/dist-server/service/station/station-axes.d.ts +0 -9
  118. package/dist-server/service/station/station-axes.js +0 -18
  119. package/dist-server/service/station/station-axes.js.map +0 -1
  120. package/dist-server/service/station/station-flow.d.ts +0 -158
  121. package/dist-server/service/station/station-flow.js +0 -257
  122. package/dist-server/service/station/station-flow.js.map +0 -1
  123. package/dist-server/service/station/station-generate.d.ts +0 -83
  124. package/dist-server/service/station/station-generate.js +0 -181
  125. package/dist-server/service/station/station-generate.js.map +0 -1
  126. package/dist-server/service/station/station-product.d.ts +0 -113
  127. package/dist-server/service/station/station-product.js +0 -124
  128. package/dist-server/service/station/station-product.js.map +0 -1
  129. package/dist-server/service/station/station-runtime-binding.d.ts +0 -6
  130. package/dist-server/service/station/station-runtime-binding.js +0 -46
  131. package/dist-server/service/station/station-runtime-binding.js.map +0 -1
  132. package/dist-server/service/station/station-screen-assignment.d.ts +0 -94
  133. package/dist-server/service/station/station-screen-assignment.js +0 -154
  134. package/dist-server/service/station/station-screen-assignment.js.map +0 -1
  135. package/dist-server/service/station/station-screen-control-withdrawal-resolver.d.ts +0 -37
  136. package/dist-server/service/station/station-screen-control-withdrawal-resolver.js +0 -130
  137. package/dist-server/service/station/station-screen-control-withdrawal-resolver.js.map +0 -1
  138. package/dist-server/service/station/station-screen-control-withdrawal.d.ts +0 -104
  139. package/dist-server/service/station/station-screen-control-withdrawal.js +0 -128
  140. package/dist-server/service/station/station-screen-control-withdrawal.js.map +0 -1
  141. package/dist-server/service/station/station-screen-draft-resolver.d.ts +0 -47
  142. package/dist-server/service/station/station-screen-draft-resolver.js +0 -155
  143. package/dist-server/service/station/station-screen-draft-resolver.js.map +0 -1
  144. package/dist-server/service/station/station-screen-layout.d.ts +0 -121
  145. package/dist-server/service/station/station-screen-layout.js +0 -202
  146. package/dist-server/service/station/station-screen-layout.js.map +0 -1
  147. package/dist-server/service/station/station-screen-list-resolver.d.ts +0 -69
  148. package/dist-server/service/station/station-screen-list-resolver.js +0 -215
  149. package/dist-server/service/station/station-screen-list-resolver.js.map +0 -1
  150. package/dist-server/service/station/station-screen-publish-subscriber.d.ts +0 -22
  151. package/dist-server/service/station/station-screen-publish-subscriber.js +0 -90
  152. package/dist-server/service/station/station-screen-publish-subscriber.js.map +0 -1
  153. package/dist-server/service/station/station-screen-release-gate.d.ts +0 -66
  154. package/dist-server/service/station/station-screen-release-gate.js +0 -67
  155. package/dist-server/service/station/station-screen-release-gate.js.map +0 -1
  156. package/dist-server/service/station/station-screen-release-record.d.ts +0 -62
  157. package/dist-server/service/station/station-screen-release-record.js +0 -139
  158. package/dist-server/service/station/station-screen-release-record.js.map +0 -1
  159. package/dist-server/service/station/station-screen-release-signature.d.ts +0 -23
  160. package/dist-server/service/station/station-screen-release-signature.js +0 -32
  161. package/dist-server/service/station/station-screen-release-signature.js.map +0 -1
  162. package/dist-server/service/station/station-screen-scorecard.d.ts +0 -15
  163. package/dist-server/service/station/station-screen-scorecard.js +0 -35
  164. package/dist-server/service/station/station-screen-scorecard.js.map +0 -1
  165. package/dist-server/service/station/station-screen-spec-resolver.d.ts +0 -36
  166. package/dist-server/service/station/station-screen-spec-resolver.js +0 -202
  167. package/dist-server/service/station/station-screen-spec-resolver.js.map +0 -1
  168. package/dist-server/service/station/station-screen-spec.d.ts +0 -104
  169. package/dist-server/service/station/station-screen-spec.js +0 -114
  170. package/dist-server/service/station/station-screen-spec.js.map +0 -1
  171. package/dist-server/service/station/station-screen-thumbnail.d.ts +0 -11
  172. package/dist-server/service/station/station-screen-thumbnail.js +0 -36
  173. package/dist-server/service/station/station-screen-thumbnail.js.map +0 -1
  174. package/dist-server/service/station/station-service-resolver.d.ts +0 -36
  175. package/dist-server/service/station/station-service-resolver.js +0 -113
  176. package/dist-server/service/station/station-service-resolver.js.map +0 -1
  177. package/dist-server/tsconfig.tsbuildinfo +0 -1
@@ -1,83 +0,0 @@
1
- import type { StationFlow, StationServiceField } from './station-flow.js';
2
- /**
3
- * A station screen from what the factory already declares — **rules, not inference.**
4
- *
5
- * ═══════════════════════════════════════════════════════════════════════════
6
- * ## Why a rule generator comes before an AI one
7
- *
8
- * Filling in a service's required fields is **arithmetic, not judgement**. The equipment is the
9
- * station's equipment; the operation is the operation; `status` is what the button means. There is
10
- * exactly one right answer and it is derivable, so a model that guesses it can only be right by
11
- * accident and wrong without saying so.
12
- *
13
- * What is left for a sentence in plain language — «검사도 같이 하는 자리다» — is which *services*
14
- * belong here. That is the part worth asking a person, or eventually a model, and it is a small
15
- * closed choice rather than an open one.
16
- *
17
- * ## What it leaves out is part of the output
18
- *
19
- * A generator that quietly omits the inspection step leaves the author thinking they were given
20
- * everything. They add it by hand, and it goes to the floor as a box that never fills, because the
21
- * equipment has no test specification behind it. So every omission is returned with its reason.
22
- *
23
- * ## It closes the loop it opens
24
- *
25
- * `goodCount` has no automatic source — a person types it. The generator is the one creating the
26
- * number pad, so it is also the one that can point the argument at it. A generator that produced
27
- * the pad and left the wiring undone would hand the author the exact defect this module was built
28
- * to warn about.
29
- * ═══════════════════════════════════════════════════════════════════════════
30
- */
31
- /** What the factory says about this station. Gathered by the resolver; this module reads only. */
32
- export interface StationFacts {
33
- equipmentName: string;
34
- /** Where it stands. Drawn so an operator can tell one bench from the next. */
35
- locationName?: string;
36
- /** The operation this bench runs, when the assignment names one. */
37
- operationKey?: string;
38
- /** ISO 8601, e.g. `PT392S`. Decides how often the screen re-reads. */
39
- operationDuration?: string;
40
- /** Test specifications the equipment carries. Empty means no inspection step. */
41
- testSpecifications: string[];
42
- /** Stop reasons this factory declares. Empty means a pause button with no reason to give. */
43
- stopReasons: string[];
44
- /** Whether any job order names this operation. Decides whether order fields are worth drawing. */
45
- hasJobOrders: boolean;
46
- /** Every service a step could call, with its fields — the schema's own list. */
47
- services: {
48
- name: string;
49
- fields: StationServiceField[];
50
- }[];
51
- /**
52
- * The axes this product declared beyond the machine's core (`StationProduct.axes`).
53
- *
54
- * The generator draws a product's axis only when the product declared it. Drawn anyway, the flow it built
55
- * would be refused by the authoring check the moment it is saved — the generator's own output failing its gate.
56
- */
57
- axes?: readonly string[];
58
- }
59
- /** Something the generator did not put in, and why. The screen says it in the reader's language. */
60
- export interface StationOmission {
61
- code: string;
62
- params?: Record<string, string>;
63
- /** The same thing in Korean, for logs and the scenario tool. */
64
- says: string;
65
- }
66
- export interface StationGenerated {
67
- flow: StationFlow;
68
- omitted: StationOmission[];
69
- }
70
- export declare function generateFlow(facts: StationFacts): StationGenerated;
71
- /**
72
- * How often to re-read, from how long the work takes.
73
- *
74
- * ── Why not one number for everybody ─────────────────────────────────────
75
- * A 30-second cut and a 392-second assembly are different screens. Reading every 5 seconds at the
76
- * assembly bench is forty pointless round trips per piece across a plant of twelve benches; reading
77
- * every 30 at the cutter means the count is a whole piece behind for most of the cycle.
78
- *
79
- * Roughly forty reads across one cycle, held between 5 and 60 seconds. The bounds matter more than
80
- * the ratio: below 5 the screen is a load generator, above 60 it looks frozen to somebody standing
81
- * in front of it.
82
- */
83
- export declare function refreshFor(duration?: string): number;
@@ -1,181 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.generateFlow = generateFlow;
4
- exports.refreshFor = refreshFor;
5
- const station_argument_sources_js_1 = require("./station-argument-sources.js");
6
- const GOOD = 'good';
7
- const SCRAP = 'scrap';
8
- function generateFlow(facts) {
9
- const steps = [];
10
- const omitted = [];
11
- /*
12
- * Who is at the bench, first and always.
13
- *
14
- * Without it every later fact is unattributed — the counts belong to nobody, and a report filed
15
- * that way cannot answer the one question anybody asks it later.
16
- */
17
- steps.push({ id: 'who', kind: 'scan', label: '작업자' });
18
- /* Where am I, and on what. Only axes the facts actually support. */
19
- const reads = ['station.equipment'];
20
- if (facts.locationName) {
21
- reads.push('location.name');
22
- }
23
- if (facts.hasJobOrders) {
24
- reads.push('order.name');
25
- /* What is being made, when the product has a word for it. */
26
- if (facts.axes?.includes('materialDefinition.name'))
27
- reads.push('materialDefinition.name');
28
- }
29
- else {
30
- omitted.push({
31
- code: 'text.omitted-order-fields',
32
- says: '작업지시 칸을 넣지 않았습니다 — 이 공정에 걸린 작업지시가 아직 없습니다'
33
- });
34
- }
35
- steps.push({ id: 'here', kind: 'show', label: '지금 이 자리', reads });
36
- /* The two counts. Both are `notable` on a production report, and neither has an automatic source. */
37
- steps.push({ id: GOOD, kind: 'enter', label: '양품 수' });
38
- steps.push({ id: SCRAP, kind: 'enter', label: '불량 수' });
39
- const report = facts.services.find(one => one.name === 'reportProduction');
40
- steps.push({ id: 'start', kind: 'run', label: '시작', action: 'start' });
41
- if (report) {
42
- steps.push({
43
- id: 'finish',
44
- action: 'finish',
45
- kind: 'run',
46
- label: '완료',
47
- service: report,
48
- arguments: wire(report.fields, { goodCount: GOOD, nonconformingCount: SCRAP })
49
- });
50
- /*
51
- * `materialActual` is left alone on purpose. What went in and how much is a recipe's business,
52
- * and guessing it from an equipment declaration would put a box on the screen that an operator
53
- * has to fill from memory.
54
- */
55
- if (report.fields.some(field => field.name === 'materialActual')) {
56
- omitted.push({
57
- code: 'text.omitted-material-actual',
58
- says: '자재 실적을 넣지 않았습니다 — 무엇이 얼마나 들어갔는지는 레시피가 정하는 것이라 설비 선언에서 끌어올 수 없습니다'
59
- });
60
- }
61
- }
62
- else {
63
- /* No production report service at all is a broken catalogue, not an ordinary omission. */
64
- omitted.push({
65
- code: 'text.omitted-no-report-service',
66
- says: '완료 단추가 아무것도 기록하지 않습니다 — reportProduction 을 카탈로그에서 못 찾았습니다'
67
- });
68
- steps.push({ id: 'finish', kind: 'run', label: '완료', action: 'finish' });
69
- }
70
- /* Stopping, and saying why. A pause button with no reason to give records nothing useful. */
71
- const stopping = facts.services.find(one => one.name === 'recordEquipmentStatePeriod');
72
- if (stopping && facts.stopReasons.length) {
73
- steps.push({
74
- id: 'pause',
75
- action: 'pause',
76
- kind: 'run',
77
- label: '일시정지',
78
- service: stopping,
79
- arguments: wire(stopping.fields, {})
80
- });
81
- }
82
- else if (!facts.stopReasons.length) {
83
- omitted.push({
84
- code: 'text.omitted-no-stop-reasons',
85
- says: '일시정지 단추를 넣지 않았습니다 — 이 공장에 정지 사유가 하나도 선언되어 있지 않습니다'
86
- });
87
- }
88
- /*
89
- * Inspection, and **only when there is one answer to which specification.**
90
- *
91
- * `specId` is required and there is no context axis for it — a bench does not carry "the
92
- * current test spec" the way it carries its own name. It comes from the equipment declaration,
93
- * which is known here and nowhere later, so it is written down as a fixed value.
94
- *
95
- * With two or more declared, no rule can pick. Generating the step anyway would hand over a
96
- * button that fails when pressed; generating it without `specId` would hand over the exact
97
- * defect this module exists to warn about. So it is left out and said out loud.
98
- *
99
- * This is why the generator is run end to end and not only unit tested: the first fixture here
100
- * had no `specId` in it, and every test was green while the real thing produced a broken step.
101
- */
102
- const inspection = facts.services.find(one => one.name === 'recordInspectionResult');
103
- const specs = facts.testSpecifications;
104
- if (!specs.length) {
105
- omitted.push({
106
- code: 'text.omitted-no-test-spec',
107
- params: { equipment: facts.equipmentName },
108
- says: `검사 단계를 넣지 않았습니다 — ${facts.equipmentName} 에 검사 명세가 붙어 있지 않습니다`
109
- });
110
- }
111
- else if (specs.length > 1) {
112
- omitted.push({
113
- code: 'text.omitted-many-test-specs',
114
- params: { equipment: facts.equipmentName, specs: specs.join(' · ') },
115
- says: `검사 단계를 넣지 않았습니다 — ${facts.equipmentName} 에 검사 명세가 ${specs.length}개(${specs.join(' · ')})라 어느 것인지 규칙으로 정할 수 없습니다`
116
- });
117
- }
118
- else if (inspection) {
119
- /* 검사 기록 mutation을 보내는 런타임은 아직 없다. 실적으로 대신 기록하지 않는다. */
120
- omitted.push({
121
- code: 'text.omitted-no-inspection-runtime',
122
- says: '검사 단계를 넣지 않았습니다 — 검사 기록 실행 연결이 아직 구현되지 않았습니다'
123
- });
124
- }
125
- /* Not drawn. How often the screen re-reads — computed from how long the work takes. */
126
- steps.push({ id: 'settings', kind: 'settings', refreshSeconds: refreshFor(facts.operationDuration) });
127
- return { flow: { steps }, omitted };
128
- }
129
- /**
130
- * Fill in every argument that has one right answer, and point the rest at the steps we just made.
131
- *
132
- * A field with no suggestion and no step to take it from is left unset **on purpose** — that is
133
- * exactly the state `checkFlow` names, and inventing a value here would silence a warning by
134
- * making it untrue rather than by fixing it.
135
- */
136
- function wire(fields, fromSteps) {
137
- const wired = {};
138
- for (const field of fields) {
139
- const entry = fromSteps[field.name];
140
- if (entry) {
141
- wired[field.name] = { from: 'input', stepId: entry };
142
- continue;
143
- }
144
- const suggested = (0, station_argument_sources_js_1.suggestSource)(field.name);
145
- if (suggested) {
146
- wired[field.name] = suggested;
147
- }
148
- }
149
- return wired;
150
- }
151
- /**
152
- * How often to re-read, from how long the work takes.
153
- *
154
- * ── Why not one number for everybody ─────────────────────────────────────
155
- * A 30-second cut and a 392-second assembly are different screens. Reading every 5 seconds at the
156
- * assembly bench is forty pointless round trips per piece across a plant of twelve benches; reading
157
- * every 30 at the cutter means the count is a whole piece behind for most of the cycle.
158
- *
159
- * Roughly forty reads across one cycle, held between 5 and 60 seconds. The bounds matter more than
160
- * the ratio: below 5 the screen is a load generator, above 60 it looks frozen to somebody standing
161
- * in front of it.
162
- */
163
- function refreshFor(duration) {
164
- const seconds = secondsOf(duration);
165
- if (!seconds) {
166
- return 10;
167
- }
168
- return Math.min(60, Math.max(5, Math.round(seconds / 40)));
169
- }
170
- /** `PT392S` · `PT7M30S` · `PT1H` → seconds. Anything else → nothing, and the caller uses its default. */
171
- function secondsOf(duration) {
172
- if (!duration) {
173
- return undefined;
174
- }
175
- const said = /^PT(?:(\d+(?:\.\d+)?)H)?(?:(\d+(?:\.\d+)?)M)?(?:(\d+(?:\.\d+)?)S)?$/.exec(duration.trim().toUpperCase());
176
- if (!said || (!said[1] && !said[2] && !said[3])) {
177
- return undefined;
178
- }
179
- return Number(said[1] ?? 0) * 3600 + Number(said[2] ?? 0) * 60 + Number(said[3] ?? 0);
180
- }
181
- //# sourceMappingURL=station-generate.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"station-generate.js","sourceRoot":"","sources":["../../../server/service/station/station-generate.ts"],"names":[],"mappings":"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","sourcesContent":["import { suggestSource } from './station-argument-sources.js'\nimport type { StationArgumentSource, StationFlow, StationServiceField, StationStep } from './station-flow.js'\n\n/**\n * A station screen from what the factory already declares — **rules, not inference.**\n *\n * ═══════════════════════════════════════════════════════════════════════════\n * ## Why a rule generator comes before an AI one\n *\n * Filling in a service's required fields is **arithmetic, not judgement**. The equipment is the\n * station's equipment; the operation is the operation; `status` is what the button means. There is\n * exactly one right answer and it is derivable, so a model that guesses it can only be right by\n * accident and wrong without saying so.\n *\n * What is left for a sentence in plain language — «검사도 같이 하는 자리다» — is which *services*\n * belong here. That is the part worth asking a person, or eventually a model, and it is a small\n * closed choice rather than an open one.\n *\n * ## What it leaves out is part of the output\n *\n * A generator that quietly omits the inspection step leaves the author thinking they were given\n * everything. They add it by hand, and it goes to the floor as a box that never fills, because the\n * equipment has no test specification behind it. So every omission is returned with its reason.\n *\n * ## It closes the loop it opens\n *\n * `goodCount` has no automatic source — a person types it. The generator is the one creating the\n * number pad, so it is also the one that can point the argument at it. A generator that produced\n * the pad and left the wiring undone would hand the author the exact defect this module was built\n * to warn about.\n * ═══════════════════════════════════════════════════════════════════════════\n */\n\n/** What the factory says about this station. Gathered by the resolver; this module reads only. */\nexport interface StationFacts {\n equipmentName: string\n /** Where it stands. Drawn so an operator can tell one bench from the next. */\n locationName?: string\n /** The operation this bench runs, when the assignment names one. */\n operationKey?: string\n /** ISO 8601, e.g. `PT392S`. Decides how often the screen re-reads. */\n operationDuration?: string\n /** Test specifications the equipment carries. Empty means no inspection step. */\n testSpecifications: string[]\n /** Stop reasons this factory declares. Empty means a pause button with no reason to give. */\n stopReasons: string[]\n /** Whether any job order names this operation. Decides whether order fields are worth drawing. */\n hasJobOrders: boolean\n /** Every service a step could call, with its fields — the schema's own list. */\n services: { name: string; fields: StationServiceField[] }[]\n /**\n * The axes this product declared beyond the machine's core (`StationProduct.axes`).\n *\n * The generator draws a product's axis only when the product declared it. Drawn anyway, the flow it built\n * would be refused by the authoring check the moment it is saved — the generator's own output failing its gate.\n */\n axes?: readonly string[]\n}\n\n/** Something the generator did not put in, and why. The screen says it in the reader's language. */\nexport interface StationOmission {\n code: string\n params?: Record<string, string>\n /** The same thing in Korean, for logs and the scenario tool. */\n says: string\n}\n\nexport interface StationGenerated {\n flow: StationFlow\n omitted: StationOmission[]\n}\n\nconst GOOD = 'good'\nconst SCRAP = 'scrap'\n\nexport function generateFlow(facts: StationFacts): StationGenerated {\n const steps: StationStep[] = []\n const omitted: StationOmission[] = []\n\n /*\n * Who is at the bench, first and always.\n *\n * Without it every later fact is unattributed — the counts belong to nobody, and a report filed\n * that way cannot answer the one question anybody asks it later.\n */\n steps.push({ id: 'who', kind: 'scan', label: '작업자' })\n\n /* Where am I, and on what. Only axes the facts actually support. */\n const reads = ['station.equipment']\n\n if (facts.locationName) {\n reads.push('location.name')\n }\n\n if (facts.hasJobOrders) {\n reads.push('order.name')\n\n /* What is being made, when the product has a word for it. */\n if (facts.axes?.includes('materialDefinition.name')) reads.push('materialDefinition.name')\n } else {\n omitted.push({\n code: 'text.omitted-order-fields',\n says: '작업지시 칸을 넣지 않았습니다 — 이 공정에 걸린 작업지시가 아직 없습니다'\n })\n }\n\n steps.push({ id: 'here', kind: 'show', label: '지금 이 자리', reads })\n\n /* The two counts. Both are `notable` on a production report, and neither has an automatic source. */\n steps.push({ id: GOOD, kind: 'enter', label: '양품 수' })\n steps.push({ id: SCRAP, kind: 'enter', label: '불량 수' })\n\n const report = facts.services.find(one => one.name === 'reportProduction')\n\n steps.push({ id: 'start', kind: 'run', label: '시작', action: 'start' })\n\n if (report) {\n steps.push({\n id: 'finish',\n action: 'finish',\n kind: 'run',\n label: '완료',\n service: report,\n arguments: wire(report.fields, { goodCount: GOOD, nonconformingCount: SCRAP })\n })\n\n /*\n * `materialActual` is left alone on purpose. What went in and how much is a recipe's business,\n * and guessing it from an equipment declaration would put a box on the screen that an operator\n * has to fill from memory.\n */\n if (report.fields.some(field => field.name === 'materialActual')) {\n omitted.push({\n code: 'text.omitted-material-actual',\n says: '자재 실적을 넣지 않았습니다 — 무엇이 얼마나 들어갔는지는 레시피가 정하는 것이라 설비 선언에서 끌어올 수 없습니다'\n })\n }\n } else {\n /* No production report service at all is a broken catalogue, not an ordinary omission. */\n omitted.push({\n code: 'text.omitted-no-report-service',\n says: '완료 단추가 아무것도 기록하지 않습니다 — reportProduction 을 카탈로그에서 못 찾았습니다'\n })\n\n steps.push({ id: 'finish', kind: 'run', label: '완료', action: 'finish' })\n }\n\n /* Stopping, and saying why. A pause button with no reason to give records nothing useful. */\n const stopping = facts.services.find(one => one.name === 'recordEquipmentStatePeriod')\n\n if (stopping && facts.stopReasons.length) {\n steps.push({\n id: 'pause',\n action: 'pause',\n kind: 'run',\n label: '일시정지',\n service: stopping,\n arguments: wire(stopping.fields, {})\n })\n } else if (!facts.stopReasons.length) {\n omitted.push({\n code: 'text.omitted-no-stop-reasons',\n says: '일시정지 단추를 넣지 않았습니다 — 이 공장에 정지 사유가 하나도 선언되어 있지 않습니다'\n })\n }\n\n /*\n * Inspection, and **only when there is one answer to which specification.**\n *\n * `specId` is required and there is no context axis for it — a bench does not carry \"the\n * current test spec\" the way it carries its own name. It comes from the equipment declaration,\n * which is known here and nowhere later, so it is written down as a fixed value.\n *\n * With two or more declared, no rule can pick. Generating the step anyway would hand over a\n * button that fails when pressed; generating it without `specId` would hand over the exact\n * defect this module exists to warn about. So it is left out and said out loud.\n *\n * This is why the generator is run end to end and not only unit tested: the first fixture here\n * had no `specId` in it, and every test was green while the real thing produced a broken step.\n */\n const inspection = facts.services.find(one => one.name === 'recordInspectionResult')\n const specs = facts.testSpecifications\n\n if (!specs.length) {\n omitted.push({\n code: 'text.omitted-no-test-spec',\n params: { equipment: facts.equipmentName },\n says: `검사 단계를 넣지 않았습니다 — ${facts.equipmentName} 에 검사 명세가 붙어 있지 않습니다`\n })\n } else if (specs.length > 1) {\n omitted.push({\n code: 'text.omitted-many-test-specs',\n params: { equipment: facts.equipmentName, specs: specs.join(' · ') },\n says: `검사 단계를 넣지 않았습니다 — ${facts.equipmentName} 에 검사 명세가 ${specs.length}개(${specs.join(' · ')})라 어느 것인지 규칙으로 정할 수 없습니다`\n })\n } else if (inspection) {\n /* 검사 기록 mutation을 보내는 런타임은 아직 없다. 실적으로 대신 기록하지 않는다. */\n omitted.push({\n code: 'text.omitted-no-inspection-runtime',\n says: '검사 단계를 넣지 않았습니다 — 검사 기록 실행 연결이 아직 구현되지 않았습니다'\n })\n }\n\n /* Not drawn. How often the screen re-reads — computed from how long the work takes. */\n steps.push({ id: 'settings', kind: 'settings', refreshSeconds: refreshFor(facts.operationDuration) })\n\n return { flow: { steps }, omitted }\n}\n\n/**\n * Fill in every argument that has one right answer, and point the rest at the steps we just made.\n *\n * A field with no suggestion and no step to take it from is left unset **on purpose** — that is\n * exactly the state `checkFlow` names, and inventing a value here would silence a warning by\n * making it untrue rather than by fixing it.\n */\nfunction wire(fields: StationServiceField[], fromSteps: Record<string, string>): Record<string, StationArgumentSource> {\n const wired: Record<string, StationArgumentSource> = {}\n\n for (const field of fields) {\n const entry = fromSteps[field.name]\n\n if (entry) {\n wired[field.name] = { from: 'input', stepId: entry }\n continue\n }\n\n const suggested = suggestSource(field.name)\n\n if (suggested) {\n wired[field.name] = suggested\n }\n }\n\n return wired\n}\n\n/**\n * How often to re-read, from how long the work takes.\n *\n * ── Why not one number for everybody ─────────────────────────────────────\n * A 30-second cut and a 392-second assembly are different screens. Reading every 5 seconds at the\n * assembly bench is forty pointless round trips per piece across a plant of twelve benches; reading\n * every 30 at the cutter means the count is a whole piece behind for most of the cycle.\n *\n * Roughly forty reads across one cycle, held between 5 and 60 seconds. The bounds matter more than\n * the ratio: below 5 the screen is a load generator, above 60 it looks frozen to somebody standing\n * in front of it.\n */\nexport function refreshFor(duration?: string): number {\n const seconds = secondsOf(duration)\n\n if (!seconds) {\n return 10\n }\n\n return Math.min(60, Math.max(5, Math.round(seconds / 40)))\n}\n\n/** `PT392S` · `PT7M30S` · `PT1H` → seconds. Anything else → nothing, and the caller uses its default. */\nfunction secondsOf(duration?: string): number | undefined {\n if (!duration) {\n return undefined\n }\n\n const said = /^PT(?:(\\d+(?:\\.\\d+)?)H)?(?:(\\d+(?:\\.\\d+)?)M)?(?:(\\d+(?:\\.\\d+)?)S)?$/.exec(duration.trim().toUpperCase())\n\n if (!said || (!said[1] && !said[2] && !said[3])) {\n return undefined\n }\n\n return Number(said[1] ?? 0) * 3600 + Number(said[2] ?? 0) * 60 + Number(said[3] ?? 0)\n}\n"]}
@@ -1,113 +0,0 @@
1
- import type { EntityManager } from 'typeorm';
2
- import type { Domain } from '@things-factory/shell';
3
- import type { StationAssignmentSituation } from './station-assignment-choice.js';
4
- import type { StationServiceField } from './station-flow.js';
5
- /**
6
- * What a product has to say before the station machinery will run for it (ADR-0083 decision 2).
7
- *
8
- * ═══════════════════════════════════════════════════════════════════════════
9
- * ## Why this file exists
10
- *
11
- * The station machinery is going to `@things-factory/station`, where operato-warehouse, and later
12
- * a yard and an energy product, will use the same authoring, the same flow model and the same
13
- * release approval. Nothing of that is about a factory. What *is* about a factory is small: which
14
- * values a screen may read, which mutations a step may call, who signs a release, and where those
15
- * values actually come from.
16
- *
17
- * This is that boundary, drawn **before** anything moves. Splitting while moving would leave the
18
- * first failure unattributable — was it the split or the move? — which is the order ADR-0078 used
19
- * when it lifted the outbox judgement out one layer at a time.
20
- *
21
- * ## Declared, never defaulted
22
- *
23
- * A product that has not declared gets an error naming what is missing, not an empty list. A
24
- * default here would mean a warehouse screen quietly offering a factory's mutations, or offering
25
- * none and reading as "this product has no services" — the silent empty this codebase keeps
26
- * finding. The same reason `registerWorkDoors` has no default door list.
27
- *
28
- * ## The assignment columns are the standard's general words (ADR-0083 decision 5)
29
- *
30
- * They used to be a factory's: `productionLineName` became `workCenterName` because the contract
31
- * already says `WorkCenter` is the general term over `ProcessCell` · `ProductionLine` ·
32
- * `ProductionUnit` · `StorageZone`. A warehouse zone and a yard block have somewhere to stand now.
33
- * `routeKey` and `operationKey` stay — a warehouse has a picking route too. No union of both
34
- * spellings: that would grow four products every time an axis is added.
35
- * ═══════════════════════════════════════════════════════════════════════════
36
- */
37
- /** Something a station step can call. The product owns the list; the machinery reads the shape. */
38
- export interface StationServiceDeclaration {
39
- /** The mutation name, exactly as the schema spells it. */
40
- name: string;
41
- /** What an author is doing when they pick this — their words, not the mutation's. */
42
- describes: string;
43
- /**
44
- * The `@InputType` whose fields this call takes — **when it takes one.**
45
- *
46
- * Absent, the machine reads the mutation's own arguments by `name` (ADR-0083 ruling ②): a warehouse's
47
- * floor doors take a task name and a scanned place, not one input object, and wrapping them in one would
48
- * give the same door two shapes.
49
- */
50
- input?: Function;
51
- /**
52
- * Nullable fields that still must not be left empty.
53
- *
54
- * Absent means "nobody wrote it down", not zero — and the two are different facts on a
55
- * production report.
56
- */
57
- notable?: string[];
58
- }
59
- /**
60
- * The facts this product knows about one station, in the words assignment selects on.
61
- *
62
- * The machinery reads what the platform owns (the equipment and its classes) itself. This answers
63
- * only what the product's own domain knows — for a factory, which line it stands on and what the
64
- * order running there is made of.
65
- */
66
- export type StationGroundReader = (manager: EntityManager, domain: Domain, stationName: string, at?: {
67
- operationKey?: string;
68
- operatorName?: string;
69
- jobOrderName?: string;
70
- }) => Promise<Pick<StationAssignmentSituation, 'workCenterName' | 'routeKey' | 'materialClassIds' | 'personnelClassIds'>>;
71
- export interface StationProduct {
72
- /** For the message when something is missing — the product says its own name. */
73
- product: string;
74
- /**
75
- * The axes this product's screens read beyond the machine's own (ADR-0083 ruling ①) — `group.name`,
76
- * e.g. `unit.epc` · `task.kind`. The closed set a screen is checked against is the machine's list and
77
- * this. Absent means the product reads only the machine's.
78
- */
79
- axes?: readonly string[];
80
- services: StationServiceDeclaration[];
81
- readGround: StationGroundReader;
82
- }
83
- /**
84
- * Called once at boot, from the product's own declaration file.
85
- *
86
- * A missing slot is caught here rather than when it is first called: the server runs with
87
- * `strict: false`, so one `as any` in a declaration file gets past the type (ADR-0083 decision 2,
88
- * "declarations are required and the module has no default"). Where a withdrawal's history goes is no
89
- * longer a product slot — it is the shared change log (ADR-0124).
90
- */
91
- export declare function declareStationProduct(product: StationProduct): void;
92
- /**
93
- * Every axis a screen of this product may read — the machine's and the product's (ADR-0083 ruling ①).
94
- * The authoring surface lists these; `checkFlow` refuses anything else.
95
- */
96
- export declare function stationAxesOf(product: Pick<StationProduct, 'axes'>): string[];
97
- /**
98
- * What the product declared — **or an error saying it has not.**
99
- *
100
- * Reading this before the declaration is loaded is a wiring mistake and shows as one. Answering
101
- * with an empty product instead would draw a screen with no services on it and no reason given.
102
- */
103
- export declare function stationProduct(): StationProduct;
104
- /** Tests only — a declaration is meant to be made once, at boot. */
105
- export declare function forgetStationProduct(): void;
106
- /**
107
- * The fields of one service, as the running schema has them.
108
- *
109
- * Reads type-graphql's own metadata rather than issuing an introspection query: it is the same
110
- * declaration the schema is built from, it needs no round trip, and it works before the server
111
- * has finished starting.
112
- */
113
- export declare function fieldsOfService(declared: StationServiceDeclaration): StationServiceField[];
@@ -1,124 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.declareStationProduct = declareStationProduct;
4
- exports.stationAxesOf = stationAxesOf;
5
- exports.stationProduct = stationProduct;
6
- exports.forgetStationProduct = forgetStationProduct;
7
- exports.fieldsOfService = fieldsOfService;
8
- const type_graphql_1 = require("type-graphql");
9
- const station_axes_js_1 = require("./station-axes.js");
10
- let declared;
11
- /**
12
- * Called once at boot, from the product's own declaration file.
13
- *
14
- * A missing slot is caught here rather than when it is first called: the server runs with
15
- * `strict: false`, so one `as any` in a declaration file gets past the type (ADR-0083 decision 2,
16
- * "declarations are required and the module has no default"). Where a withdrawal's history goes is no
17
- * longer a product slot — it is the shared change log (ADR-0124).
18
- */
19
- function declareStationProduct(product) {
20
- const missing = ['services', 'readGround'].filter(one => !product?.[one]);
21
- if (missing.length) {
22
- throw new Error(`station: ${product?.product ?? '이 제품'} 의 선언에 ${missing.join(' · ')} 이(가) 없습니다 — 현장 화면이 서려면 다 채워야 합니다.`);
23
- }
24
- /* 규칙은 한 자리다 — 화면의 `registerStationAxes` 가 같은 것을 부른다(`shared/station-axes.js`). */
25
- const wrong = (0, station_axes_js_1.wrongDeclaredAxes)(product.axes ?? []);
26
- if (wrong.length) {
27
- throw new Error(`station: ${product.product} 의 axes 선언이 틀렸습니다 — ${wrong.map(one => JSON.stringify(one)).join(' · ')}. ` +
28
- '축은 `무리.이름` 모양이고, 기계가 이미 가진 축은 다시 선언하지 않습니다.');
29
- }
30
- declared = product;
31
- }
32
- /**
33
- * Every axis a screen of this product may read — the machine's and the product's (ADR-0083 ruling ①).
34
- * The authoring surface lists these; `checkFlow` refuses anything else.
35
- */
36
- function stationAxesOf(product) {
37
- return [...station_axes_js_1.STATION_AXES, ...(product.axes ?? [])];
38
- }
39
- /**
40
- * What the product declared — **or an error saying it has not.**
41
- *
42
- * Reading this before the declaration is loaded is a wiring mistake and shows as one. Answering
43
- * with an empty product instead would draw a screen with no services on it and no reason given.
44
- */
45
- function stationProduct() {
46
- if (!declared) {
47
- throw new Error('station: 이 제품이 선언을 하지 않았습니다 — 서비스 목록 · 바탕을 읽는 자리 · 기록을 남기는 자리를 declareStationProduct 로 등록해야 현장 화면이 섭니다.');
48
- }
49
- return declared;
50
- }
51
- /** Tests only — a declaration is meant to be made once, at boot. */
52
- function forgetStationProduct() {
53
- declared = undefined;
54
- }
55
- /**
56
- * The fields of one service, as the running schema has them.
57
- *
58
- * Reads type-graphql's own metadata rather than issuing an introspection query: it is the same
59
- * declaration the schema is built from, it needs no round trip, and it works before the server
60
- * has finished starting.
61
- */
62
- function fieldsOfService(declared) {
63
- const storage = (0, type_graphql_1.getMetadataStorage)();
64
- const notable = new Set(declared.notable ?? []);
65
- if (!declared.input) {
66
- return argumentsOfMutation(declared.name, notable);
67
- }
68
- /*
69
- * Fields are stored against the class, and a class that extends another has its parent's fields
70
- * under the parent's target. Walking the prototype chain keeps an inherited field from going
71
- * missing — which would look exactly like the schema not having it.
72
- */
73
- const targets = new Set();
74
- for (let at = declared.input; at && at !== Function.prototype; at = Object.getPrototypeOf(at)) {
75
- targets.add(at);
76
- }
77
- return storage.fields
78
- .filter(field => targets.has(field.target))
79
- .map(field => ({
80
- name: field.name,
81
- required: !field.typeOptions?.nullable,
82
- /*
83
- * The description the field's own owner wrote.
84
- *
85
- * `goodCount` on a screen is a raw schema name to a plant manager. The input type already
86
- * carries `@Field({ description })` for many of them — the person who added the field is the
87
- * one who can say what it means, and they already did. Carrying it through beats a second
88
- * table here that goes stale the day a field is added: a field with no description shows its
89
- * name, which is what happened anyway before.
90
- */
91
- ...(field.description ? { describes: field.description } : {}),
92
- ...(notable.has(field.name) ? { notable: true } : {})
93
- }));
94
- }
95
- /**
96
- * A door that takes no input object — **its own arguments, as the schema has them** (ADR-0083 ruling ②).
97
- *
98
- * The same metadata the schema is built from, read in the same place as the input-type fields, so a door's
99
- * arguments cannot drift from what the running server takes. `@Ctx()` and other non-argument parameters are
100
- * not fields an author fills.
101
- *
102
- * A name the schema does not have is said. Answered with no fields, a misspelt door would look like a door
103
- * that needs nothing, and the screen built on it would call something that is not there.
104
- */
105
- function argumentsOfMutation(name, notable) {
106
- const storage = (0, type_graphql_1.getMetadataStorage)();
107
- const mutation = storage.mutations.find(one => one.schemaName === name);
108
- if (!mutation) {
109
- throw new Error(`station: 선언한 문 ${name} 이(가) 스키마에 없습니다 — 이름이 틀렸거나 그 리졸버가 등록되지 않았습니다.`);
110
- }
111
- return storage.params
112
- .filter(param => param.kind === 'arg' && param.target === mutation.target && param.methodName === mutation.methodName)
113
- .sort((a, b) => a.index - b.index)
114
- .map(param => {
115
- const arg = param;
116
- return {
117
- name: arg.name,
118
- required: !arg.typeOptions?.nullable,
119
- ...(arg.description ? { describes: arg.description } : {}),
120
- ...(notable.has(arg.name) ? { notable: true } : {})
121
- };
122
- });
123
- }
124
- //# sourceMappingURL=station-product.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"station-product.js","sourceRoot":"","sources":["../../../server/service/station/station-product.ts"],"names":[],"mappings":"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","sourcesContent":["import { getMetadataStorage } from 'type-graphql'\nimport type { EntityManager } from 'typeorm'\nimport type { Domain } from '@things-factory/shell'\n\nimport type { StationAssignmentSituation } from './station-assignment-choice.js'\nimport type { StationServiceField } from './station-flow.js'\nimport { STATION_AXES, wrongDeclaredAxes } from './station-axes.js'\n\n/**\n * What a product has to say before the station machinery will run for it (ADR-0083 decision 2).\n *\n * ═══════════════════════════════════════════════════════════════════════════\n * ## Why this file exists\n *\n * The station machinery is going to `@things-factory/station`, where operato-warehouse, and later\n * a yard and an energy product, will use the same authoring, the same flow model and the same\n * release approval. Nothing of that is about a factory. What *is* about a factory is small: which\n * values a screen may read, which mutations a step may call, who signs a release, and where those\n * values actually come from.\n *\n * This is that boundary, drawn **before** anything moves. Splitting while moving would leave the\n * first failure unattributable — was it the split or the move? — which is the order ADR-0078 used\n * when it lifted the outbox judgement out one layer at a time.\n *\n * ## Declared, never defaulted\n *\n * A product that has not declared gets an error naming what is missing, not an empty list. A\n * default here would mean a warehouse screen quietly offering a factory's mutations, or offering\n * none and reading as \"this product has no services\" — the silent empty this codebase keeps\n * finding. The same reason `registerWorkDoors` has no default door list.\n *\n * ## The assignment columns are the standard's general words (ADR-0083 decision 5)\n *\n * They used to be a factory's: `productionLineName` became `workCenterName` because the contract\n * already says `WorkCenter` is the general term over `ProcessCell` · `ProductionLine` ·\n * `ProductionUnit` · `StorageZone`. A warehouse zone and a yard block have somewhere to stand now.\n * `routeKey` and `operationKey` stay — a warehouse has a picking route too. No union of both\n * spellings: that would grow four products every time an axis is added.\n * ═══════════════════════════════════════════════════════════════════════════\n */\n\n/** Something a station step can call. The product owns the list; the machinery reads the shape. */\nexport interface StationServiceDeclaration {\n /** The mutation name, exactly as the schema spells it. */\n name: string\n /** What an author is doing when they pick this — their words, not the mutation's. */\n describes: string\n /**\n * The `@InputType` whose fields this call takes — **when it takes one.**\n *\n * Absent, the machine reads the mutation's own arguments by `name` (ADR-0083 ruling ②): a warehouse's\n * floor doors take a task name and a scanned place, not one input object, and wrapping them in one would\n * give the same door two shapes.\n */\n input?: Function\n /**\n * Nullable fields that still must not be left empty.\n *\n * Absent means \"nobody wrote it down\", not zero — and the two are different facts on a\n * production report.\n */\n notable?: string[]\n}\n\n/**\n * The facts this product knows about one station, in the words assignment selects on.\n *\n * The machinery reads what the platform owns (the equipment and its classes) itself. This answers\n * only what the product's own domain knows — for a factory, which line it stands on and what the\n * order running there is made of.\n */\nexport type StationGroundReader = (\n manager: EntityManager,\n domain: Domain,\n stationName: string,\n at?: { operationKey?: string; operatorName?: string; jobOrderName?: string }\n) => Promise<Pick<StationAssignmentSituation, 'workCenterName' | 'routeKey' | 'materialClassIds' | 'personnelClassIds'>>\n\nexport interface StationProduct {\n /** For the message when something is missing — the product says its own name. */\n product: string\n /**\n * The axes this product's screens read beyond the machine's own (ADR-0083 ruling ①) — `group.name`,\n * e.g. `unit.epc` · `task.kind`. The closed set a screen is checked against is the machine's list and\n * this. Absent means the product reads only the machine's.\n */\n axes?: readonly string[]\n services: StationServiceDeclaration[]\n readGround: StationGroundReader\n}\n\nlet declared: StationProduct | undefined\n\n/**\n * Called once at boot, from the product's own declaration file.\n *\n * A missing slot is caught here rather than when it is first called: the server runs with\n * `strict: false`, so one `as any` in a declaration file gets past the type (ADR-0083 decision 2,\n * \"declarations are required and the module has no default\"). Where a withdrawal's history goes is no\n * longer a product slot — it is the shared change log (ADR-0124).\n */\nexport function declareStationProduct(product: StationProduct): void {\n const missing = (['services', 'readGround'] as const).filter(one => !product?.[one])\n\n if (missing.length) {\n throw new Error(\n `station: ${product?.product ?? '이 제품'} 의 선언에 ${missing.join(' · ')} 이(가) 없습니다 — 현장 화면이 서려면 다 채워야 합니다.`\n )\n }\n\n /* 규칙은 한 자리다 — 화면의 `registerStationAxes` 가 같은 것을 부른다(`shared/station-axes.js`). */\n const wrong = wrongDeclaredAxes(product.axes ?? [])\n\n if (wrong.length) {\n throw new Error(\n `station: ${product.product} 의 axes 선언이 틀렸습니다 — ${wrong.map(one => JSON.stringify(one)).join(' · ')}. ` +\n '축은 `무리.이름` 모양이고, 기계가 이미 가진 축은 다시 선언하지 않습니다.'\n )\n }\n\n declared = product\n}\n\n/**\n * Every axis a screen of this product may read — the machine's and the product's (ADR-0083 ruling ①).\n * The authoring surface lists these; `checkFlow` refuses anything else.\n */\nexport function stationAxesOf(product: Pick<StationProduct, 'axes'>): string[] {\n return [...STATION_AXES, ...(product.axes ?? [])]\n}\n\n/**\n * What the product declared — **or an error saying it has not.**\n *\n * Reading this before the declaration is loaded is a wiring mistake and shows as one. Answering\n * with an empty product instead would draw a screen with no services on it and no reason given.\n */\nexport function stationProduct(): StationProduct {\n if (!declared) {\n throw new Error(\n 'station: 이 제품이 선언을 하지 않았습니다 — 서비스 목록 · 바탕을 읽는 자리 · 기록을 남기는 자리를 declareStationProduct 로 등록해야 현장 화면이 섭니다.'\n )\n }\n\n return declared\n}\n\n/** Tests only — a declaration is meant to be made once, at boot. */\nexport function forgetStationProduct(): void {\n declared = undefined\n}\n\n/**\n * The fields of one service, as the running schema has them.\n *\n * Reads type-graphql's own metadata rather than issuing an introspection query: it is the same\n * declaration the schema is built from, it needs no round trip, and it works before the server\n * has finished starting.\n */\nexport function fieldsOfService(declared: StationServiceDeclaration): StationServiceField[] {\n const storage = getMetadataStorage()\n const notable = new Set(declared.notable ?? [])\n\n if (!declared.input) {\n return argumentsOfMutation(declared.name, notable)\n }\n\n /*\n * Fields are stored against the class, and a class that extends another has its parent's fields\n * under the parent's target. Walking the prototype chain keeps an inherited field from going\n * missing — which would look exactly like the schema not having it.\n */\n const targets = new Set<Function>()\n\n for (let at: Function | null = declared.input; at && at !== Function.prototype; at = Object.getPrototypeOf(at)) {\n targets.add(at)\n }\n\n return storage.fields\n .filter(field => targets.has(field.target as Function))\n .map(field => ({\n name: field.name,\n required: !field.typeOptions?.nullable,\n /*\n * The description the field's own owner wrote.\n *\n * `goodCount` on a screen is a raw schema name to a plant manager. The input type already\n * carries `@Field({ description })` for many of them — the person who added the field is the\n * one who can say what it means, and they already did. Carrying it through beats a second\n * table here that goes stale the day a field is added: a field with no description shows its\n * name, which is what happened anyway before.\n */\n ...(field.description ? { describes: field.description } : {}),\n ...(notable.has(field.name) ? { notable: true } : {})\n }))\n}\n\n/**\n * A door that takes no input object — **its own arguments, as the schema has them** (ADR-0083 ruling ②).\n *\n * The same metadata the schema is built from, read in the same place as the input-type fields, so a door's\n * arguments cannot drift from what the running server takes. `@Ctx()` and other non-argument parameters are\n * not fields an author fills.\n *\n * A name the schema does not have is said. Answered with no fields, a misspelt door would look like a door\n * that needs nothing, and the screen built on it would call something that is not there.\n */\nfunction argumentsOfMutation(name: string, notable: Set<string>): StationServiceField[] {\n const storage = getMetadataStorage()\n const mutation = storage.mutations.find(one => one.schemaName === name)\n\n if (!mutation) {\n throw new Error(`station: 선언한 문 ${name} 이(가) 스키마에 없습니다 — 이름이 틀렸거나 그 리졸버가 등록되지 않았습니다.`)\n }\n\n return storage.params\n .filter(param => param.kind === 'arg' && param.target === mutation.target && param.methodName === mutation.methodName)\n .sort((a, b) => a.index - b.index)\n .map(param => {\n const arg = param as Extract<typeof param, { kind: 'arg' }>\n return {\n name: arg.name,\n required: !arg.typeOptions?.nullable,\n ...(arg.description ? { describes: arg.description } : {}),\n ...(notable.has(arg.name) ? { notable: true } : {})\n }\n })\n}\n\n"]}
@@ -1,6 +0,0 @@
1
- import type { StationFlow, StationStep } from './station-flow';
2
- export type StationRunAction = 'start' | 'finish' | 'pause' | 'resume';
3
- /** 실행 의미는 표시 이름과 분리한다. 옛 흐름의 명확한 이름만 호환한다. */
4
- export declare function runAction(step: StationStep): StationRunAction | undefined;
5
- /** 현 런타임은 good/scrap 두 수량만 가진다. 임의 입력을 양품으로 바꾸지 않는다. */
6
- export declare function entryCounts(flow: StationFlow, stepId: string): 'good' | 'scrap' | undefined;
@@ -1,46 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.runAction = runAction;
4
- exports.entryCounts = entryCounts;
5
- /** 실행 의미는 표시 이름과 분리한다. 옛 흐름의 명확한 이름만 호환한다. */
6
- function runAction(step) {
7
- if (step.service && !['reportProduction', 'recordEquipmentStatePeriod'].includes(step.service.name))
8
- return undefined;
9
- const action = step.action ?? legacyAction(step.label);
10
- if (step.service?.name === 'recordEquipmentStatePeriod' && action !== 'pause' && action !== 'resume')
11
- return undefined;
12
- if (step.service?.name === 'reportProduction' && action !== 'start' && action !== 'finish')
13
- return undefined;
14
- return action;
15
- }
16
- function legacyAction(label) {
17
- const text = (label ?? '').trim().toLowerCase();
18
- if (text === '완료' || text === 'finish')
19
- return 'finish';
20
- if (text === '일시정지' || text === 'pause')
21
- return 'pause';
22
- if (text === '재개' || text === 'resume')
23
- return 'resume';
24
- if (text === '시작' || text === 'start')
25
- return 'start';
26
- }
27
- /** 현 런타임은 good/scrap 두 수량만 가진다. 임의 입력을 양품으로 바꾸지 않는다. */
28
- function entryCounts(flow, stepId) {
29
- const targets = new Set();
30
- for (const step of flow.steps) {
31
- for (const [field, source] of Object.entries(step.arguments ?? {})) {
32
- if (source.from !== 'input' || source.stepId !== stepId)
33
- continue;
34
- if (step.service?.name !== 'reportProduction')
35
- return undefined;
36
- targets.add(field);
37
- }
38
- }
39
- if (targets.size !== 1)
40
- return undefined;
41
- if (targets.has('goodCount'))
42
- return 'good';
43
- if (targets.has('nonconformingCount'))
44
- return 'scrap';
45
- }
46
- //# sourceMappingURL=station-runtime-binding.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"station-runtime-binding.js","sourceRoot":"","sources":["../../../server/service/station/station-runtime-binding.ts"],"names":[],"mappings":";;AAKA,8BAMC;AAWD,kCAYC;AA9BD,8CAA8C;AAC9C,SAAgB,SAAS,CAAC,IAAiB;IACzC,IAAI,IAAI,CAAC,OAAO,IAAI,CAAC,CAAC,kBAAkB,EAAE,4BAA4B,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC;QAAE,OAAO,SAAS,CAAA;IACrH,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,IAAI,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;IACtD,IAAI,IAAI,CAAC,OAAO,EAAE,IAAI,KAAK,4BAA4B,IAAI,MAAM,KAAK,OAAO,IAAI,MAAM,KAAK,QAAQ;QAAE,OAAO,SAAS,CAAA;IACtH,IAAI,IAAI,CAAC,OAAO,EAAE,IAAI,KAAK,kBAAkB,IAAI,MAAM,KAAK,OAAO,IAAI,MAAM,KAAK,QAAQ;QAAE,OAAO,SAAS,CAAA;IAC5G,OAAO,MAAM,CAAA;AACf,CAAC;AAED,SAAS,YAAY,CAAC,KAAc;IAClC,MAAM,IAAI,GAAG,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAA;IAC/C,IAAI,IAAI,KAAK,IAAI,IAAI,IAAI,KAAK,QAAQ;QAAE,OAAO,QAAQ,CAAA;IACvD,IAAI,IAAI,KAAK,MAAM,IAAI,IAAI,KAAK,OAAO;QAAE,OAAO,OAAO,CAAA;IACvD,IAAI,IAAI,KAAK,IAAI,IAAI,IAAI,KAAK,QAAQ;QAAE,OAAO,QAAQ,CAAA;IACvD,IAAI,IAAI,KAAK,IAAI,IAAI,IAAI,KAAK,OAAO;QAAE,OAAO,OAAO,CAAA;AACvD,CAAC;AAED,wDAAwD;AACxD,SAAgB,WAAW,CAAC,IAAiB,EAAE,MAAc;IAC3D,MAAM,OAAO,GAAG,IAAI,GAAG,EAAU,CAAA;IACjC,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;QAC9B,KAAK,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,SAAS,IAAI,EAAE,CAAC,EAAE,CAAC;YACnE,IAAI,MAAM,CAAC,IAAI,KAAK,OAAO,IAAI,MAAM,CAAC,MAAM,KAAK,MAAM;gBAAE,SAAQ;YACjE,IAAI,IAAI,CAAC,OAAO,EAAE,IAAI,KAAK,kBAAkB;gBAAE,OAAO,SAAS,CAAA;YAC/D,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAA;QACpB,CAAC;IACH,CAAC;IACD,IAAI,OAAO,CAAC,IAAI,KAAK,CAAC;QAAE,OAAO,SAAS,CAAA;IACxC,IAAI,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC;QAAE,OAAO,MAAM,CAAA;IAC3C,IAAI,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC;QAAE,OAAO,OAAO,CAAA;AACvD,CAAC","sourcesContent":["import type { StationFlow, StationStep } from './station-flow'\n\nexport type StationRunAction = 'start' | 'finish' | 'pause' | 'resume'\n\n/** 실행 의미는 표시 이름과 분리한다. 옛 흐름의 명확한 이름만 호환한다. */\nexport function runAction(step: StationStep): StationRunAction | undefined {\n if (step.service && !['reportProduction', 'recordEquipmentStatePeriod'].includes(step.service.name)) return undefined\n const action = step.action ?? legacyAction(step.label)\n if (step.service?.name === 'recordEquipmentStatePeriod' && action !== 'pause' && action !== 'resume') return undefined\n if (step.service?.name === 'reportProduction' && action !== 'start' && action !== 'finish') return undefined\n return action\n}\n\nfunction legacyAction(label?: string): StationRunAction | undefined {\n const text = (label ?? '').trim().toLowerCase()\n if (text === '완료' || text === 'finish') return 'finish'\n if (text === '일시정지' || text === 'pause') return 'pause'\n if (text === '재개' || text === 'resume') return 'resume'\n if (text === '시작' || text === 'start') return 'start'\n}\n\n/** 현 런타임은 good/scrap 두 수량만 가진다. 임의 입력을 양품으로 바꾸지 않는다. */\nexport function entryCounts(flow: StationFlow, stepId: string): 'good' | 'scrap' | undefined {\n const targets = new Set<string>()\n for (const step of flow.steps) {\n for (const [field, source] of Object.entries(step.arguments ?? {})) {\n if (source.from !== 'input' || source.stepId !== stepId) continue\n if (step.service?.name !== 'reportProduction') return undefined\n targets.add(field)\n }\n }\n if (targets.size !== 1) return undefined\n if (targets.has('goodCount')) return 'good'\n if (targets.has('nonconformingCount')) return 'scrap'\n}\n"]}