@fmsim/machine 1.0.57 → 1.0.58

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.58",
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": "a96ffbe06662de2e62fcd78033b1f0d614206599"
58
58
  }
package/src/agv.ts CHANGED
@@ -1,5 +1,7 @@
1
1
  import { Component, ComponentNature, Properties } from '@hatiolab/things-scene'
2
2
  import MCSVehicle from './mcs-vehicle'
3
+ import { getVaueOnRanges } from './utils/get-value-on-ranges'
4
+ import { BATTERY_RATE_LEGEND } from './features/mcs-status-default'
3
5
 
4
6
  const NATURE: ComponentNature = {
5
7
  mutable: false,
@@ -22,22 +24,32 @@ export default class AGV extends MCSVehicle {
22
24
  return this.root.agvLegendTheme || super.getLegendFallback()
23
25
  }
24
26
 
27
+ getBatteryLegendTheme() {
28
+ return this.root.batteryRateLegendTheme || BATTERY_RATE_LEGEND
29
+ }
30
+
31
+ getBatteryColor(rate: number) {
32
+ return getVaueOnRanges(rate, this.getBatteryLegendTheme())
33
+ }
34
+
25
35
  render(ctx: CanvasRenderingContext2D) {
26
36
  var { left, top, width, height } = this.bounds
27
37
 
28
38
  ctx.translate(left, top)
29
39
  ctx.beginPath()
30
40
 
41
+ // 작은 크기에서도 보이도록 최소 선 두께 설정
42
+ const minLineWidth = 0.5
43
+ const scaleFactor = Math.min(width, height) / 100
44
+ const lineWidth = Math.max(minLineWidth, scaleFactor)
45
+
31
46
  ctx.strokeStyle = this.auxColor
32
47
  ctx.fillStyle = this.statusColor!
33
- ctx.lineWidth = 1
48
+ ctx.lineWidth = lineWidth
34
49
  ctx.lineCap = 'round'
35
50
  ctx.setLineDash([])
36
51
 
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)
52
+ // 기본 사각형 그리기
41
53
  ctx.rect(1, 0, width - 2, height)
42
54
  ctx.rect(GAP + 1, GAP, width - (GAP + 1) * 2, height - GAP * 2)
43
55
 
@@ -46,7 +58,7 @@ export default class AGV extends MCSVehicle {
46
58
 
47
59
  ctx.beginPath()
48
60
 
49
- // draw wheel metapore
61
+ // 바퀴 그리기
50
62
  ctx.strokeStyle = 'black'
51
63
 
52
64
  ctx.moveTo(0, (height * 1) / 3)
@@ -59,23 +71,85 @@ export default class AGV extends MCSVehicle {
59
71
  ctx.translate(-left, -top)
60
72
  }
61
73
 
74
+ // 사선 패턴을 그리는 함수
75
+ drawHatchPattern(ctx: CanvasRenderingContext2D, x: number, y: number, width: number, height: number) {
76
+ // 크기에 따라 패턴 간격과 선 두께 조정 (2배 덜 조밀하게)
77
+ const minSpacing = 4 // 최소 간격 2배 증가 (원래 2의 2배)
78
+ const maxSpacing = 16 // 최대 간격 2배 증가 (원래 8의 2배)
79
+ const preferredSpacing = Math.max(minSpacing, Math.min(width, height) / 5) // 간격 계산 시 나누는 값 1/2로 감소
80
+ const spacing = Math.min(preferredSpacing, maxSpacing)
81
+
82
+ // 선 두께 계산 (작은 크기에서도 보이도록)
83
+ const minLineWidth = 0.35
84
+ const scaleFactor = Math.min(width, height) / 130
85
+ const lineWidth = Math.max(minLineWidth, scaleFactor)
86
+
87
+ ctx.beginPath()
88
+ ctx.strokeStyle = 'black'
89
+ ctx.lineWidth = lineWidth
90
+
91
+ // 배터리 박스 내부로 클리핑 설정
92
+ ctx.save()
93
+ ctx.rect(x, y, width, height)
94
+ ctx.clip()
95
+
96
+ // 사선 패턴 그리기
97
+ for (let i = -height; i < width + height; i += spacing) {
98
+ ctx.moveTo(x + i, y)
99
+ ctx.lineTo(x + i - height, y + height)
100
+ }
101
+
102
+ ctx.stroke()
103
+ // 클리핑 해제
104
+ ctx.restore()
105
+ }
106
+
62
107
  postrender(ctx: CanvasRenderingContext2D): void {
63
108
  super.postrender(ctx)
64
109
 
65
110
  const BATTERYRATE = this.data?.BATTERYRATE
66
111
 
67
- if (typeof BATTERYRATE == 'number') {
112
+ if (typeof BATTERYRATE === 'number') {
68
113
  const { left, top, width, height } = this.bounds
69
114
  ctx.translate(left, top)
70
115
 
71
- const batteryHeight = height / 5
72
- const batteryMargin = 1
73
- const batteryWidth = width - 2 * batteryMargin
116
+ // 배터리 크기 위치 설정 (최소 크기 보장)
117
+ const minBatteryHeight = 2
118
+ const minBatteryWidth = 4
119
+ const preferredHeight = height / 5
120
+ const preferredWidth = (width * 3) / 4
121
+
122
+ const batteryHeight = Math.max(minBatteryHeight, preferredHeight)
123
+ const batteryWidth = Math.max(minBatteryWidth, preferredWidth)
124
+ const batteryX = (width - batteryWidth) / 2
125
+ const batteryY = (height - batteryHeight) * 1.1
126
+
127
+ // 배터리 테두리 그리기 (크기에 비례한 선 두께)
128
+ const minLineWidth = 0.5
129
+ const scaleFactor = Math.min(width, height) / 100
130
+ const lineWidth = Math.max(minLineWidth, scaleFactor)
131
+
132
+ // 배터리 배경을 흰색으로 채우기
133
+ ctx.fillStyle = 'white'
134
+ ctx.fillRect(batteryX, batteryY, batteryWidth, batteryHeight)
135
+
136
+ // 배터리 전체 영역에 사선 패턴 그리기
137
+ this.drawHatchPattern(ctx, batteryX, batteryY, batteryWidth, batteryHeight)
138
+
139
+ // 배터리 잔량 표시
140
+ const fillWidth = (batteryWidth * BATTERYRATE) / 100
141
+
142
+ // 배터리 잔량에 따른 색상 설정 - 테마 적용
143
+ ctx.fillStyle = this.getBatteryColor(BATTERYRATE)
144
+
145
+ // 배터리 잔량 영역 채우기
146
+ ctx.fillRect(batteryX, batteryY, fillWidth, batteryHeight)
74
147
 
75
- 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)
148
+ // 배터리 테두리 다시 그리기
149
+ ctx.beginPath()
150
+ ctx.strokeStyle = 'black'
151
+ ctx.lineWidth = lineWidth
152
+ ctx.strokeRect(batteryX, batteryY, batteryWidth, batteryHeight)
79
153
 
80
154
  ctx.translate(-left, -top)
81
155
  }
@@ -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
  }