@operato/scene-storage 10.0.0-beta.28 → 10.0.0-beta.31

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 (58) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/dist/asrs-crane-3d.d.ts +10 -0
  3. package/dist/asrs-crane-3d.js +17 -0
  4. package/dist/asrs-crane-3d.js.map +1 -1
  5. package/dist/asrs-crane.d.ts +58 -13
  6. package/dist/asrs-crane.js +120 -16
  7. package/dist/asrs-crane.js.map +1 -1
  8. package/dist/asrs-rack.d.ts +58 -19
  9. package/dist/asrs-rack.js +107 -20
  10. package/dist/asrs-rack.js.map +1 -1
  11. package/dist/box.d.ts +10 -3
  12. package/dist/box.js +1 -2
  13. package/dist/box.js.map +1 -1
  14. package/dist/generic-container-3d.js.map +1 -1
  15. package/dist/generic-container.d.ts +12 -2
  16. package/dist/generic-container.js +1 -2
  17. package/dist/generic-container.js.map +1 -1
  18. package/dist/index.d.ts +3 -0
  19. package/dist/index.js +2 -0
  20. package/dist/index.js.map +1 -1
  21. package/dist/pallet.d.ts +9 -2
  22. package/dist/pallet.js +1 -2
  23. package/dist/pallet.js.map +1 -1
  24. package/dist/parcel.d.ts +10 -3
  25. package/dist/parcel.js +1 -2
  26. package/dist/parcel.js.map +1 -1
  27. package/dist/rack-cell-3d.d.ts +25 -0
  28. package/dist/rack-cell-3d.js +88 -0
  29. package/dist/rack-cell-3d.js.map +1 -0
  30. package/dist/rack-cell.d.ts +64 -0
  31. package/dist/rack-cell.js +197 -0
  32. package/dist/rack-cell.js.map +1 -0
  33. package/dist/spot-3d.js.map +1 -1
  34. package/dist/spot.d.ts +12 -11
  35. package/dist/spot.js +2 -3
  36. package/dist/spot.js.map +1 -1
  37. package/dist/templates/index.d.ts +42 -0
  38. package/dist/templates/index.js +43 -1
  39. package/dist/templates/index.js.map +1 -1
  40. package/package.json +9 -4
  41. package/src/asrs-crane-3d.ts +20 -0
  42. package/src/asrs-crane.ts +153 -17
  43. package/src/asrs-rack.ts +137 -22
  44. package/src/box.ts +15 -5
  45. package/src/generic-container-3d.ts +1 -1
  46. package/src/generic-container.ts +22 -7
  47. package/src/index.ts +3 -0
  48. package/src/pallet.ts +16 -6
  49. package/src/parcel.ts +15 -5
  50. package/src/rack-cell-3d.ts +101 -0
  51. package/src/rack-cell.ts +241 -0
  52. package/src/spot-3d.ts +1 -1
  53. package/src/spot.ts +17 -7
  54. package/src/templates/index.ts +43 -1
  55. package/test/setup.js +279 -0
  56. package/test/test-asrs-crane.ts +319 -0
  57. package/tsconfig.json +2 -1
  58. package/tsconfig.tsbuildinfo +1 -1
@@ -1,6 +1,6 @@
1
1
  /*
2
2
  * things-scene catalog templates for the storage domain — pallet/box/parcel
3
- * variants, ASRS rack/crane, and the virtual `spot` placement marker.
3
+ * variants, ASRS rack/crane, ASRS aisle composite, and the virtual `spot` placement marker.
4
4
  */
5
5
  import spot from './spot.js';
6
6
  const pallet = new URL('../../icons/pallet.png', import.meta.url).href;
@@ -108,6 +108,48 @@ export default [
108
108
  carriageHeight: 100
109
109
  }
110
110
  },
111
+ {
112
+ type: 'group',
113
+ description: 'AS/RS aisle — 4-level rack pair + stacker crane',
114
+ group: 'storage',
115
+ icon: asrsRack,
116
+ model: {
117
+ type: 'group',
118
+ top: 100,
119
+ left: 100,
120
+ width: 840,
121
+ height: 220,
122
+ components: [
123
+ {
124
+ type: 'asrs-rack',
125
+ top: 100,
126
+ left: 100,
127
+ width: 800,
128
+ height: 80,
129
+ levels: 4,
130
+ bays: 8
131
+ },
132
+ {
133
+ type: 'asrs-crane',
134
+ top: 140,
135
+ left: 420,
136
+ width: 60,
137
+ height: 220,
138
+ status: 'idle',
139
+ carriageHeight: 0
140
+ },
141
+ {
142
+ type: 'asrs-rack',
143
+ top: 320,
144
+ left: 100,
145
+ width: 800,
146
+ height: 80,
147
+ levels: 4,
148
+ bays: 8
149
+ }
150
+ ]
151
+ }
152
+ },
111
153
  spot
