@fmsim/machine 2.0.0-beta.104 → 2.0.0-beta.107

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.
@@ -49,9 +49,9 @@ export function carrierSegments(full, empty, emptyEmpty, colors = CARRIER_COLORS
49
49
  const total = f + e + ee;
50
50
  const r = (c) => (total > 0 ? c / total : 0);
51
51
  return [
52
- { label: 'Full', count: f, ratio: r(f), color: colors.full },
53
- { label: 'Empty', count: e, ratio: r(e), color: colors.empty },
54
- { label: 'Empty\nEmpty', count: ee, ratio: r(ee), color: colors.emptyEmpty }
52
+ { key: 'FULL', label: 'Full', count: f, ratio: r(f), color: colors.full },
53
+ { key: 'EMPTY', label: 'Empty', count: e, ratio: r(e), color: colors.empty },
54
+ { key: 'EMPTYEMPTY', label: 'Empty\nEmpty', count: ee, ratio: r(ee), color: colors.emptyEmpty }
55
55
  ];
56
56
  }
57
57
  /** 캐리어 정상/비정상 세그먼트 (Normal / Abnormal). total 0 이면 모든 ratio 0. */
@@ -61,10 +61,32 @@ export function abnormalSegments(normal, abnormal) {
61
61
  const total = n + a;
62
62
  const r = (c) => (total > 0 ? c / total : 0);
63
63
  return [
64
- { label: 'Normal', count: n, ratio: r(n), color: ABNORMAL_COLORS.normal },
65
- { label: 'Abnormal', count: a, ratio: r(a), color: ABNORMAL_COLORS.abnormal }
64
+ { key: 'NORMAL', label: 'Normal', count: n, ratio: r(n), color: ABNORMAL_COLORS.normal },
65
+ { key: 'ABNORMAL', label: 'Abnormal', count: a, ratio: r(a), color: ABNORMAL_COLORS.abnormal }
66
66
  ];
67
67
  }
68
+ /**
69
+ * 누적 비율 위치(pos, 0..span 범위)가 속한 세그먼트를 반환한다. ratio 0 세그먼트는
70
+ * 화면에 없으므로 건너뛴다. 경계(pos == span)·초과 클릭은 마지막 비영 세그먼트로 귀속하고,
71
+ * 데이터가 전혀 없으면(모든 ratio 0) null.
72
+ *
73
+ * renderSegmentGauge 의 배치와 동일 규칙(누적 채움)이며, 2D 렌더와 클릭 판정이 이 함수를
74
+ * 공유해 drift 를 막는다. 방향(landscape 왼→오 / portrait 아래→위)은 호출부가 pos 로 흡수한다.
75
+ */
76
+ export function pickSegmentAt(segments, pos, span) {
77
+ let acc = 0;
78
+ let lastNonZero = null;
79
+ for (const seg of segments) {
80
+ const segLen = span * seg.ratio;
81
+ if (segLen <= 0)
82
+ continue;
83
+ lastNonZero = seg;
84
+ if (pos < acc + segLen)
85
+ return seg;
86
+ acc += segLen;
87
+ }
88
+ return lastNonZero;
89
+ }
68
90
  /** 캐리어 세그먼트 — canonical raw data(CARRIER_*) 에서 직접. 3D 처럼 raw data 소스용. */
