@things-factory/station 10.1.33

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 (185) hide show
  1. package/README.md +124 -0
  2. package/dist-client/index.d.ts +11 -0
  3. package/dist-client/index.js +13 -0
  4. package/dist-client/index.js.map +1 -0
  5. package/dist-client/station/scene/index.d.ts +11 -0
  6. package/dist-client/station/scene/index.js +12 -0
  7. package/dist-client/station/scene/index.js.map +1 -0
  8. package/dist-client/station/scene/station-context-binding.d.ts +25 -0
  9. package/dist-client/station/scene/station-context-binding.js +39 -0
  10. package/dist-client/station/scene/station-context-binding.js.map +1 -0
  11. package/dist-client/station/scene/station-context-scene.d.ts +28 -0
  12. package/dist-client/station/scene/station-context-scene.js +138 -0
  13. package/dist-client/station/scene/station-context-scene.js.map +1 -0
  14. package/dist-client/station/scene/station-counts-scene.d.ts +6 -0
  15. package/dist-client/station/scene/station-counts-scene.js +51 -0
  16. package/dist-client/station/scene/station-counts-scene.js.map +1 -0
  17. package/dist-client/station/scene/station-field-scene.d.ts +6 -0
  18. package/dist-client/station/scene/station-field-scene.js +89 -0
  19. package/dist-client/station/scene/station-field-scene.js.map +1 -0
  20. package/dist-client/station/scene/station-icons.d.ts +82 -0
  21. package/dist-client/station/scene/station-icons.js +131 -0
  22. package/dist-client/station/scene/station-icons.js.map +1 -0
  23. package/dist-client/station/scene/station-numpad-scene.d.ts +9 -0
  24. package/dist-client/station/scene/station-numpad-scene.js +144 -0
  25. package/dist-client/station/scene/station-numpad-scene.js.map +1 -0
  26. package/dist-client/station/scene/station-scene-style.d.ts +51 -0
  27. package/dist-client/station/scene/station-scene-style.js +108 -0
  28. package/dist-client/station/scene/station-scene-style.js.map +1 -0
  29. package/dist-client/station/scene/station-scene.d.ts +295 -0
  30. package/dist-client/station/scene/station-scene.js +68 -0
  31. package/dist-client/station/scene/station-scene.js.map +1 -0
  32. package/dist-client/station/scene/station-who-scene.d.ts +6 -0
  33. package/dist-client/station/scene/station-who-scene.js +48 -0
  34. package/dist-client/station/scene/station-who-scene.js.map +1 -0
  35. package/dist-client/station/scene/station-work-panel-scene.d.ts +28 -0
  36. package/dist-client/station/scene/station-work-panel-scene.js +37 -0
  37. package/dist-client/station/scene/station-work-panel-scene.js.map +1 -0
  38. package/dist-client/station/station-board-binding.d.ts +16 -0
  39. package/dist-client/station/station-board-binding.js +20 -0
  40. package/dist-client/station/station-board-binding.js.map +1 -0
  41. package/dist-client/station/station-context.d.ts +178 -0
  42. package/dist-client/station/station-context.js +30 -0
  43. package/dist-client/station/station-context.js.map +1 -0
  44. package/dist-client/station/station-palette.d.ts +34 -0
  45. package/dist-client/station/station-palette.js +119 -0
  46. package/dist-client/station/station-palette.js.map +1 -0
  47. package/dist-client/station/station-runtime.d.ts +47 -0
  48. package/dist-client/station/station-runtime.js +47 -0
  49. package/dist-client/station/station-runtime.js.map +1 -0
  50. package/dist-client/station/station-scene-context.d.ts +68 -0
  51. package/dist-client/station/station-scene-context.js +62 -0
  52. package/dist-client/station/station-scene-context.js.map +1 -0
  53. package/dist-client/station/station-screen-flow.d.ts +16 -0
  54. package/dist-client/station/station-screen-flow.js +58 -0
  55. package/dist-client/station/station-screen-flow.js.map +1 -0
  56. package/dist-client/station/station-tile-registry.d.ts +89 -0
  57. package/dist-client/station/station-tile-registry.js +86 -0
  58. package/dist-client/station/station-tile-registry.js.map +1 -0
  59. package/dist-client/station/station-work-panel-view.d.ts +23 -0
  60. package/dist-client/station/station-work-panel-view.js +125 -0
  61. package/dist-client/station/station-work-panel-view.js.map +1 -0
  62. package/dist-client/station/station-work-runtime.d.ts +47 -0
  63. package/dist-client/station/station-work-runtime.js +161 -0
  64. package/dist-client/station/station-work-runtime.js.map +1 -0
  65. package/dist-client/station/station-work-scenarios.d.ts +41 -0
  66. package/dist-client/station/station-work-scenarios.js +59 -0
  67. package/dist-client/station/station-work-scenarios.js.map +1 -0
  68. package/dist-client/station/task-counts.d.ts +32 -0
  69. package/dist-client/station/task-counts.js +26 -0
  70. package/dist-client/station/task-counts.js.map +1 -0
  71. package/dist-client/tsconfig.tsbuildinfo +1 -0
  72. package/dist-server/activities/activity-station-screen-control-withdrawal.d.ts +62 -0
  73. package/dist-server/activities/activity-station-screen-control-withdrawal.js +147 -0
  74. package/dist-server/activities/activity-station-screen-control-withdrawal.js.map +1 -0
  75. package/dist-server/activities/activity-station-screen-release.d.ts +81 -0
  76. package/dist-server/activities/activity-station-screen-release.js +147 -0
  77. package/dist-server/activities/activity-station-screen-release.js.map +1 -0
  78. package/dist-server/activities/index.d.ts +49 -0
  79. package/dist-server/activities/index.js +21 -0
  80. package/dist-server/activities/index.js.map +1 -0
  81. package/dist-server/index.d.ts +3 -0
  82. package/dist-server/index.js +8 -0
  83. package/dist-server/index.js.map +1 -0
  84. package/dist-server/service/index.d.ts +23 -0
  85. package/dist-server/service/index.js +41 -0
  86. package/dist-server/service/index.js.map +1 -0
  87. package/dist-server/service/station/deletion-changes.d.ts +14 -0
  88. package/dist-server/service/station/deletion-changes.js +25 -0
  89. package/dist-server/service/station/deletion-changes.js.map +1 -0
  90. package/dist-server/service/station/index.d.ts +13 -0
  91. package/dist-server/service/station/index.js +25 -0
  92. package/dist-server/service/station/index.js.map +1 -0
  93. package/dist-server/service/station/reversibility.d.ts +21 -0
  94. package/dist-server/service/station/reversibility.js +24 -0
  95. package/dist-server/service/station/reversibility.js.map +1 -0
  96. package/dist-server/service/station/screen-model-digest.d.ts +60 -0
  97. package/dist-server/service/station/screen-model-digest.js +97 -0
  98. package/dist-server/service/station/screen-model-digest.js.map +1 -0
  99. package/dist-server/service/station/staged-material.d.ts +62 -0
  100. package/dist-server/service/station/staged-material.js +44 -0
  101. package/dist-server/service/station/staged-material.js.map +1 -0
  102. package/dist-server/service/station/station-argument-sources.d.ts +31 -0
  103. package/dist-server/service/station/station-argument-sources.js +66 -0
  104. package/dist-server/service/station/station-argument-sources.js.map +1 -0
  105. package/dist-server/service/station/station-assignment-choice.d.ts +81 -0
  106. package/dist-server/service/station/station-assignment-choice.js +105 -0
  107. package/dist-server/service/station/station-assignment-choice.js.map +1 -0
  108. package/dist-server/service/station/station-assignment-resolver.d.ts +104 -0
  109. package/dist-server/service/station/station-assignment-resolver.js +426 -0
  110. package/dist-server/service/station/station-assignment-resolver.js.map +1 -0
  111. package/dist-server/service/station/station-assignment-service.d.ts +33 -0
  112. package/dist-server/service/station/station-assignment-service.js +64 -0
  113. package/dist-server/service/station/station-assignment-service.js.map +1 -0
  114. package/dist-server/service/station/station-axes.d.ts +9 -0
  115. package/dist-server/service/station/station-axes.js +16 -0
  116. package/dist-server/service/station/station-axes.js.map +1 -0
  117. package/dist-server/service/station/station-flow.d.ts +156 -0
  118. package/dist-server/service/station/station-flow.js +255 -0
  119. package/dist-server/service/station/station-flow.js.map +1 -0
  120. package/dist-server/service/station/station-generate.d.ts +76 -0
  121. package/dist-server/service/station/station-generate.js +178 -0
  122. package/dist-server/service/station/station-generate.js.map +1 -0
  123. package/dist-server/service/station/station-product.d.ts +123 -0
  124. package/dist-server/service/station/station-product.js +77 -0
  125. package/dist-server/service/station/station-product.js.map +1 -0
  126. package/dist-server/service/station/station-runtime-binding.d.ts +6 -0
  127. package/dist-server/service/station/station-runtime-binding.js +46 -0
  128. package/dist-server/service/station/station-runtime-binding.js.map +1 -0
  129. package/dist-server/service/station/station-screen-assignment.d.ts +94 -0
  130. package/dist-server/service/station/station-screen-assignment.js +154 -0
  131. package/dist-server/service/station/station-screen-assignment.js.map +1 -0
  132. package/dist-server/service/station/station-screen-control-withdrawal-resolver.d.ts +37 -0
  133. package/dist-server/service/station/station-screen-control-withdrawal-resolver.js +130 -0
  134. package/dist-server/service/station/station-screen-control-withdrawal-resolver.js.map +1 -0
  135. package/dist-server/service/station/station-screen-control-withdrawal.d.ts +104 -0
  136. package/dist-server/service/station/station-screen-control-withdrawal.js +128 -0
  137. package/dist-server/service/station/station-screen-control-withdrawal.js.map +1 -0
  138. package/dist-server/service/station/station-screen-draft-resolver.d.ts +47 -0
  139. package/dist-server/service/station/station-screen-draft-resolver.js +153 -0
  140. package/dist-server/service/station/station-screen-draft-resolver.js.map +1 -0
  141. package/dist-server/service/station/station-screen-layout.d.ts +119 -0
  142. package/dist-server/service/station/station-screen-layout.js +202 -0
  143. package/dist-server/service/station/station-screen-layout.js.map +1 -0
  144. package/dist-server/service/station/station-screen-list-resolver.d.ts +69 -0
  145. package/dist-server/service/station/station-screen-list-resolver.js +215 -0
  146. package/dist-server/service/station/station-screen-list-resolver.js.map +1 -0
  147. package/dist-server/service/station/station-screen-publish-subscriber.d.ts +22 -0
  148. package/dist-server/service/station/station-screen-publish-subscriber.js +90 -0
  149. package/dist-server/service/station/station-screen-publish-subscriber.js.map +1 -0
  150. package/dist-server/service/station/station-screen-release-gate.d.ts +66 -0
  151. package/dist-server/service/station/station-screen-release-gate.js +67 -0
  152. package/dist-server/service/station/station-screen-release-gate.js.map +1 -0
  153. package/dist-server/service/station/station-screen-release-record.d.ts +62 -0
  154. package/dist-server/service/station/station-screen-release-record.js +139 -0
  155. package/dist-server/service/station/station-screen-release-record.js.map +1 -0
  156. package/dist-server/service/station/station-screen-release-signature.d.ts +23 -0
  157. package/dist-server/service/station/station-screen-release-signature.js +32 -0
  158. package/dist-server/service/station/station-screen-release-signature.js.map +1 -0
  159. package/dist-server/service/station/station-screen-scorecard.d.ts +15 -0
  160. package/dist-server/service/station/station-screen-scorecard.js +35 -0
  161. package/dist-server/service/station/station-screen-scorecard.js.map +1 -0
  162. package/dist-server/service/station/station-screen-spec-resolver.d.ts +36 -0
  163. package/dist-server/service/station/station-screen-spec-resolver.js +202 -0
  164. package/dist-server/service/station/station-screen-spec-resolver.js.map +1 -0
  165. package/dist-server/service/station/station-screen-spec.d.ts +104 -0
  166. package/dist-server/service/station/station-screen-spec.js +114 -0
  167. package/dist-server/service/station/station-screen-spec.js.map +1 -0
  168. package/dist-server/service/station/station-screen-thumbnail.d.ts +11 -0
  169. package/dist-server/service/station/station-screen-thumbnail.js +36 -0
  170. package/dist-server/service/station/station-screen-thumbnail.js.map +1 -0
  171. package/dist-server/service/station/station-service-resolver.d.ts +27 -0
  172. package/dist-server/service/station/station-service-resolver.js +95 -0
  173. package/dist-server/service/station/station-service-resolver.js.map +1 -0
  174. package/dist-server/tsconfig.tsbuildinfo +1 -0
  175. package/package.json +44 -0
  176. package/shared/station-axes.d.ts +4 -0
  177. package/shared/station-axes.js +155 -0
  178. package/shared/station-screen-flow-check.d.ts +12 -0
  179. package/shared/station-screen-flow-check.js +74 -0
  180. package/things-scene.config.js +15 -0
  181. package/translations/en.json +42 -0
  182. package/translations/ja.json +42 -0
  183. package/translations/ko.json +42 -0
  184. package/translations/ms.json +42 -0
  185. package/translations/zh.json +42 -0
