@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/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,35 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
## [10.0.0-beta.31](https://github.com/things-scene/operato-scene/compare/v10.0.0-beta.30...v10.0.0-beta.31) (2026-05-08)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### :house: Code Refactoring
|
|
10
|
+
|
|
11
|
+
* _realObject 캐스트 16개 제거 (Phase C Round 2 후속) ([8616c6c](https://github.com/things-scene/operato-scene/commit/8616c6c45e38a9bbcc244a77cee53479a756df85))
|
|
12
|
+
* 39개 buildRealObject 캐스트 제거 (Phase D Round 4 후속) ([b86681c](https://github.com/things-scene/operato-scene/commit/b86681c07d7bf41cb93ade9855afb21902ed8186))
|
|
13
|
+
* **mfg/transport/storage:** typed state Round 2 — 14개 컴포넌트 적용 ([f1765f6](https://github.com/things-scene/operato-scene/commit/f1765f62d12cf4c20ea26a2297e98069b9621a45))
|
|
14
|
+
* 도메인 mixin 캐스트 추가 정리 7개 (Phase D Round 4 후속) ([1546b71](https://github.com/things-scene/operato-scene/commit/1546b7183969a38952872a5327b8329c2fa1ab7a))
|
|
15
|
+
* 불필요한 (this as any) 캐스트 52개 제거 (Phase C) ([989dd11](https://github.com/things-scene/operato-scene/commit/989dd1147f4c768167fdc8b9a9e98acb0495a408))
|
|
16
|
+
* 함수 인자 (this as any) 13개 제거 (Phase D Round 4 후속) ([b0a400c](https://github.com/things-scene/operato-scene/commit/b0a400c9716fdec22518cc6ad0bd1c1e69bda89d))
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
## [10.0.0-beta.30](https://github.com/things-scene/operato-scene/compare/v10.0.0-beta.29...v10.0.0-beta.30) (2026-05-07)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
### :rocket: New Features
|
|
24
|
+
|
|
25
|
+
* **conveyance:** InductStation + CrossBeltLine + Chute 3D 시뮬레이션 전면 개선 ([e729b9d](https://github.com/things-scene/operato-scene/commit/e729b9dbb736ce1f12a4de789738ec1e58bf1dbc))
|
|
26
|
+
* **scene-base/storage/conveyance:** 배치 전략 + RackCell + ItemGenerator 신규 추가 ([7f1f1a4](https://github.com/things-scene/operato-scene/commit/7f1f1a456bbe6aedcf1f85b91a9cc72fe970d292))
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
### :house: Code Refactoring
|
|
30
|
+
|
|
31
|
+
* Phase A 후속 — _Base 캐스트 워크어라운드 95개 제거 ([3fce16c](https://github.com/things-scene/operato-scene/commit/3fce16c5c6d727e043cadac39bf52b9c331db94a))
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
|
|
6
35
|
## [10.0.0-beta.28](https://github.com/things-scene/operato-scene/compare/v10.0.0-beta.27...v10.0.0-beta.28) (2026-05-05)
|
|
7
36
|
|
|
8
37
|
|
package/dist/asrs-crane-3d.d.ts
CHANGED
|
@@ -1,6 +1,16 @@
|
|
|
1
|
+
import * as THREE from 'three';
|
|
1
2
|
import { RealObjectGroup } from '@hatiolab/things-scene';
|
|
2
3
|
export declare class AsrsCrane3D extends RealObjectGroup {
|
|
3
4
|
build(): void;
|
|
5
|
+
/** Sub-frame where carriers attach during transport (fork tool-centre-point). */
|
|
6
|
+
private _carriageFrame?;
|
|
7
|
+
/**
|
|
8
|
+
* Return the carriage TCP anchor. Carriers attached to this frame will
|
|
9
|
+
* follow carriage movement as `carriageHeight` changes and the crane rebuilds.
|
|
10
|
+
*
|
|
11
|
+
* Callers should re-fetch this after any state change that triggers rebuild.
|
|
12
|
+
*/
|
|
13
|
+
getCarriageFrame(): THREE.Object3D | undefined;
|
|
4
14
|
updateDimension(): void;
|
|
5
15
|
onchange(after: Record<string, unknown>, before: Record<string, unknown>): void;
|
|
6
16
|
updateAlpha(): void;
|
package/dist/asrs-crane-3d.js
CHANGED
|
@@ -144,6 +144,23 @@ export class AsrsCrane3D extends RealObjectGroup {
|
|
|
144
144
|
// Place lamp near the corner of the base, away from the mast
|
|
145
145
|
lampMesh.position.set(width * 0.3, baseY + railH + baseH + lampH / 2, height * 0.3);
|
|
146
146
|
this.object3d.add(lampMesh);
|
|
147
|
+
// ── Carriage frame (invisible anchor for carrier attach) ──────────
|
|
148
|
+
// Placed at the top of the shuttle, where cargo rests.
|
|
149
|
+
this._carriageFrame = new THREE.Object3D();
|
|
150
|
+
this._carriageFrame.name = 'crane-carriage-tcp';
|
|
151
|
+
this._carriageFrame.position.set(0, carriageY - carriageH / 2 - shuttleH, 0);
|
|
152
|
+
this.object3d.add(this._carriageFrame);
|
|
153
|
+
}
|
|
154
|
+
/** Sub-frame where carriers attach during transport (fork tool-centre-point). */
|
|
155
|
+
_carriageFrame;
|
|
156
|
+
/**
|
|
157
|
+
* Return the carriage TCP anchor. Carriers attached to this frame will
|
|
158
|
+
* follow carriage movement as `carriageHeight` changes and the crane rebuilds.
|
|
159
|
+
*
|
|
160
|
+
* Callers should re-fetch this after any state change that triggers rebuild.
|
|
161
|
+
*/
|
|
162
|
+
getCarriageFrame() {
|
|
163
|
+
return this._carriageFrame;
|
|
147
164
|
}
|
|
148
165
|
updateDimension() { }
|
|
149
166
|
onchange(after, before) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"asrs-crane-3d.js","sourceRoot":"","sources":["../src/asrs-crane-3d.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * Copyright © HatioLab Inc. All rights reserved.\n *\n * AsrsCrane 3D — stacker crane for AS/RS aisles.\n *\n * LO-POLY but structurally complete. The signature parts:\n *\n * - tall vertical mast (the dominant element — runs floor → ceiling)\n * - base unit (motor housing at floor level, runs on the floor rail)\n * - top guide (sliding block at ceiling that runs on the ceiling rail —\n * stabilizes the mast under acceleration)\n * - carriage (horizontal block that slides up/down the mast at\n * `state.carriageHeight`)\n * - shuttle/fork attachment on the carriage (extends sideways into a cell\n * to extract / insert a pallet)\n * - status lamp on top of the base unit\n *\n * The base unit + top guide combo is what visually distinguishes a stacker\n * crane from a forklift — the stacker is *constrained* to the aisle, riding\n * rails top and bottom, and that constraint is the visual signature.\n */\n\nimport * as THREE from 'three'\nimport * as BufferGeometryUtils from 'three/examples/jsm/utils/BufferGeometryUtils.js'\nimport { RealObjectGroup } from '@hatiolab/things-scene'\n\nconst MAST_COLOR = 0x4a5060\nconst BASE_COLOR = 0x33394a\nconst SHUTTLE_COLOR = 0x222233\nconst RAIL_COLOR = 0x222222\n\nexport class AsrsCrane3D extends RealObjectGroup {\n build() {\n super.build()\n\n const { width, height, depth = 200 } = this.component.state\n const bodyColor = (this.component.state.bodyColor as string) || '#888'\n const emissiveColor = (this.component.state.lampEmissive as string) || '#222222'\n const status = this.component.state.status\n const lampIntensity = status && status !== 'idle' ? 1.5 : 0.2\n // Clamp carriageHeight to the available mast travel — keeps the carriage\n // inside the shaft even if state.carriageHeight is stale (e.g. left over\n // from an older height-scale convention).\n const carriageRaw = (this.component.state.carriageHeight as number) ?? depth * 0.4\n const carriageHeight = Math.max(0, Math.min(carriageRaw, depth * 0.85))\n\n const baseY = -depth / 2\n\n // Proportions\n const baseH = depth * 0.10\n const topGuideH = depth * 0.05\n const mastH = depth - baseH - topGuideH\n const mastW = width * 0.35\n const mastD = height * 0.35\n\n const bodyMaterial = new THREE.MeshStandardMaterial({\n color: bodyColor,\n metalness: 0.4,\n roughness: 0.5\n })\n const mastMaterial = new THREE.MeshStandardMaterial({\n color: MAST_COLOR,\n metalness: 0.85,\n roughness: 0.3\n })\n const baseMaterial = new THREE.MeshStandardMaterial({\n color: BASE_COLOR,\n metalness: 0.7,\n roughness: 0.4\n })\n const shuttleMaterial = new THREE.MeshStandardMaterial({\n color: SHUTTLE_COLOR,\n metalness: 0.85,\n roughness: 0.3\n })\n\n // ── Floor rail (visible track under the base) ─────────────────────\n const railH = baseH * 0.25\n const railGeo = new THREE.BoxGeometry(width * 1.15, railH, mastD * 0.4)\n const railMesh = new THREE.Mesh(\n railGeo,\n new THREE.MeshStandardMaterial({ color: RAIL_COLOR, metalness: 0.9, roughness: 0.3 })\n )\n railMesh.position.set(0, baseY + railH / 2, 0)\n railMesh.receiveShadow = true\n this.object3d.add(railMesh)\n\n // ── Base unit (motor housing on floor rail) ───────────────────────\n const baseGeo = new THREE.BoxGeometry(width * 0.95, baseH, height * 0.85)\n const baseMesh = new THREE.Mesh(baseGeo, baseMaterial)\n baseMesh.position.set(0, baseY + railH + baseH / 2, 0)\n baseMesh.castShadow = true\n baseMesh.receiveShadow = true\n this.object3d.add(baseMesh)\n\n // Body color tint band on base unit (subtle status indication)\n const tintH = baseH * 0.15\n const tintGeo = new THREE.BoxGeometry(width * 0.95, tintH, height * 0.85)\n const tintMaterial = new THREE.MeshStandardMaterial({\n color: bodyColor,\n transparent: true,\n opacity: 0.6,\n metalness: 0.1,\n roughness: 0.6\n })\n const tintMesh = new THREE.Mesh(tintGeo, tintMaterial)\n tintMesh.position.set(0, baseY + railH + baseH - tintH / 2, 0)\n this.object3d.add(tintMesh)\n\n // ── Mast (vertical column from base top to ceiling) ───────────────\n const mastY = baseY + railH + baseH + mastH / 2\n const mastGeo = new THREE.BoxGeometry(mastW, mastH, mastD)\n const mastMesh = new THREE.Mesh(mastGeo, mastMaterial)\n mastMesh.position.set(0, mastY, 0)\n mastMesh.castShadow = true\n this.object3d.add(mastMesh)\n\n // ── Carriage (horizontal block sliding on mast at carriageHeight) ─\n const carriageW = width * 0.85\n const carriageH = baseH * 0.7\n const carriageD = mastD * 1.4\n const carriageY = baseY + railH + baseH + carriageHeight + carriageH / 2\n const carriageGeo = new THREE.BoxGeometry(carriageW, carriageH, carriageD)\n const carriageMesh = new THREE.Mesh(carriageGeo, bodyMaterial)\n carriageMesh.position.set(0, carriageY, 0)\n carriageMesh.castShadow = true\n this.object3d.add(carriageMesh)\n\n // ── Shuttle / fork attachment on carriage (extends sideways) ──────\n const shuttleW = width * 1.05\n const shuttleH = carriageH * 0.5\n const shuttleD = carriageD * 0.6\n const shuttleGeo = new THREE.BoxGeometry(shuttleW, shuttleH, shuttleD)\n const shuttleMesh = new THREE.Mesh(shuttleGeo, shuttleMaterial)\n shuttleMesh.position.set(0, carriageY - carriageH / 2 - shuttleH / 2, 0)\n shuttleMesh.castShadow = true\n this.object3d.add(shuttleMesh)\n\n // ── Top guide (sliding block on ceiling rail) ─────────────────────\n const topGuideGeo = new THREE.BoxGeometry(width * 0.7, topGuideH, height * 0.6)\n const topGuideMesh = new THREE.Mesh(topGuideGeo, baseMaterial)\n topGuideMesh.position.set(0, baseY + depth - topGuideH / 2, 0)\n topGuideMesh.castShadow = true\n this.object3d.add(topGuideMesh)\n\n // Ceiling rail (small visual cue at very top)\n const ceilingRailGeo = new THREE.BoxGeometry(width * 1.15, topGuideH * 0.4, mastD * 0.4)\n const ceilingRailMesh = new THREE.Mesh(\n ceilingRailGeo,\n new THREE.MeshStandardMaterial({ color: RAIL_COLOR, metalness: 0.9, roughness: 0.3 })\n )\n ceilingRailMesh.position.set(0, baseY + depth - topGuideH * 0.2, 0)\n this.object3d.add(ceilingRailMesh)\n\n // ── Status lamp on top of base unit ───────────────────────────────\n const lampR = Math.min(width, height) * 0.04\n const lampH = lampR * 1.5\n const lampMaterial = new THREE.MeshStandardMaterial({\n color: emissiveColor,\n emissive: emissiveColor,\n emissiveIntensity: lampIntensity,\n metalness: 0,\n roughness: 0.3\n })\n const lampGeo = new THREE.CylinderGeometry(lampR, lampR * 0.85, lampH, 12)\n const lampMesh = new THREE.Mesh(lampGeo, lampMaterial)\n // Place lamp near the corner of the base, away from the mast\n lampMesh.position.set(width * 0.3, baseY + railH + baseH + lampH / 2, height * 0.3)\n this.object3d.add(lampMesh)\n }\n\n updateDimension() {}\n\n onchange(after: Record<string, unknown>, before: Record<string, unknown>) {\n if (\n 'status' in after ||\n 'bodyColor' in after ||\n 'lampEmissive' in after ||\n 'carriageHeight' in after ||\n 'width' in after ||\n 'height' in after ||\n 'depth' in after\n ) {\n this.update()\n return\n }\n super.onchange(after, before)\n }\n\n updateAlpha() {}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"asrs-crane-3d.js","sourceRoot":"","sources":["../src/asrs-crane-3d.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * Copyright © HatioLab Inc. All rights reserved.\n *\n * AsrsCrane 3D — stacker crane for AS/RS aisles.\n *\n * LO-POLY but structurally complete. The signature parts:\n *\n * - tall vertical mast (the dominant element — runs floor → ceiling)\n * - base unit (motor housing at floor level, runs on the floor rail)\n * - top guide (sliding block at ceiling that runs on the ceiling rail —\n * stabilizes the mast under acceleration)\n * - carriage (horizontal block that slides up/down the mast at\n * `state.carriageHeight`)\n * - shuttle/fork attachment on the carriage (extends sideways into a cell\n * to extract / insert a pallet)\n * - status lamp on top of the base unit\n *\n * The base unit + top guide combo is what visually distinguishes a stacker\n * crane from a forklift — the stacker is *constrained* to the aisle, riding\n * rails top and bottom, and that constraint is the visual signature.\n */\n\nimport * as THREE from 'three'\nimport * as BufferGeometryUtils from 'three/examples/jsm/utils/BufferGeometryUtils.js'\nimport { RealObjectGroup } from '@hatiolab/things-scene'\n\nconst MAST_COLOR = 0x4a5060\nconst BASE_COLOR = 0x33394a\nconst SHUTTLE_COLOR = 0x222233\nconst RAIL_COLOR = 0x222222\n\nexport class AsrsCrane3D extends RealObjectGroup {\n build() {\n super.build()\n\n const { width, height, depth = 200 } = this.component.state\n const bodyColor = (this.component.state.bodyColor as string) || '#888'\n const emissiveColor = (this.component.state.lampEmissive as string) || '#222222'\n const status = this.component.state.status\n const lampIntensity = status && status !== 'idle' ? 1.5 : 0.2\n // Clamp carriageHeight to the available mast travel — keeps the carriage\n // inside the shaft even if state.carriageHeight is stale (e.g. left over\n // from an older height-scale convention).\n const carriageRaw = (this.component.state.carriageHeight as number) ?? depth * 0.4\n const carriageHeight = Math.max(0, Math.min(carriageRaw, depth * 0.85))\n\n const baseY = -depth / 2\n\n // Proportions\n const baseH = depth * 0.10\n const topGuideH = depth * 0.05\n const mastH = depth - baseH - topGuideH\n const mastW = width * 0.35\n const mastD = height * 0.35\n\n const bodyMaterial = new THREE.MeshStandardMaterial({\n color: bodyColor,\n metalness: 0.4,\n roughness: 0.5\n })\n const mastMaterial = new THREE.MeshStandardMaterial({\n color: MAST_COLOR,\n metalness: 0.85,\n roughness: 0.3\n })\n const baseMaterial = new THREE.MeshStandardMaterial({\n color: BASE_COLOR,\n metalness: 0.7,\n roughness: 0.4\n })\n const shuttleMaterial = new THREE.MeshStandardMaterial({\n color: SHUTTLE_COLOR,\n metalness: 0.85,\n roughness: 0.3\n })\n\n // ── Floor rail (visible track under the base) ─────────────────────\n const railH = baseH * 0.25\n const railGeo = new THREE.BoxGeometry(width * 1.15, railH, mastD * 0.4)\n const railMesh = new THREE.Mesh(\n railGeo,\n new THREE.MeshStandardMaterial({ color: RAIL_COLOR, metalness: 0.9, roughness: 0.3 })\n )\n railMesh.position.set(0, baseY + railH / 2, 0)\n railMesh.receiveShadow = true\n this.object3d.add(railMesh)\n\n // ── Base unit (motor housing on floor rail) ───────────────────────\n const baseGeo = new THREE.BoxGeometry(width * 0.95, baseH, height * 0.85)\n const baseMesh = new THREE.Mesh(baseGeo, baseMaterial)\n baseMesh.position.set(0, baseY + railH + baseH / 2, 0)\n baseMesh.castShadow = true\n baseMesh.receiveShadow = true\n this.object3d.add(baseMesh)\n\n // Body color tint band on base unit (subtle status indication)\n const tintH = baseH * 0.15\n const tintGeo = new THREE.BoxGeometry(width * 0.95, tintH, height * 0.85)\n const tintMaterial = new THREE.MeshStandardMaterial({\n color: bodyColor,\n transparent: true,\n opacity: 0.6,\n metalness: 0.1,\n roughness: 0.6\n })\n const tintMesh = new THREE.Mesh(tintGeo, tintMaterial)\n tintMesh.position.set(0, baseY + railH + baseH - tintH / 2, 0)\n this.object3d.add(tintMesh)\n\n // ── Mast (vertical column from base top to ceiling) ───────────────\n const mastY = baseY + railH + baseH + mastH / 2\n const mastGeo = new THREE.BoxGeometry(mastW, mastH, mastD)\n const mastMesh = new THREE.Mesh(mastGeo, mastMaterial)\n mastMesh.position.set(0, mastY, 0)\n mastMesh.castShadow = true\n this.object3d.add(mastMesh)\n\n // ── Carriage (horizontal block sliding on mast at carriageHeight) ─\n const carriageW = width * 0.85\n const carriageH = baseH * 0.7\n const carriageD = mastD * 1.4\n const carriageY = baseY + railH + baseH + carriageHeight + carriageH / 2\n const carriageGeo = new THREE.BoxGeometry(carriageW, carriageH, carriageD)\n const carriageMesh = new THREE.Mesh(carriageGeo, bodyMaterial)\n carriageMesh.position.set(0, carriageY, 0)\n carriageMesh.castShadow = true\n this.object3d.add(carriageMesh)\n\n // ── Shuttle / fork attachment on carriage (extends sideways) ──────\n const shuttleW = width * 1.05\n const shuttleH = carriageH * 0.5\n const shuttleD = carriageD * 0.6\n const shuttleGeo = new THREE.BoxGeometry(shuttleW, shuttleH, shuttleD)\n const shuttleMesh = new THREE.Mesh(shuttleGeo, shuttleMaterial)\n shuttleMesh.position.set(0, carriageY - carriageH / 2 - shuttleH / 2, 0)\n shuttleMesh.castShadow = true\n this.object3d.add(shuttleMesh)\n\n // ── Top guide (sliding block on ceiling rail) ─────────────────────\n const topGuideGeo = new THREE.BoxGeometry(width * 0.7, topGuideH, height * 0.6)\n const topGuideMesh = new THREE.Mesh(topGuideGeo, baseMaterial)\n topGuideMesh.position.set(0, baseY + depth - topGuideH / 2, 0)\n topGuideMesh.castShadow = true\n this.object3d.add(topGuideMesh)\n\n // Ceiling rail (small visual cue at very top)\n const ceilingRailGeo = new THREE.BoxGeometry(width * 1.15, topGuideH * 0.4, mastD * 0.4)\n const ceilingRailMesh = new THREE.Mesh(\n ceilingRailGeo,\n new THREE.MeshStandardMaterial({ color: RAIL_COLOR, metalness: 0.9, roughness: 0.3 })\n )\n ceilingRailMesh.position.set(0, baseY + depth - topGuideH * 0.2, 0)\n this.object3d.add(ceilingRailMesh)\n\n // ── Status lamp on top of base unit ───────────────────────────────\n const lampR = Math.min(width, height) * 0.04\n const lampH = lampR * 1.5\n const lampMaterial = new THREE.MeshStandardMaterial({\n color: emissiveColor,\n emissive: emissiveColor,\n emissiveIntensity: lampIntensity,\n metalness: 0,\n roughness: 0.3\n })\n const lampGeo = new THREE.CylinderGeometry(lampR, lampR * 0.85, lampH, 12)\n const lampMesh = new THREE.Mesh(lampGeo, lampMaterial)\n // Place lamp near the corner of the base, away from the mast\n lampMesh.position.set(width * 0.3, baseY + railH + baseH + lampH / 2, height * 0.3)\n this.object3d.add(lampMesh)\n\n // ── Carriage frame (invisible anchor for carrier attach) ──────────\n // Placed at the top of the shuttle, where cargo rests.\n this._carriageFrame = new THREE.Object3D()\n this._carriageFrame.name = 'crane-carriage-tcp'\n this._carriageFrame.position.set(0, carriageY - carriageH / 2 - shuttleH, 0)\n this.object3d.add(this._carriageFrame)\n }\n\n /** Sub-frame where carriers attach during transport (fork tool-centre-point). */\n private _carriageFrame?: THREE.Object3D\n\n /**\n * Return the carriage TCP anchor. Carriers attached to this frame will\n * follow carriage movement as `carriageHeight` changes and the crane rebuilds.\n *\n * Callers should re-fetch this after any state change that triggers rebuild.\n */\n getCarriageFrame(): THREE.Object3D | undefined {\n return this._carriageFrame\n }\n\n updateDimension() {}\n\n onchange(after: Record<string, unknown>, before: Record<string, unknown>) {\n if (\n 'status' in after ||\n 'bodyColor' in after ||\n 'lampEmissive' in after ||\n 'carriageHeight' in after ||\n 'width' in after ||\n 'height' in after ||\n 'depth' in after\n ) {\n this.update()\n return\n }\n super.onchange(after, before)\n }\n\n updateAlpha() {}\n}\n"]}
|
package/dist/asrs-crane.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { Component, ComponentNature, RealObject } from '@hatiolab/things-scene';
|
|
2
|
-
import
|
|
2
|
+
import type { SlotDef, State, Material3D } from '@hatiolab/things-scene';
|
|
3
|
+
import { type AttachFrame, type Alignment, type Heights, type LegendBinding, type MoveOptions, type PlacementArchetype } from '@operato/scene-base';
|
|
4
|
+
import { AsrsCrane3D } from './asrs-crane-3d.js';
|
|
3
5
|
/**
|
|
4
6
|
* AsrsCrane status — the operating state of a stacker crane in an AS/RS aisle.
|
|
5
7
|
*
|
|
@@ -11,37 +13,80 @@ import { type Alignment, type Heights, type LegendBinding, type PlacementArchety
|
|
|
11
13
|
* - `error` — fault / e-stop / collision warning
|
|
12
14
|
*/
|
|
13
15
|
export type AsrsCraneStatus = 'idle' | 'moving' | 'loading' | 'unloading' | 'error';
|
|
14
|
-
|
|
16
|
+
/** AsrsCrane 컴포넌트 state */
|
|
17
|
+
export interface AsrsCraneState extends State {
|
|
18
|
+
status?: AsrsCraneStatus;
|
|
19
|
+
carriageHeight?: number;
|
|
20
|
+
material3d?: Material3D;
|
|
21
|
+
}
|
|
22
|
+
declare const AsrsCrane_base: any;
|
|
15
23
|
/**
|
|
16
24
|
* AsrsCrane — the stacker / retrieval crane that runs in the aisle of an
|
|
17
25
|
* AS/RS, moving cargo between the load port and the rack cells.
|
|
18
26
|
*
|
|
19
27
|
* Structure: a tall vertical mast that translates along a floor + ceiling
|
|
20
28
|
* rail (the aisle), with a carriage that slides up/down the mast carrying a
|
|
21
|
-
* shuttle / forks.
|
|
22
|
-
*
|
|
23
|
-
*
|
|
29
|
+
* shuttle / forks.
|
|
30
|
+
*
|
|
31
|
+
* **Monitoring mode**: crane status is driven by data binding
|
|
32
|
+
* (`state.status`, `state.carriageHeight`). The carrier is referenced
|
|
33
|
+
* via data binding — it is NOT a child of the crane in monitoring mode.
|
|
24
34
|
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
* fmsim's CarrierManager pattern. Adding the carrier as a child would mix
|
|
29
|
-
* static placement with the dynamic data-driven flow we deliberately keep
|
|
30
|
-
* separate (see Phase A4 commit notes).
|
|
35
|
+
* **Simulation mode**: call `crane.pick(carrier)` / `crane.place(carrier, rackCell)`
|
|
36
|
+
* (or `crane.pickAndPlace(carrier, rackCell)`). Mover handles navigation +
|
|
37
|
+
* engage + reparent. During transit the carrier IS a child of the crane.
|
|
31
38
|
*/
|
|
32
|
-
export default class AsrsCrane extends
|
|
39
|
+
export default class AsrsCrane extends AsrsCrane_base {
|
|
40
|
+
state: AsrsCraneState;
|
|
41
|
+
_realObject?: AsrsCrane3D;
|
|
33
42
|
static legends: Record<string, LegendBinding>;
|
|
34
43
|
static placement: PlacementArchetype;
|
|
35
44
|
static align: Alignment;
|
|
36
45
|
static defaultDepth: (h: Heights) => number;
|
|
46
|
+
/** Yaw offset: crane model is drawn with the aisle axis along X (right = forward). */
|
|
47
|
+
static yawOffset: number;
|
|
37
48
|
get nature(): ComponentNature;
|
|
38
49
|
get anchors(): never[];
|
|
50
|
+
/** Stacker crane carries at most one load at a time on its forks. */
|
|
51
|
+
get slots(): SlotDef[];
|
|
52
|
+
/**
|
|
53
|
+
* Return the 3D attach frame on the crane's carriage (fork tip).
|
|
54
|
+
* Carriers are attached here while the crane is in transit (pick phase).
|
|
55
|
+
*
|
|
56
|
+
* The AsrsCrane3D exposes `getCarriageFrame()` — a sub-Object3D that
|
|
57
|
+
* tracks the carriage height and sits at the fork TCP. If the 3D object
|
|
58
|
+
* isn't built yet (e.g. before scene initialization), fall back to the
|
|
59
|
+
* crane's own object3d centre.
|
|
60
|
+
*/
|
|
61
|
+
attachPointFor(carrier: Component): AttachFrame | null;
|
|
62
|
+
/**
|
|
63
|
+
* Domain-specific actuation between arrival and reparent.
|
|
64
|
+
*
|
|
65
|
+
* Simulation sequence for PICK:
|
|
66
|
+
* 1. Mover.pick() navigates crane to carrier position (moveTo).
|
|
67
|
+
* 2. engage('pick') → snap carriage height + status 'loading'.
|
|
68
|
+
* 3. Carrier is reparented to crane (becomes child).
|
|
69
|
+
*
|
|
70
|
+
* For now: set status and snap carriage height. A full ASRS simulation
|
|
71
|
+
* would tween the carriageHeight here (animate AsrsCrane3D).
|
|
72
|
+
*
|
|
73
|
+
* Status lifecycle:
|
|
74
|
+
* idle → (moveTo running) → engage fires → loading/unloading → (reparent) → idle
|
|
75
|
+
* The 'moving' state is not set from Mover.moveTo() because TypeScript
|
|
76
|
+
* can't call super.moveTo() on an `: any`-typed mixin. WCS data binding
|
|
77
|
+
* sets 'moving' in monitoring mode; override pick()/place() to set it
|
|
78
|
+
* in full simulation environments.
|
|
79
|
+
*/
|
|
80
|
+
engage(target: Component, kind: 'pick' | 'place', _options?: MoveOptions): Promise<void>;
|
|
81
|
+
/** Fetch a carrier from a rack cell (semantically = pick). */
|
|
82
|
+
fetch(carrier: Component, options?: MoveOptions): Promise<void>;
|
|
83
|
+
/** Deposit a carrier into a rack cell (semantically = place). */
|
|
84
|
+
deposit(carrier: Component, cell: Component, options?: MoveOptions): Promise<void>;
|
|
39
85
|
/**
|
|
40
86
|
* 2D — top-down rectangle showing the crane's footprint along the aisle.
|
|
41
87
|
* The crane is much taller than wide, so the 2D mark is small.
|
|
42
88
|
*/
|
|
43
89
|
render(ctx: CanvasRenderingContext2D): void;
|
|
44
|
-
get fillStyle(): string;
|
|
45
90
|
buildRealObject(): RealObject | undefined;
|
|
46
91
|
}
|
|
47
92
|
export {};
|
package/dist/asrs-crane.js
CHANGED
|
@@ -2,8 +2,8 @@ import { __decorate } from "tslib";
|
|
|
2
2
|
/*
|
|
3
3
|
* Copyright © HatioLab Inc. All rights reserved.
|
|
4
4
|
*/
|
|
5
|
-
import {
|
|
6
|
-
import { Legendable, Placeable } from '@operato/scene-base';
|
|
5
|
+
import { ContainerAbstract, ContainerCapacity, sceneComponent } from '@hatiolab/things-scene';
|
|
6
|
+
import { CarrierHolder, Legendable, Mover, Placeable } from '@operato/scene-base';
|
|
7
7
|
import { AsrsCrane3D } from './asrs-crane-3d.js';
|
|
8
8
|
const BODY_LEGEND = {
|
|
9
9
|
idle: '#888',
|
|
@@ -49,25 +49,36 @@ const NATURE = {
|
|
|
49
49
|
],
|
|
50
50
|
help: 'scene/component/asrs-crane'
|
|
51
51
|
};
|
|
52
|
-
|
|
52
|
+
// Mixin chain: Mover → CarrierHolder → ContainerCapacity → Legendable → Placeable → ContainerAbstract
|
|
53
|
+
//
|
|
54
|
+
// Mover: pick / place / pickAndPlace / moveTo / engage primitives
|
|
55
|
+
// CarrierHolder: attachPointFor() — where the carrier sits on the crane (carriage fork)
|
|
56
|
+
// ContainerCapacity: receive() / dispatch() / canReceive() / slots — slot tracking +
|
|
57
|
+
// TRANSFER_SLOT_KEY bookkeeping during transit
|
|
58
|
+
// Legendable: status → bodyColor / lampEmissive colour mapping
|
|
59
|
+
// Placeable: floor-archetype 3D positioning
|
|
60
|
+
// ContainerAbstract: child management — carrier becomes a child while in transit
|
|
61
|
+
//
|
|
62
|
+
// Note: ContainerAbstract replaces Shape. The 2D outline is drawn manually in
|
|
63
|
+
// render() below (a simple top-down rectangle), matching the old Shape output
|
|
64
|
+
// without the Shape base-class overhead.
|
|
53
65
|
/**
|
|
54
66
|
* AsrsCrane — the stacker / retrieval crane that runs in the aisle of an
|
|
55
67
|
* AS/RS, moving cargo between the load port and the rack cells.
|
|
56
68
|
*
|
|
57
69
|
* Structure: a tall vertical mast that translates along a floor + ceiling
|
|
58
70
|
* rail (the aisle), with a carriage that slides up/down the mast carrying a
|
|
59
|
-
* shuttle / forks.
|
|
60
|
-
* but its visual height is full ceiling — by far the tallest single
|
|
61
|
-
* component in a typical scene.
|
|
71
|
+
* shuttle / forks.
|
|
62
72
|
*
|
|
63
|
-
*
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
*
|
|
73
|
+
* **Monitoring mode**: crane status is driven by data binding
|
|
74
|
+
* (`state.status`, `state.carriageHeight`). The carrier is referenced
|
|
75
|
+
* via data binding — it is NOT a child of the crane in monitoring mode.
|
|
76
|
+
*
|
|
77
|
+
* **Simulation mode**: call `crane.pick(carrier)` / `crane.place(carrier, rackCell)`
|
|
78
|
+
* (or `crane.pickAndPlace(carrier, rackCell)`). Mover handles navigation +
|
|
79
|
+
* engage + reparent. During transit the carrier IS a child of the crane.
|
|
69
80
|
*/
|
|
70
|
-
let AsrsCrane = class AsrsCrane extends
|
|
81
|
+
let AsrsCrane = class AsrsCrane extends Mover(CarrierHolder(ContainerCapacity(Legendable(Placeable(ContainerAbstract))))) {
|
|
71
82
|
static legends = {
|
|
72
83
|
bodyColor: { from: 'status', legend: BODY_LEGEND },
|
|
73
84
|
lampEmissive: { from: 'status', legend: LAMP_EMISSIVE_LEGEND }
|
|
@@ -75,24 +86,98 @@ let AsrsCrane = class AsrsCrane extends Base {
|
|
|
75
86
|
static placement = 'floor';
|
|
76
87
|
static align = 'bottom';
|
|
77
88
|
static defaultDepth = (h) => h.ceiling - h.floor;
|
|
89
|
+
/** Yaw offset: crane model is drawn with the aisle axis along X (right = forward). */
|
|
90
|
+
static yawOffset = 0;
|
|
78
91
|
get nature() {
|
|
79
92
|
return NATURE;
|
|
80
93
|
}
|
|
81
94
|
get anchors() {
|
|
82
95
|
return [];
|
|
83
96
|
}
|
|
97
|
+
// ── ContainerCapacity ─────────────────────────────────────────────────────
|
|
98
|
+
/** Stacker crane carries at most one load at a time on its forks. */
|
|
99
|
+
get slots() {
|
|
100
|
+
return [{ id: 'forks', maxCount: 1 }];
|
|
101
|
+
}
|
|
102
|
+
// ── CarrierHolder — attach frame (carriage fork position) ─────────────────
|
|
103
|
+
/**
|
|
104
|
+
* Return the 3D attach frame on the crane's carriage (fork tip).
|
|
105
|
+
* Carriers are attached here while the crane is in transit (pick phase).
|
|
106
|
+
*
|
|
107
|
+
* The AsrsCrane3D exposes `getCarriageFrame()` — a sub-Object3D that
|
|
108
|
+
* tracks the carriage height and sits at the fork TCP. If the 3D object
|
|
109
|
+
* isn't built yet (e.g. before scene initialization), fall back to the
|
|
110
|
+
* crane's own object3d centre.
|
|
111
|
+
*/
|
|
112
|
+
attachPointFor(carrier) {
|
|
113
|
+
const ro = this._realObject;
|
|
114
|
+
const frame = ro?.getCarriageFrame?.();
|
|
115
|
+
if (frame) {
|
|
116
|
+
const carrierDepth = resolveCarrierDepth(carrier);
|
|
117
|
+
return { attach: frame, localPosition: { x: 0, y: carrierDepth / 2, z: 0 } };
|
|
118
|
+
}
|
|
119
|
+
const root = this._realObject?.object3d;
|
|
120
|
+
if (!root)
|
|
121
|
+
return null;
|
|
122
|
+
return { attach: root };
|
|
123
|
+
}
|
|
124
|
+
// ── Mover overrides ───────────────────────────────────────────────────────
|
|
125
|
+
/**
|
|
126
|
+
* Domain-specific actuation between arrival and reparent.
|
|
127
|
+
*
|
|
128
|
+
* Simulation sequence for PICK:
|
|
129
|
+
* 1. Mover.pick() navigates crane to carrier position (moveTo).
|
|
130
|
+
* 2. engage('pick') → snap carriage height + status 'loading'.
|
|
131
|
+
* 3. Carrier is reparented to crane (becomes child).
|
|
132
|
+
*
|
|
133
|
+
* For now: set status and snap carriage height. A full ASRS simulation
|
|
134
|
+
* would tween the carriageHeight here (animate AsrsCrane3D).
|
|
135
|
+
*
|
|
136
|
+
* Status lifecycle:
|
|
137
|
+
* idle → (moveTo running) → engage fires → loading/unloading → (reparent) → idle
|
|
138
|
+
* The 'moving' state is not set from Mover.moveTo() because TypeScript
|
|
139
|
+
* can't call super.moveTo() on an `: any`-typed mixin. WCS data binding
|
|
140
|
+
* sets 'moving' in monitoring mode; override pick()/place() to set it
|
|
141
|
+
* in full simulation environments.
|
|
142
|
+
*/
|
|
143
|
+
async engage(target, kind, _options = {}) {
|
|
144
|
+
if (kind === 'pick') {
|
|
145
|
+
this.setState({ status: 'loading' });
|
|
146
|
+
const carrierY = resolveCarrierCenterY(target);
|
|
147
|
+
if (carrierY !== null) {
|
|
148
|
+
this.setState({ carriageHeight: carrierY });
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
else {
|
|
152
|
+
this.setState({ status: 'unloading' });
|
|
153
|
+
}
|
|
154
|
+
// In a full simulation: await carriage-motion tween here.
|
|
155
|
+
}
|
|
156
|
+
// ── Domain aliases ────────────────────────────────────────────────────────
|
|
157
|
+
/** Fetch a carrier from a rack cell (semantically = pick). */
|
|
158
|
+
fetch(carrier, options) {
|
|
159
|
+
return this.pick(carrier, options);
|
|
160
|
+
}
|
|
161
|
+
/** Deposit a carrier into a rack cell (semantically = place). */
|
|
162
|
+
deposit(carrier, cell, options) {
|
|
163
|
+
return this.place(carrier, cell, options);
|
|
164
|
+
}
|
|
165
|
+
// ── 2D rendering ─────────────────────────────────────────────────────────
|
|
84
166
|
/**
|
|
85
167
|
* 2D — top-down rectangle showing the crane's footprint along the aisle.
|
|
86
168
|
* The crane is much taller than wide, so the 2D mark is small.
|
|
87
169
|
*/
|
|
88
170
|
render(ctx) {
|
|
89
171
|
const { width, height, left, top } = this.state;
|
|
172
|
+
const fillColor = this.state.bodyColor || '#888';
|
|
173
|
+
ctx.save();
|
|
174
|
+
ctx.fillStyle = fillColor;
|
|
90
175
|
ctx.beginPath();
|
|
91
176
|
ctx.rect(left, top, width, height);
|
|
177
|
+
ctx.fill();
|
|
178
|
+
ctx.restore();
|
|
92
179
|
}
|
|
93
|
-
|
|
94
|
-
return this.state.bodyColor || '#888';
|
|
95
|
-
}
|
|
180
|
+
// ── 3D ───────────────────────────────────────────────────────────────────
|
|
96
181
|
buildRealObject() {
|
|
97
182
|
return new AsrsCrane3D(this);
|
|
98
183
|
}
|
|
@@ -101,4 +186,23 @@ AsrsCrane = __decorate([
|
|
|
101
186
|
sceneComponent('asrs-crane')
|
|
102
187
|
], AsrsCrane);
|
|
103
188
|
export default AsrsCrane;
|
|
189
|
+
function resolveCarrierDepth(c) {
|
|
190
|
+
const eff = c._realObject?.effectiveDepth;
|
|
191
|
+
if (typeof eff === 'number' && Number.isFinite(eff))
|
|
192
|
+
return eff;
|
|
193
|
+
return numOr(c?.state?.depth, 0);
|
|
194
|
+
}
|
|
195
|
+
function resolveCarrierCenterY(c) {
|
|
196
|
+
const pos = c.state;
|
|
197
|
+
if (!pos)
|
|
198
|
+
return null;
|
|
199
|
+
// zPos is the 3D Y center of a Placeable component in things-scene
|
|
200
|
+
const zPos = numOr(pos.zPos, NaN);
|
|
201
|
+
if (!Number.isNaN(zPos))
|
|
202
|
+
return zPos;
|
|
203
|
+
return null;
|
|
204
|
+
}
|
|
205
|
+
function numOr(v, dflt) {
|
|
206
|
+
return typeof v === 'number' && Number.isFinite(v) ? v : dflt;
|
|
207
|
+
}
|
|
104
208
|
//# sourceMappingURL=asrs-crane.js.map
|
package/dist/asrs-crane.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"asrs-crane.js","sourceRoot":"","sources":["../src/asrs-crane.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * Copyright © HatioLab Inc. All rights reserved.\n */\nimport { Component, ComponentNature, RealObject, RectPath, Shape, sceneComponent } from '@hatiolab/things-scene'\nimport {\n Legendable,\n Placeable,\n type Alignment,\n type Heights,\n type LegendBinding,\n type PlacementArchetype\n} from '@operato/scene-base'\n\nimport { AsrsCrane3D } from './asrs-crane-3d.js'\n\n/**\n * AsrsCrane status — the operating state of a stacker crane in an AS/RS aisle.\n *\n * - `idle` — parked at home position\n * - `moving` — translating along the aisle (horizontal) or along the\n * mast (vertical); the two motions are typically combined\n * - `loading` — extracting a pallet from a rack cell\n * - `unloading` — placing a pallet into a rack cell\n * - `error` — fault / e-stop / collision warning\n */\nexport type AsrsCraneStatus = 'idle' | 'moving' | 'loading' | 'unloading' | 'error'\n\nconst BODY_LEGEND = {\n idle: '#888',\n moving: '#aabbcc',\n loading: '#ffaa00',\n unloading: '#ffaa00',\n error: '#c66',\n default: '#888'\n}\n\nconst LAMP_EMISSIVE_LEGEND = {\n idle: '#222222',\n moving: '#44ff44',\n loading: '#ffaa00',\n unloading: '#ffaa00',\n error: '#ff3333',\n default: '#222222'\n}\n\nconst NATURE: ComponentNature = {\n mutable: false,\n resizable: true,\n rotatable: true,\n properties: [\n {\n type: 'select',\n label: 'status',\n name: 'status',\n property: {\n options: [\n { display: 'Idle', value: 'idle' },\n { display: 'Moving', value: 'moving' },\n { display: 'Loading', value: 'loading' },\n { display: 'Unloading', value: 'unloading' },\n { display: 'Error', value: 'error' }\n ]\n }\n },\n {\n type: 'number',\n label: 'carriage-height',\n name: 'carriageHeight',\n placeholder: 'mm — height of carriage on mast'\n }\n ],\n help: 'scene/component/asrs-crane'\n}\n\nconst Base = Legendable(Placeable(RectPath(Shape))) as unknown as typeof Component\n\n/**\n * AsrsCrane — the stacker / retrieval crane that runs in the aisle of an\n * AS/RS, moving cargo between the load port and the rack cells.\n *\n * Structure: a tall vertical mast that translates along a floor + ceiling\n * rail (the aisle), with a carriage that slides up/down the mast carrying a\n * shuttle / forks. The whole assembly's footprint is narrow (mast width)\n * but its visual height is full ceiling — by far the tallest single\n * component in a typical scene.\n *\n * Currently Shape-based (no children). The carrier the crane is *currently\n * carrying* is best modeled via data binding (a `currentCarrier` data field\n * on the crane → looked up to a Pallet/Box elsewhere in the scene), as in\n * fmsim's CarrierManager pattern. Adding the carrier as a child would mix\n * static placement with the dynamic data-driven flow we deliberately keep\n * separate (see Phase A4 commit notes).\n */\n@sceneComponent('asrs-crane')\nexport default class AsrsCrane extends Base {\n static legends: Record<string, LegendBinding> = {\n bodyColor: { from: 'status', legend: BODY_LEGEND },\n lampEmissive: { from: 'status', legend: LAMP_EMISSIVE_LEGEND }\n }\n\n static placement: PlacementArchetype = 'floor'\n static align: Alignment = 'bottom'\n static defaultDepth = (h: Heights) => h.ceiling - h.floor\n\n get nature() {\n return NATURE\n }\n\n get anchors() {\n return []\n }\n\n /**\n * 2D — top-down rectangle showing the crane's footprint along the aisle.\n * The crane is much taller than wide, so the 2D mark is small.\n */\n render(ctx: CanvasRenderingContext2D) {\n const { width, height, left, top } = this.state\n ctx.beginPath()\n ctx.rect(left, top, width, height)\n }\n\n get fillStyle() {\n return (this.state.bodyColor as string) || '#888'\n }\n\n buildRealObject(): RealObject | undefined {\n return new AsrsCrane3D(this as any)\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"asrs-crane.js","sourceRoot":"","sources":["../src/asrs-crane.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * Copyright © HatioLab Inc. All rights reserved.\n */\nimport { Component, ComponentNature, ContainerAbstract, ContainerCapacity, RealObject, sceneComponent } from '@hatiolab/things-scene'\nimport type { SlotDef, State, Material3D } from '@hatiolab/things-scene'\nimport {\n CarrierHolder,\n Legendable,\n Mover,\n Placeable,\n type AttachFrame,\n type Alignment,\n type Heights,\n type LegendBinding,\n type MoveOptions,\n type PlacementArchetype\n} from '@operato/scene-base'\n\nimport { AsrsCrane3D } from './asrs-crane-3d.js'\n\n/**\n * AsrsCrane status — the operating state of a stacker crane in an AS/RS aisle.\n *\n * - `idle` — parked at home position\n * - `moving` — translating along the aisle (horizontal) or along the\n * mast (vertical); the two motions are typically combined\n * - `loading` — extracting a pallet from a rack cell\n * - `unloading` — placing a pallet into a rack cell\n * - `error` — fault / e-stop / collision warning\n */\nexport type AsrsCraneStatus = 'idle' | 'moving' | 'loading' | 'unloading' | 'error'\n\n/** AsrsCrane 컴포넌트 state */\nexport interface AsrsCraneState extends State {\n // ── 운영 상태 ──\n status?: AsrsCraneStatus\n\n // ── 액추에이터 ──\n carriageHeight?: number\n\n // ── 3D 재질 ──\n material3d?: Material3D\n}\n\nconst BODY_LEGEND = {\n idle: '#888',\n moving: '#aabbcc',\n loading: '#ffaa00',\n unloading: '#ffaa00',\n error: '#c66',\n default: '#888'\n}\n\nconst LAMP_EMISSIVE_LEGEND = {\n idle: '#222222',\n moving: '#44ff44',\n loading: '#ffaa00',\n unloading: '#ffaa00',\n error: '#ff3333',\n default: '#222222'\n}\n\nconst NATURE: ComponentNature = {\n mutable: false,\n resizable: true,\n rotatable: true,\n properties: [\n {\n type: 'select',\n label: 'status',\n name: 'status',\n property: {\n options: [\n { display: 'Idle', value: 'idle' },\n { display: 'Moving', value: 'moving' },\n { display: 'Loading', value: 'loading' },\n { display: 'Unloading', value: 'unloading' },\n { display: 'Error', value: 'error' }\n ]\n }\n },\n {\n type: 'number',\n label: 'carriage-height',\n name: 'carriageHeight',\n placeholder: 'mm — height of carriage on mast'\n }\n ],\n help: 'scene/component/asrs-crane'\n}\n\n// Mixin chain: Mover → CarrierHolder → ContainerCapacity → Legendable → Placeable → ContainerAbstract\n//\n// Mover: pick / place / pickAndPlace / moveTo / engage primitives\n// CarrierHolder: attachPointFor() — where the carrier sits on the crane (carriage fork)\n// ContainerCapacity: receive() / dispatch() / canReceive() / slots — slot tracking +\n// TRANSFER_SLOT_KEY bookkeeping during transit\n// Legendable: status → bodyColor / lampEmissive colour mapping\n// Placeable: floor-archetype 3D positioning\n// ContainerAbstract: child management — carrier becomes a child while in transit\n//\n// Note: ContainerAbstract replaces Shape. The 2D outline is drawn manually in\n// render() below (a simple top-down rectangle), matching the old Shape output\n// without the Shape base-class overhead.\n/**\n * AsrsCrane — the stacker / retrieval crane that runs in the aisle of an\n * AS/RS, moving cargo between the load port and the rack cells.\n *\n * Structure: a tall vertical mast that translates along a floor + ceiling\n * rail (the aisle), with a carriage that slides up/down the mast carrying a\n * shuttle / forks.\n *\n * **Monitoring mode**: crane status is driven by data binding\n * (`state.status`, `state.carriageHeight`). The carrier is referenced\n * via data binding — it is NOT a child of the crane in monitoring mode.\n *\n * **Simulation mode**: call `crane.pick(carrier)` / `crane.place(carrier, rackCell)`\n * (or `crane.pickAndPlace(carrier, rackCell)`). Mover handles navigation +\n * engage + reparent. During transit the carrier IS a child of the crane.\n */\n@sceneComponent('asrs-crane')\nexport default class AsrsCrane extends Mover(CarrierHolder(ContainerCapacity(Legendable(Placeable(ContainerAbstract))))) {\n declare state: AsrsCraneState\n declare _realObject?: AsrsCrane3D\n\n static legends: Record<string, LegendBinding> = {\n bodyColor: { from: 'status', legend: BODY_LEGEND },\n lampEmissive: { from: 'status', legend: LAMP_EMISSIVE_LEGEND }\n }\n\n static placement: PlacementArchetype = 'floor'\n static align: Alignment = 'bottom'\n static defaultDepth = (h: Heights) => h.ceiling - h.floor\n\n /** Yaw offset: crane model is drawn with the aisle axis along X (right = forward). */\n static yawOffset = 0\n\n get nature() {\n return NATURE\n }\n\n get anchors() {\n return []\n }\n\n // ── ContainerCapacity ─────────────────────────────────────────────────────\n\n /** Stacker crane carries at most one load at a time on its forks. */\n get slots(): SlotDef[] {\n return [{ id: 'forks', maxCount: 1 }]\n }\n\n // ── CarrierHolder — attach frame (carriage fork position) ─────────────────\n\n /**\n * Return the 3D attach frame on the crane's carriage (fork tip).\n * Carriers are attached here while the crane is in transit (pick phase).\n *\n * The AsrsCrane3D exposes `getCarriageFrame()` — a sub-Object3D that\n * tracks the carriage height and sits at the fork TCP. If the 3D object\n * isn't built yet (e.g. before scene initialization), fall back to the\n * crane's own object3d centre.\n */\n attachPointFor(carrier: Component): AttachFrame | null {\n const ro = this._realObject\n const frame = ro?.getCarriageFrame?.()\n if (frame) {\n const carrierDepth = resolveCarrierDepth(carrier)\n return { attach: frame, localPosition: { x: 0, y: carrierDepth / 2, z: 0 } }\n }\n const root = this._realObject?.object3d\n if (!root) return null\n return { attach: root }\n }\n\n // ── Mover overrides ───────────────────────────────────────────────────────\n\n /**\n * Domain-specific actuation between arrival and reparent.\n *\n * Simulation sequence for PICK:\n * 1. Mover.pick() navigates crane to carrier position (moveTo).\n * 2. engage('pick') → snap carriage height + status 'loading'.\n * 3. Carrier is reparented to crane (becomes child).\n *\n * For now: set status and snap carriage height. A full ASRS simulation\n * would tween the carriageHeight here (animate AsrsCrane3D).\n *\n * Status lifecycle:\n * idle → (moveTo running) → engage fires → loading/unloading → (reparent) → idle\n * The 'moving' state is not set from Mover.moveTo() because TypeScript\n * can't call super.moveTo() on an `: any`-typed mixin. WCS data binding\n * sets 'moving' in monitoring mode; override pick()/place() to set it\n * in full simulation environments.\n */\n async engage(\n target: Component,\n kind: 'pick' | 'place',\n _options: MoveOptions = {}\n ): Promise<void> {\n if (kind === 'pick') {\n this.setState({ status: 'loading' as AsrsCraneStatus })\n const carrierY = resolveCarrierCenterY(target)\n if (carrierY !== null) {\n this.setState({ carriageHeight: carrierY })\n }\n } else {\n this.setState({ status: 'unloading' as AsrsCraneStatus })\n }\n // In a full simulation: await carriage-motion tween here.\n }\n\n // ── Domain aliases ────────────────────────────────────────────────────────\n\n /** Fetch a carrier from a rack cell (semantically = pick). */\n fetch(carrier: Component, options?: MoveOptions): Promise<void> {\n return this.pick(carrier, options)\n }\n\n /** Deposit a carrier into a rack cell (semantically = place). */\n deposit(carrier: Component, cell: Component, options?: MoveOptions): Promise<void> {\n return this.place(carrier, cell, options)\n }\n\n // ── 2D rendering ─────────────────────────────────────────────────────────\n\n /**\n * 2D — top-down rectangle showing the crane's footprint along the aisle.\n * The crane is much taller than wide, so the 2D mark is small.\n */\n render(ctx: CanvasRenderingContext2D) {\n const { width, height, left, top } = this.state\n const fillColor = (this.state.bodyColor as string) || '#888'\n ctx.save()\n ctx.fillStyle = fillColor\n ctx.beginPath()\n ctx.rect(left, top, width, height)\n ctx.fill()\n ctx.restore()\n }\n\n // ── 3D ───────────────────────────────────────────────────────────────────\n\n buildRealObject(): RealObject | undefined {\n return new AsrsCrane3D(this)\n }\n}\n\nfunction resolveCarrierDepth(c: Component): number {\n const eff = (c as any)._realObject?.effectiveDepth\n if (typeof eff === 'number' && Number.isFinite(eff)) return eff\n return numOr((c as any)?.state?.depth, 0)\n}\n\nfunction resolveCarrierCenterY(c: Component): number | null {\n const pos = (c as any).state\n if (!pos) return null\n // zPos is the 3D Y center of a Placeable component in things-scene\n const zPos = numOr(pos.zPos, NaN)\n if (!Number.isNaN(zPos)) return zPos\n return null\n}\n\nfunction numOr(v: unknown, dflt: number): number {\n return typeof v === 'number' && Number.isFinite(v) ? v : dflt\n}\n"]}
|
package/dist/asrs-rack.d.ts
CHANGED
|
@@ -1,39 +1,78 @@
|
|
|
1
1
|
import { Component, ComponentNature, RealObject } from '@hatiolab/things-scene';
|
|
2
|
-
import
|
|
3
|
-
|
|
2
|
+
import type { State, Material3D } from '@hatiolab/things-scene';
|
|
3
|
+
import { CellMap, type AttachFrame, type Alignment, type Heights, type PlacementArchetype } from '@operato/scene-base';
|
|
4
|
+
/** AsrsRack 컴포넌트 state */
|
|
5
|
+
export interface AsrsRackState extends State {
|
|
6
|
+
bays?: number;
|
|
7
|
+
levels?: number;
|
|
8
|
+
debugCells?: boolean;
|
|
9
|
+
material3d?: Material3D;
|
|
10
|
+
}
|
|
11
|
+
declare const AsrsRack_base: any;
|
|
4
12
|
/**
|
|
5
13
|
* AsrsRack — a multi-level high-bay storage rack, the structural backbone of
|
|
6
14
|
* an AS/RS (Automated Storage / Retrieval System).
|
|
7
15
|
*
|
|
8
16
|
* `levels` × `bays` cells form a vertical grid. Each cell holds one logistics
|
|
9
17
|
* package (Pallet / Box / Parcel). A pair of AsrsRacks separated by an aisle
|
|
10
|
-
* (where an AsrsCrane runs) is the typical AS/RS configuration
|
|
11
|
-
* single-rack unit and lets users compose multi-rack systems by placing them
|
|
12
|
-
* side by side. A future `AsrsAisle` composite may bundle the pair + crane.
|
|
18
|
+
* (where an AsrsCrane runs) is the typical AS/RS configuration.
|
|
13
19
|
*
|
|
14
|
-
* **
|
|
15
|
-
*
|
|
16
|
-
* pallet load. Users can shorten via explicit `state.depth` for warehouses
|
|
17
|
-
* with smaller envelopes.
|
|
20
|
+
* **Monitoring mode** (default): pallets/boxes are direct children of the rack,
|
|
21
|
+
* placed by the WCS data binding. No RackCell children are created.
|
|
18
22
|
*
|
|
19
|
-
* **
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
* z lands on the rack's overall bottom (parent.zPos + parent.depth = ceiling),
|
|
23
|
-
* which isn't quite cell-level resolution — true per-cell z positioning is
|
|
24
|
-
* a v3 concern (the cargo would need to know which cell-row it's in).
|
|
23
|
+
* **Simulation mode**: call `rack._buildCells()` after placing the rack on the
|
|
24
|
+
* scene. This creates RackCell children at the correct 3D positions. The
|
|
25
|
+
* AsrsCrane then navigates to individual RackCells for pick-and-place.
|
|
25
26
|
*
|
|
26
|
-
*
|
|
27
|
-
* occupancy state is implicit in the children, not a status flag.
|
|
27
|
+
* **Placement**: `floor` archetype, full ceiling depth by default.
|
|
28
28
|
*/
|
|
29
|
-
export default class AsrsRack extends
|
|
29
|
+
export default class AsrsRack extends AsrsRack_base {
|
|
30
|
+
state: AsrsRackState;
|
|
30
31
|
static placement: PlacementArchetype;
|
|
31
32
|
static align: Alignment;
|
|
32
33
|
static defaultDepth: (h: Heights) => number;
|
|
33
34
|
get nature(): ComponentNature;
|
|
34
35
|
get anchors(): never[];
|
|
35
|
-
/**
|
|
36
|
+
/**
|
|
37
|
+
* Derive the cell topology from the rack's current dimensions and bay/level
|
|
38
|
+
* counts. The CellMap is rebuilt fresh each time (state changes trigger
|
|
39
|
+
* re-reads via things-scene's invalidation pipeline).
|
|
40
|
+
*
|
|
41
|
+
* Coordinate convention (matches things-scene 3D):
|
|
42
|
+
* X = bay axis (left → right)
|
|
43
|
+
* Y = level axis (floor → ceiling, the rack's `depth` state property)
|
|
44
|
+
* Z = row axis (front → back, the rack's `height` state property)
|
|
45
|
+
*/
|
|
46
|
+
get cellMap(): CellMap;
|
|
47
|
+
/**
|
|
48
|
+
* Create RackCell child components for each cell in the CellMap.
|
|
49
|
+
*
|
|
50
|
+
* Called explicitly to enter simulation mode — monitoring-mode racks
|
|
51
|
+
* never call this (pallets are direct children, no explicit cells).
|
|
52
|
+
*
|
|
53
|
+
* Idempotent: removes existing rack-cell children first.
|
|
54
|
+
*/
|
|
55
|
+
_buildCells(): void;
|
|
56
|
+
/**
|
|
57
|
+
* Allow:
|
|
58
|
+
* - Carriable components (pallets, boxes, parcels) — direct children in monitoring mode.
|
|
59
|
+
* - RackCell — created by _buildCells() in simulation mode.
|
|
60
|
+
*
|
|
61
|
+
* Block:
|
|
62
|
+
* - Everything else (sensors, labels, etc. can be siblings of the rack, not children).
|
|
63
|
+
*/
|
|
36
64
|
containable(component: Component): boolean;
|
|
65
|
+
/**
|
|
66
|
+
* Attach frame for carriers that are DIRECT children of the rack
|
|
67
|
+
* (monitoring mode, where pallets go directly into the rack without
|
|
68
|
+
* explicit RackCell components).
|
|
69
|
+
*
|
|
70
|
+
* In simulation mode, carriers become children of their RackCell,
|
|
71
|
+
* and each RackCell provides its own attachPointFor(). So this method
|
|
72
|
+
* is only invoked on direct-child carriers in monitoring mode — it
|
|
73
|
+
* returns the rack's own object3d as the attach frame (default behavior).
|
|
74
|
+
*/
|
|
75
|
+
attachPointFor(_carrier: Component): AttachFrame | null;
|
|
37
76
|
/**
|
|
38
77
|
* 2D — top-down rectangle showing the rack footprint, with subdivisions
|
|
39
78
|
* suggesting the bay layout (lines parallel to the aisle).
|