@fmsim/machine 1.0.57 → 1.0.59

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/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@fmsim/machine",
3
3
  "description": "Layout View를 구성하는 설비(machine)들과 유닛을 구현한 모듈입니다.",
4
4
  "author": "heartyoh",
5
- "version": "1.0.57",
5
+ "version": "1.0.59",
6
6
  "main": "dist/index.js",
7
7
  "module": "dist/index.js",
8
8
  "things-scene": true,
@@ -54,5 +54,5 @@
54
54
  "prettier --write"
55
55
  ]
56
56
  },
57
- "gitHead": "3a0d8544748a35b293cdfe299d1d790884d85f5b"
57
+ "gitHead": "10f16c81ddcca25469e0f87b6edc8b45f07f1f46"
58
58
  }
package/src/agv.ts CHANGED
@@ -1,11 +1,24 @@
1
1
  import { Component, ComponentNature, Properties } from '@hatiolab/things-scene'
2
+ import { themesColorRange } from '@fmsim/api'
2
3
  import MCSVehicle from './mcs-vehicle'
4
+ import { getVaueOnRanges } from './utils/get-value-on-ranges'
5
+ import { BATTERY_RATE_LEGEND } from './features/mcs-status-default'
3
6
 
4
7
  const NATURE: ComponentNature = {
5
8
  mutable: false,
6
9
  resizable: true,
7
10
  rotatable: true,
8
- properties: [...MCSVehicle.properties]
11
+ properties: [
12
+ ...MCSVehicle.properties,
13
+ {
14
+ type: 'select',
15
+ label: 'battery-rate-legend-name',
16
+ name: 'batteryRateLegendName',
17
+ property: {
18
+ options: themesColorRange
19
+ }
20
+ }
21
+ ]
9
22
  }
10
23
 
11
24
  const GAP = 2
@@ -22,22 +35,40 @@ export default class AGV extends MCSVehicle {
22
35
  return this.root.agvLegendTheme || super.getLegendFallback()
23
36
  }
24
37
 