112
154
  ];
113
155
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/templates/index.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * things-scene catalog templates for the storage domain — pallet/box/parcel\n * variants, ASRS rack/crane, and the virtual `spot` placement marker.\n */\nimport spot from './spot.js'\nconst pallet = new URL('../../icons/pallet.png', import.meta.url).href\nconst box = new URL('../../icons/box.png', import.meta.url).href\nconst parcel = new URL('../../icons/parcel.png', import.meta.url).href\nconst asrsRack = new URL('../../icons/asrs-rack.png', import.meta.url).href\nconst asrsCrane = new URL('../../icons/asrs-crane.png', import.meta.url).href\n\nexport default [\n {\n type: 'pallet',\n description: 'wood pallet (EUR / EPAL)',\n group: 'storage',\n icon: pallet,\n model: {\n type: 'pallet',\n top: 100,\n left: 100,\n width: 80,\n height: 80,\n material: 'wood'\n }\n },\n {\n type: 'pallet',\n description: 'plastic pallet',\n group: 'storage',\n icon: pallet,\n model: {\n type: 'pallet',\n top: 100,\n left: 250,\n width: 80,\n height: 80,\n material: 'plastic'\n }\n },\n {\n type: 'box',\n description: 'wood crate',\n group: 'storage',\n icon: box,\n model: {\n type: 'box',\n top: 100,\n left: 400,\n width: 80,\n height: 80,\n material: 'wood'\n }\n },\n {\n type: 'box',\n description: 'plastic tote',\n group: 'storage',\n icon: box,\n model: {\n type: 'box',\n top: 100,\n left: 520,\n width: 80,\n height: 80,\n material: 'plastic'\n }\n },\n {\n type: 'parcel',\n description: 'cardboard parcel',\n group: 'storage',\n icon: parcel,\n model: {\n type: 'parcel',\n top: 100,\n left: 640,\n width: 60,\n height: 80\n }\n },\n {\n type: 'asrs-rack',\n description: 'AS/RS storage rack (multi-level)',\n group: 'storage',\n icon: asrsRack,\n model: {\n type: 'asrs-rack',\n top: 300,\n left: 100,\n width: 800,\n height: 200,\n levels: 4,\n bays: 5\n }\n },\n {\n type: 'asrs-crane',\n description: 'AS/RS stacker crane',\n group: 'storage',\n icon: asrsCrane,\n model: {\n type: 'asrs-crane',\n top: 550,\n left: 400,\n width: 100,\n height: 200,\n status: 'idle',\n carriageHeight: 100\n }\n },\n spot\n]\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/templates/index.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * things-scene catalog templates for the storage domain — pallet/box/parcel\n * variants, ASRS rack/crane, ASRS aisle composite, and the virtual `spot` placement marker.\n */\nimport spot from './spot.js'\nconst pallet = new URL('../../icons/pallet.png', import.meta.url).href\nconst box = new URL('../../icons/box.png', import.meta.url).href\nconst parcel = new URL('../../icons/parcel.png', import.meta.url).href\nconst asrsRack = new URL('../../icons/asrs-rack.png', import.meta.url).href\nconst asrsCrane = new URL('../../icons/asrs-crane.png', import.meta.url).href\n\nexport default [\n {\n type: 'pallet',\n description: 'wood pallet (EUR / EPAL)',\n group: 'storage',\n icon: pallet,\n model: {\n type: 'pallet',\n top: 100,\n left: 100,\n width: 80,\n height: 80,\n material: 'wood'\n }\n },\n {\n type: 'pallet',\n description: 'plastic pallet',\n group: 'storage',\n icon: pallet,\n model: {\n type: 'pallet',\n top: 100,\n left: 250,\n width: 80,\n height: 80,\n material: 'plastic'\n }\n },\n {\n type: 'box',\n description: 'wood crate',\n group: 'storage',\n icon: box,\n model: {\n type: 'box',\n top: 100,\n left: 400,\n width: 80,\n height: 80,\n material: 'wood'\n }\n },\n {\n type: 'box',\n description: 'plastic tote',\n group: 'storage',\n icon: box,\n model: {\n type: 'box',\n top: 100,\n left: 520,\n width: 80,\n height: 80,\n material: 'plastic'\n }\n },\n {\n type: 'parcel',\n description: 'cardboard parcel',\n group: 'storage',\n icon: parcel,\n model: {\n type: 'parcel',\n top: 100,\n left: 640,\n width: 60,\n height: 80\n }\n },\n {\n type: 'asrs-rack',\n description: 'AS/RS storage rack (multi-level)',\n group: 'storage',\n icon: asrsRack,\n model: {\n type: 'asrs-rack',\n top: 300,\n left: 100,\n width: 800,\n height: 200,\n levels: 4,\n bays: 5\n }\n },\n {\n type: 'asrs-crane',\n description: 'AS/RS stacker crane',\n group: 'storage',\n icon: asrsCrane,\n model: {\n type: 'asrs-crane',\n top: 550,\n left: 400,\n width: 100,\n height: 200,\n status: 'idle',\n carriageHeight: 100\n }\n },\n {\n type: 'group',\n description: 'AS/RS aisle — 4-level rack pair + stacker crane',\n group: 'storage',\n icon: asrsRack,\n model: {\n type: 'group',\n top: 100,\n left: 100,\n width: 840,\n height: 220,\n components: [\n {\n type: 'asrs-rack',\n top: 100,\n left: 100,\n width: 800,\n height: 80,\n levels: 4,\n bays: 8\n },\n {\n type: 'asrs-crane',\n top: 140,\n left: 420,\n width: 60,\n height: 220,\n status: 'idle',\n carriageHeight: 0\n },\n {\n type: 'asrs-rack',\n top: 320,\n left: 100,\n width: 800,\n height: 80,\n levels: 4,\n bays: 8\n }\n ]\n }\n },\n spot\n]\n"]}
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@operato/scene-storage",
3
3
  "description": "Storage-domain components for things-scene (smart factory / logistics) — pallet, box, parcel; AS/RS and shelves planned.",