69
91
  export function carrierSegmentsFromData(data, colors) {
70
92
  const d = data && typeof data === 'object' ? data : {};
@@ -1 +1 @@
1
- {"version":3,"file":"stocker-gauge.js","sourceRoot":"","sources":["../../src/utils/stocker-gauge.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * 스토커 게이지 세그먼트 계산 (2D/3D 공통 단일 소스).\n *\n * 2D(StockerCapacityBar) 와 3D(machine-3d buildGauge) 가 각자 세그먼트 비율/색상을\n * 중복 계산하면 필드/색상 drift 가 발생한다(실제로 carrier 필드명이 어긋난 전례).\n * 이 모듈이 carrier/abnormal 세그먼트 계산의 단일 소스이며 순수 함수라 단위 테스트가\n * 용이하다. 표현(라벨 포맷·watermark·테두리색)은 렌더러별로 다르므로 여기서 다루지 않는다.\n *\n * canonical 데이터 필드(2D 기준):\n * - carrier : CARRIER_FULL / CARRIER_EMPTY / CARRIER_EMPTY_EMPTY\n * - abnormal : CARRIER_NORMAL / CARRIER_ABNORMAL\n */\n\nimport { Legend } from '../features/mcs-status-default'\n\nexport type StockerGaugeMode = 'capacity' | 'carrier' | 'abnormal'\n\nexport interface GaugeSegment {\n /** 세그먼트 라벨(2D per-segment 표시용, '\\n' 은 줄바꿈). */\n label: string\n /** 카운트 원본값. */\n count: number\n /** 전체 대비 비율 (0~1). total 이 0 이면 0. */\n ratio: number\n /** 채움 색상. */\n color: string\n}\n\n/** carrier 세그먼트 색상 (2D/3D 공통). LEGEND_CARRIER 기본값과 동일 — 테마 미지정 시 폴백. */\nexport const CARRIER_COLORS = {\n full: '#4AA7FE',\n empty: '#8BDA5B',\n emptyEmpty: '#019D59'\n}\n\nexport interface CarrierColors {\n full: string\n empty: string\n emptyEmpty: string\n}\n\n/**\n * carrier legend(테마)에서 full/empty/emptyEmpty 색상 추출.\n * carrier 컴포넌트의 색 취득 로직(statusColor = legend[status] || legend.default)과 동일하게,\n * status 키(FULL/EMPTY/EMPTYEMPTY)로 직접 조회한다. 키/default 모두 없으면 CARRIER_COLORS 로 폴백.\n */\nexport function carrierColorsFromLegend(legend?: Legend): CarrierColors {\n const pick = (key: string, fallback: string) => (legend && (legend[key] || legend.default)) || fallback\n return {\n full: pick('FULL', CARRIER_COLORS.full),\n empty: pick('EMPTY', CARRIER_COLORS.empty),\n emptyEmpty: pick('EMPTYEMPTY', CARRIER_COLORS.emptyEmpty)\n }\n}\n\n/** abnormal 세그먼트 색상 (2D/3D 공통). */\nexport const ABNORMAL_COLORS = {\n normal: '#8BDA5B',\n abnormal: '#F4B4B4'\n}\n\nfunction num(v: unknown): number {\n const n = Number(v)\n return Number.isFinite(n) && n > 0 ? n : 0\n}\n\n/**\n * 캐리어 상태 세그먼트 (Full / Empty / EmptyEmpty). total 0 이면 모든 ratio 0.\n * colors 미지정 시 CARRIER_COLORS(기본). 테마 색은 carrierColorsFromLegend 로 만들어 전달.\n */\nexport function carrierSegments(\n full: unknown,\n empty: unknown,\n emptyEmpty: unknown,\n colors: CarrierColors = CARRIER_COLORS\n): GaugeSegment[] {\n const f = num(full)\n const e = num(empty)\n const ee = num(emptyEmpty)\n const total = f + e + ee\n const r = (c: number) => (total > 0 ? c / total : 0)\n return [\n { label: 'Full', count: f, ratio: r(f), color: colors.full },\n { label: 'Empty', count: e, ratio: r(e), color: colors.empty },\n { label: 'Empty\\nEmpty', count: ee, ratio: r(ee), color: colors.emptyEmpty }\n ]\n}\n\n/** 캐리어 정상/비정상 세그먼트 (Normal / Abnormal). total 0 이면 모든 ratio 0. */\nexport function abnormalSegments(normal: unknown, abnormal: unknown): GaugeSegment[] {\n const n = num(normal)\n const a = num(abnormal)\n const total = n + a\n const r = (c: number) => (total > 0 ? c / total : 0)\n return [\n { label: 'Normal', count: n, ratio: r(n), color: ABNORMAL_COLORS.normal },\n { label: 'Abnormal', count: a, ratio: r(a), color: ABNORMAL_COLORS.abnormal }\n ]\n}\n\n/** 캐리어 세그먼트 — canonical raw data(CARRIER_*) 에서 직접. 3D 처럼 raw data 소스용. */\nexport function carrierSegmentsFromData(data: any, colors?: CarrierColors): GaugeSegment[] {\n const d = data && typeof data === 'object' ? data : {}\n return carrierSegments(d.CARRIER_FULL, d.CARRIER_EMPTY, d.CARRIER_EMPTY_EMPTY, colors)\n}\n\n/** 비정상 세그먼트 — canonical raw data(CARRIER_*) 에서 직접. */\nexport function abnormalSegmentsFromData(data: any): GaugeSegment[] {\n const d = data && typeof data === 'object' ? data : {}\n return abnormalSegments(d.CARRIER_NORMAL, d.CARRIER_ABNORMAL)\n}\n"]}
1
+ {"version":3,"file":"stocker-gauge.js","sourceRoot":"","sources":["../../src/utils/stocker-gauge.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * 스토커 게이지 세그먼트 계산 (2D/3D 공통 단일 소스).\n *\n * 2D(StockerCapacityBar) 와 3D(machine-3d buildGauge) 가 각자 세그먼트 비율/색상을\n * 중복 계산하면 필드/색상 drift 가 발생한다(실제로 carrier 필드명이 어긋난 전례).\n * 이 모듈이 carrier/abnormal 세그먼트 계산의 단일 소스이며 순수 함수라 단위 테스트가\n * 용이하다. 표현(라벨 포맷·watermark·테두리색)은 렌더러별로 다르므로 여기서 다루지 않는다.\n *\n * canonical 데이터 필드(2D 기준):\n * - carrier : CARRIER_FULL / CARRIER_EMPTY / CARRIER_EMPTY_EMPTY\n * - abnormal : CARRIER_NORMAL / CARRIER_ABNORMAL\n */\n\nimport { Legend } from '../features/mcs-status-default'\n\nexport type StockerGaugeMode = 'capacity' | 'carrier' | 'abnormal'\n\nexport interface GaugeSegment {\n /** 안정적 식별 토큰 (대문자, 클릭 이벤트 payload·데이터 필드 관례와 일치). */\n key: 'FULL' | 'EMPTY' | 'EMPTYEMPTY' | 'NORMAL' | 'ABNORMAL'\n /** 세그먼트 라벨(2D per-segment 표시용, '\\n' 은 줄바꿈). */\n label: string\n /** 카운트 원본값. */\n count: number\n /** 전체 대비 비율 (0~1). total 이 0 이면 0. */\n ratio: number\n /** 채움 색상. */\n color: string\n}\n\n/** carrier 세그먼트 색상 (2D/3D 공통). LEGEND_CARRIER 기본값과 동일 — 테마 미지정 시 폴백. */\nexport const CARRIER_COLORS = {\n full: '#4AA7FE',\n empty: '#8BDA5B',\n emptyEmpty: '#019D59'\n}\n\nexport interface CarrierColors {\n full: string\n empty: string\n emptyEmpty: string\n}\n\n/**\n * carrier legend(테마)에서 full/empty/emptyEmpty 색상 추출.\n * carrier 컴포넌트의 색 취득 로직(statusColor = legend[status] || legend.default)과 동일하게,\n * status 키(FULL/EMPTY/EMPTYEMPTY)로 직접 조회한다. 키/default 모두 없으면 CARRIER_COLORS 로 폴백.\n */\nexport function carrierColorsFromLegend(legend?: Legend): CarrierColors {\n const pick = (key: string, fallback: string) => (legend && (legend[key] || legend.default)) || fallback\n return {\n full: pick('FULL', CARRIER_COLORS.full),\n empty: pick('EMPTY', CARRIER_COLORS.empty),\n emptyEmpty: pick('EMPTYEMPTY', CARRIER_COLORS.emptyEmpty)\n }\n}\n\n/** abnormal 세그먼트 색상 (2D/3D 공통). */\nexport const ABNORMAL_COLORS = {\n normal: '#8BDA5B',\n abnormal: '#F4B4B4'\n}\n\nfunction num(v: unknown): number {\n const n = Number(v)\n return Number.isFinite(n) && n > 0 ? n : 0\n}\n\n/**\n * 캐리어 상태 세그먼트 (Full / Empty / EmptyEmpty). total 0 이면 모든 ratio 0.\n * colors 미지정 시 CARRIER_COLORS(기본). 테마 색은 carrierColorsFromLegend 로 만들어 전달.\n */\nexport function carrierSegments(\n full: unknown,\n empty: unknown,\n emptyEmpty: unknown,\n colors: CarrierColors = CARRIER_COLORS\n): GaugeSegment[] {\n const f = num(full)\n const e = num(empty)\n const ee = num(emptyEmpty)\n const total = f + e + ee\n const r = (c: number) => (total > 0 ? c / total : 0)\n return [\n { key: 'FULL', label: 'Full', count: f, ratio: r(f), color: colors.full },\n { key: 'EMPTY', label: 'Empty', count: e, ratio: r(e), color: colors.empty },\n { key: 'EMPTYEMPTY', label: 'Empty\\nEmpty', count: ee, ratio: r(ee), color: colors.emptyEmpty }\n ]\n}\n\n/** 캐리어 정상/비정상 세그먼트 (Normal / Abnormal). total 0 이면 모든 ratio 0. */\nexport function abnormalSegments(normal: unknown, abnormal: unknown): GaugeSegment[] {\n const n = num(normal)\n const a = num(abnormal)\n const total = n + a\n const r = (c: number) => (total > 0 ? c / total : 0)\n return [\n { key: 'NORMAL', label: 'Normal', count: n, ratio: r(n), color: ABNORMAL_COLORS.normal },\n { key: 'ABNORMAL', label: 'Abnormal', count: a, ratio: r(a), color: ABNORMAL_COLORS.abnormal }\n ]\n}\n\n/**\n * 누적 비율 위치(pos, 0..span 범위)가 속한 세그먼트를 반환한다. ratio 0 세그먼트는\n * 화면에 없으므로 건너뛴다. 경계(pos == span)·초과 클릭은 마지막 비영 세그먼트로 귀속하고,\n * 데이터가 전혀 없으면(모든 ratio 0) null.\n *\n * renderSegmentGauge 의 배치와 동일 규칙(누적 채움)이며, 2D 렌더와 클릭 판정이 이 함수를\n * 공유해 drift 를 막는다. 방향(landscape 왼→오 / portrait 아래→위)은 호출부가 pos 로 흡수한다.\n */\nexport function pickSegmentAt(segments: GaugeSegment[], pos: number, span: number): GaugeSegment | null {\n let acc = 0\n let lastNonZero: GaugeSegment | null = null\n for (const seg of segments) {\n const segLen = span * seg.ratio\n if (segLen <= 0) continue\n lastNonZero = seg\n if (pos < acc + segLen) return seg\n acc += segLen\n }\n return lastNonZero\n}\n\n/** 캐리어 세그먼트 — canonical raw data(CARRIER_*) 에서 직접. 3D 처럼 raw data 소스용. */\nexport function carrierSegmentsFromData(data: any, colors?: CarrierColors): GaugeSegment[] {\n const d = data && typeof data === 'object' ? data : {}\n return carrierSegments(d.CARRIER_FULL, d.CARRIER_EMPTY, d.CARRIER_EMPTY_EMPTY, colors)\n}\n\n/** 비정상 세그먼트 — canonical raw data(CARRIER_*) 에서 직접. */\nexport function abnormalSegmentsFromData(data: any): GaugeSegment[] {\n const d = data && typeof data === 'object' ? data : {}\n return abnormalSegments(d.CARRIER_NORMAL, d.CARRIER_ABNORMAL)\n}\n"]}
@@ -1,234 +1,34 @@
1
1
  import { __decorate } from "tslib";
2
2
  import { sceneComponent } from '@hatiolab/things-scene';
3
- import { themesColorRange } from '@fmsim/api';
4
3
  import { LEGEND_CAPACITY } from './features/mcs-status-default';
5
- import { safeRound } from './utils/safe-round';
6
- import { getValueOnRanges } from './utils/get-value-on-ranges';
7
4
  import { MCSZoneMixin, MCSZoneMixinProperties } from './features/mcs-zone-mixin';
8
5
  import { ParentObjectMixinProperties } from './features/parent-object-mixin';
6
+ import { CapacityGaugeMixin, CapacityGaugeMixinProperties } from './features/capacity-gauge-mixin';
9
7
  import MCSUnit from './mcs-unit';
8
+ /**
9
+ * 존(zone) 게이지.
10
+ *
11
+ * mode 에 따라 capacity / carrier / abnormal 3가지 게이지를 표현한다 — 그리는 규칙은
12
+ * StockerCapacityBar 와 동일하며 CapacityGaugeMixin 에서 공유한다(단일 소스).
13
+ * 스토커 게이지와 달리 존 게이지는 자체 id 로 데이터를 받는 Unit(identifiable) 이며,
14
+ * legend 폴백만 zone 전역 테마(zoneCapacityLegendTheme)로 다르다.
15
+ *
16
+ * mode 소스: root.stockerGaugeMode — 스토커 게이지와 같은 런타임 스위치로 함께 전환된다.
17
+ */
10
18
  const NATURE = {
11
19
  mutable: false,
12
20
  resizable: true,
13
21
  rotatable: true,
14
- properties: [
15
- ...MCSZoneMixinProperties,
16
- ...ParentObjectMixinProperties,
17
- {
18
- type: 'select',
19
- label: 'legend-name',
20
- name: 'legendName',
21
- property: {
22
- options: themesColorRange
23
- }
24
- },
25
- {
26
- type: 'number',
27
- name: 'currentUsage',
28
- label: 'current-usage'
29
- },
30
- {
31
- type: 'number',
32
- name: 'highWatermark',
33
- label: 'high-watermark'
34
- },
35
- {
36
- type: 'number',
37
- name: 'lowWatermark',
38
- label: 'low-watermark'
39
- },
40
- {
41
- type: 'number',
42
- name: 'maxCapacity',
43
- label: 'max-capacity'
44
- },
45
- {
46
- type: 'boolean',
47
- name: 'fullup',
48
- label: 'fullup'
49
- },
50
- {
51
- type: 'number',
52
- label: 'round',
53
- name: 'round',
54
- property: {
55
- min: 0
56
- }
57
- },
58
- {
59
- type: 'hidden',
60
- name: 'usage',
61
- label: 'usage'
62
- }
63
- ],
22
+ properties: [...MCSZoneMixinProperties, ...ParentObjectMixinProperties, ...CapacityGaugeMixinProperties],
64
23
  'value-property': 'usage'
65
24
  };
66
- var controlHandler = {
67
- ondragmove: function (point, index, component) {
68
- var { left, top, width, height } = component.model;
69
- var transcoorded = component.transcoordP2S(point.x, point.y);
70
- var round = ((transcoorded.x - left) / (width / 2)) * 100;
71
- round = safeRound(round, width, height);
72
- component.set({
73
- round
74
- });
75
- }
76
- };
77
- let ZoneCapacityBar = class ZoneCapacityBar extends MCSZoneMixin(MCSUnit) {
25
+ let ZoneCapacityBar = class ZoneCapacityBar extends CapacityGaugeMixin(MCSZoneMixin(MCSUnit)) {
78
26
  static get nature() {
79
27
  return NATURE;
80
28
  }
81
- get controls() {
82
- var { left, top, width, round, height } = this.state;
83
- round = round == undefined ? 0 : safeRound(round, width, height);
84
- return [
85
- {
86
- x: left + (width / 2) * (round / 100),
87
- y: top,
88
- handler: controlHandler
89
- }
90
- ];
91
- }
92
29
  getLegendFallback() {
93
30
  return this.root.zoneCapacityLegendTheme || LEGEND_CAPACITY;
94
31
  }
95
- get statusColor() {
96
- const { fullup } = this.state;
97
- return fullup ? 'red' : getValueOnRanges(this.usage, this.legend);
98
- }
99
- get text() {
100
- return this.usage + '%';
101
- }
102
- render(context) {
103
- const { left, top, width, height } = this.bounds;
104
- if (width >= height) {
105
- this.renderLandscape(context);
106
- }
107
- else {
108
- this.renderPortrait(context);
109
- }
110
- }
111
- renderLandscape(context) {
112
- const { left, top, width, height } = this.bounds;
113
- const { highWatermark, lowWatermark, lineWidth, strokeStyle, round, fillStyle = 'white' } = this.state;
114
- context.save();
115
- context.translate(left, top);
116
- // gauge background
117
- context.beginPath();
118
- context.fillStyle = fillStyle;
119
- context.roundRect(0, 0, width, height, round);
120
- context.fill();
121
- context.stroke();
122
- context.clip();
123
- // gauge foreground
124
- context.beginPath();
125
- context.fillStyle = this.statusColor;
126
- context.rect(0, 0, (width / 100) * this.usage, height);
127
- context.fill();
128
- // high-watermark level
129
- if (highWatermark && highWatermark > 0 && highWatermark < 100) {
130
- context.beginPath();
131
- context.strokeStyle = 'black';
132
- context.lineWidth = 1;
133
- context.setLineDash([3]);
134
- const x = (width / 100) * highWatermark;
135
- context.moveTo(x, 0);
136
- context.lineTo(x, height);
137
- context.stroke();
138
- }
139
- // low-watermark level
140
- if (lowWatermark && lowWatermark > 0 && lowWatermark < 100) {
141
- context.beginPath();
142
- context.strokeStyle = 'black';
143
- context.lineWidth = 1;
144
- context.setLineDash([3]);
145
- const x = (width / 100) * lowWatermark;
146
- context.moveTo(x, 0);
147
- context.lineTo(x, height);
148
- context.stroke();
149
- }
150
- // gauge outline
151
- context.beginPath();
152
- context.setLineDash([]);
153
- context.strokeStyle = strokeStyle;
154
- context.lineWidth = lineWidth;
155
- context.roundRect(0, 0, width, height, round);
156
- context.stroke();
157
- context.beginPath();
158
- context.translate(-left, -top);
159
- context.restore();
160
- }
161
- renderPortrait(context) {
162
- const { left, top, width, height } = this.bounds;
163
- const { highWatermark, lowWatermark, lineWidth, strokeStyle, round, fillStyle = 'white' } = this.state;
164
- context.save();
165
- context.translate(left, top);
166
- // gauge background
167
- context.beginPath();
168
- context.fillStyle = fillStyle;
169
- context.roundRect(0, 0, width, height, round);
170
- context.fill();
171
- context.clip();
172
- // gauge foreground
173
- context.beginPath();
174
- context.fillStyle = this.statusColor;
175
- context.rect(0, height - (height / 100) * this.usage, width, (height / 100) * this.usage);
176
- context.fill();
177
- // high-watermark level
178
- if (highWatermark && highWatermark > 0 && highWatermark < 100) {
179
- context.beginPath();
180
- context.strokeStyle = 'black';
181
- context.lineWidth = 1;
182
- context.setLineDash([3]);
183
- const y = height - (height / 100) * highWatermark;
184
- context.moveTo(0, y);
185
- context.lineTo(width, y);
186
- context.stroke();
187
- }
188
- // low-watermark level
189
- if (lowWatermark && lowWatermark > 0 && lowWatermark < 100) {
190
- context.beginPath();
191
- context.strokeStyle = 'black';
192
- context.lineWidth = 1;
193
- context.setLineDash([3]);
194
- const y = height - (height / 100) * lowWatermark;
195
- context.moveTo(0, y);
196
- context.lineTo(width, y);
197
- context.stroke();
198
- }
199
- // gauge outline
200
- context.beginPath();
201
- context.setLineDash([]);
202
- context.strokeStyle = strokeStyle;
203
- context.lineWidth = lineWidth;
204
- context.roundRect(0, 0, width, height, round);
205
- context.stroke();
206
- context.beginPath();
207
- context.translate(-left, -top);
208
- context.restore();
209
- }
210
- get usage() {
211
- const { currentUsage = 0, maxCapacity = 100 } = this.state;
212
- return Math.round((currentUsage / maxCapacity) * 100);
213
- }
214
- set usage(usage) {
215
- // intentionally have done nothing
216
- }
217
- onchangeData(after, before) {
218
- const { CURRENTCAPACITY = 0, MAXCAPACITY = 100, HIGHWATERMARK = 100, LOWWATERMARK = 0, FULLUP = 'F' } = this.data && typeof this.data == 'object' ? this.data : {};
219
- const currentUsage = Number(CURRENTCAPACITY) || 0;
220
- const maxCapacity = Number(MAXCAPACITY) || 100;
221
- const highWatermark = Number(HIGHWATERMARK) || 100;
222
- const lowWatermark = Number(LOWWATERMARK) || 0;
223
- this.setState({
224
- currentUsage,
225
- maxCapacity,
226
- highWatermark,
227
- lowWatermark,
228
- fullup: FULLUP == 'T',
229
- usage: Math.round((currentUsage / maxCapacity) * 100)
230
- });
231
- }
232
32
  };
233
33
  ZoneCapacityBar = __decorate([
234
34
  sceneComponent('ZoneCapacityBar')
@@ -1 +1 @@
1
- {"version":3,"file":"zone-capacity-bar.js","sourceRoot":"","sources":["../src/zone-capacity-bar.ts"],"names":[],"mappings":"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","sourcesContent":["import { Component, ComponentNature, POSITION, Properties, sceneComponent } from '@hatiolab/things-scene'\nimport { themesColorRange } from '@fmsim/api'\nimport { LEGEND_CAPACITY } from './features/mcs-status-default'\nimport { safeRound } from './utils/safe-round'\nimport { getValueOnRanges } from './utils/get-value-on-ranges'\nimport { MCSZoneMixin, MCSZoneMixinProperties } from './features/mcs-zone-mixin'\nimport { ParentObjectMixinProperties } from './features/parent-object-mixin'\nimport MCSUnit from './mcs-unit'\n\nconst NATURE: ComponentNature = {\n mutable: false,\n resizable: true,\n rotatable: true,\n properties: [\n ...MCSZoneMixinProperties,\n ...ParentObjectMixinProperties,\n {\n type: 'select',\n label: 'legend-name',\n name: 'legendName',\n property: {\n options: themesColorRange\n }\n },\n {\n type: 'number',\n name: 'currentUsage',\n label: 'current-usage'\n },\n {\n type: 'number',\n name: 'highWatermark',\n label: 'high-watermark'\n },\n {\n type: 'number',\n name: 'lowWatermark',\n label: 'low-watermark'\n },\n {\n type: 'number',\n name: 'maxCapacity',\n label: 'max-capacity'\n },\n {\n type: 'boolean',\n name: 'fullup',\n label: 'fullup'\n },\n {\n type: 'number',\n label: 'round',\n name: 'round',\n property: {\n min: 0\n }\n },\n {\n type: 'hidden',\n name: 'usage',\n label: 'usage'\n }\n ],\n 'value-property': 'usage'\n}\n\nvar controlHandler = {\n ondragmove: function (point: POSITION, index: number, component: Component) {\n var { left, top, width, height } = component.model\n\n var transcoorded = component.transcoordP2S(point.x, point.y)\n var round = ((transcoorded.x - left) / (width / 2)) * 100\n\n round = safeRound(round, width, height)\n\n component.set({\n round\n })\n }\n}\n\n@sceneComponent('ZoneCapacityBar')\nexport default class ZoneCapacityBar extends MCSZoneMixin(MCSUnit) {\n static get nature() {\n return NATURE\n }\n\n get controls() {\n var { left, top, width, round, height } = this.state\n round = round == undefined ? 0 : safeRound(round, width, height)\n\n return [\n {\n x: left + (width / 2) * (round / 100),\n y: top,\n handler: controlHandler\n }\n ]\n }\n\n getLegendFallback() {\n return (this.root as any).zoneCapacityLegendTheme || LEGEND_CAPACITY\n }\n\n get statusColor() {\n const { fullup } = this.state\n return fullup ? 'red' : getValueOnRanges(this.usage, this.legend)\n }\n\n get text() {\n return this.usage + '%'\n }\n\n render(context: CanvasRenderingContext2D) {\n const { left, top, width, height } = this.bounds\n\n if (width >= height) {\n this.renderLandscape(context)\n } else {\n this.renderPortrait(context)\n }\n }\n\n renderLandscape(context: CanvasRenderingContext2D) {\n const { left, top, width, height } = this.bounds\n const { highWatermark, lowWatermark, lineWidth, strokeStyle, round, fillStyle = 'white' } = this.state\n\n context.save()\n context.translate(left, top)\n\n // gauge background\n context.beginPath()\n\n context.fillStyle = fillStyle\n context.roundRect(0, 0, width, height, round)\n\n context.fill()\n context.stroke()\n\n context.clip()\n\n // gauge foreground\n context.beginPath()\n\n context.fillStyle = this.statusColor!\n\n context.rect(0, 0, (width / 100) * this.usage, height)\n context.fill()\n\n // high-watermark level\n\n if (highWatermark && highWatermark > 0 && highWatermark < 100) {\n context.beginPath()\n\n context.strokeStyle = 'black'\n context.lineWidth = 1\n context.setLineDash([3])\n\n const x = (width / 100) * highWatermark\n context.moveTo(x, 0)\n context.lineTo(x, height)\n\n context.stroke()\n }\n\n // low-watermark level\n\n if (lowWatermark && lowWatermark > 0 && lowWatermark < 100) {\n context.beginPath()\n\n context.strokeStyle = 'black'\n context.lineWidth = 1\n context.setLineDash([3])\n\n const x = (width / 100) * lowWatermark\n context.moveTo(x, 0)\n context.lineTo(x, height)\n\n context.stroke()\n }\n\n // gauge outline\n context.beginPath()\n\n context.setLineDash([])\n context.strokeStyle = strokeStyle\n context.lineWidth = lineWidth\n context.roundRect(0, 0, width, height, round)\n context.stroke()\n\n context.beginPath()\n\n context.translate(-left, -top)\n context.restore()\n }\n\n renderPortrait(context: CanvasRenderingContext2D) {\n const { left, top, width, height } = this.bounds\n const { highWatermark, lowWatermark, lineWidth, strokeStyle, round, fillStyle = 'white' } = this.state\n\n context.save()\n context.translate(left, top)\n\n // gauge background\n context.beginPath()\n\n context.fillStyle = fillStyle\n context.roundRect(0, 0, width, height, round)\n\n context.fill()\n\n context.clip()\n\n // gauge foreground\n context.beginPath()\n\n context.fillStyle = this.statusColor!\n\n context.rect(0, height - (height / 100) * this.usage, width, (height / 100) * this.usage)\n context.fill()\n\n // high-watermark level\n\n if (highWatermark && highWatermark > 0 && highWatermark < 100) {\n context.beginPath()\n\n context.strokeStyle = 'black'\n context.lineWidth = 1\n context.setLineDash([3])\n\n const y = height - (height / 100) * highWatermark\n context.moveTo(0, y)\n context.lineTo(width, y)\n\n context.stroke()\n }\n\n // low-watermark level\n\n if (lowWatermark && lowWatermark > 0 && lowWatermark < 100) {\n context.beginPath()\n\n context.strokeStyle = 'black'\n context.lineWidth = 1\n context.setLineDash([3])\n\n const y = height - (height / 100) * lowWatermark\n context.moveTo(0, y)\n context.lineTo(width, y)\n\n context.stroke()\n }\n\n // gauge outline\n context.beginPath()\n\n context.setLineDash([])\n context.strokeStyle = strokeStyle\n context.lineWidth = lineWidth\n context.roundRect(0, 0, width, height, round)\n context.stroke()\n\n context.beginPath()\n\n context.translate(-left, -top)\n context.restore()\n }\n\n get usage() {\n const { currentUsage = 0, maxCapacity = 100 } = this.state\n return Math.round((currentUsage / maxCapacity) * 100)\n }\n\n set usage(usage: number) {\n // intentionally have done nothing\n }\n\n onchangeData(after: Record<string, any>, before: Record<string, any>): void {\n const {\n CURRENTCAPACITY = 0,\n MAXCAPACITY = 100,\n HIGHWATERMARK = 100,\n LOWWATERMARK = 0,\n FULLUP = 'F'\n } = this.data && typeof this.data == 'object' ? this.data : {}\n\n const currentUsage = Number(CURRENTCAPACITY) || 0\n const maxCapacity = Number(MAXCAPACITY) || 100\n const highWatermark = Number(HIGHWATERMARK) || 100\n const lowWatermark = Number(LOWWATERMARK) || 0\n\n this.setState({\n currentUsage,\n maxCapacity,\n highWatermark,\n lowWatermark,\n fullup: FULLUP == 'T',\n usage: Math.round((currentUsage / maxCapacity) * 100)\n })\n }\n}\n"]}
1
+ {"version":3,"file":"zone-capacity-bar.js","sourceRoot":"","sources":["../src/zone-capacity-bar.ts"],"names":[],"mappings":";AAAA,OAAO,EAAmB,cAAc,EAAE,MAAM,wBAAwB,CAAA;AACxE,OAAO,EAAE,eAAe,EAAU,MAAM,+BAA+B,CAAA;AACvE,OAAO,EAAE,YAAY,EAAE,sBAAsB,EAAE,MAAM,2BAA2B,CAAA;AAChF,OAAO,EAAE,2BAA2B,EAAE,MAAM,gCAAgC,CAAA;AAC5E,OAAO,EAAE,kBAAkB,EAAE,4BAA4B,EAAE,MAAM,iCAAiC,CAAA;AAClG,OAAO,OAAO,MAAM,YAAY,CAAA;AAEhC;;;;;;;;;GASG;AAEH,MAAM,MAAM,GAAoB;IAC9B,OAAO,EAAE,KAAK;IACd,SAAS,EAAE,IAAI;IACf,SAAS,EAAE,IAAI;IACf,UAAU,EAAE,CAAC,GAAG,sBAAsB,EAAE,GAAG,2BAA2B,EAAE,GAAG,4BAA4B,CAAC;IACxG,gBAAgB,EAAE,OAAO;CAC1B,CAAA;AAGc,IAAM,eAAe,GAArB,MAAM,eAAgB,SAAQ,kBAAkB,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC;IACpF,MAAM,KAAK,MAAM;QACf,OAAO,MAAM,CAAA;IACf,CAAC;IAED,iBAAiB;QACf,OAAQ,IAAI,CAAC,IAAY,CAAC,uBAAuB,IAAI,eAAe,CAAA;IACtE,CAAC;CACF,CAAA;AARoB,eAAe;IADnC,cAAc,CAAC,iBAAiB,CAAC;GACb,eAAe,CAQnC;eARoB,eAAe","sourcesContent":["import { ComponentNature, sceneComponent } from '@hatiolab/things-scene'\nimport { LEGEND_CAPACITY, Legend } from './features/mcs-status-default'\nimport { MCSZoneMixin, MCSZoneMixinProperties } from './features/mcs-zone-mixin'\nimport { ParentObjectMixinProperties } from './features/parent-object-mixin'\nimport { CapacityGaugeMixin, CapacityGaugeMixinProperties } from './features/capacity-gauge-mixin'\nimport MCSUnit from './mcs-unit'\n\n/**\n * 존(zone) 게이지.\n *\n * mode 에 따라 capacity / carrier / abnormal 3가지 게이지를 표현한다 — 그리는 규칙은\n * StockerCapacityBar 와 동일하며 CapacityGaugeMixin 에서 공유한다(단일 소스).\n * 스토커 게이지와 달리 존 게이지는 자체 id 로 데이터를 받는 Unit(identifiable) 이며,\n * legend 폴백만 zone 전역 테마(zoneCapacityLegendTheme)로 다르다.\n *\n * mode 소스: root.stockerGaugeMode — 스토커 게이지와 같은 런타임 스위치로 함께 전환된다.\n */\n\nconst NATURE: ComponentNature = {\n mutable: false,\n resizable: true,\n rotatable: true,\n properties: [...MCSZoneMixinProperties, ...ParentObjectMixinProperties, ...CapacityGaugeMixinProperties],\n 'value-property': 'usage'\n}\n\n@sceneComponent('ZoneCapacityBar')\nexport default class ZoneCapacityBar extends CapacityGaugeMixin(MCSZoneMixin(MCSUnit)) {\n static get nature() {\n return NATURE\n }\n\n getLegendFallback(): Legend {\n return (this.root as any).zoneCapacityLegendTheme || LEGEND_CAPACITY\n }\n}\n"]}
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@fmsim/machine",
3
3
  "description": "Layout View를 구성하는 설비(machine)들과 유닛을 구현한 모듈입니다.",
4
4
  "author": "heartyoh",
5
- "version": "2.0.0-beta.104",
5
+ "version": "2.0.0-beta.107",
6
6
  "main": "dist/index.js",
7
7
  "module": "dist/index.js",
8
8
  "things-scene": true,
@@ -55,5 +55,5 @@
55
55
  "prettier --write"
56
56
  ]
57
57
  },
58
- "gitHead": "9fd007d34fca773bdf6aa56f06f2ff9b189133d8"
58
+ "gitHead": "ca008c3f449ffaef0ab3e1866ee0ac66f3bd89e9"
59
59
  }