package/package.json ADDED
@@ -0,0 +1,44 @@
1
+ {
2
+ "name": "@things-factory/station",
3
+ "version": "10.1.33",
4
+ "main": "dist-server/index.js",
5
+ "browser": "dist-client/index.js",
6
+ "things-factory": true,
7
+ "things-scene": true,
8
+ "author": "heartyoh <heartyoh@hatiolab.com>",
9
+ "description": "Shop-floor screen authoring — the machine. A product declares axes, callable doors, roles, the ground reader and where its changes are recorded",
10
+ "license": "MIT",
11
+ "publishConfig": {
12
+ "access": "public",
13
+ "@things-factory:registry": "https://registry.npmjs.org"
14
+ },
15
+ "repository": {
16
+ "type": "git",
17
+ "url": "git+https://github.com/hatiolab/things-factory.git",
18
+ "directory": "packages/station"
19
+ },
20
+ "scripts": {
21
+ "typecheck": "tsc -p server/tsconfig.json --noEmit --tsBuildInfoFile node_modules/.cache/typecheck/server.tsbuildinfo && tsc -p client/tsconfig.json --noEmit --tsBuildInfoFile node_modules/.cache/typecheck/client.tsbuildinfo",
22
+ "build": "npm run build:server && npm run build:client",
23
+ "build:server": "npx tsc --p ./server/tsconfig.json",
24
+ "clean:server": "npx rimraf dist-server",
25
+ "clean": "npm run clean:server && npm run clean:client",
26
+ "pretest": "npm run typecheck",
27
+ "test": "node --import ./test/resolve-ts-siblings.mjs --test --test-force-exit test/*.test.ts",
28
+ "copy:files": "copyfiles -e \"./client/**/*.{ts,js,json}\" -u 1 \"./client/**/*\" dist-client",
29
+ "build:client": "npm run copy:files && npx tsc --p ./client/tsconfig.json",
30
+ "clean:client": "npx rimraf dist-client"
31
+ },
32
+ "dependencies": {
33
+ "@hatiolab/things-scene": "^10.0.0",
34
+ "@operato/i18n": "^10.0.0",
35
+ "@operato/styles": "^10.0.0",
36
+ "@things-factory/auth-base": "^10.1.33",
37
+ "@things-factory/board-service": "^10.1.33",
38
+ "@things-factory/ops-master": "^10.1.33",
39
+ "@things-factory/shell": "^10.1.33",
40
+ "@things-factory/worklist": "^10.1.33",
41
+ "i18next": "^24.2.1"
42
+ },
43
+ "gitHead": "7fb7b6587846b5ff6ee7a4983652d9561b4d6b2c"
44
+ }
@@ -0,0 +1,4 @@
1
+ /** The closed set of context axis names a station screen may read. Declared once; see the .js. */
2
+ export const STATION_AXES: readonly string[]
3
+ export function isStationAxis(axis: string): boolean
4
+ export function nearestAxes(said: string, howMany?: number): string[]
@@ -0,0 +1,155 @@
1
+ /*
2
+ * Every context axis a station screen can read — the closed set, declared once.
3
+ *
4
+ * ── Why this file exists ──────────────────────────────────────────────────────────────────
5
+ * The design named three closed sets as the reason this authoring surface is safe, and the axis
6
+ * names were one of them. Four comments across the server said `stationBindingNames()` was "the
7
+ * only source" — and the server never imported it. Measured on 2026-09-11:
8
+ *
9
+ * reads: ['station.equipmnet', '완전히.지어낸.축', ''] → checkFlow: 0 problems
10
+ * → requires: all three, empty string too
11
+ * → drawn: three fields
12
+ *
13
+ * A typo drew a field that is blank forever. In the preview it looked exactly like a correct axis
14
+ * with no data behind it right now — which is the confusion this whole module was built to remove.
15
+ * The set was declared closed and implemented open.
16
+ *
17
+ * ── Why the names live here and not on either side ────────────────────────────────────────
18
+ * Closing it first put the list on the server, because the server is the side that has to refuse
19
+ * a bad name — a check the browser can skip is not a closed set. That left two lists: this one and
20
+ * the keys `stationValues()` produces, held together by a test. A test catches drift; it does not
21
+ * make one source. Ruled 2026-09-16 (architecture): the app keeps these names, on the condition
22
+ * that there is exactly one original.
23
+ *
24
+ * So the list sits where both sides can import it, next to `station-screen-flow-check.js`, which
25
+ * is here for the same reason. Plain JS with a hand-written .d.ts: the client and server compile
26
+ * separately with their own outDir, and importing one build into the other moves it (a single
27
+ * `import type` once relocated 339 files). Nothing here imports UI or DB.
28
+ *
29
+ * The projection that *produces* the values is still `stationValues()` on the client, because it
30
+ * formats each one for a reader — that is code, not a name list. `test/station-axes.test.ts`
31
+ * checks the projection covers exactly this declaration.
32
+ * ──────────────────────────────────────────────────────────────────────────────────────────
33
+ */
34
+
35
+ const STATION_AXES = [
36
+ /* Where the operator is standing, and when. */
37
+ 'station.equipment',
38
+ 'station.operation',
39
+ 'station.shift',
40
+ 'station.workDate',
41
+ 'station.windowBasis',
42
+ 'station.windowFrom',
43
+ 'station.windowTo',
44
+ 'station.problem',
45
+ 'station.readAt',
46
+ 'station.live',
47
+
48
+ 'location.name',
49
+ 'location.type',
50
+ 'location.level',
51
+ 'productionLine.name',
52
+
53
+ /* Who scanned in. Until this is filled nothing else is attributable. */
54
+ 'operator.name',
55
+ 'operator.personnelClassIds',
56
+ 'operator.scannedAt',
57
+
58
+ /* What is being made. */
59
+ 'order.name',
60
+ 'order.material',
61
+ 'order.quantity',
62
+ 'order.uom',
63
+ 'order.status',
64
+ 'order.directive',
65
+ 'order.directiveVersion',
66
+ 'order.route',
67
+ 'materialDefinition.name',
68
+ 'materialDefinition.description',
69
+ 'materialDefinition.materialClassIds',
70
+
71
+ /* How it is made. */
72
+ 'route.key',
73
+ 'route.steps',
74
+ 'route.stepNumber',
75
+ 'route.stepCount',
76
+ 'route.nextStep',
77
+ 'directive.name',
78
+ 'directive.version',
79
+ 'directive.description',
80
+ 'directive.steps',
81
+ 'directive.stepCount',
82
+ 'operation.name',
83
+ 'operation.description',
84
+ 'operation.duration',
85
+
86
+ /* What is happening right now. */
87
+ 'task.name',
88
+ 'task.since',
89
+ 'paused.at',
90
+ 'paused.reason',
91
+ 'paused.detail',
92
+
93
+ /* How much, this window. */
94
+ 'window.good',
95
+ 'window.scrap',
96
+ 'window.tasks',
97
+
98
+ /* The equipment's own state. */
99
+ 'state.status',
100
+ 'state.knownUntil',
101
+ 'state.reason',
102
+
103
+ /* Counts of things waiting. */
104
+ 'staged.count',
105
+ 'candidates.count',
106
+ 'maintenance.count'
107
+ ]
108
+
109
+ const KNOWN = new Set(STATION_AXES)
110
+
111
+ function isStationAxis(axis) {
112
+ return KNOWN.has(axis)
113
+ }
114
+
115
+ /*
116
+ * The closest names to one that was not recognised.
117
+ *
118
+ * A screen that says «no such axis» and stops has told the author they are wrong without telling
119
+ * them what is right, and with fifty-five names that is most of the work left undone. One
120
+ * character out is the common case, so it is the case worth answering.
121
+ */
122
+ function nearestAxes(said, howMany = 3) {
123
+ const asked = String(said ?? '').trim().toLowerCase()
124
+
125
+ if (!asked) {
126
+ return []
127
+ }
128
+
129
+ return STATION_AXES.map(axis => ({ axis, far: distance(asked, axis.toLowerCase()) }))
130
+ .sort((a, b) => a.far - b.far)
131
+ .slice(0, howMany)
132
+ .filter(one => one.far <= Math.max(3, Math.floor(asked.length / 3)))
133
+ .map(one => one.axis)
134
+ }
135
+
136
+ /* Levenshtein, two rows. Fifty-five short strings — nothing here needs to be cleverer. */
137
+ function distance(a, b) {
138
+ let previous = Array.from({ length: b.length + 1 }, (_, at) => at)
139
+
140
+ for (let i = 1; i <= a.length; i++) {
141
+ const row = [i]
142
+
143
+ for (let j = 1; j <= b.length; j++) {
144
+ row[j] = Math.min(previous[j] + 1, row[j - 1] + 1, previous[j - 1] + (a[i - 1] === b[j - 1] ? 0 : 1))
145
+ }
146
+
147
+ previous = row
148
+ }
149
+
150
+ return previous[b.length]
151
+ }
152
+
153
+ exports.STATION_AXES = STATION_AXES
154
+ exports.isStationAxis = isStationAxis
155
+ exports.nearestAxes = nearestAxes
@@ -0,0 +1,12 @@
1
+ export interface FlowIssue {
2
+ page: string
3
+ component?: string
4
+ blocking: boolean
5
+ /** Translated as `error.station-flow-<code>`. */
6
+ code: string
7
+ params: Record<string, string>
8
+ /** English fallback; `{page}` and the params fill it. */
9
+ message: string
10
+ }
11
+ export function checkScreenFlow(flow: unknown): FlowIssue[]
12
+ export function blockingFlowIssue(model: unknown): FlowIssue | undefined
@@ -0,0 +1,74 @@
1
+ /* Shared by the browser authoring checks and the server publication gate. No UI or DB imports. */
2
+ const own = (object, key) => Object.prototype.hasOwnProperty.call(object, key)
3
+ const record = value => value !== null && typeof value === 'object' && !Array.isArray(value)
4
+
5
+ /*
6
+ * Each issue carries a code and params; `message` is the English fallback. The sentence a person reads
7
+ * comes from `error.station-flow-<code>` in the translations: the modeller translates it in the browser,
8
+ * the server turns the first blocking issue into a refusal (`flowRefusal`).
9
+ */
10
+ function checkScreenFlow(flow) {
11
+ const issues = []
12
+ const error = (page, code, message, component, params = {}) =>
13
+ issues.push({ page, component, blocking: true, code, params, message })
14
+ if (!record(flow) || !record(flow.screens)) {
15
+ error('', 'flow-malformed', 'the screen flow is malformed.')
16
+ return issues
17
+ }
18
+ if (flow.version !== 1) error('', 'flow-version-unsupported', 'this screen flow version is not supported.')
19
+ if (typeof flow.start !== 'string' || !own(flow.screens, flow.start)) error('', 'flow-start-missing', 'there is no start screen. set one.')
20
+ const edges = new Map()
21
+ for (const [id, model] of Object.entries(flow.screens)) {
22
+ const links = []
23
+ edges.set(id, links)
24
+ if (!/^[a-z][a-z0-9_-]{0,39}$/.test(id) || ['constructor', 'prototype', '__proto__'].includes(id))
25
+ error(id, 'flow-screen-id-invalid', 'screen id {page} is invalid. use up to 40 characters starting with a lowercase letter.')
26
+ const stack = [model], seen = new Set()
27
+ while (stack.length) {
28
+ const part = stack.pop()
29
+ if (!record(part) || seen.has(part)) { error(id, 'flow-components-malformed', 'the components of screen {page} are malformed.'); continue }
30
+ seen.add(part)
31
+ if (part.components !== undefined) {
32
+ if (!Array.isArray(part.components)) error(id, 'flow-components-malformed', 'the components of screen {page} are malformed.', part.id)
33
+ else stack.push(...part.components)
34
+ }
35
+ if (part.type !== 'station-work-panel') continue
36
+ if (part.view === 'navigation') {
37
+ if (!['next', 'back', 'cancel', 'next-work'].includes(part.flowAction ?? 'next')) error(id, 'flow-action-invalid', 'the navigation action on screen {page} is invalid.', part.id)
38
+ if (!['none', 'order', 'materials', 'ready'].includes(part.flowGuard ?? 'none')) error(id, 'flow-guard-invalid', 'the transition condition on screen {page} is invalid.', part.id)
39
+ if (!['none', 'inputs', 'work'].includes(part.resetScope ?? 'none')) error(id, 'flow-reset-invalid', 'the reset scope on screen {page} is invalid.', part.id)
40
+ }
41
+ if (part.view === 'navigation' || (part.view === 'commit' && part.targetScreen != null && part.targetScreen !== '')) {
42
+ if (typeof part.targetScreen !== 'string' || part.targetScreen === '') error(id, 'flow-target-unset', 'screen {page} has no screen to move to.', part.id)
43
+ else if (!own(flow.screens, part.targetScreen)) error(id, 'flow-target-missing', 'screen {page} moves to {target}, which does not exist. choose a screen to move to.', part.id, { target: part.targetScreen })
44
+ else links.push(part.targetScreen)
45
+ }
46
+ }
47
+ }
48
+ const reached = new Set(), queue = [flow.start]
49
+ while (queue.length) {
50
+ const id = queue.pop()
51
+ if (reached.has(id)) continue
52
+ reached.add(id)
53
+ queue.push(...(edges.get(id) || []))
54
+ }
55
+ for (const id of edges.keys()) if (!reached.has(id)) issues.push({ page: id, component: undefined, blocking: false, code: 'flow-screen-unreachable', params: {}, message: 'screen {page} cannot be reached from the start screen.' })
56
+ return issues
57
+ }
58
+
59
+ /** The first issue that stops publication, or undefined. A model that is not JSON is itself such an issue. */
60
+ function blockingFlowIssue(model) {
61
+ let parsed = model
62
+ if (typeof model === 'string') {
63
+ try {
64
+ parsed = JSON.parse(model)
65
+ } catch {
66
+ return { page: '', component: undefined, blocking: true, code: 'flow-model-unreadable', params: {}, message: 'the screen model could not be read.' }
67
+ }
68
+ }
69
+ if (!record(parsed) || !own(parsed, 'stationPages')) return undefined
70
+ return checkScreenFlow(parsed.stationPages).find(issue => issue.blocking)
71
+ }
72
+
73
+ exports.checkScreenFlow = checkScreenFlow
74
+ exports.blockingFlowIssue = blockingFlowIssue
@@ -0,0 +1,15 @@
1
+ /*
2
+ * 저작면이 이 꾸러미에서 걷어 갈 것 — 그룹 하나와 공통 타일 다섯.
3
+ *
4
+ * `package.json` 의 `"things-scene": true` 와 이 파일이 짝이다. `@things-factory/env` 가 의존 꾸러미를
5
+ * 훑어 모으고, `board-ui` 의 모델러가 `registerGroup` · `registerTemplate` 로 세운다. 앱이 따로
6
+ * 등록하는 코드를 쓰지 않는다.
7
+ *
8
+ * 산출물에서 읽는다 — 이 파일은 빌드를 안 지나므로 `client/` 의 TypeScript 를 가리킬 수 없다.
9
+ */
10
+ import { stationGroup, stationCommonTiles } from './dist-client/station/station-palette.js'
11
+
12
+ export default {
13
+ groups: [stationGroup],
14
+ templates: stationCommonTiles
15
+ }
@@ -0,0 +1,42 @@
1
+ {
2
+ "error.control-already-off": "[control withdrawal] this screen is no longer a controlled document. reject this item.",
3
+ "error.control-not-on": "this screen is not a controlled document.",
4
+ "error.control-withdrawal-already-raised": "a control withdrawal for this screen is already awaiting approval.",
5
+ "error.control-withdrawal-carried-missing": "[control withdrawal] the approved item has no screen setting, so it was not applied.",
6
+ "error.control-withdrawal-door-missing": "the control withdrawal approval form is not installed in this plant.",
7
+ "error.control-withdrawal-needs-approval": "document control can only be removed through a control withdrawal approval.",
8
+ "error.control-withdrawal-reason-missing": "enter the reason for withdrawing control.",
9
+ "error.control-withdrawal-spec-gone": "[control withdrawal] the requested screen setting no longer exists. reject this item.",
10
+ "error.report-completed-without-counts": "this is the last operation of the job order. enter the good count to complete it.",
11
+ "error.station-release-approval-missing": "this screen is a controlled document with no approved version. submit a release approval first.",
12
+ "error.station-release-approval-stale": "this screen is a controlled document and was changed after approval. submit a release approval again.",
13
+ "error.station-release-carried-missing": "[screen release] the approved item has no screen, so it was not applied.",
14
+ "error.station-spec-missing": "the screen settings were not found.",
15
+ "error.time-range-end-not-after-start": "the end time is not after the start time. enter a later end time.",
16
+ "field.board": "board",
17
+ "field.equipment": "equipment",
18
+ "field.kind": "kind",
19
+ "field.maintenance": "maintenance",
20
+ "field.quality": "quality",
21
+ "field.station": "station",
22
+ "field.subject": "subject",
23
+ "text.check-final-count-has-no-source": "「{step}」 records without a good count. If this station is the last operation of the order, the server refuses the completion. Connect a step that takes the good count.",
24
+ "text.check-nobody-reads-this-entry": "no step uses the value entered in 「{step}」.",
25
+ "text.check-notable-has-no-source": "「{step}」 records {field} as 「not entered」.",
26
+ "text.check-required-has-no-source": "「{step}」 has nothing feeding its required {field} — pressing it will fail.",
27
+ "text.check-service-has-no-such-field": "{service} has no {field} — check whether the service changed.",
28
+ "text.check-source-comes-later": "「{from}」, which feeds {field}, comes after this step. move 「{from}」 earlier.",
29
+ "text.check-source-does-not-ask": "{field} is fed by 「{from}」, but that step has no input.",
30
+ "text.check-source-step-gone": "{field} is fed by a deleted step. add that step again or change the source.",
31
+ "text.check-unknown-axis": "there is no value called {axis} — pick one from the list.",
32
+ "text.check-unknown-axis-did-you-mean": "there is no value called {axis} — did you mean {near}?",
33
+ "text.omitted-many-test-specs": "no inspection step — {equipment} carries {specs}, and no rule can pick which one. add the step and choose.",
34
+ "text.omitted-material-actual": "no material actuals step. materials come from the recipe.",
35
+ "text.omitted-no-inspection-runtime": "no inspection step. station screens do not support inspection records yet.",
36
+ "text.omitted-no-report-service": "the finish button records nothing. the production report service was not found.",
37
+ "text.omitted-no-stop-reasons": "no pause button. no stop reasons are registered.",
38
+ "text.omitted-no-test-spec": "no inspection step — {equipment} carries no test specification.",
39
+ "text.omitted-order-fields": "no job order fields. nothing is scheduled on this operation yet.",
40
+ "text.station-runtime-not-connected": "「{step}」 has nothing to run — a step will not be run by standing in for another.",
41
+ "title.work": "work"
42
+ }
@@ -0,0 +1,42 @@
1
+ {
2
+ "error.control-already-off": "[統制解除承認] この画面はすでに統制文書ではありません。この案件を差し戻してください。",
3
+ "error.control-not-on": "この画面は統制文書ではありません。",
4
+ "error.control-withdrawal-already-raised": "この画面の統制解除はすでに承認待ちです。",
5
+ "error.control-withdrawal-carried-missing": "[統制解除承認] 承認案件に画面設定の情報がないため、反映できませんでした。",
6
+ "error.control-withdrawal-door-missing": "統制解除承認の様式がインストールされていません。",
7
+ "error.control-withdrawal-needs-approval": "統制文書の統制は「統制解除承認」でのみ解除できます。",
8
+ "error.control-withdrawal-reason-missing": "統制解除の理由を入力してください。",
9
+ "error.control-withdrawal-spec-gone": "[統制解除承認] 申請した画面設定が削除されました。この案件を差し戻してください。",
10
+ "error.report-completed-without-counts": "この工程は作業指示の最後の工程です。完了にするには良品数を入力してください。",
11
+ "error.station-release-approval-missing": "この画面は統制文書で、承認済みのバージョンがありません。先に発行承認を申請してください。",
12
+ "error.station-release-approval-stale": "この画面は統制文書で、承認後に変更されました。発行承認を再度申請してください。",
13
+ "error.station-release-carried-missing": "[画面発行承認] 承認案件に画面の情報がないため、反映できませんでした。",
14
+ "error.station-spec-missing": "画面設定が見つかりません。",
15
+ "error.time-range-end-not-after-start": "終了時刻が開始時刻と同じか、それより前です。開始時刻より後の終了時刻を入力してください。",
16
+ "field.board": "ボード",
17
+ "field.equipment": "設備",
18
+ "field.kind": "種類",
19
+ "field.maintenance": "保全",
20
+ "field.quality": "良品率",
21
+ "field.station": "作業台",
22
+ "field.subject": "対象",
23
+ "text.check-final-count-has-no-source": "「{step}」は良品数なしで記録されます。この作業台が指示の最終工程なら、サーバーは完了を拒否します。良品数を受け取るステップをつないでください。",
24
+ "text.check-nobody-reads-this-entry": "「{step}」に入力した値を使うステップがありません。",
25
+ "text.check-notable-has-no-source": "「{step}」を押すと {field} は「未入力」として記録されます。",
26
+ "text.check-required-has-no-source": "「{step}」 has nothing feeding its required {field} — pressing it will fail.",
27
+ "text.check-service-has-no-such-field": "{service} has no {field} — check whether the service changed.",
28
+ "text.check-source-comes-later": "{field} の値を渡す「{from}」ステップがこのステップより後にあります。「{from}」を前に移してください。",
29
+ "text.check-source-does-not-ask": "{field} は「{from}」から値を受け取りますが、そのステップに入力欄がありません。",
30
+ "text.check-source-step-gone": "{field} は削除されたステップから値を受け取ります。そのステップを再追加するか、出所を変えてください。",
31
+ "text.check-unknown-axis": "there is no value called {axis} — pick one from the list.",
32
+ "text.check-unknown-axis-did-you-mean": "there is no value called {axis} — did you mean {near}?",
33
+ "text.omitted-many-test-specs": "no inspection step — {equipment} carries {specs}, and no rule can pick which one. add the step and choose.",
34
+ "text.omitted-material-actual": "資材実績のステップを入れていません。投入資材はレシピで決まります。",
35
+ "text.omitted-no-inspection-runtime": "検査ステップを入れていません。現場画面ではまだ検査記録に対応していません。",
36
+ "text.omitted-no-report-service": "完了ボタンは実績を記録しません。実績報告サービスが見つかりませんでした。",
37
+ "text.omitted-no-stop-reasons": "一時停止ボタンを入れていません。登録された停止理由がありません。",
38
+ "text.omitted-no-test-spec": "no inspection step — {equipment} carries no test specification.",
39
+ "text.omitted-order-fields": "作業指示欄を入れていません。この工程に予定された作業指示がありません。",
40
+ "text.station-runtime-not-connected": "「{step}」の実行がつながっていません。ほかの業務で代わりに実行することはしません。",
41
+ "title.work": "業務"
42
+ }
@@ -0,0 +1,42 @@
1
+ {
2
+ "error.control-already-off": "[통제 해제 승인] 이 화면은 이미 통제 문서가 아닙니다. 이 건을 반려하십시오.",
3
+ "error.control-not-on": "이 화면은 통제 문서가 아닙니다.",
4
+ "error.control-withdrawal-already-raised": "이 화면의 통제 해제가 이미 결재 중입니다.",
5
+ "error.control-withdrawal-carried-missing": "[통제 해제 승인] 승인 건에 화면 설정 정보가 없어 반영하지 못했습니다.",
6
+ "error.control-withdrawal-door-missing": "통제 해제 승인 양식이 설치되어 있지 않습니다.",
7
+ "error.control-withdrawal-needs-approval": "통제 문서의 통제는 「통제 해제 승인」으로만 해제할 수 있습니다.",
8
+ "error.control-withdrawal-reason-missing": "통제 해제 사유를 입력하십시오.",
9
+ "error.control-withdrawal-spec-gone": "[통제 해제 승인] 요청한 화면 설정이 삭제되었습니다. 이 건을 반려하십시오.",
10
+ "error.report-completed-without-counts": "이 공정은 작업지시의 마지막 공정입니다. 완료하려면 양품 수를 적으십시오.",
11
+ "error.station-release-approval-missing": "이 화면은 통제 문서이고 승인된 버전이 없습니다. 발행 승인을 먼저 올리십시오.",
12
+ "error.station-release-approval-stale": "이 화면은 통제 문서이고 승인 뒤에 바뀌었습니다. 발행 승인을 다시 올리십시오.",
13
+ "error.station-release-carried-missing": "[화면 발행 승인] 승인 건에 화면 정보가 없어 반영하지 못했습니다.",
14
+ "error.station-spec-missing": "화면 설정을 찾지 못했습니다.",
15
+ "error.time-range-end-not-after-start": "종료 시각이 시작 시각과 같거나 앞섭니다. 시작 시각보다 늦은 종료 시각을 입력하십시오.",
16
+ "field.board": "보드",
17
+ "field.equipment": "설비",
18
+ "field.kind": "종류",
19
+ "field.maintenance": "정비",
20
+ "field.quality": "품질률",
21
+ "field.station": "작업대",
22
+ "field.subject": "대상",
23
+ "text.check-final-count-has-no-source": "「{step}」을 눌러도 양품 수 없이 기록됩니다. 이 작업대가 지시의 마지막 공정이면 서버가 완료를 거절합니다. 양품 수를 받는 단계를 연결하십시오.",
24
+ "text.check-nobody-reads-this-entry": "「{step}」에 넣은 값을 쓰는 단계가 없습니다.",
25
+ "text.check-notable-has-no-source": "「{step}」을 누르면 {field} 이(가) 「미입력」으로 기록됩니다.",
26
+ "text.check-required-has-no-source": "「{step}」의 필수 칸 {field} 에 출처가 없습니다 — 누르면 실패합니다.",
27
+ "text.check-service-has-no-such-field": "{service} 에 {field} 칸이 없습니다 — 서비스가 바뀌었는지 보십시오.",
28
+ "text.check-source-comes-later": "{field} 값을 주는 「{from}」 단계가 이 단계보다 뒤에 있습니다. 「{from}」을 앞으로 옮기십시오.",
29
+ "text.check-source-does-not-ask": "{field} 이(가) 「{from}」에서 값을 받는데, 그 단계에는 입력 칸이 없습니다.",
30
+ "text.check-source-step-gone": "{field} 이(가) 삭제된 단계에서 값을 받습니다. 그 단계를 다시 추가하거나 출처를 바꾸십시오.",
31
+ "text.check-unknown-axis": "{axis} 라는 값은 없습니다 — 고를 수 있는 것에서 골라 주십시오.",
32
+ "text.check-unknown-axis-did-you-mean": "{axis} 라는 값은 없습니다 — 혹시 {near} 입니까?",
33
+ "text.omitted-many-test-specs": "검사 단계를 넣지 않았습니다 — {equipment} 에 검사 명세가 여럿({specs})이라 어느 것인지 규칙으로 정할 수 없습니다. 단계를 더하고 골라 주십시오.",
34
+ "text.omitted-material-actual": "자재 실적 단계를 넣지 않았습니다. 투입 자재는 레시피에서 정합니다.",
35
+ "text.omitted-no-inspection-runtime": "검사 단계를 넣지 않았습니다. 현장 화면에서 검사 기록을 아직 지원하지 않습니다.",
36
+ "text.omitted-no-report-service": "완료 버튼이 실적을 기록하지 않습니다. 실적 보고 서비스를 찾지 못했습니다.",
37
+ "text.omitted-no-stop-reasons": "일시정지 버튼을 넣지 않았습니다. 등록된 정지 사유가 없습니다.",
38
+ "text.omitted-no-test-spec": "검사 단계를 넣지 않았습니다 — {equipment} 에 검사 명세가 붙어 있지 않습니다.",
39
+ "text.omitted-order-fields": "작업지시 칸을 넣지 않았습니다. 이 공정에 잡힌 작업지시가 없습니다.",
40
+ "text.station-runtime-not-connected": "「{step}」의 실행 연결이 없습니다. 다른 업무로 대신 실행하지 않습니다.",
41
+ "title.work": "업무"
42
+ }
@@ -0,0 +1,42 @@
1
+ {
2
+ "error.control-already-off": "[penarikan balik kawalan] skrin ini bukan lagi dokumen terkawal. tolak item ini.",
3
+ "error.control-not-on": "skrin ini bukan dokumen terkawal.",
4
+ "error.control-withdrawal-already-raised": "penarikan balik kawalan bagi skrin ini sudah menunggu kelulusan.",
5
+ "error.control-withdrawal-carried-missing": "[penarikan balik kawalan] item yang diluluskan tiada tetapan skrin, jadi ia tidak digunakan.",
6
+ "error.control-withdrawal-door-missing": "borang kelulusan penarikan balik kawalan tidak dipasang di kilang ini.",
7
+ "error.control-withdrawal-needs-approval": "kawalan dokumen hanya boleh dikeluarkan melalui kelulusan penarikan balik kawalan.",
8
+ "error.control-withdrawal-reason-missing": "masukkan sebab menarik balik kawalan.",
9
+ "error.control-withdrawal-spec-gone": "[penarikan balik kawalan] tetapan skrin yang diminta tidak lagi wujud. tolak item ini.",
10
+ "error.report-completed-without-counts": "ini operasi terakhir pesanan kerja. masukkan kiraan baik untuk menyelesaikannya.",
11
+ "error.station-release-approval-missing": "skrin ini ialah dokumen terkawal tanpa versi yang diluluskan. hantar kelulusan keluaran dahulu.",
12
+ "error.station-release-approval-stale": "skrin ini ialah dokumen terkawal dan telah diubah selepas kelulusan. hantar kelulusan keluaran sekali lagi.",
13
+ "error.station-release-carried-missing": "[keluaran skrin] item yang diluluskan tiada skrin, jadi ia tidak digunakan.",
14
+ "error.station-spec-missing": "tetapan skrin tidak ditemui.",
15
+ "error.time-range-end-not-after-start": "masa tamat tidak selepas masa mula. masukkan masa tamat yang lebih lewat.",
16
+ "field.board": "papan",
17
+ "field.equipment": "peralatan",
18
+ "field.kind": "jenis",
19
+ "field.maintenance": "penyelenggaraan",
20
+ "field.quality": "kualiti",
21
+ "field.station": "stesen",
22
+ "field.subject": "subjek",
23
+ "text.check-final-count-has-no-source": "「{step}」 merekod tanpa bilangan baik. Jika stesen ini operasi terakhir pesanan, pelayan menolak penyiapan. Sambungkan langkah yang mengambil bilangan baik.",
24
+ "text.check-nobody-reads-this-entry": "tiada langkah menggunakan nilai yang dimasukkan dalam 「{step}」.",
25
+ "text.check-notable-has-no-source": "「{step}」 merekod {field} sebagai 「tidak dimasukkan」.",
26
+ "text.check-required-has-no-source": "「{step}」 has nothing feeding its required {field} — pressing it will fail.",
27
+ "text.check-service-has-no-such-field": "{service} has no {field} — check whether the service changed.",
28
+ "text.check-source-comes-later": "「{from}」, yang membekalkan {field}, datang selepas langkah ini. alihkan 「{from}」 lebih awal.",
29
+ "text.check-source-does-not-ask": "{field} dibekalkan oleh 「{from}」, tetapi langkah itu tiada input.",
30
+ "text.check-source-step-gone": "{field} dibekalkan oleh langkah yang dipadam. tambah semula langkah itu atau tukar sumbernya.",
31
+ "text.check-unknown-axis": "there is no value called {axis} — pick one from the list.",
32
+ "text.check-unknown-axis-did-you-mean": "there is no value called {axis} — did you mean {near}?",
33
+ "text.omitted-many-test-specs": "no inspection step — {equipment} carries {specs}, and no rule can pick which one. add the step and choose.",
34
+ "text.omitted-material-actual": "tiada langkah bahan sebenar. bahan datang daripada resipi.",
35
+ "text.omitted-no-inspection-runtime": "tiada langkah pemeriksaan. skrin stesen belum menyokong rekod pemeriksaan.",
36
+ "text.omitted-no-report-service": "butang siap tidak merekod apa-apa. perkhidmatan laporan pengeluaran tidak ditemui.",
37
+ "text.omitted-no-stop-reasons": "tiada butang jeda. tiada sebab henti didaftarkan.",
38
+ "text.omitted-no-test-spec": "no inspection step — {equipment} carries no test specification.",
39
+ "text.omitted-order-fields": "tiada medan pesanan kerja. belum ada apa dijadualkan pada operasi ini.",
40
+ "text.station-runtime-not-connected": "「{step}」 tiada sambungan pelaksanaan — langkah ini tidak akan dijalankan melalui kerja lain.",
41
+ "title.work": "kerja"
42
+ }
@@ -0,0 +1,42 @@
1
+ {
2
+ "error.control-already-off": "[解除受控审批] 此画面已不是受控文件。请驳回此事项。",
3
+ "error.control-not-on": "此画面不是受控文件。",
4
+ "error.control-withdrawal-already-raised": "此画面的解除受控申请已在审批中。",
5
+ "error.control-withdrawal-carried-missing": "[解除受控审批] 审批事项中没有画面设置信息,未能应用。",
6
+ "error.control-withdrawal-door-missing": "未安装解除受控审批表单。",
7
+ "error.control-withdrawal-needs-approval": "受控文件的受控状态只能通过\"解除受控审批\"解除。",
8
+ "error.control-withdrawal-reason-missing": "请填写解除受控的原因。",
9
+ "error.control-withdrawal-spec-gone": "[解除受控审批] 申请的画面设置已被删除。请驳回此事项。",
10
+ "error.report-completed-without-counts": "该工序是工单的最后一道工序。完工请填写良品数。",
11
+ "error.station-release-approval-missing": "此画面是受控文件,没有已批准的版本。请先提交发布审批。",
12
+ "error.station-release-approval-stale": "此画面是受控文件,批准后已被修改。请重新提交发布审批。",
13
+ "error.station-release-carried-missing": "[画面发布审批] 审批事项中没有画面信息,未能应用。",
14
+ "error.station-spec-missing": "未找到画面设置。",
15
+ "error.time-range-end-not-after-start": "结束时间早于或等于开始时间。请填写晚于开始时间的结束时间。",
16
+ "field.board": "看板",
17
+ "field.equipment": "设备",
18
+ "field.kind": "类型",
19
+ "field.maintenance": "维护",
20
+ "field.quality": "良品率",
21
+ "field.station": "工位",
22
+ "field.subject": "对象",
23
+ "text.check-final-count-has-no-source": "「{step}」会在没有良品数的情况下记录。如果该工位是工单的最后工序,服务器会拒绝完工。请连接接收良品数的步骤。",
24
+ "text.check-nobody-reads-this-entry": "没有步骤使用「{step}」中输入的值。",
25
+ "text.check-notable-has-no-source": "按下「{step}」时,{field} 记录为「未填写」。",
26
+ "text.check-required-has-no-source": "「{step}」 has nothing feeding its required {field} — pressing it will fail.",
27
+ "text.check-service-has-no-such-field": "{service} has no {field} — check whether the service changed.",
28
+ "text.check-source-comes-later": "提供 {field} 的「{from}」步骤在此步骤之后。请将「{from}」前移。",
29
+ "text.check-source-does-not-ask": "{field} 从「{from}」取值,但该步骤没有输入项。",
30
+ "text.check-source-step-gone": "{field} 从已删除的步骤取值。请重新添加该步骤或更改来源。",
31
+ "text.check-unknown-axis": "there is no value called {axis} — pick one from the list.",
32
+ "text.check-unknown-axis-did-you-mean": "there is no value called {axis} — did you mean {near}?",
33
+ "text.omitted-many-test-specs": "no inspection step — {equipment} carries {specs}, and no rule can pick which one. add the step and choose.",
34
+ "text.omitted-material-actual": "未加入物料实绩步骤。投入物料由配方决定。",
35
+ "text.omitted-no-inspection-runtime": "未加入检验步骤。现场画面尚不支持检验记录。",
36
+ "text.omitted-no-report-service": "完成按钮不记录报工。未找到报工服务。",
37
+ "text.omitted-no-stop-reasons": "未加入暂停按钮。没有登记的停机原因。",
38
+ "text.omitted-no-test-spec": "no inspection step — {equipment} carries no test specification.",
39
+ "text.omitted-order-fields": "未加入工单栏。此工序尚未安排工单。",
40
+ "text.station-runtime-not-connected": "「{step}」没有连接执行。不会用其他业务代为执行。",
41
+ "title.work": "业务"
42
+ }