4
4
  "author": "heartyoh",
5
- "version": "10.0.0-beta.28",
5
+ "version": "10.0.0-beta.31",
6
6
  "type": "module",
7
7
  "main": "dist/index.js",
8
8
  "module": "dist/index.js",
@@ -21,24 +21,29 @@
21
21
  "build": "tsc",
22
22
  "prepublishOnly": "tsc",
23
23
  "lint": "eslint src/ && prettier \"src/**/*.ts\" --check",
24
- "format": "eslint src/ --fix && prettier \"src/**/*.ts\" --write"
24
+ "format": "eslint src/ --fix && prettier \"src/**/*.ts\" --write",
25
+ "test": "mocha --require should --require ./test/setup.js --node-option import=tsx \"test/**/test-*.ts\""
25
26
  },
26
27
  "dependencies": {
27
28
  "@hatiolab/things-scene": "^10.0.0-beta.1",
28
- "@operato/scene-base": "^10.0.0-beta.24",
29
+ "@operato/scene-base": "^10.0.0-beta.31",
29
30
  "three": "^0.183.0"
30
31
  },
31
32
  "devDependencies": {
32
33
  "@hatiolab/prettier-config": "^1.0.0",
34
+ "@types/mocha": "^10.0.0",
33
35
  "@types/three": "^0.183.0",
34
36
  "@typescript-eslint/eslint-plugin": "^8.0.0",
35
37
  "@typescript-eslint/parser": "^8.0.0",
36
38
  "eslint": "^9.18.0",
37
39
  "eslint-config-prettier": "^10.0.1",
40
+ "mocha": "^11.0.0",
38
41
  "prettier": "^3.2.5",
42
+ "should": "^13.2.3",
39
43
  "tslib": "^2.3.1",
44
+ "tsx": "^4.21.0",
40
45
  "typescript": "^5.0.4"
41
46
  },
42
47
  "prettier": "@hatiolab/prettier-config",
43
- "gitHead": "40634f9afc681d852d6028a329cedb51cc4fb94f"
48
+ "gitHead": "fdafbd04fd083a43690be937230c7d96a3ee5da3"
44
49
  }
@@ -167,6 +167,26 @@ export class AsrsCrane3D extends RealObjectGroup {
167
167
  // Place lamp near the corner of the base, away from the mast
168
168
  lampMesh.position.set(width * 0.3, baseY + railH + baseH + lampH / 2, height * 0.3)
169
169
  this.object3d.add(lampMesh)
170
+
171
+ // ── Carriage frame (invisible anchor for carrier attach) ──────────
172
+ // Placed at the top of the shuttle, where cargo rests.
173
+ this._carriageFrame = new THREE.Object3D()
174
+ this._carriageFrame.name = 'crane-carriage-tcp'
175
+ this._carriageFrame.position.set(0, carriageY - carriageH / 2 - shuttleH, 0)
176
+ this.object3d.add(this._carriageFrame)
177
+ }
178
+
179
+ /** Sub-frame where carriers attach during transport (fork tool-centre-point). */
180
+ private _carriageFrame?: THREE.Object3D
181
+
182
+ /**
183
+ * Return the carriage TCP anchor. Carriers attached to this frame will
184
+ * follow carriage movement as `carriageHeight` changes and the crane rebuilds.
185
+ *
186
+ * Callers should re-fetch this after any state change that triggers rebuild.
187
+ */
188
+ getCarriageFrame(): THREE.Object3D | undefined {
189
+ return this._carriageFrame
170
190
  }
171
191
 
172
192
  updateDimension() {}
