@fmsim/machine 2.0.0-beta.10 → 2.0.0-beta.100
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/dist/agv.js +6 -2
- package/dist/agv.js.map +1 -1
- package/dist/carrier-manager.js +183 -0
- package/dist/carrier-manager.js.map +1 -0
- package/dist/carrier.js +99 -9
- package/dist/carrier.js.map +1 -1
- package/dist/conveyor-join.js +8 -2
- package/dist/conveyor-join.js.map +1 -1
- package/dist/conveyor.js +8 -0
- package/dist/conveyor.js.map +1 -1
- package/dist/crane.js +144 -1
- package/dist/crane.js.map +1 -1
- package/dist/factories/agv-3d.js +8 -9
- package/dist/factories/agv-3d.js.map +1 -1
- package/dist/factories/conveyor-3d.js +168 -81
- package/dist/factories/conveyor-3d.js.map +1 -1
- package/dist/factories/crane-3d.js +48 -12
- package/dist/factories/crane-3d.js.map +1 -1
- package/dist/factories/crane-rail-3d.js +6 -6
- package/dist/factories/crane-rail-3d.js.map +1 -1
- package/dist/factories/machine-3d.js +527 -181
- package/dist/factories/machine-3d.js.map +1 -1
- package/dist/factories/oht-3d.js +20 -20
- package/dist/factories/oht-3d.js.map +1 -1
- package/dist/factories/shuttle-3d.js +2 -5
- package/dist/factories/shuttle-3d.js.map +1 -1
- package/dist/factories/z-priority-defaults.js +33 -0
- package/dist/factories/z-priority-defaults.js.map +1 -0
- package/dist/factories/z-priority.js +54 -0
- package/dist/factories/z-priority.js.map +1 -0
- package/dist/features/conveyor-mixin.js.map +1 -1
- package/dist/features/mcs-status-default.js +15 -1
- package/dist/features/mcs-status-default.js.map +1 -1
- package/dist/features/mcs-status-mixin.js +22 -0
- package/dist/features/mcs-status-mixin.js.map +1 -1
- package/dist/features/mcs-zone-mixin.js.map +1 -1
- package/dist/features/parent-object-mixin.js +15 -7
- package/dist/features/parent-object-mixin.js.map +1 -1
- package/dist/floating-container.js +5 -0
- package/dist/floating-container.js.map +1 -1
- package/dist/index.js +52 -34
- package/dist/index.js.map +1 -1
- package/dist/mcs-carrier-holder.js +56 -22
- package/dist/mcs-carrier-holder.js.map +1 -1
- package/dist/mcs-machine.js +39 -0
- package/dist/mcs-machine.js.map +1 -1
- package/dist/mcs-unit.js +20 -0
- package/dist/mcs-unit.js.map +1 -1
- package/dist/mcs-vehicle.js +8 -3
- package/dist/mcs-vehicle.js.map +1 -1
- package/dist/node.js +10 -3
- package/dist/node.js.map +1 -1
- package/dist/oht.js +4 -0
- package/dist/oht.js.map +1 -1
- package/dist/port-flow.js +4 -0
- package/dist/port-flow.js.map +1 -1
- package/dist/port.js +4 -0
- package/dist/port.js.map +1 -1
- package/dist/scanner.js +30 -0
- package/dist/scanner.js.map +1 -0
- package/dist/scene/root-container-override.js +160 -96
- package/dist/scene/root-container-override.js.map +1 -1
- package/dist/shelf.js +4 -0
- package/dist/shelf.js.map +1 -1
- package/dist/static-container.js +5 -0
- package/dist/static-container.js.map +1 -1
- package/dist/stocker-abnormal-bar.js +5 -1
- package/dist/stocker-abnormal-bar.js.map +1 -1
- package/dist/stocker-capacity-bar.js +253 -6
- package/dist/stocker-capacity-bar.js.map +1 -1
- package/dist/stocker-carrier-bar.js +5 -1
- package/dist/stocker-carrier-bar.js.map +1 -1
- package/dist/stocker.js +6 -0
- package/dist/stocker.js.map +1 -1
- package/dist/templates/backdrop.js +30 -0
- package/dist/templates/backdrop.js.map +1 -0
- package/dist/templates/decal.js +21 -0
- package/dist/templates/decal.js.map +1 -0
- package/dist/templates/index.js +7 -1
- package/dist/templates/index.js.map +1 -1
- package/dist/templates/scanner.js +20 -0
- package/dist/templates/scanner.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/utils/crane-position.js +83 -0
- package/dist/utils/crane-position.js.map +1 -0
- package/dist/utils/data-value.js +21 -0
- package/dist/utils/data-value.js.map +1 -0
- package/dist/utils/fit-gauge-scale.js +28 -0
- package/dist/utils/fit-gauge-scale.js.map +1 -0
- package/dist/utils/get-value-on-ranges.js +2 -4
- package/dist/utils/get-value-on-ranges.js.map +1 -1
- package/dist/utils/stocker-gauge.js +78 -0
- package/dist/utils/stocker-gauge.js.map +1 -0
- package/dist/zone-capacity-bar.js +3 -3
- package/dist/zone-capacity-bar.js.map +1 -1
- package/icons/backdrop.png +0 -0
- package/icons/decal.png +0 -0
- package/jest.config.js +10 -0
- package/package.json +4 -3
- package/src/agv.ts +7 -2
- package/src/carrier-manager.ts +223 -0
- package/src/carrier.ts +103 -10
- package/src/conveyor-join.ts +5 -2
- package/src/conveyor.ts +8 -0
- package/src/crane.ts +181 -1
- package/src/factories/agv-3d.ts +9 -10
- package/src/factories/conveyor-3d.ts +171 -84
- package/src/factories/crane-3d.ts +53 -12
- package/src/factories/crane-rail-3d.ts +8 -7
- package/src/factories/machine-3d.ts +616 -191
- package/src/factories/oht-3d.ts +20 -20
- package/src/factories/shuttle-3d.ts +2 -6
- package/src/factories/z-priority-defaults.ts +38 -0
- package/src/factories/z-priority.ts +60 -0
- package/src/features/conveyor-mixin.ts +1 -1
- package/src/features/mcs-status-default.ts +15 -1
- package/src/features/mcs-status-mixin.ts +26 -4
- package/src/features/mcs-zone-mixin.ts +1 -1
- package/src/features/parent-object-mixin.ts +17 -8
- package/src/floating-container.ts +5 -0
- package/src/index.ts +58 -34
- package/src/mcs-carrier-holder.ts +57 -34
- package/src/mcs-machine.ts +42 -0
- package/src/mcs-unit.ts +24 -1
- package/src/mcs-vehicle.ts +9 -6
- package/src/node.ts +9 -3
- package/src/oht.ts +6 -1
- package/src/port-flow.ts +5 -0
- package/src/port.ts +5 -0
- package/src/scanner.ts +29 -0
- package/src/scene/root-container-override.ts +154 -103
- package/src/shelf.ts +5 -0
- package/src/static-container.ts +5 -0
- package/src/stocker-abnormal-bar.ts +6 -1
- package/src/stocker-capacity-bar.ts +298 -7
- package/src/stocker-carrier-bar.ts +6 -1
- package/src/stocker.ts +7 -0
- package/src/templates/backdrop.ts +30 -0
- package/src/templates/decal.ts +21 -0
- package/src/templates/index.ts +7 -1
- package/src/templates/scanner.ts +20 -0
- package/src/utils/crane-position.ts +105 -0
- package/src/utils/data-value.ts +19 -0
- package/src/utils/fit-gauge-scale.ts +35 -0
- package/src/utils/get-value-on-ranges.ts +2 -4
- package/src/utils/stocker-gauge.ts +111 -0
- package/src/zone-capacity-bar.ts +3 -3
- package/translations/en.json +26 -1
- package/translations/ja.json +26 -1
- package/translations/ko.json +26 -1
- package/translations/ms.json +26 -1
- package/translations/zh.json +26 -1
package/src/mcs-machine.ts
CHANGED
|
@@ -26,6 +26,19 @@ export default class MCSMachine extends MCSStatusMixin(ContainerAbstract) {
|
|
|
26
26
|
return false
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
+
/**
|
|
30
|
+
* id 후보를 조회할 때 백엔드에 보낼 타입명. 값이 있으면 검색(lookup) 대상.
|
|
31
|
+
* 기본은 자기 타입. 자기 타입과 다른 타입으로 조회해야 하는 특수 케이스는 override.
|
|
32
|
+
*/
|
|
33
|
+
get idLookupType(): string | undefined {
|
|
34
|
+
return this.state.type
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** MCS 머신 — parentObject(부모 머신) 후보 수집 시 머신으로 식별. */
|
|
38
|
+
get isMcsMachine(): boolean {
|
|
39
|
+
return true
|
|
40
|
+
}
|
|
41
|
+
|
|
29
42
|
containable(component: Component) {
|
|
30
43
|
return ['rect', 'ellipse'].includes(component.state.type)
|
|
31
44
|
}
|
|
@@ -70,6 +83,35 @@ export default class MCSMachine extends MCSStatusMixin(ContainerAbstract) {
|
|
|
70
83
|
return LEGEND_MACHINE
|
|
71
84
|
}
|
|
72
85
|
|
|
86
|
+
/**
|
|
87
|
+
* In 2D, ignore `state.alpha`; preserve everything else.
|
|
88
|
+
*
|
|
89
|
+
* things-scene's base prerender (component-drawing.ts:31, 44) does:
|
|
90
|
+
* const fade = (alpha || 1) - this._delta.fade
|
|
91
|
+
* if (fade) context.globalAlpha = fade
|
|
92
|
+
*
|
|
93
|
+
* It *overwrites* `globalAlpha` (not multiplies), so the inherited
|
|
94
|
+
* parent alpha is also lost — that's a base-layer quirk we match the
|
|
95
|
+
* recovery for. Then at line 48 it applies editMode+hidden dimming
|
|
96
|
+
* with a *= 0.5.
|
|
97
|
+
*
|
|
98
|
+
* Our recovery:
|
|
99
|
+
* 1. Capture the inherited `globalAlpha` *before* super runs.
|
|
100
|
+
* 2. Let super do its work (transforms / shadow / dim flags).
|
|
101
|
+
* 3. Restore globalAlpha to inherited × editMode-hidden dim factor —
|
|
102
|
+
* i.e., what super would have produced if our state.alpha were 1.
|
|
103
|
+
*
|
|
104
|
+
* 3D path is unaffected: 3D factories read `state.alpha` directly via
|
|
105
|
+
* their `updateAlpha()` implementations and continue to honor it (with
|
|
106
|
+
* per-config opacity fallback).
|
|
107
|
+
*/
|
|
108
|
+
prerender(ctx: any) {
|
|
109
|
+
const inherited = ctx.globalAlpha
|
|
110
|
+
super.prerender(ctx)
|
|
111
|
+
const dim = this.app.isEditMode && this.hidden ? 0.5 : 1
|
|
112
|
+
ctx.globalAlpha = inherited * dim
|
|
113
|
+
}
|
|
114
|
+
|
|
73
115
|
render(ctx) {
|
|
74
116
|
var { round = 0, lineWidth } = this.state
|
|
75
117
|
var { left, top, width, height } = this.bounds
|
package/src/mcs-unit.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ContainerAbstract } from '@hatiolab/things-scene'
|
|
2
2
|
import { getPopupData } from '@fmsim/api'
|
|
3
3
|
|
|
4
4
|
import { MCSStatusMixin, MCSStatusMixinProperties } from './features/mcs-status-mixin'
|
|
@@ -13,6 +13,18 @@ export default class MCSUnit extends MCSStatusMixin(ParentObjectMixin(ContainerA
|
|
|
13
13
|
return [...MCSStatusMixinProperties, ...ParentObjectMixinProperties]
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
+
get showMoveHandle() {
|
|
17
|
+
return false
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* id 후보를 조회할 때 백엔드에 보낼 타입명. 값이 있으면 검색(lookup) 대상.
|
|
22
|
+
* 기본은 자기 타입. 특수 케이스는 override.
|
|
23
|
+
*/
|
|
24
|
+
get idLookupType(): string | undefined {
|
|
25
|
+
return this.state.type
|
|
26
|
+
}
|
|
27
|
+
|
|
16
28
|
get hasTextProperty() {
|
|
17
29
|
return true
|
|
18
30
|
}
|
|
@@ -49,6 +61,17 @@ export default class MCSUnit extends MCSStatusMixin(ParentObjectMixin(ContainerA
|
|
|
49
61
|
return LEGEND_UNIT
|
|
50
62
|
}
|
|
51
63
|
|
|
64
|
+
/**
|
|
65
|
+
* In 2D, ignore `state.alpha`; preserve inherited alpha + editMode-hidden dim.
|
|
66
|
+
* See MCSMachine.prerender for the full rationale.
|
|
67
|
+
*/
|
|
68
|
+
prerender(ctx: any) {
|
|
69
|
+
const inherited = ctx.globalAlpha
|
|
70
|
+
super.prerender(ctx)
|
|
71
|
+
const dim = this.app.isEditMode && this.hidden ? 0.5 : 1
|
|
72
|
+
ctx.globalAlpha = inherited * dim
|
|
73
|
+
}
|
|
74
|
+
|
|
52
75
|
contains(x, y) {
|
|
53
76
|
var { left, top, width, height, lineWidth = 0 } = this.state
|
|
54
77
|
var extend = lineWidth / 2
|
package/src/mcs-vehicle.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { Properties } from '@hatiolab/things-scene'
|
|
2
1
|
import { LEGEND_VEHICLE, Legend } from './features/mcs-status-default'
|
|
3
2
|
import MCSCarrierHolder from './mcs-carrier-holder'
|
|
4
3
|
import Node from './node'
|
|
@@ -124,22 +123,26 @@ export default class MCSVehicle extends MCSCarrierHolder {
|
|
|
124
123
|
|
|
125
124
|
if ((this.root as any).vehicleAnimation) {
|
|
126
125
|
this.trigger('waypoint', {
|
|
127
|
-
from: beforeNode,
|
|
126
|
+
from: beforeNode || afterNode,
|
|
128
127
|
to: afterNode,
|
|
129
128
|
duration: 5000
|
|
130
129
|
})
|
|
131
|
-
} else {
|
|
132
|
-
const fromCenter = beforeNode
|
|
130
|
+
} else if (beforeNode) {
|
|
131
|
+
const fromCenter = beforeNode.center
|
|
133
132
|
const toCenter = afterNode.center
|
|
134
133
|
|
|
135
|
-
const { x: startX, y: startY } = beforeNode
|
|
134
|
+
const { x: startX, y: startY } = beforeNode.transcoordS2T(fromCenter.x, fromCenter.y)
|
|
136
135
|
const { x: endX, y: endY } = afterNode.transcoordS2T(toCenter.x, toCenter.y)
|
|
137
136
|
|
|
138
137
|
const theta = Math.atan2(endY - startY, endX - startX) + Math.PI / 2
|
|
139
138
|
|
|
140
|
-
// 위치 설정
|
|
141
139
|
this.location = { x: endX, y: endY }
|
|
142
140
|
this.setState('rotation', theta)
|
|
141
|
+
} else {
|
|
142
|
+
// beforeNode 없음: afterNode 위치로 즉시 이동
|
|
143
|
+
const toCenter = afterNode.center
|
|
144
|
+
const { x, y } = afterNode.transcoordS2T(toCenter.x, toCenter.y)
|
|
145
|
+
this.location = { x, y }
|
|
143
146
|
}
|
|
144
147
|
}
|
|
145
148
|
}
|
package/src/node.ts
CHANGED
|
@@ -28,11 +28,17 @@ export default class Node extends Shape {
|
|
|
28
28
|
static RADIUS = 8
|
|
29
29
|
|
|
30
30
|
is3dish() {
|
|
31
|
-
return
|
|
31
|
+
return true
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
|
|
35
|
-
|
|
34
|
+
/**
|
|
35
|
+
* Node 는 기본적으로 컨테이너 드롭 입양 불가 — 라인 머신(AGVLine/OHTLine/CraneRail)만
|
|
36
|
+
* 자기 containable 화이트리스트로 Node 를 받는다. 단 FloorPlate 에는 들어갈 수 있게 허용.
|
|
37
|
+
* FloorPlate 는 things-scene 코어 컨테이너라 거기에 'Node' 를 명시할 수 없으므로
|
|
38
|
+
* Node 측에서 해결한다 (FloorPlate 의 기본 containable = component.isDescendible(this)).
|
|
39
|
+
*/
|
|
40
|
+
isDescendible(container?: any) {
|
|
41
|
+
return container?.state?.type === 'floor-plate'
|
|
36
42
|
}
|
|
37
43
|
|
|
38
44
|
render(context) {
|
package/src/oht.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Component, ComponentNature,
|
|
1
|
+
import { Component, ComponentNature, sceneComponent } from '@hatiolab/things-scene'
|
|
2
2
|
import MCSVehicle from './mcs-vehicle'
|
|
3
3
|
|
|
4
4
|
const NATURE: ComponentNature = {
|
|
@@ -20,6 +20,11 @@ export default class OHT extends MCSVehicle {
|
|
|
20
20
|
return 'black'
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
+
/** OHT 의 parentObject 는 OHTLine. */
|
|
24
|
+
get parentObjectTypes(): string[] {
|
|
25
|
+
return ['OHTLine']
|
|
26
|
+
}
|
|
27
|
+
|
|
23
28
|
getLegendFallback() {
|
|
24
29
|
return (this.root as any).ohtLegendTheme || super.getLegendFallback()
|
|
25
30
|
}
|
package/src/port-flow.ts
CHANGED
|
@@ -45,6 +45,11 @@ export default class PortFlow extends ParentObjectMixin(RectPath(Shape)) {
|
|
|
45
45
|
return NATURE
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
+
/** PortFlow 의 parentObject 는 Port (유닛). */
|
|
49
|
+
get parentObjectTypes(): string[] {
|
|
50
|
+
return ['Port']
|
|
51
|
+
}
|
|
52
|
+
|
|
48
53
|
get hasTextProperty() {
|
|
49
54
|
return this.getState('showText') || false
|
|
50
55
|
}
|
package/src/port.ts
CHANGED
|
@@ -23,6 +23,11 @@ export default class Port extends MCSZoneMixin(MCSCarrierHolder) {
|
|
|
23
23
|
return (this.root as any).portLegendTheme || super.getLegendFallback()
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
+
/** Port 는 후보가 매우 많아 부모 머신 없이는 id 리스트를 조회하지 않는다 (machine 필수). */
|
|
27
|
+
get idLookupRequiresMachine() {
|
|
28
|
+
return true
|
|
29
|
+
}
|
|
30
|
+
|
|
26
31
|
render(ctx: CanvasRenderingContext2D) {
|
|
27
32
|
var { left, top, width, height } = this.bounds
|
|
28
33
|
|
package/src/scanner.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { Component, ComponentNature, Triangle, sceneComponent } from '@hatiolab/things-scene'
|
|
2
|
+
import { MCSStatusMixin } from './features/mcs-status-mixin'
|
|
3
|
+
|
|
4
|
+
const NATURE: ComponentNature = {
|
|
5
|
+
mutable: true,
|
|
6
|
+
resizable: false,
|
|
7
|
+
rotatable: true,
|
|
8
|
+
properties: [
|
|
9
|
+
{
|
|
10
|
+
type: 'number',
|
|
11
|
+
label: 'round',
|
|
12
|
+
name: 'round',
|
|
13
|
+
property: { min: 0, max: 100, step: 1 }
|
|
14
|
+
}
|
|
15
|
+
]
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
@sceneComponent('Scanner')
|
|
19
|
+
export default class Scanner extends MCSStatusMixin(Triangle) {
|
|
20
|
+
static get nature() {
|
|
21
|
+
return NATURE
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
is3dish() {
|
|
25
|
+
return true
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
Component.memoize(Scanner.prototype, 'path', false)
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Component, RootContainer } from '@hatiolab/things-scene'
|
|
2
2
|
import { themesColorMap, themesColorRange, themesAnimationMap } from '@fmsim/api'
|
|
3
3
|
import { Legend } from '../features/mcs-status-default'
|
|
4
|
+
import CarrierManager from '../carrier-manager'
|
|
4
5
|
|
|
5
6
|
// RootContainer 클래스 타입 확장
|
|
6
7
|
declare module '@hatiolab/things-scene' {
|
|
@@ -24,11 +25,28 @@ declare module '@hatiolab/things-scene' {
|
|
|
24
25
|
equipmentLegendTheme?: any
|
|
25
26
|
style: any
|
|
26
27
|
vehicleAnimation?: boolean
|
|
28
|
+
// 스토커 게이지 표시 모드 — 런타임 전용 transient (모델 비저장, 항상 'capacity' 시작)
|
|
29
|
+
stockerGaugeMode: string
|
|
30
|
+
_stockerGaugeMode?: string
|
|
31
|
+
_refreshStockerGauges(): void
|
|
27
32
|
// 캐시 관련 속성 추가
|
|
28
33
|
_animationConfigCache?: Map<string, any>
|
|
34
|
+
_carrierManager?: CarrierManager
|
|
35
|
+
carrierManager: CarrierManager
|
|
29
36
|
}
|
|
30
37
|
}
|
|
31
38
|
|
|
39
|
+
/**
|
|
40
|
+
* 스토커 적재율 게이지 표시 모드 — 런타임 선택지.
|
|
41
|
+
* value 는 엔진 canonical(2D StockerCapacityBar / 3D buildGauge 공통), label 은 표시명.
|
|
42
|
+
* 런타임 UI(뷰어 컨트롤)가 이 목록으로 selector 를 구성한다.
|
|
43
|
+
*/
|
|
44
|
+
export const STOCKER_GAUGE_MODES: { value: string; label: string }[] = [
|
|
45
|
+
{ value: 'capacity', label: 'Capacity' },
|
|
46
|
+
{ value: 'carrier', label: 'Carrier Status' },
|
|
47
|
+
{ value: 'abnormal', label: 'Abnormal Carrier' }
|
|
48
|
+
]
|
|
49
|
+
|
|
32
50
|
const ModelLayer = (Component as any).register('model-layer') as any
|
|
33
51
|
|
|
34
52
|
Object.defineProperty(ModelLayer.prototype, 'nature', {
|
|
@@ -194,6 +212,22 @@ Object.defineProperty(ModelLayer.prototype, 'nature', {
|
|
|
194
212
|
property: {
|
|
195
213
|
options: themesColorRange
|
|
196
214
|
}
|
|
215
|
+
},
|
|
216
|
+
{
|
|
217
|
+
type: 'legend',
|
|
218
|
+
label: '',
|
|
219
|
+
name: 'MOOD',
|
|
220
|
+
property: {
|
|
221
|
+
label: 'MOOD'
|
|
222
|
+
}
|
|
223
|
+
},
|
|
224
|
+
{
|
|
225
|
+
type: 'select',
|
|
226
|
+
label: 'light-preset',
|
|
227
|
+
name: 'lightPreset',
|
|
228
|
+
property: {
|
|
229
|
+
options: ['dark', 'medium', 'bright']
|
|
230
|
+
}
|
|
197
231
|
}
|
|
198
232
|
],
|
|
199
233
|
'value-property': 'text'
|
|
@@ -201,128 +235,145 @@ Object.defineProperty(ModelLayer.prototype, 'nature', {
|
|
|
201
235
|
}
|
|
202
236
|
})
|
|
203
237
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
Object.defineProperty(RootContainer.prototype, 'agvlineLegendTheme', {
|
|
219
|
-
get: function () {
|
|
220
|
-
const { agvlineLegendName } = this.rootModel.state
|
|
221
|
-
return agvlineLegendName && this.state.style[agvlineLegendName]
|
|
222
|
-
}
|
|
223
|
-
})
|
|
224
|
-
|
|
225
|
-
Object.defineProperty(RootContainer.prototype, 'ohtlineLegendTheme', {
|
|
226
|
-
get: function () {
|
|
227
|
-
const { ohtlineLegendName } = this.rootModel.state
|
|
228
|
-
return ohtlineLegendName && this.state.style[ohtlineLegendName]
|
|
229
|
-
}
|
|
230
|
-
})
|
|
231
|
-
|
|
232
|
-
Object.defineProperty(RootContainer.prototype, 'conveyorLegendTheme', {
|
|
233
|
-
get: function () {
|
|
234
|
-
const { conveyorLegendName } = this.rootModel.state
|
|
235
|
-
return conveyorLegendName && this.state.style[conveyorLegendName]
|
|
236
|
-
}
|
|
237
|
-
})
|
|
238
|
-
|
|
239
|
-
Object.defineProperty(RootContainer.prototype, 'stokerLegendTheme', {
|
|
240
|
-
get: function () {
|
|
241
|
-
const { stokerLegendName } = this.rootModel.state
|
|
242
|
-
return stokerLegendName && this.state.style[stokerLegendName]
|
|
243
|
-
}
|
|
244
|
-
})
|
|
245
|
-
|
|
246
|
-
Object.defineProperty(RootContainer.prototype, 'agvLegendTheme', {
|
|
247
|
-
get: function () {
|
|
248
|
-
const { agvLegendName } = this.rootModel.state
|
|
249
|
-
return agvLegendName && this.state.style[agvLegendName]
|
|
250
|
-
}
|
|
251
|
-
})
|
|
252
|
-
|
|
253
|
-
Object.defineProperty(RootContainer.prototype, 'ohtLegendTheme', {
|
|
254
|
-
get: function () {
|
|
255
|
-
const { ohtLegendName } = this.rootModel.state
|
|
256
|
-
return ohtLegendName && this.state.style[ohtLegendName]
|
|
257
|
-
}
|
|
258
|
-
})
|
|
259
|
-
|
|
260
|
-
Object.defineProperty(RootContainer.prototype, 'shuttleLegendTheme', {
|
|
261
|
-
get: function () {
|
|
262
|
-
const { shuttleLegendName } = this.rootModel.state
|
|
263
|
-
return shuttleLegendName && this.state.style[shuttleLegendName]
|
|
264
|
-
}
|
|
265
|
-
})
|
|
238
|
+
/**
|
|
239
|
+
* 조명 프리셋 — 3 단계 밝기 토글.
|
|
240
|
+
*
|
|
241
|
+
* fmsim 은 디테일 조명 옵션 UI 를 노출하지 않으므로 단일 select 로 단순화.
|
|
242
|
+
* medium = things-scene 의 현재 default 값과 동일 (외관 변화 없음).
|
|
243
|
+
* 사용자가 select 변경 시 onchange 가 exposure + hemiIntensity + dirLightIntensity
|
|
244
|
+
* 를 일괄 set → three-capability 의 변경 감지 핸들러가 즉시 갱신.
|
|
245
|
+
*/
|
|
246
|
+
const LIGHT_PRESETS: Record<string, { exposure: number; hemiIntensity: number; dirLightIntensity: number }> = {
|
|
247
|
+
dark: { exposure: 1.2, hemiIntensity: 4.0, dirLightIntensity: 0.6 },
|
|
248
|
+
medium: { exposure: 1.2, hemiIntensity: 4.0, dirLightIntensity: 2.0 },
|
|
249
|
+
bright: { exposure: 1.2, hemiIntensity: 4.0, dirLightIntensity: 5.0 }
|
|
250
|
+
}
|
|
266
251
|
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
252
|
+
const _origModelLayerOnchange = ModelLayer.prototype.onchange
|
|
253
|
+
ModelLayer.prototype.onchange = function (after: any) {
|
|
254
|
+
if (after && Object.prototype.hasOwnProperty.call(after, 'lightPreset')) {
|
|
255
|
+
const preset = LIGHT_PRESETS[after.lightPreset as string]
|
|
256
|
+
if (preset) {
|
|
257
|
+
this.set({
|
|
258
|
+
exposure: preset.exposure,
|
|
259
|
+
hemiIntensity: preset.hemiIntensity,
|
|
260
|
+
dirLightIntensity: preset.dirLightIntensity
|
|
261
|
+
})
|
|
262
|
+
}
|
|
271
263
|
}
|
|
272
|
-
|
|
264
|
+
return _origModelLayerOnchange.call(this, after)
|
|
265
|
+
}
|
|
273
266
|
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
267
|
+
// === LegendTheme / AnimationTheme value resolve ===
|
|
268
|
+
//
|
|
269
|
+
// rootModel.state.style[name] 에 저장된 legend/animation 값은 보드 직렬화
|
|
270
|
+
// 형태에 따라 (a) 정상 object 또는 (b) JSON 문자열 둘 다로 나타난다 (2026-06-09
|
|
271
|
+
// 진단 — consumer 인 carrier.ts:animationPreset 이 같은 이유로 typeof string
|
|
272
|
+
// 분기 + JSON5.parse 처리하는 선례 존재). LegendTheme getter 들이 이 분기를
|
|
273
|
+
// 두지 않아 string 통과 → getValueOnRanges 매칭 실패 → 3D gauge 의 색상이
|
|
274
|
+
// fallback 으로만 결정되던 증상.
|
|
275
|
+
//
|
|
276
|
+
// fix 는 getter 한 곳에서 lazy parse. WeakMap<state, Map<rawString, parsed>>
|
|
277
|
+
// 으로 *raw string 자체를 key* — 보드 편집으로 raw 가 바뀌면 자연 무효화,
|
|
278
|
+
// 같은 raw 의 반복 lookup 은 O(1). root 가 GC 되면 cache 도 같이 GC.
|
|
279
|
+
const _legendParseCache = new WeakMap<object, Map<string, any>>()
|
|
280
280
|
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
281
|
+
function parseLegendIfString(state: any, raw: any): any {
|
|
282
|
+
if (typeof raw !== 'string') return raw
|
|
283
|
+
let cache = _legendParseCache.get(state)
|
|
284
|
+
if (!cache) {
|
|
285
|
+
cache = new Map()
|
|
286
|
+
_legendParseCache.set(state, cache)
|
|
285
287
|
}
|
|
286
|
-
|
|
288
|
+
if (cache.has(raw)) return cache.get(raw)
|
|
289
|
+
let parsed: any
|
|
290
|
+
try { parsed = JSON.parse(raw) } catch { parsed = raw } // parse 실패 시 raw 보존
|
|
291
|
+
cache.set(raw, parsed)
|
|
292
|
+
return parsed
|
|
293
|
+
}
|
|
287
294
|
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
const
|
|
291
|
-
|
|
295
|
+
function legendThemeGetter(stateKey: string) {
|
|
296
|
+
return function (this: any) {
|
|
297
|
+
const name = this.rootModel.state[stateKey]
|
|
298
|
+
const raw = name && this.state.style[name]
|
|
299
|
+
return parseLegendIfString(this.state, raw)
|
|
292
300
|
}
|
|
293
|
-
}
|
|
301
|
+
}
|
|
294
302
|
|
|
295
|
-
Object.defineProperty(RootContainer.prototype, '
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
})
|
|
303
|
+
Object.defineProperty(RootContainer.prototype, 'equipmentLegendTheme', { get: legendThemeGetter('equipmentLegendName') })
|
|
304
|
+
Object.defineProperty(RootContainer.prototype, 'bufferLegendTheme', { get: legendThemeGetter('bufferLegendName') })
|
|
305
|
+
Object.defineProperty(RootContainer.prototype, 'agvlineLegendTheme', { get: legendThemeGetter('agvlineLegendName') })
|
|
306
|
+
Object.defineProperty(RootContainer.prototype, 'ohtlineLegendTheme', { get: legendThemeGetter('ohtlineLegendName') })
|
|
307
|
+
Object.defineProperty(RootContainer.prototype, 'conveyorLegendTheme', { get: legendThemeGetter('conveyorLegendName') })
|
|
308
|
+
Object.defineProperty(RootContainer.prototype, 'stokerLegendTheme', { get: legendThemeGetter('stokerLegendName') })
|
|
309
|
+
Object.defineProperty(RootContainer.prototype, 'agvLegendTheme', { get: legendThemeGetter('agvLegendName') })
|
|
310
|
+
Object.defineProperty(RootContainer.prototype, 'ohtLegendTheme', { get: legendThemeGetter('ohtLegendName') })
|
|
311
|
+
Object.defineProperty(RootContainer.prototype, 'shuttleLegendTheme', { get: legendThemeGetter('shuttleLegendName') })
|
|
312
|
+
Object.defineProperty(RootContainer.prototype, 'craneLegendTheme', { get: legendThemeGetter('craneLegendName') })
|
|
313
|
+
Object.defineProperty(RootContainer.prototype, 'shelfLegendTheme', { get: legendThemeGetter('shelfLegendName') })
|
|
314
|
+
Object.defineProperty(RootContainer.prototype, 'portLegendTheme', { get: legendThemeGetter('portLegendName') })
|
|
315
|
+
Object.defineProperty(RootContainer.prototype, 'carrierLegendTheme', { get: legendThemeGetter('carrierLegendName') })
|
|
316
|
+
Object.defineProperty(RootContainer.prototype, 'carrierAnimationTheme', { get: legendThemeGetter('carrierAnimationName') })
|
|
317
|
+
Object.defineProperty(RootContainer.prototype, 'stockerCapacityLegendTheme', { get: legendThemeGetter('stockerCapacityLegendName') })
|
|
318
|
+
Object.defineProperty(RootContainer.prototype, 'zoneCapacityLegendTheme', { get: legendThemeGetter('zoneCapacityLegendName') })
|
|
319
|
+
Object.defineProperty(RootContainer.prototype, 'batteryRateLegendTheme', { get: legendThemeGetter('batteryRateLegendName') })
|
|
301
320
|
|
|
302
|
-
Object.defineProperty(RootContainer.prototype, '
|
|
321
|
+
Object.defineProperty(RootContainer.prototype, 'vehicleAnimation', {
|
|
303
322
|
get: function () {
|
|
304
|
-
const {
|
|
305
|
-
return
|
|
323
|
+
const { vehicleAnimation } = this.rootModel.state
|
|
324
|
+
return !!vehicleAnimation
|
|
306
325
|
}
|
|
307
326
|
})
|
|
308
327
|
|
|
309
|
-
Object.defineProperty(RootContainer.prototype, '
|
|
328
|
+
Object.defineProperty(RootContainer.prototype, 'carrierManager', {
|
|
310
329
|
get: function () {
|
|
311
|
-
|
|
312
|
-
|
|
330
|
+
if (!this._carrierManager) {
|
|
331
|
+
this._carrierManager = new CarrierManager(this)
|
|
332
|
+
}
|
|
333
|
+
return this._carrierManager
|
|
313
334
|
}
|
|
314
335
|
})
|
|
315
336
|
|
|
316
|
-
|
|
337
|
+
// === 스토커 게이지 표시 모드 (런타임 전용 transient) ===
|
|
338
|
+
//
|
|
339
|
+
// 모델(state)에 저장하지 않는다 — nature 속성이 아니므로 보드 직렬화에 포함되지 않고,
|
|
340
|
+
// 보드를 다시 열면 항상 기본값 'capacity' 로 시작한다. 실행 중 뷰어 컨트롤 등이
|
|
341
|
+
// `scene.rootContainer.stockerGaugeMode = 'carrier'` 로 동적 변경한다.
|
|
342
|
+
// (주의: scene.root 은 model_layer 라 override 가 없다. 반드시 rootContainer 에 설정.
|
|
343
|
+
// 컴포넌트의 this.root 는 RootContainer(isRoot)로 해석되어 이 값을 읽는다.)
|
|
344
|
+
//
|
|
345
|
+
// 2D(StockerCapacityBar.gaugeMode) 와 3D(machine-3d buildGauge) 가 모두 이 값을 읽어
|
|
346
|
+
// 게이지 표현이 항상 일치한다. 값이 바뀌면 setter 가 2D 리드로우 + 3D 게이지 재빌드를
|
|
347
|
+
// 즉시 트리거한다.
|
|
348
|
+
Object.defineProperty(RootContainer.prototype, 'stockerGaugeMode', {
|
|
317
349
|
get: function () {
|
|
318
|
-
|
|
319
|
-
|
|
350
|
+
return this._stockerGaugeMode || 'capacity'
|
|
351
|
+
},
|
|
352
|
+
set: function (mode: string) {
|
|
353
|
+
const next = mode || 'capacity'
|
|
354
|
+
if (this._stockerGaugeMode === next) return
|
|
355
|
+
this._stockerGaugeMode = next
|
|
356
|
+
this._refreshStockerGauges()
|
|
320
357
|
}
|
|
321
358
|
})
|
|
322
359
|
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
360
|
+
/** scene 하위 STOCKER(3D 게이지) + StockerCapacityBar(2D 게이지) 를 새 모드로 갱신. */
|
|
361
|
+
RootContainer.prototype._refreshStockerGauges = function () {
|
|
362
|
+
const walk = (container: any) => {
|
|
363
|
+
const kids = container?.components
|
|
364
|
+
if (!Array.isArray(kids)) return
|
|
365
|
+
for (const c of kids) {
|
|
366
|
+
const type = c?.state?.type
|
|
367
|
+
if (type === 'STOCKER') {
|
|
368
|
+
// 3D 게이지는 스토커 자신의 real-object 가 그린다 → 재빌드 (2D 모드면 _realObject 없음)
|
|
369
|
+
c._realObject?.update?.()
|
|
370
|
+
} else if (type === 'StockerCapacityBar' || type === 'MCSGaugeCapacityBar') {
|
|
371
|
+
// 2D 게이지는 자식 컴포넌트가 그린다 → 리드로우
|
|
372
|
+
c.invalidate?.()
|
|
373
|
+
}
|
|
374
|
+
walk(c)
|
|
375
|
+
}
|
|
327
376
|
}
|
|
328
|
-
|
|
377
|
+
walk(this)
|
|
378
|
+
this.invalidate?.()
|
|
379
|
+
}
|
package/src/shelf.ts
CHANGED
|
@@ -19,6 +19,11 @@ export default class Shelf extends MCSZoneMixin(MCSCarrierHolder) {
|
|
|
19
19
|
return 'black'
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
+
/** Shelf 의 parentObject 는 STOCKER. */
|
|
23
|
+
get parentObjectTypes(): string[] {
|
|
24
|
+
return ['STOCKER']
|
|
25
|
+
}
|
|
26
|
+
|
|
22
27
|
getLegendFallback() {
|
|
23
28
|
return (this.root as any).shelfLegendTheme || super.getLegendFallback()
|
|
24
29
|
}
|
package/src/static-container.ts
CHANGED
|
@@ -13,6 +13,11 @@ const NATURE = {
|
|
|
13
13
|
|
|
14
14
|
@sceneComponent('container')
|
|
15
15
|
export default class StaticContainer extends Container {
|
|
16
|
+
// things-scene 의 `declare const Container_base` 패턴이 base 인스턴스 타입의 accessor
|
|
17
|
+
// 정보를 object-type property 로 평탄화하여 TS2611 발생. operato-scene 은 position
|
|
18
|
+
// override 케이스가 없어 미발현, fmsim 만 해당. things-scene 측 mixin 타입 패턴이
|
|
19
|
+
// 개선되면 제거.
|
|
20
|
+
// @ts-expect-error TS2611
|
|
16
21
|
get position() {
|
|
17
22
|
return 'static'
|
|
18
23
|
}
|
|
@@ -72,6 +72,11 @@ export default class StockerAbnormalBar extends ParentObjectMixin(RectPath(Shape
|
|
|
72
72
|
return NATURE
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
+
/** StockerAbnormalBar 의 parentObject 는 STOCKER. */
|
|
76
|
+
get parentObjectTypes(): string[] {
|
|
77
|
+
return ['STOCKER']
|
|
78
|
+
}
|
|
79
|
+
|
|
75
80
|
get controls() {
|
|
76
81
|
var { left, top, width, round, height } = this.state
|
|
77
82
|
round = round == undefined ? 0 : safeRound(round, width, height)
|
|
@@ -232,7 +237,7 @@ export default class StockerAbnormalBar extends ParentObjectMixin(RectPath(Shape
|
|
|
232
237
|
const {
|
|
233
238
|
CARRIER_NORMAL = 0,
|
|
234
239
|
CARRIER_ABNORMAL = 0
|
|
235
|
-
} = typeof this.data == 'object' ? this.data : {}
|
|
240
|
+
} = this.data && typeof this.data == 'object' ? this.data : {}
|
|
236
241
|
|
|
237
242
|
this.setState({
|
|
238
243
|
carrierNormal: Number(CARRIER_NORMAL) || 0,
|