38
+ getBatteryLegendFallback() {
39
+ return this.root.batteryRateLegendTheme || BATTERY_RATE_LEGEND
40
+ }
41
+
42
+ getBatteryLegendTheme() {
43
+ const { batteryRateLegendName } = this.state
44
+
45
+ if (batteryRateLegendName) {
46
+ return this.root?.style?.[batteryRateLegendName]
47
+ }
48
+ }
49
+
50
+ getBatteryColor(rate: number) {
51
+ return getVaueOnRanges(rate, this.getBatteryLegendTheme() || this.getBatteryLegendFallback())
52
+ }
53
+
25
54
  render(ctx: CanvasRenderingContext2D) {
26
55
  var { left, top, width, height } = this.bounds
27
56
 
28
57
  ctx.translate(left, top)
29
58
  ctx.beginPath()
30
59
 
60
+ // 작은 크기에서도 보이도록 최소 선 두께 설정
61
+ const minLineWidth = 0.5
62
+ const scaleFactor = Math.min(width, height) / 100
63
+ const lineWidth = Math.max(minLineWidth, scaleFactor)
64
+
31
65
  ctx.strokeStyle = this.auxColor
32
66
  ctx.fillStyle = this.statusColor!
33
- ctx.lineWidth = 1
67
+ ctx.lineWidth = lineWidth
34
68
  ctx.lineCap = 'round'
35
69
  ctx.setLineDash([])
36
70
 
37
- // Performance aware
38
- // const radius = Math.round(Math.min(width, height) * 0.1)
39
- // ctx.roundRect(1, 0, width - 2, height, radius)
40
- // ctx.roundRect(GAP + 1, GAP, width - (GAP + 1) * 2, height - GAP * 2, radius - 1)
71
+ // 기본 사각형 그리기
41
72
  ctx.rect(1, 0, width - 2, height)
42
73
  ctx.rect(GAP + 1, GAP, width - (GAP + 1) * 2, height - GAP * 2)
43
74
 
@@ -46,7 +77,7 @@ export default class AGV extends MCSVehicle {
46
77
 
47
78
  ctx.beginPath()
48
79
 
49
- // draw wheel metapore
80
+ // 바퀴 그리기
50
81
  ctx.strokeStyle = 'black'
51
82
 
52
83
  ctx.moveTo(0, (height * 1) / 3)
@@ -59,23 +90,109 @@ export default class AGV extends MCSVehicle {
59
90
  ctx.translate(-left, -top)
60
91
  }
61
92
 
93
+ // 사선 패턴을 그리는 함수
94
+ drawHatchPattern(ctx: CanvasRenderingContext2D, x: number, y: number, width: number, height: number) {
95
+ // 크기에 따라 패턴 간격과 선 두께 조정 (2배 덜 조밀하게)
96
+ const minSpacing = 4 // 최소 간격 2배 증가 (원래 2의 2배)
97
+ const maxSpacing = 16 // 최대 간격 2배 증가 (원래 8의 2배)
98
+ const preferredSpacing = Math.max(minSpacing, Math.min(width, height) / 5) // 간격 계산 시 나누는 값 1/2로 감소
99
+ const spacing = Math.min(preferredSpacing, maxSpacing)
100
+
101
+ // 선 두께 계산 (작은 크기에서도 보이도록)
102
+ const minLineWidth = 0.35
103
+ const scaleFactor = Math.min(width, height) / 130
104
+ const lineWidth = Math.max(minLineWidth, scaleFactor)
105
+
106
+ ctx.beginPath()
107
+ ctx.strokeStyle = 'black'
108
+ ctx.lineWidth = lineWidth
109
+
110
+ // 배터리 박스 내부로 클리핑 설정
111
+ ctx.save()
112
+ ctx.rect(x, y, width, height)
113
+ ctx.clip()
114
+
115
+ // 사선 패턴 그리기
116
+ for (let i = -height; i < width + height; i += spacing) {
117
+ ctx.moveTo(x + i, y)
118
+ ctx.lineTo(x + i - height, y + height)
119
+ }
120
+
121
+ ctx.stroke()
122
+ // 클리핑 해제
123
+ ctx.restore()
124
+ }
125
+
62
126
  postrender(ctx: CanvasRenderingContext2D): void {
63
127
  super.postrender(ctx)
64
128
 
65
129
  const BATTERYRATE = this.data?.BATTERYRATE
66
130
 
67
- if (typeof BATTERYRATE == 'number') {
131
+ if (typeof BATTERYRATE === 'number') {
68
132
  const { left, top, width, height } = this.bounds
69
133
  ctx.translate(left, top)
70
134
 
71
- const batteryHeight = height / 5
72
- const batteryMargin = 1
73
- const batteryWidth = width - 2 * batteryMargin
135
+ // 배터리 크기 위치 설정 (최소 크기 보장)
136
+ const minBatteryHeight = 2
137
+ const minBatteryWidth = 4
138
+ const preferredHeight = height / 5
139
+ const preferredWidth = (width * 3) / 4
140
+
141
+ const batteryHeight = Math.max(minBatteryHeight, preferredHeight)
142
+ const batteryWidth = Math.max(minBatteryWidth, preferredWidth)
143
+ const batteryX = (width - batteryWidth) / 2
144
+ const batteryY = (height - batteryHeight) * 1.1
145
+
146
+ // 배터리 테두리 그리기 (크기에 비례한 선 두께)
147
+ const minLineWidth = 0.5
148
+ const scaleFactor = Math.min(width, height) / 100
149
+ const lineWidth = Math.max(minLineWidth, scaleFactor)
150
+
151
+ // 배터리 배경을 흰색으로 채우기
152
+ ctx.fillStyle = 'white'
153
+ ctx.fillRect(batteryX, batteryY, batteryWidth, batteryHeight)
154
+
155
+ // 배터리 전체 영역에 사선 패턴 그리기
156
+ this.drawHatchPattern(ctx, batteryX, batteryY, batteryWidth, batteryHeight)
157
+
158
+ // 배터리 잔량 표시
159
+ const fillWidth = (batteryWidth * BATTERYRATE) / 100
74
160
 
161
+ // 배터리 잔량에 따른 색상 설정 - 테마 적용
162
+ ctx.fillStyle = this.getBatteryColor(BATTERYRATE)
163
+
164
+ // 배터리 잔량 영역 채우기
165
+ ctx.fillRect(batteryX, batteryY, fillWidth, batteryHeight)
166
+
167
+ // 배터리 테두리 다시 그리기
168
+ ctx.beginPath()
169
+ ctx.strokeStyle = 'black'
170
+ ctx.lineWidth = lineWidth
171
+ ctx.strokeRect(batteryX, batteryY, batteryWidth, batteryHeight)
172
+
173
+ // 배터리 잔량 텍스트 표시
174
+ // const textWidth = batteryWidth * 0.7
175
+ // const textHeight = batteryHeight * 0.7
176
+ // const textBgX = batteryX + (batteryWidth - textWidth) / 2
177
+ // const textBgY = batteryY + (batteryHeight - textHeight) / 2
178
+
179
+ // ctx.fillStyle = 'white'
180
+ // ctx.fillRect(textBgX, textBgY, textWidth, textHeight)
181
+
182
+ // 텍스트 그리기
183
+ ctx.font = `4px Roboto, Arial, sans-serif`
75
184
  ctx.fillStyle = 'black'
76
- ctx.fillRect(batteryMargin, height - batteryHeight, batteryWidth, batteryHeight)
77
- ctx.fillStyle = BATTERYRATE < 20 ? 'red' : BATTERYRATE < 40 ? 'yellow' : 'green'
78
- ctx.fillRect(batteryMargin, height - batteryHeight, (batteryWidth * BATTERYRATE) / 100, batteryHeight)
185
+ ctx.textAlign = 'center'
186
+ ctx.textBaseline = 'middle'
187
+
188
+ // 배터리 잔량 텍스트 (정수 + % 포함)
189
+ const text = `${Math.round(BATTERYRATE)}%`
190
+
191
+ // 텍스트 위치 (배터리 중앙)
192
+ const textX = batteryX + batteryWidth / 2
193
+ const textY = height // batteryY + batteryHeight // / 2
194
+
195
+ ctx.fillText(text, textX, textY)
79
196
 
80
197
  ctx.translate(-left, -top)
81
198
  }
package/src/carrier.ts CHANGED
@@ -150,9 +150,9 @@ export default class Carrier extends MCSStatusMixin(ParentObjectMixin(Shape)) {
150
150
  }
151
151
 
152
152
  contains(x, y) {
153
- const EMPTYTYPE = this.getState('EMPTYTYPE')
153
+ const { id: CARRIERNAME } = this.state || {}
154
154
 
155
- if (!EMPTYTYPE) {
155
+ if (!CARRIERNAME) {
156
156
  return false
157
157
  }
158
158
 
@@ -183,9 +183,9 @@ export default class Carrier extends MCSStatusMixin(ParentObjectMixin(Shape)) {
183
183
  }
184
184
 
185
185
  render(ctx: CanvasRenderingContext2D) {
186
- const { EMPTYTYPE, showText } = this.state
186
+ const { EMPTYTYPE, id: CARRIERNAME, showText } = this.state
187
187
 
188
- if (!EMPTYTYPE) {
188
+ if (!CARRIERNAME) {
189
189
  return
190
190
  }
191
191
 
@@ -67,3 +67,11 @@ export const LEGEND_CAPACITY: Legend = {
67
67
  '80~100': '#FD9B00',
68
68
  default: '#F0F0F0'
69
69
  }
70
+
71
+ export const BATTERY_RATE_LEGEND: Legend = {
72
+ '0~20': '#EB3245', // 0-20%: 빨간색 (위험)
73
+ '20~40': '#FD9B00', // 20-40%: 주황색 (경고)
74
+ '40~60': '#F4F226', // 40-60%: 노란색 (주의)
75
+ '60~100': '#8BDA5B', // 60-100%: 녹색 (정상)
76
+ default: '#F0F0F0'
77
+ }
@@ -10,6 +10,7 @@ declare module '@hatiolab/things-scene' {
10
10
  carrierAnimationTheme?: any
11
11
  stockerCapacityLegendTheme?: any
12
12
  zoneCapacityLegendTheme?: any
13
+ batteryRateLegendTheme?: any
13
14
  bufferLegendTheme?: any
14
15
  agvlineLegendTheme?: any
15
16
  ohtlineLegendTheme?: any
@@ -183,6 +184,14 @@ Object.defineProperty(ModelLayer.prototype, 'nature', {
183
184
  property: {
184
185
  options: themesColorRange
185
186
  }
187
+ },
188
+ {
189
+ type: 'select',
190
+ label: 'battery-rate-legend-name',
191
+ name: 'batteryRateLegendName',
192
+ property: {
193
+ options: themesColorRange
194
+ }
186
195
  }
187
196
  ],
188
197
  'value-property': 'text'
@@ -302,6 +311,13 @@ Object.defineProperty(RootContainer.prototype, 'zoneCapacityLegendTheme', {
302
311
  }
303
312
  })
304
313
 
314
+ Object.defineProperty(RootContainer.prototype, 'batteryRateLegendTheme', {
315
+ get: function () {
316
+ const { batteryRateLegendName } = this.rootModel.state
317
+ return batteryRateLegendName && this.state.style[batteryRateLegendName]
318
+ }
319
+ })
320
+
305
321
  Object.defineProperty(RootContainer.prototype, 'vehicleAnimation', {
306
322
  get: function () {
307
323
  const { vehicleAnimation } = this.rootModel.state
@@ -18,7 +18,6 @@ import ohtLine from './oht-line'
18
18
  import conveyor from './conveyor'
19
19
 
20
20
  import Node from './node'
21
- import NodePath from './node-path'
22
21
  import DataSubscription from './data-subscription'
23
22
 
24
23
  export default [
@@ -42,6 +41,5 @@ export default [
42
41
  Port,
43
42
 
44
43
  Node,
45
- NodePath,
46
44
  DataSubscription
47
45
  ]
@@ -45,5 +45,6 @@
45
45
  "label.equipment-legend-name": "equipment",
46
46
  "label.node-machine": "node machine",
47
47
  "label.nodes": "nodes",
48
- "label.vehicle-animation": "vehicle animation"
48
+ "label.vehicle-animation": "vehicle animation",
49
+ "label.battery-rate-legend-name": "battery rate"
49
50
  }
@@ -47,5 +47,6 @@
47
47
  "label.equipment-legend-name": "equipment",
48
48
  "label.node-machine": "node machine",
49
49
  "label.nodes": "nodes",
50
- "label.vehicle-animation": "vehicle animation"
50
+ "label.vehicle-animation": "vehicle animation",
51
+ "label.battery-rate-legend-name": "battery rate"
51
52
  }
@@ -48,5 +48,6 @@
48
48
  "label.equipment-legend-name": "장비",
49
49
  "label.node-machine": "node machine",
50
50
  "label.nodes": "nodes",
51
- "label.vehicle-animation": "vehicle animation"
51
+ "label.vehicle-animation": "vehicle animation",
52
+ "label.battery-rate-legend-name": "배터리 잔량"
52
53
  }
@@ -47,5 +47,6 @@
47
47
  "label.equipment-legend-name": "equipment",
48
48
  "label.node-machine": "node machine",
49
49
  "label.nodes": "nodes",
50
- "label.vehicle-animation": "vehicle animation"
50
+ "label.vehicle-animation": "vehicle animation",
51
+ "label.battery-rate-legend-name": "battery rate"
51
52
  }
@@ -47,5 +47,6 @@
47
47
  "label.equipment-legend-name": "equipment",
48
48
  "label.node-machine": "node machine",
49
49
  "label.nodes": "nodes",
50
- "label.vehicle-animation": "vehicle animation"
50
+ "label.vehicle-animation": "vehicle animation",
51
+ "label.battery-rate-legend-name": "battery rate"
51
52
  }