@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.
- package/CHANGELOG.md +29 -0
- package/dist/asrs-crane-3d.d.ts +10 -0
- package/dist/asrs-crane-3d.js +17 -0
- package/dist/asrs-crane-3d.js.map +1 -1
- package/dist/asrs-crane.d.ts +58 -13
- package/dist/asrs-crane.js +120 -16
- package/dist/asrs-crane.js.map +1 -1
- package/dist/asrs-rack.d.ts +58 -19
- package/dist/asrs-rack.js +107 -20
- package/dist/asrs-rack.js.map +1 -1
- package/dist/box.d.ts +10 -3
- package/dist/box.js +1 -2
- package/dist/box.js.map +1 -1
- package/dist/generic-container-3d.js.map +1 -1
- package/dist/generic-container.d.ts +12 -2
- package/dist/generic-container.js +1 -2
- package/dist/generic-container.js.map +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/pallet.d.ts +9 -2
- package/dist/pallet.js +1 -2
- package/dist/pallet.js.map +1 -1
- package/dist/parcel.d.ts +10 -3
- package/dist/parcel.js +1 -2
- package/dist/parcel.js.map +1 -1
- package/dist/rack-cell-3d.d.ts +25 -0
- package/dist/rack-cell-3d.js +88 -0
- package/dist/rack-cell-3d.js.map +1 -0
- package/dist/rack-cell.d.ts +64 -0
- package/dist/rack-cell.js +197 -0
- package/dist/rack-cell.js.map +1 -0
- package/dist/spot-3d.js.map +1 -1
- package/dist/spot.d.ts +12 -11
- package/dist/spot.js +2 -3
- package/dist/spot.js.map +1 -1
- package/dist/templates/index.d.ts +42 -0
- package/dist/templates/index.js +43 -1
- package/dist/templates/index.js.map +1 -1
- package/package.json +9 -4
- package/src/asrs-crane-3d.ts +20 -0
- package/src/asrs-crane.ts +153 -17
- package/src/asrs-rack.ts +137 -22
- package/src/box.ts +15 -5
- package/src/generic-container-3d.ts +1 -1
- package/src/generic-container.ts +22 -7
- package/src/index.ts +3 -0
- package/src/pallet.ts +16 -6
- package/src/parcel.ts +15 -5
- package/src/rack-cell-3d.ts +101 -0
- package/src/rack-cell.ts +241 -0
- package/src/spot-3d.ts +1 -1
- package/src/spot.ts +17 -7
- package/src/templates/index.ts +43 -1
- package/test/setup.js +279 -0
- package/test/test-asrs-crane.ts +319 -0
- package/tsconfig.json +2 -1
- package/tsconfig.tsbuildinfo +1 -1
package/dist/templates/index.js
CHANGED
|
@@ -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.
|
|
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.
|
|
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": "
|
|
48
|
+
"gitHead": "fdafbd04fd083a43690be937230c7d96a3ee5da3"
|
|
44
49
|
}
|
package/src/asrs-crane-3d.ts
CHANGED
|
@@ -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,
|
|
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
|
-
|
|
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.
|
|
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
|
-
*
|
|
88
|
-
*
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
*
|
|
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
|
|
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
|
-
|
|
124
|
-
return (this.state.bodyColor as string) || '#888'
|
|
125
|
-
}
|
|
242
|
+
// ── 3D ───────────────────────────────────────────────────────────────────
|
|
126
243
|
|
|
127
244
|
buildRealObject(): RealObject | undefined {
|
|
128
|
-
return new AsrsCrane3D(this
|
|
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
|
-
|
|
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
|
|
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
|
-
* **
|
|
51
|
-
*
|
|
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
|
-
* **
|
|
56
|
-
*
|
|
57
|
-
*
|
|
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
|
-
*
|
|
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
|
|
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
|
-
|
|
80
|
-
|
|
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
|
|
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
|
|
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 {
|
|
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
|
|
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
|
|
107
|
+
return new Box3D(this)
|
|
98
108
|
}
|
|
99
109
|
}
|