@fmsim/machine 2.0.0-beta.98 → 2.0.0
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/carrier-manager.js +9 -1
- package/dist/carrier-manager.js.map +1 -1
- package/dist/carrier.js +69 -52
- package/dist/carrier.js.map +1 -1
- package/dist/crane.js +15 -6
- package/dist/crane.js.map +1 -1
- package/dist/factories/agv-3d.js +9 -27
- package/dist/factories/agv-3d.js.map +1 -1
- package/dist/factories/conveyor-3d.js +11 -19
- package/dist/factories/conveyor-3d.js.map +1 -1
- package/dist/factories/crane-3d.js +8 -17
- package/dist/factories/crane-3d.js.map +1 -1
- package/dist/factories/crane-rail-3d.js +8 -12
- package/dist/factories/crane-rail-3d.js.map +1 -1
- package/dist/factories/machine-3d.js +100 -63
- package/dist/factories/machine-3d.js.map +1 -1
- package/dist/factories/oht-3d.js +9 -27
- package/dist/factories/oht-3d.js.map +1 -1
- package/dist/factories/shuttle-3d.js +11 -27
- package/dist/factories/shuttle-3d.js.map +1 -1
- package/dist/features/capacity-gauge-mixin.js +520 -0
- package/dist/features/capacity-gauge-mixin.js.map +1 -0
- package/dist/features/mcs-status-default.js +13 -0
- package/dist/features/mcs-status-default.js.map +1 -1
- package/dist/features/parent-object-mixin.js +5 -1
- package/dist/features/parent-object-mixin.js.map +1 -1
- package/dist/mcs-machine.js +4 -23
- package/dist/mcs-machine.js.map +1 -1
- package/dist/mcs-unit.js +2 -5
- package/dist/mcs-unit.js.map +1 -1
- package/dist/mcs-vehicle.js +3 -1
- package/dist/mcs-vehicle.js.map +1 -1
- package/dist/scene/root-container-override.js +5 -2
- package/dist/scene/root-container-override.js.map +1 -1
- package/dist/stocker-capacity-bar.js +11 -372
- package/dist/stocker-capacity-bar.js.map +1 -1
- package/dist/stocker.js +29 -1
- package/dist/stocker.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/utils/crane-position.js +23 -14
- package/dist/utils/crane-position.js.map +1 -1
- package/dist/utils/stocker-gauge.js +48 -10
- package/dist/utils/stocker-gauge.js.map +1 -1
- package/dist/zone-capacity-bar.js +13 -213
- package/dist/zone-capacity-bar.js.map +1 -1
- package/package.json +4 -4
- package/src/carrier-manager.ts +9 -1
- package/src/carrier.ts +73 -52
- package/src/crane.ts +14 -5
- package/src/factories/agv-3d.ts +10 -26
- package/src/factories/conveyor-3d.ts +12 -19
- package/src/factories/crane-3d.ts +9 -17
- package/src/factories/crane-rail-3d.ts +9 -13
- package/src/factories/machine-3d.ts +125 -66
- package/src/factories/oht-3d.ts +10 -26
- package/src/factories/shuttle-3d.ts +12 -26
- package/src/features/capacity-gauge-mixin.ts +642 -0
- package/src/features/mcs-status-default.ts +13 -0
- package/src/features/parent-object-mixin.ts +5 -1
- package/src/mcs-machine.ts +4 -23
- package/src/mcs-unit.ts +2 -5
- package/src/mcs-vehicle.ts +3 -1
- package/src/scene/root-container-override.ts +5 -2
- package/src/stocker-capacity-bar.ts +15 -476
- package/src/stocker.ts +29 -1
- package/src/utils/crane-position.ts +46 -14
- package/src/utils/stocker-gauge.ts +63 -10
- package/src/zone-capacity-bar.ts +17 -282
- package/translations/en.json +4 -1
- package/translations/ja.json +4 -1
- package/translations/ko.json +4 -1
- package/translations/ms.json +4 -1
- package/translations/zh.json +4 -1
|
@@ -9,11 +9,11 @@
|
|
|
9
9
|
*/
|
|
10
10
|
import * as THREE from 'three';
|
|
11
11
|
import * as BufferGeometryUtils from 'three/examples/jsm/utils/BufferGeometryUtils.js';
|
|
12
|
-
import { RealObjectGroup, registerRealObjectFactory, disposeObject3D } from '@hatiolab/things-scene';
|
|
13
|
-
import { LEGEND_CAPACITY } from '../features/mcs-status-default';
|
|
12
|
+
import { RealObjectGroup, registerRealObjectFactory, disposeObject3D, MaterialBank } from '@hatiolab/things-scene';
|
|
13
|
+
import { LEGEND_CAPACITY, resolveCarrierLegend } from '../features/mcs-status-default';
|
|
14
14
|
import { getValueOnRanges } from '../utils/get-value-on-ranges';
|
|
15
15
|
import { fitGaugeScale } from '../utils/fit-gauge-scale';
|
|
16
|
-
import { carrierSegmentsFromData, abnormalSegmentsFromData,
|
|
16
|
+
import { carrierSegmentsFromData, abnormalSegmentsFromData, carrierColorsFromLegend, ABNORMAL_COLORS } from '../utils/stocker-gauge';
|
|
17
17
|
import { Z_FMSIM } from './z-priority';
|
|
18
18
|
/**
|
|
19
19
|
* 포트 기본 높이 (절대값).
|
|
@@ -232,6 +232,8 @@ class MCSRealObject3D extends RealObjectGroup {
|
|
|
232
232
|
? this.buildTriangleGeometry(depth)
|
|
233
233
|
: this.buildBoxGeometry(w, depth, h);
|
|
234
234
|
const isPlane = shape === 'plane';
|
|
235
|
+
// Standard body 는 MaterialBank 공유(색 변형) — 인스턴스 dedup + faint 안전(코어가 공유 base 의
|
|
236
|
+
// 변형만 스왑, 원본 base 불변). plane 은 unlit MeshBasicMaterial 이라 bank(Standard 전용) 비대상 → raw.
|
|
235
237
|
const material = isPlane
|
|
236
238
|
? new THREE.MeshBasicMaterial({
|
|
237
239
|
color,
|
|
@@ -239,11 +241,13 @@ class MCSRealObject3D extends RealObjectGroup {
|
|
|
239
241
|
transparent: opacity < 1,
|
|
240
242
|
side: THREE.DoubleSide,
|
|
241
243
|
})
|
|
242
|
-
:
|
|
243
|
-
metalness,
|
|
244
|
+
: MaterialBank.material('mcs|body', Object.assign({ metalness,
|
|
244
245
|
roughness,
|
|
245
|
-
opacity,
|
|
246
|
-
|
|
246
|
+
opacity, side: opacity < 1 ? THREE.DoubleSide : THREE.FrontSide }, (this._config.emissiveIntensity
|
|
247
|
+
? { emissive: color, emissiveIntensity: this._config.emissiveIntensity }
|
|
248
|
+
: {})), color);
|
|
249
|
+
// z-fighting 방지: 바닥면(floor grid)보다 앞. bank body 는 공유 base 라 polygonOffset 은 모든 mcs
|
|
250
|
+
// body 공통 상수 — 반환 material 에 세팅(같은 값이라 멱등, 인스턴스별 발산 없음).
|
|
247
251
|
material.polygonOffset = true;
|
|
248
252
|
material.polygonOffsetFactor = -1;
|
|
249
253
|
material.polygonOffsetUnit = -1;
|
|
@@ -275,6 +279,10 @@ class MCSRealObject3D extends RealObjectGroup {
|
|
|
275
279
|
this._ownMesh.castShadow = !isPlane;
|
|
276
280
|
this._ownMesh.receiveShadow = true;
|
|
277
281
|
this.components3D.add(this._ownMesh);
|
|
282
|
+
// mesh 는 super.build() *이후* 생성되므로, 코어의 build-시점 faint 적용이 이 mesh 에 못 닿는다.
|
|
283
|
+
// mesh 생성 직후 명시 적용 — 코어 applyMaterialOpacity 가 content-alpha × faint 를 일괄 처리.
|
|
284
|
+
// view 모드에선 factor=1(=state.alpha 없음) → canonical(config.opacity) 그대로, 고객 화면 불변.
|
|
285
|
+
this.updateAlpha();
|
|
278
286
|
}
|
|
279
287
|
onchange(after, before) {
|
|
280
288
|
if ('width' in after ||
|
|
@@ -284,7 +292,8 @@ class MCSRealObject3D extends RealObjectGroup {
|
|
|
284
292
|
'strokeStyle' in after ||
|
|
285
293
|
'id' in after ||
|
|
286
294
|
'EMPTYTYPE' in after ||
|
|
287
|
-
'CARRIERSTATUS' in after
|
|
295
|
+
'CARRIERSTATUS' in after ||
|
|
296
|
+
'showText' in after) {
|
|
288
297
|
// 연속 setState에 의한 다중 rebuild 방지: 한 프레임에 한 번만 실행
|
|
289
298
|
if (!this._updatePending) {
|
|
290
299
|
this._updatePending = true;
|
|
@@ -301,7 +310,7 @@ class MCSRealObject3D extends RealObjectGroup {
|
|
|
301
310
|
* 상판 텍스처: 컴포넌트의 2D render()를 캔버스에 그려서 텍스처로 사용
|
|
302
311
|
*/
|
|
303
312
|
buildTopTexture(w, h) {
|
|
304
|
-
var _a;
|
|
313
|
+
var _a, _b;
|
|
305
314
|
const scale = 4;
|
|
306
315
|
const cw = this.nextPow2(w * scale);
|
|
307
316
|
const ch = this.nextPow2(h * scale);
|
|
@@ -318,8 +327,9 @@ class MCSRealObject3D extends RealObjectGroup {
|
|
|
318
327
|
// 바탕: 캐리어 상태 색상 (사용 영역만)
|
|
319
328
|
ctx.fillStyle = statusColor;
|
|
320
329
|
ctx.fillRect(0, 0, usedW, usedH);
|
|
321
|
-
// 텍스트 (사용 영역 중앙)
|
|
322
|
-
|
|
330
|
+
// 텍스트 (사용 영역 중앙) — showText(= 현재 홀더의 showCarrierText) off 면 미표시. 2D 와 동일.
|
|
331
|
+
const showText = !!((_b = this.component.state) === null || _b === void 0 ? void 0 : _b.showText);
|
|
332
|
+
if (text && showText) {
|
|
323
333
|
const fontSize = Math.round(Math.min(usedW, usedH) * 0.7);
|
|
324
334
|
ctx.font = `bold ${fontSize}px Arial`;
|
|
325
335
|
ctx.textAlign = 'center';
|
|
@@ -369,24 +379,6 @@ class MCSRealObject3D extends RealObjectGroup {
|
|
|
369
379
|
}
|
|
370
380
|
// RealObjectGroup 기본 동작 비활성화
|
|
371
381
|
updateDimension() { }
|
|
372
|
-
updateAlpha() {
|
|
373
|
-
const state = this.component.state;
|
|
374
|
-
// alpha가 명시적으로 설정되어 있으면 그 값을 사용, 없으면 config 기본값 적용
|
|
375
|
-
const effective = ('alpha' in state) ? state.alpha : this._config.opacity;
|
|
376
|
-
this.object3d.traverse((o) => {
|
|
377
|
-
if (!o.material)
|
|
378
|
-
return;
|
|
379
|
-
const materials = Array.isArray(o.material) ? o.material : [o.material];
|
|
380
|
-
for (const m of materials) {
|
|
381
|
-
if (!m.visible)
|
|
382
|
-
continue; // openTop invisible face 무시
|
|
383
|
-
if (m.map)
|
|
384
|
-
continue; // 텍스처 material은 자체 transparent 유지
|
|
385
|
-
m.opacity = effective;
|
|
386
|
-
m.transparent = effective < 1;
|
|
387
|
-
}
|
|
388
|
-
});
|
|
389
|
-
}
|
|
390
382
|
}
|
|
391
383
|
// ── 헬퍼 ────────────────────────────────────────────────
|
|
392
384
|
/**
|
|
@@ -461,11 +453,9 @@ class OHTLineRealObject3D extends RealObjectGroup {
|
|
|
461
453
|
const flangeW = beltW * 0.8; // 플랜지 폭
|
|
462
454
|
const webH = beamH - flangeH * 2; // 웹 높이
|
|
463
455
|
const webW = Math.max(flangeW * 0.25, 1); // 웹 두께
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
roughness: 0.4,
|
|
468
|
-
});
|
|
456
|
+
// 공유 base(MaterialBank) — OHTLine rail 은 정적 색이라 전 인스턴스 dedup. faint 는 코어가
|
|
457
|
+
// 공유 base 의 변형을 스왑(원본 불변). dispose 는 bank-safe(update 에서 material.dispose 안 함).
|
|
458
|
+
const mat = MaterialBank.material('oht-line|rail', { metalness: 0.5, roughness: 0.4 }, OHT_LINE_RAIL_COLOR);
|
|
469
459
|
const geoms = [];
|
|
470
460
|
// 상부 플랜지
|
|
471
461
|
const topFlange = isVertical
|
|
@@ -488,12 +478,14 @@ class OHTLineRealObject3D extends RealObjectGroup {
|
|
|
488
478
|
this._mesh = new THREE.Mesh(BufferGeometryUtils.mergeGeometries(geoms), mat);
|
|
489
479
|
this._mesh.castShadow = true;
|
|
490
480
|
this.object3d.add(this._mesh);
|
|
481
|
+
// mesh 가 super.build() 이후 생성 → 코어 faint 를 명시 적용(편집 흐림이 mesh 에 닿게).
|
|
482
|
+
this.updateAlpha();
|
|
491
483
|
}
|
|
492
484
|
update() {
|
|
493
485
|
if (this._mesh) {
|
|
494
486
|
this.object3d.remove(this._mesh);
|
|
495
487
|
this._mesh.geometry.dispose();
|
|
496
|
-
|
|
488
|
+
// material 은 MaterialBank 공유(앱 수명) — 여기서 dispose 하면 다른 인스턴스가 깨진다.
|
|
497
489
|
this._mesh = undefined;
|
|
498
490
|
}
|
|
499
491
|
this.build();
|
|
@@ -508,8 +500,8 @@ class OHTLineRealObject3D extends RealObjectGroup {
|
|
|
508
500
|
}
|
|
509
501
|
super.onchange(after, before);
|
|
510
502
|
}
|
|
503
|
+
// updateAlpha override 제거 — 코어 RealObject.updateAlpha(=applyMaterialOpacity)로 수렴(faint).
|
|
511
504
|
updateDimension() { }
|
|
512
|
-
updateAlpha() { }
|
|
513
505
|
}
|
|
514
506
|
registerRealObjectFactory('OHTLine', (component) => new OHTLineRealObject3D(component));
|
|
515
507
|
// STOCKER: 벽면에 용량 게이지 바를 포함하는 커스텀 3D 오브젝트
|
|
@@ -523,7 +515,7 @@ class StockerRealObject3D extends MCSRealObject3D {
|
|
|
523
515
|
this.buildGauge();
|
|
524
516
|
}
|
|
525
517
|
buildGauge() {
|
|
526
|
-
var _a;
|
|
518
|
+
var _a, _b;
|
|
527
519
|
const comp = this.component;
|
|
528
520
|
const data = comp.data || {};
|
|
529
521
|
const gaugeMode = ((_a = comp.root) === null || _a === void 0 ? void 0 : _a.stockerGaugeMode) || 'capacity';
|
|
@@ -559,9 +551,12 @@ class StockerRealObject3D extends MCSRealObject3D {
|
|
|
559
551
|
ctx.strokeStyle = gauge.borderColor;
|
|
560
552
|
ctx.lineWidth = 12;
|
|
561
553
|
ctx.strokeRect(6, 6, canvasW - 12, canvasH - 12);
|
|
562
|
-
// 세그먼트 채움 (왼→오)
|
|
554
|
+
// 세그먼트 채움 (왼→오) + carrier/abnormal 은 세그먼트별 숫자(%) 표기 (2D 와 동일)
|
|
563
555
|
const innerW = canvasW - pad * 2;
|
|
564
556
|
const innerH = canvasH - pad * 2;
|
|
557
|
+
// 컴포넌트 폰트 설정(fontFamily/bold/italic) 을 그대로, 크기만 텍스처 scale 배 (2D 와 상대 크기 일치).
|
|
558
|
+
const fontSize = (Number((_b = this.component.state) === null || _b === void 0 ? void 0 : _b.fontSize) || 15) * scale;
|
|
559
|
+
const font = this.gaugeFont(fontSize);
|
|
565
560
|
let x = pad;
|
|
566
561
|
for (const seg of gauge.segments) {
|
|
567
562
|
const segW = Math.max(innerW * seg.ratio, seg.ratio > 0 ? 1 : 0);
|
|
@@ -569,14 +564,19 @@ class StockerRealObject3D extends MCSRealObject3D {
|
|
|
569
564
|
continue;
|
|
570
565
|
ctx.fillStyle = seg.color;
|
|
571
566
|
ctx.fillRect(x, pad, segW, innerH);
|
|
567
|
+
if (gauge.perSegment) {
|
|
568
|
+
this.drawGaugeSegmentNumber(ctx, seg.ratio, x, pad, segW, innerH, font, fontSize);
|
|
569
|
+
}
|
|
572
570
|
x += segW;
|
|
573
571
|
}
|
|
574
|
-
//
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
572
|
+
// capacity: 전체 중앙에 usage% 한 개 (2D 와 동일). carrier/abnormal 은 위 세그먼트별 숫자 사용.
|
|
573
|
+
if (gauge.label) {
|
|
574
|
+
ctx.font = font;
|
|
575
|
+
ctx.textAlign = 'center';
|
|
576
|
+
ctx.textBaseline = 'middle';
|
|
577
|
+
ctx.fillStyle = '#333333';
|
|
578
|
+
ctx.fillText(gauge.label, canvasW / 2, canvasH / 2);
|
|
579
|
+
}
|
|
580
580
|
// 텍스처 → Plane
|
|
581
581
|
const texture = new THREE.CanvasTexture(canvas);
|
|
582
582
|
texture.colorSpace = THREE.SRGBColorSpace;
|
|
@@ -601,31 +601,39 @@ class StockerRealObject3D extends MCSRealObject3D {
|
|
|
601
601
|
var _a;
|
|
602
602
|
switch (mode) {
|
|
603
603
|
case 'carrier': {
|
|
604
|
-
// 세그먼트 비율/색상은 2D 와 공유되는 단일 소스에서 계산 (필드/색상 drift 방지)
|
|
605
|
-
|
|
604
|
+
// 세그먼트 비율/색상은 2D 와 공유되는 단일 소스에서 계산 (필드/색상 drift 방지).
|
|
605
|
+
// 색상은 carrier 컴포넌트와 동일한 legend(테마)에서 취득.
|
|
606
|
+
const colors = carrierColorsFromLegend(resolveCarrierLegend(this.component));
|
|
607
|
+
const [f, e, ee] = carrierSegmentsFromData(data, colors);
|
|
606
608
|
const total = f.count + e.count + ee.count;
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
609
|
+
// 접두사 없는 세그먼트별 숫자(%)로 표기 (capacity·2D 와 동일). label(중앙 한 개) 미사용.
|
|
610
|
+
return total > 0
|
|
611
|
+
? {
|
|
612
|
+
segments: [f, e, ee].map(s => ({ ratio: s.ratio, color: s.color })),
|
|
613
|
+
borderColor: colors.full,
|
|
614
|
+
label: null,
|
|
615
|
+
perSegment: true,
|
|
616
|
+
}
|
|
617
|
+
: { segments: [{ ratio: 1, color: '#dddddd' }], borderColor: colors.full, label: null, perSegment: false };
|
|
615
618
|
}
|
|
616
619
|
case 'abnormal': {
|
|
617
620
|
const [n, a] = abnormalSegmentsFromData(data);
|
|
618
621
|
const total = n.count + a.count;
|
|
619
|
-
|
|
620
|
-
?
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
622
|
+
return total > 0
|
|
623
|
+
? {
|
|
624
|
+
segments: [n, a].map(s => ({ ratio: s.ratio, color: s.color })),
|
|
625
|
+
borderColor: a.count > 0 ? ABNORMAL_COLORS.abnormal : ABNORMAL_COLORS.normal,
|
|
626
|
+
label: null,
|
|
627
|
+
perSegment: true,
|
|
628
|
+
}
|
|
629
|
+
: {
|
|
630
|
+
segments: [{ ratio: 1, color: '#dddddd' }],
|
|
631
|
+
borderColor: ABNORMAL_COLORS.normal,
|
|
632
|
+
label: null,
|
|
633
|
+
perSegment: false,
|
|
634
|
+
};
|
|
627
635
|
}
|
|
628
|
-
default: { // capacity
|
|
636
|
+
default: { // capacity — 전체 중앙에 usage% 한 개
|
|
629
637
|
const cur = Number(data.CURRENTCAPACITY) || 0;
|
|
630
638
|
const max = Number(data.MAXCAPACITY) || 100;
|
|
631
639
|
const fullup = data.FULLUP === 'T';
|
|
@@ -636,10 +644,39 @@ class StockerRealObject3D extends MCSRealObject3D {
|
|
|
636
644
|
segments: [{ ratio: usage / 100, color }],
|
|
637
645
|
borderColor: color,
|
|
638
646
|
label: `${usage}%`,
|
|
647
|
+
perSegment: false,
|
|
639
648
|
};
|
|
640
649
|
}
|
|
641
650
|
}
|
|
642
651
|
}
|
|
652
|
+
/** 컴포넌트 폰트 설정(fontFamily/bold/italic)으로 CSS font 문자열 조합. size 는 텍스처 스케일 반영값. */
|
|
653
|
+
gaugeFont(sizePx) {
|
|
654
|
+
const st = this.component.state || {};
|
|
655
|
+
const parts = [];
|
|
656
|
+
if (st.bold)
|
|
657
|
+
parts.push('bold');
|
|
658
|
+
if (st.italic)
|
|
659
|
+
parts.push('italic');
|
|
660
|
+
parts.push(`${sizePx}px`);
|
|
661
|
+
parts.push(st.fontFamily || 'sans-serif');
|
|
662
|
+
return parts.join(' ');
|
|
663
|
+
}
|
|
664
|
+
/** 세그먼트 영역 중심에 숫자(%)만 표기. 좁으면 값% → 값 → 생략 (2D _drawSegmentLabel 과 동일 정책). */
|
|
665
|
+
drawGaugeSegmentNumber(ctx, ratio, x, y, w, h, font, fontSize) {
|
|
666
|
+
ctx.font = font;
|
|
667
|
+
ctx.textAlign = 'center';
|
|
668
|
+
ctx.textBaseline = 'middle';
|
|
669
|
+
ctx.fillStyle = '#333333';
|
|
670
|
+
const value = `${Math.round(ratio * 100)}`;
|
|
671
|
+
const cx = x + w / 2;
|
|
672
|
+
const cy = y + h / 2;
|
|
673
|
+
if (fontSize <= h && ctx.measureText(`${value}%`).width <= w) {
|
|
674
|
+
ctx.fillText(`${value}%`, cx, cy);
|
|
675
|
+
}
|
|
676
|
+
else if (fontSize <= h && ctx.measureText(value).width <= w) {
|
|
677
|
+
ctx.fillText(value, cx, cy);
|
|
678
|
+
}
|
|
679
|
+
}
|
|
643
680
|
update() {
|
|
644
681
|
this.clearGauge();
|
|
645
682
|
super.update();
|