package/src/asrs-crane.ts CHANGED
@@ -1,13 +1,18 @@
1
1
  /*
2
2
  * Copyright © HatioLab Inc. All rights reserved.
3
3
  */
4
- import { Component, ComponentNature, RealObject, RectPath, Shape, sceneComponent } from '@hatiolab/things-scene'
4
+ import { Component, ComponentNature, ContainerAbstract, ContainerCapacity, RealObject, sceneComponent } from '@hatiolab/things-scene'
5
+ import type { SlotDef, State, Material3D } from '@hatiolab/things-scene'
5
6
  import {
7
+ CarrierHolder,
6
8
  Legendable,
9
+ Mover,
7
10
  Placeable,
11
+ type AttachFrame,
8
12
  type Alignment,
9
13
  type Heights,
10
14
  type LegendBinding,
15
+ type MoveOptions,
11
16
  type PlacementArchetype
12
17
  } from '@operato/scene-base'
13
18
 
@@ -25,6 +30,18 @@ import { AsrsCrane3D } from './asrs-crane-3d.js'
25
30
  */
26
31
  export type AsrsCraneStatus = 'idle' | 'moving' | 'loading' | 'unloading' | 'error'
27
32
 
33
+ /** AsrsCrane 컴포넌트 state */
34
+ export interface AsrsCraneState extends State {
35
+ // ── 운영 상태 ──
36
+ status?: AsrsCraneStatus
37
+
38
+ // ── 액추에이터 ──
39
+ carriageHeight?: number
40
+
41
+ // ── 3D 재질 ──
42
+ material3d?: Material3D
43
+ }
44
+
28
45
  const BODY_LEGEND = {
29
46
  idle: '#888',
30
47
  moving: '#aabbcc',
@@ -72,27 +89,40 @@ const NATURE: ComponentNature = {
72
89
  help: 'scene/component/asrs-crane'
73
90
  }
74
91
 
75
- const Base = Legendable(Placeable(RectPath(Shape))) as unknown as typeof Component
76
-
92
+ // Mixin chain: Mover → CarrierHolder → ContainerCapacity → Legendable Placeable ContainerAbstract
93
+ //
94
+ // Mover: pick / place / pickAndPlace / moveTo / engage primitives
95
+ // CarrierHolder: attachPointFor() — where the carrier sits on the crane (carriage fork)
96
+ // ContainerCapacity: receive() / dispatch() / canReceive() / slots — slot tracking +
97
+ // TRANSFER_SLOT_KEY bookkeeping during transit
98
+ // Legendable: status → bodyColor / lampEmissive colour mapping
99
+ // Placeable: floor-archetype 3D positioning
100
+ // ContainerAbstract: child management — carrier becomes a child while in transit
101
+ //
102
+ // Note: ContainerAbstract replaces Shape. The 2D outline is drawn manually in
103
+ // render() below (a simple top-down rectangle), matching the old Shape output
104
+ // without the Shape base-class overhead.
77
105
  /**
78
106
  * AsrsCrane — the stacker / retrieval crane that runs in the aisle of an
79
107
  * AS/RS, moving cargo between the load port and the rack cells.
80
108
  *
81
109
  * Structure: a tall vertical mast that translates along a floor + ceiling
82
110
  * rail (the aisle), with a carriage that slides up/down the mast carrying a
83
- * shuttle / forks. The whole assembly's footprint is narrow (mast width)
84
- * but its visual height is full ceiling — by far the tallest single
85
- * component in a typical scene.
111
+ * shuttle / forks.
86
112
  *
87
- * Currently Shape-based (no children). The carrier the crane is *currently
88
- * carrying* is best modeled via data binding (a `currentCarrier` data field
89
- * on the crane looked up to a Pallet/Box elsewhere in the scene), as in
90
- * fmsim's CarrierManager pattern. Adding the carrier as a child would mix
91
- * static placement with the dynamic data-driven flow we deliberately keep
92
- * separate (see Phase A4 commit notes).
113
+ * **Monitoring mode**: crane status is driven by data binding
114
+ * (`state.status`, `state.carriageHeight`). The carrier is referenced
115
+ * via data binding it is NOT a child of the crane in monitoring mode.
116
+ *
117
+ * **Simulation mode**: call `crane.pick(carrier)` / `crane.place(carrier, rackCell)`
118
+ * (or `crane.pickAndPlace(carrier, rackCell)`). Mover handles navigation +
119
+ * engage + reparent. During transit the carrier IS a child of the crane.
93
120
  */
94
121
  @sceneComponent('asrs-crane')
95
- export default class AsrsCrane extends Base {
122
+ export default class AsrsCrane extends Mover(CarrierHolder(ContainerCapacity(Legendable(Placeable(ContainerAbstract))))) {
123
+ declare state: AsrsCraneState
124
+ declare _realObject?: AsrsCrane3D
125
+
96
126
  static legends: Record<string, LegendBinding> = {
97
127
  bodyColor: { from: 'status', legend: BODY_LEGEND },
98
128
  lampEmissive: { from: 'status', legend: LAMP_EMISSIVE_LEGEND }
@@ -102,6 +132,9 @@ export default class AsrsCrane extends Base {
102
132
  static align: Alignment = 'bottom'
103
133
  static defaultDepth = (h: Heights) => h.ceiling - h.floor
104
134
 
135
+ /** Yaw offset: crane model is drawn with the aisle axis along X (right = forward). */
136
+ static yawOffset = 0
137
+
105
138
  get nature() {
106
139
  return NATURE
107
140
  }
@@ -110,21 +143,124 @@ export default class AsrsCrane extends Base {
110
143
  return []
111
144
  }
112
145
 
146
+ // ── ContainerCapacity ─────────────────────────────────────────────────────
147
+
148
+ /** Stacker crane carries at most one load at a time on its forks. */
149
+ get slots(): SlotDef[] {
150
+ return [{ id: 'forks', maxCount: 1 }]
151
+ }
152
+
153
+ // ── CarrierHolder — attach frame (carriage fork position) ─────────────────
154
+
155
+ /**
156
+ * Return the 3D attach frame on the crane's carriage (fork tip).
157
+ * Carriers are attached here while the crane is in transit (pick phase).
158
+ *
159
+ * The AsrsCrane3D exposes `getCarriageFrame()` — a sub-Object3D that
160
+ * tracks the carriage height and sits at the fork TCP. If the 3D object
161
+ * isn't built yet (e.g. before scene initialization), fall back to the
162
+ * crane's own object3d centre.
163
+ */
164
+ attachPointFor(carrier: Component): AttachFrame | null {
165
+ const ro = this._realObject
166
+ const frame = ro?.getCarriageFrame?.()
167
+ if (frame) {
168
+ const carrierDepth = resolveCarrierDepth(carrier)
169
+ return { attach: frame, localPosition: { x: 0, y: carrierDepth / 2, z: 0 } }
170
+ }
171
+ const root = this._realObject?.object3d
172
+ if (!root) return null
173
+ return { attach: root }
174
+ }
175
+
176
+ // ── Mover overrides ───────────────────────────────────────────────────────
177
+
178
+ /**
179
+ * Domain-specific actuation between arrival and reparent.
180
+ *
181
+ * Simulation sequence for PICK:
182
+ * 1. Mover.pick() navigates crane to carrier position (moveTo).
183
+ * 2. engage('pick') → snap carriage height + status 'loading'.
184
+ * 3. Carrier is reparented to crane (becomes child).
185
+ *
186
+ * For now: set status and snap carriage height. A full ASRS simulation
187
+ * would tween the carriageHeight here (animate AsrsCrane3D).
188
+ *
189
+ * Status lifecycle:
190
+ * idle → (moveTo running) → engage fires → loading/unloading → (reparent) → idle
191
+ * The 'moving' state is not set from Mover.moveTo() because TypeScript
192
+ * can't call super.moveTo() on an `: any`-typed mixin. WCS data binding
193
+ * sets 'moving' in monitoring mode; override pick()/place() to set it
194
+ * in full simulation environments.
195
+ */
196
+ async engage(
197
+ target: Component,
198
+ kind: 'pick' | 'place',
199
+ _options: MoveOptions = {}
200
+ ): Promise<void> {
201
+ if (kind === 'pick') {
202
+ this.setState({ status: 'loading' as AsrsCraneStatus })
203
+ const carrierY = resolveCarrierCenterY(target)
204
+ if (carrierY !== null) {
205
+ this.setState({ carriageHeight: carrierY })
206
+ }
207
+ } else {
208
+ this.setState({ status: 'unloading' as AsrsCraneStatus })
209
+ }
210
+ // In a full simulation: await carriage-motion tween here.
211
+ }
212
+
213
+ // ── Domain aliases ────────────────────────────────────────────────────────
214
+
215
+ /** Fetch a carrier from a rack cell (semantically = pick). */
216
+ fetch(carrier: Component, options?: MoveOptions): Promise<void> {
217
+ return this.pick(carrier, options)
218
+ }
219
+
220
+ /** Deposit a carrier into a rack cell (semantically = place). */
221
+ deposit(carrier: Component, cell: Component, options?: MoveOptions): Promise<void> {
222
+ return this.place(carrier, cell, options)
223
+ }
224
+
225
+ // ── 2D rendering ─────────────────────────────────────────────────────────
226
+
113
227
  /**
114
228
  * 2D — top-down rectangle showing the crane's footprint along the aisle.
115
229
  * The crane is much taller than wide, so the 2D mark is small.
116
230
  */
117
231
  render(ctx: CanvasRenderingContext2D) {
118
232
  const { width, height, left, top } = this.state
233
+ const fillColor = (this.state.bodyColor as string) || '#888'
234
+ ctx.save()
235
+ ctx.fillStyle = fillColor
119
236
  ctx.beginPath()
120
237
  ctx.rect(left, top, width, height)
238
+ ctx.fill()
239
+ ctx.restore()
121
240
  }
122
241
 
123
- get fillStyle() {
124
- return (this.state.bodyColor as string) || '#888'
125
- }
242
+ // ── 3D ───────────────────────────────────────────────────────────────────
126
243
 
127
244
  buildRealObject(): RealObject | undefined {
128
- return new AsrsCrane3D(this as any)
245
+ return new AsrsCrane3D(this)
129
246
  }
130
247
  }
248
+
249
+ function resolveCarrierDepth(c: Component): number {
250
+ const eff = (c as any)._realObject?.effectiveDepth
251
+ if (typeof eff === 'number' && Number.isFinite(eff)) return eff
252
+ return numOr((c as any)?.state?.depth, 0)
253
+ }
254
+
255
+ function resolveCarrierCenterY(c: Component): number | null {
256
+ const pos = (c as any).state
257
+ if (!pos) return null
258
+ // zPos is the 3D Y center of a Placeable component in things-scene
259
+ const zPos = numOr(pos.zPos, NaN)
260
+ if (!Number.isNaN(zPos)) return zPos
261
+ return null
262
+ }
263
+
264
+ function numOr(v: unknown, dflt: number): number {
265
+ return typeof v === 'number' && Number.isFinite(v) ? v : dflt
266
+ }
package/src/asrs-rack.ts CHANGED
@@ -2,8 +2,13 @@
2
2
  * Copyright © HatioLab Inc. All rights reserved.
3
3
  */
4
4
  import { Component, ComponentNature, ContainerAbstract, RealObject, sceneComponent } from '@hatiolab/things-scene'
5
+ import type { State, Material3D } from '@hatiolab/things-scene'
5
6
  import {
7
+ CellContainer,
8
+ CellMap,
9
+ CarrierHolder,
6
10
  Placeable,
11
+ type AttachFrame,
7
12
  type Alignment,
8
13
  type Heights,
9
14
  type PlacementArchetype
@@ -11,6 +16,19 @@ import {
11
16
 
12
17
  import { AsrsRack3D } from './asrs-rack-3d.js'
13
18
 
19
+ /** AsrsRack 컴포넌트 state */
20
+ export interface AsrsRackState extends State {
21
+ // ── 토폴로지 ──
22
+ bays?: number
23
+ levels?: number
24
+
25
+ // ── 디버그 ──
26
+ debugCells?: boolean
27
+
28
+ // ── 3D 재질 ──
29
+ material3d?: Material3D
30
+ }
31
+
14
32
  const NATURE: ComponentNature = {
15
33
  mutable: false,
16
34
  resizable: true,
@@ -35,35 +53,33 @@ const NATURE: ComponentNature = {
35
53
  // `ContainerAbstract` (not `Container`) — Container = MixinHTMLElement(ContainerAbstract),
36
54
  // which forces `isHTMLElement(): true` and trips the 3D pipeline's
37
55
  // addObject DOM-skip gate. ASRS rack lives only in the 3D scene graph.
38
- const Base = Placeable(ContainerAbstract) as unknown as typeof Component
39
-
56
+ //
57
+ // Mixin chain: CellContainer → CarrierHolder → Placeable → ContainerAbstract
58
+ // CellContainer: cell topology (cellMap, cell(), findAvailableCell(), occupiedCellIds())
59
+ // CarrierHolder: 3D attach-point protocol (attachPointFor, containable gates)
60
+ // Placeable: floor-archetype positioning
61
+ // ContainerAbstract: child component management
40
62
  /**
41
63
  * AsrsRack — a multi-level high-bay storage rack, the structural backbone of
42
64
  * an AS/RS (Automated Storage / Retrieval System).
43
65
  *
44
66
  * `levels` × `bays` cells form a vertical grid. Each cell holds one logistics
45
67
  * package (Pallet / Box / Parcel). A pair of AsrsRacks separated by an aisle
46
- * (where an AsrsCrane runs) is the typical AS/RS configuration; v1 ships the
47
- * single-rack unit and lets users compose multi-rack systems by placing them
48
- * side by side. A future `AsrsAisle` composite may bundle the pair + crane.
68
+ * (where an AsrsCrane runs) is the typical AS/RS configuration.
49
69
  *
50
- * **Placement**: `floor` archetype, full ceiling depth by default AS/RS
51
- * racks typically span floor to ceiling, with levels sized to fit the tallest
52
- * pallet load. Users can shorten via explicit `state.depth` for warehouses
53
- * with smaller envelopes.
70
+ * **Monitoring mode** (default): pallets/boxes are direct children of the rack,
71
+ * placed by the WCS data binding. No RackCell children are created.
54
72
  *
55
- * **Container-based**. Cells host stored cargo as children each child's
56
- * left/top within the rack's bounds determines which cell it occupies. The
57
- * stacking pass in `Placeable.computeDefaultZPos` ensures each child cargo's
58
- * z lands on the rack's overall bottom (parent.zPos + parent.depth = ceiling),
59
- * which isn't quite cell-level resolution — true per-cell z positioning is
60
- * a v3 concern (the cargo would need to know which cell-row it's in).
73
+ * **Simulation mode**: call `rack._buildCells()` after placing the rack on the
74
+ * scene. This creates RackCell children at the correct 3D positions. The
75
+ * AsrsCrane then navigates to individual RackCells for pick-and-place.
61
76
  *
62
- * No Legendable for v1 racks are passive structures; their per-cell
63
- * occupancy state is implicit in the children, not a status flag.
77
+ * **Placement**: `floor` archetype, full ceiling depth by default.
64
78
  */
65
79
  @sceneComponent('asrs-rack')
66
- export default class AsrsRack extends Base {
80
+ export default class AsrsRack extends CellContainer(CarrierHolder(Placeable(ContainerAbstract))) {
81
+ declare state: AsrsRackState
82
+
67
83
  static placement: PlacementArchetype = 'floor'
68
84
  static align: Alignment = 'bottom'
69
85
  static defaultDepth = (h: Heights) => h.ceiling - h.floor
@@ -76,13 +92,110 @@ export default class AsrsRack extends Base {
76
92
  return []
77
93
  }
78
94
 
79
- /** Operation cargo (pallets / boxes / parcels) goes in the rack's cells. */
80
- containable(component: Component) {
95
+ // ── CellContainer ─────────────────────────────────────────────────────────
96
+
97
+ /**
98
+ * Derive the cell topology from the rack's current dimensions and bay/level
99
+ * counts. The CellMap is rebuilt fresh each time (state changes trigger
100
+ * re-reads via things-scene's invalidation pipeline).
101
+ *
102
+ * Coordinate convention (matches things-scene 3D):
103
+ * X = bay axis (left → right)
104
+ * Y = level axis (floor → ceiling, the rack's `depth` state property)
105
+ * Z = row axis (front → back, the rack's `height` state property)
106
+ */
107
+ get cellMap(): CellMap {
108
+ const bays = Math.max(1, Math.floor((this.state.bays as number) || 5))
109
+ const levels = Math.max(1, Math.floor((this.state.levels as number) || 4))
110
+ const width = (this.state.width as number) || 1000
111
+ const rackDepth = (this.state.depth as number) || 3000 // Y: floor→ceiling
112
+ const rackHeight = (this.state.height as number) || 600 // Z: front→back
113
+
114
+ return CellMap.grid({
115
+ bays,
116
+ rows: 1,
117
+ levels,
118
+ bayWidth: width / bays,
119
+ rowDepth: rackHeight,
120
+ levelHeight: rackDepth / levels
121
+ })
122
+ }
123
+
124
+ /**
125
+ * Create RackCell child components for each cell in the CellMap.
126
+ *
127
+ * Called explicitly to enter simulation mode — monitoring-mode racks
128
+ * never call this (pallets are direct children, no explicit cells).
129
+ *
130
+ * Idempotent: removes existing rack-cell children first.
131
+ */
132
+ _buildCells(): void {
133
+ // Remove existing rack-cell children
134
+ const existing = (this.components as Component[] | undefined) ?? []
135
+ for (const child of [...existing]) {
136
+ if ((child as any).state?.type === 'rack-cell') {
137
+ this.removeComponent(child)
138
+ }
139
+ }
140
+
141
+ // Create a RackCell for each cell in the map
142
+ const RackCellClass = (Component as any).register('rack-cell') as (new (...args: any[]) => Component) | undefined
143
+ if (!RackCellClass) {
144
+ console.warn('AsrsRack._buildCells: rack-cell type not registered. Import rack-cell.ts first.')
145
+ return
146
+ }
147
+
148
+ const context = this._app
149
+ for (const cell of this.cellMap.cells) {
150
+ const model = {
151
+ type: 'rack-cell',
152
+ cellId: cell.id,
153
+ width: cell.size.width,
154
+ height: cell.size.depth, // 2D height = 3D Z depth
155
+ depth: cell.size.height // 3D Y = level height
156
+ }
157
+ const rackCell = new RackCellClass(model, context)
158
+ this.addComponent(rackCell)
159
+ }
160
+ }
161
+
162
+ // ── Container gates ───────────────────────────────────────────────────────
163
+
164
+ /**
165
+ * Allow:
166
+ * - Carriable components (pallets, boxes, parcels) — direct children in monitoring mode.
167
+ * - RackCell — created by _buildCells() in simulation mode.
168
+ *
169
+ * Block:
170
+ * - Everything else (sensors, labels, etc. can be siblings of the rack, not children).
171
+ */
172
+ containable(component: Component): boolean {
173
+ if ((component as any).state?.type === 'rack-cell') return true
81
174
  const archetype = (component.constructor as any).placement
82
175
  if (archetype === 'operation') return true
83
- return component.isDescendible(this as any)
176
+ return component.isDescendible(this)
177
+ }
178
+
179
+ // ── CarrierHolder — attach frame for direct carrier children ─────────────
180
+
181
+ /**
182
+ * Attach frame for carriers that are DIRECT children of the rack
183
+ * (monitoring mode, where pallets go directly into the rack without
184
+ * explicit RackCell components).
185
+ *
186
+ * In simulation mode, carriers become children of their RackCell,
187
+ * and each RackCell provides its own attachPointFor(). So this method
188
+ * is only invoked on direct-child carriers in monitoring mode — it
189
+ * returns the rack's own object3d as the attach frame (default behavior).
190
+ */
191
+ attachPointFor(_carrier: Component): AttachFrame | null {
192
+ const root = this._realObject?.object3d
193
+ if (!root) return null
194
+ return { attach: root }
84
195
  }
85
196
 
197
+ // ── 2D rendering ─────────────────────────────────────────────────────────
198
+
86
199
  /**
87
200
  * 2D — top-down rectangle showing the rack footprint, with subdivisions
88
201
  * suggesting the bay layout (lines parallel to the aisle).
@@ -106,7 +219,9 @@ export default class AsrsRack extends Base {
106
219
  return '#a0a0a8'
107
220
  }
108
221
 
222
+ // ── 3D ───────────────────────────────────────────────────────────────────
223
+
109
224
  buildRealObject(): RealObject | undefined {
110
- return new AsrsRack3D(this as any)
225
+ return new AsrsRack3D(this)
111
226
  }
112
227
  }
package/src/box.ts CHANGED
@@ -1,7 +1,8 @@
1
1
  /*
2
2
  * Copyright © HatioLab Inc. All rights reserved.
3
3
  */
4
- import { Component, ComponentNature, RealObject, RectPath, Shape, sceneComponent } from '@hatiolab/things-scene'
4
+ import { ComponentNature, RealObject, RectPath, Shape, sceneComponent } from '@hatiolab/things-scene'
5
+ import type { State, Material3D } from '@hatiolab/things-scene'
5
6
  import {
6
7
  Carriable,
7
8
  Legendable,
@@ -26,6 +27,15 @@ import { Box3D } from './box-3d.js'
26
27
  */
27
28
  export type BoxMaterial = 'wood' | 'plastic'
28
29
 
30
+ /** Box 컴포넌트 state */
31
+ export interface BoxState extends State {
32
+ // ── 외관 ──
33
+ material?: BoxMaterial
34
+
35
+ // ── 3D 재질 ──
36
+ material3d?: Material3D
37
+ }
38
+
29
39
  const BODY_LEGEND = {
30
40
  wood: '#a87644',
31
41
  plastic: '#3a5078',
@@ -54,8 +64,6 @@ const NATURE: ComponentNature = {
54
64
 
55
65
  // Carriable: a box can be a child of any CarrierHolder (Pallet for stacking,
56
66
  // AGV deck, robot-arm gripper, Spot for staging).
57
- const Base = Carriable(Legendable(Placeable(RectPath(Shape)))) as unknown as typeof Component
58
-
59
67
  /**
60
68
  * Box — a generic stackable container for goods. Wood crate or plastic tote
61
69
  * variants distinguished by `material` prop.
@@ -65,7 +73,9 @@ const Base = Carriable(Legendable(Placeable(RectPath(Shape)))) as unknown as typ
65
73
  * scene-tree). If a future use case needs nested boxes, extend Container.
66
74
  */
67
75
  @sceneComponent('box')
68
- export default class Box extends Base {
76
+ export default class Box extends Carriable(Legendable(Placeable(RectPath(Shape)))) {
77
+ declare state: BoxState
78
+
69
79
  static legends: Record<string, LegendBinding> = {
70
80
  bodyColor: { from: 'material', legend: BODY_LEGEND }
71
81
  }
@@ -94,6 +104,6 @@ export default class Box extends Base {
94
104
  }
95
105
 
96
106
  buildRealObject(): RealObject | undefined {
97
- return new Box3D(this as any)
107
+ return new Box3D(this)
98
108
  }
99
109
  }
@@ -31,7 +31,7 @@ export class GenericContainer3D extends RealObjectGLTF {
31
31
  }
32
32
 
33
33
  private _applyActuators() {
34
- const state = this.component.state as any
34
+ const state = this.component.state
35
35
  applyActuators(this, state.actuators, state.actuatorValues)
36
36
  }
37
37