@fmsim/machine 1.0.47 → 1.0.49

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/src/agv-line.ts CHANGED
@@ -14,7 +14,7 @@ export default class AGVLine extends MCSTransport {
14
14
  }
15
15
 
16
16
  containable(component: Component) {
17
- return ['AGV'].includes(component.state.type)
17
+ return ['AGV', 'Node'].includes(component.state.type)
18
18
  }
19
19
 
20
20
  get auxColor() {
package/src/carrier.ts CHANGED
@@ -59,7 +59,7 @@ export default class Carrier extends MCSStatusMixin(ParentObjectMixin(Shape)) {
59
59
  }
60
60
 
61
61
  if (decorator || border || arrow) {
62
- this.trigger('animatoroff')
62
+ this.trigger('deco-off')
63
63
  }
64
64
  }
65
65
 
@@ -76,18 +76,18 @@ export default class Carrier extends MCSStatusMixin(ParentObjectMixin(Shape)) {
76
76
  }
77
77
 
78
78
  if (decorator) {
79
- this.trigger('animatoroff')
80
- this.trigger('icon', decorator)
79
+ this.trigger('deco-icon-off')
80
+ this.trigger('deco-icon', decorator)
81
81
  }
82
82
 
83
83
  if (border) {
84
- this.trigger('animatoroff')
85
- this.trigger('border', border)
84
+ this.trigger('deco-border-off')
85
+ this.trigger('deco-border', border)
86
86
  }
87
87
 
88
88
  if (arrow) {
89
- this.trigger('animatoroff')
90
- this.trigger('bouncing', arrow)
89
+ this.trigger('deco-arrow-off')
90
+ this.trigger('deco-arrow', arrow)
91
91
  }
92
92
  }
93
93
  }
package/src/conveyor.ts CHANGED
@@ -32,7 +32,7 @@ export default class Conveyor extends MCSTransport {
32
32
  }
33
33
 
34
34
  containable(component: Component) {
35
- return ['Shuttle', 'Port'].includes(component.state.type)
35
+ return ['Shuttle', 'Port', 'Node'].includes(component.state.type)
36
36
  }
37
37
 
38
38
  renderConveyor(ctx: CanvasRenderingContext2D) {
@@ -4,101 +4,168 @@
4
4
 
5
5
  import '@material/web/icon/icon.js'
6
6
 
7
- import { css, html } from 'lit'
8
- import { customElement, state } from 'lit/decorators.js'
7
+ import { css, html, nothing } from 'lit'
8
+ import { customElement, state, query, queryAll } from 'lit/decorators.js'
9
9
 
10
10
  import { OxFormField } from '@operato/input'
11
- import { Node } from '../types'
12
11
 
13
12
  @customElement('ox-input-nodes')
14
13
  export class OxInputNodes extends OxFormField {
15
14
  static styles = [
16
15
  css`
17
16
  :host {
18
- display: flex;
19
- --md-icon-size: 1.4em;
20
- }
21
-
22
- fieldset {
23
- flex: 1;
24
- font-size: 0.8em;
25
- border: 0;
26
- border-bottom: 1px solid;
27
- background-color: var(--md-sys-color-surface-variant);
28
- padding: var(--spacing-medium);
29
- }
30
-
31
- ul {
32
17
  display: flex;
33
18
  flex-direction: column;
34
- gap: var(--spacing-small);
35
-
36
- padding: 0;
37
- margin: 0;
19
+ overflow: hidden;
20
+
21
+ --md-icon-size: 14px;
22
+ --spacing-large: 16px;
23
+ --spacing-medium: 8px;
24
+ --spacing-small: 4px;
25
+ --spacing-tiny: 2px;
26
+ --padding-default: 4px;
27
+ --button-color: rgba(0, 0, 0, 0.5);
28
+ --button-border: 1px solid rgba(0, 0, 0, 0.15);
29
+ --button-background-color: rgba(0, 0, 0, 0.05);
30
+ --button-background-focus-color: rgba(0, 0, 0, 0.1);
31
+ --button-active-border: 1px solid rgba(0, 0, 0, 0.2);
38
32
  }
39
33
 
40
- li {
34
+ div {
41
35
  display: flex;
42
- flex-direction: row;
43
- align-items: center;
44
- gap: var(--spacing-large);
36
+ flex-flow: row nowrap;
37
+ gap: var(--spacing-medium);
38
+ margin-bottom: var(--spacing-small);
45
39
  }
46
40
 
47
- li > * {
48
- flex: 1;
41
+ button {
42
+ border: var(--button-border);
43
+ border-radius: var(--border-radius);
44
+ background-color: var(--button-background-color);
45
+ padding: var(--spacing-small) var(--padding-default);
46
+ line-height: 0.8;
47
+ color: var(--button-color);
48
+ cursor: pointer;
49
+ }
50
+ button + button {
51
+ margin-left: -5px;
52
+ }
53
+ button:focus,
54
+ button:hover,
55
+ button:active {
56
+ border: var(--button-active-border);
57
+ background-color: var(--button-background-focus-color);
58
+ color: rgba(0, 0, 0, 0.5);
49
59
  }
50
60
 
51
- div[data-name] {
52
- text-align: right;
61
+ input {
62
+ flex: 1;
63
+ border: 0;
64
+ border: 1px solid rgba(0, 0, 0, 0.15);
65
+ padding: var(--spacing-tiny);
66
+ font: var(--input-font);
67
+ min-width: 50px;
53
68
  }
54
69
 
55
- div[data-value] {
56
- display: flex;
57
- justify-content: space-between;
70
+ input:focus {
71
+ outline: none;
72
+ border: 1px solid rgba(0, 0, 0, 0.2);
58
73
  }
59
74
  `
60
75
  ]
61
76
 
62
- @state() value: Node[] = []
77
+ @state() value: string[] = []
78
+
79
+ @queryAll('[data-record]') records!: NodeListOf<HTMLElement>
80
+ @query('[data-record-new] [data-id]') addinput!: HTMLInputElement
81
+
82
+ private _changingNow: boolean = false
83
+
84
+ firstUpdated() {
85
+ this.renderRoot.addEventListener('change', this._onChange.bind(this))
86
+ }
63
87
 
64
88
  render() {
65
- const nodes = this.value || ([] as Node[])
89
+ const value = this.value || []
66
90
 
67
91
  return html`
68
- <fieldset>
69
- <ul>
70
- ${nodes.map(
71
- ({ id, name }) => html`
72
- <li @change=${this.onChange}>
73
- <div data-id=${id}>${id}</div>
74
- <input type="text" name="name" value=${name || ''} />
75
- </li>
76
- `
77
- )}
78
- </ul>
79
- </fieldset>
92
+ ${value.map(
93
+ item => html`
94
+ <div data-record>
95
+ <input
96
+ type="text"
97
+ data-id
98
+ .value=${item}
99
+ @keyup=${(e: KeyboardEvent) => e.key === 'Enter' && this._build()}
100
+ />
101
+ <button
102
+ class="record-action"
103
+ @click=${(e: MouseEvent) => this._delete(e)}
104
+ tabindex="-1"
105
+ ?disabled=${this.disabled}
106
+ >
107
+ <md-icon>remove</md-icon>
108
+ </button>
109
+ </div>
110
+ `
111
+ )}
112
+ ${this.disabled
113
+ ? nothing
114
+ : html`
115
+ <div data-record-new>
116
+ <input type="text" data-id value="" @keyup=${(e: KeyboardEvent) => e.key === 'Enter' && this._add()} />
117
+ <button class="record-action" @click=${(e: MouseEvent) => this._add()} tabindex="-1">
118
+ <md-icon>add</md-icon>
119
+ </button>
120
+ </div>
121
+ `}
80
122
  `
81
123
  }
82
124
 
83
- onChange(e: Event) {
84
- e.stopPropagation()
125
+ _onChange(e: Event) {
126
+ if (this._changingNow) {
127
+ return
128
+ }
129
+
130
+ this._changingNow = true
85
131
 
86
- const lis = this.renderRoot.querySelectorAll('li')
132
+ const input = e.target as HTMLInputElement
133
+
134
+ const record = (e.target as Element).closest('[data-record],[data-record-new]') as HTMLElement
135
+
136
+ if (record.hasAttribute('data-record')) {
137
+ this._build()
138
+ } else if (record.hasAttribute('data-record-new') && input.hasAttribute('data-value')) {
139
+ this._add()
140
+ }
141
+
142
+ this._changingNow = false
143
+ }
144
+
145
+ _build(includeNewRecord?: boolean) {
146
+ const selector = includeNewRecord ? '[data-record],[data-record-new]' : '[data-record]'
147
+ const records = this.renderRoot.querySelectorAll(selector) as NodeListOf<HTMLElement>
148
+
149
+ this.value = Array.from(records)
150
+ .map(record => (record.querySelector('[data-id]')! as HTMLInputElement).value as string)
151
+ .filter(Boolean)
152
+ .sort()
153
+
154
+ this.dispatchEvent(new CustomEvent('change', { bubbles: true, composed: true, detail: this.value }))
155
+ }
156
+
157
+ _add() {
158
+ this._build(true)
159
+
160
+ this.addinput.value = ''
161
+ this.addinput.focus()
162
+ }
87
163
 
88
- this.value = Array.from(lis).map(li => {
89
- const id = li.querySelector('[data-id]')?.getAttribute('data-id')
90
- const name = li.querySelector('input')?.value
91
- const node = this.value.find(node => node.id === id)
164
+ _delete(e: MouseEvent) {
165
+ const record = (e.target as Element).closest('[data-record]') as HTMLElement
92
166
 
93
- return { ...node!, name }
94
- })
167
+ ;(record!.querySelector('[data-id]') as HTMLInputElement)!.value = ''
95
168
 
96
- this.dispatchEvent(
97
- new CustomEvent('change', {
98
- bubbles: true,
99
- composed: true,
100
- detail: this.value
101
- })
102
- )
169
+ this._build()
103
170
  }
104
171
  }
@@ -4,23 +4,10 @@ import { html } from 'lit'
4
4
  import { customElement } from 'lit/decorators.js'
5
5
 
6
6
  import { OxPropertyEditor, PropertySpec } from '@operato/property-editor'
7
- import { Node } from '../types'
8
7
 
9
8
  @customElement('ox-property-editor-nodes')
10
9
  export class PropertyEditorNodes extends OxPropertyEditor {
11
- editorTemplate(value: Node[], spec: PropertySpec) {
12
- // const { defaultValue = [] } = spec
13
- // value ||= []
14
-
15
- // const valueProperty = defaultValue
16
- // .map(({ name, ...others }) => {
17
- // const node = value.find(v => v.name == name)
18
- // if (node) {
19
- // return { ...others, ...node }
20
- // }
21
- // })
22
- // .filter(Boolean)
23
-
24
- return html` <ox-input-nodes .value=${value} fullwidth></ox-input-nodes> `
10
+ editorTemplate(value: string[], spec: PropertySpec) {
11
+ return html` <ox-input-nodes .value=${value}></ox-input-nodes> `
25
12
  }
26
13
  }
@@ -20,6 +20,10 @@ export const MCSStatusMixinProperties = [
20
20
 
21
21
  export function MCSStatusMixin<TBase extends Constructor>(Base: TBase) {
22
22
  return class extends Base {
23
+ isLVComponent() {
24
+ return true
25
+ }
26
+
23
27
  get status(): string | undefined {
24
28
  return
25
29
  }
@@ -1,8 +1,22 @@
1
+ import { Properties } from '@hatiolab/things-scene'
1
2
  import { LEGEND_VEHICLE, Legend } from './features/mcs-status-default'
2
3
  import MCSCarrierHolder from './mcs-carrier-holder'
4
+ import Node from './node'
3
5
 
4
6
  const DIRECTION_GAP = 3
5
7
 
8
+ function findNode(vehicle: MCSVehicle, nodeId: string, nodeMachine: string) {
9
+ if (!nodeId || !nodeMachine) {
10
+ return null
11
+ }
12
+
13
+ const nodes = vehicle.root.findAllById(nodeId).filter(node => node.state.type == 'Node') as Node[]
14
+
15
+ return nodes.find((node: Node) => {
16
+ return node.nodeMachine === nodeMachine
17
+ })
18
+ }
19
+
6
20
  /**
7
21
  * MCS용 Unit > Transport들의 공통 속성을 정의한 오브젝트
8
22
  */
@@ -21,6 +35,8 @@ export default class MCSVehicle extends MCSCarrierHolder {
21
35
  ]
22
36
  }
23
37
 
38
+ private lastWaypoint: { NodeId: string; NodeMachine: string } | null = null
39
+
24
40
  getLegendFallback(): Legend {
25
41
  return LEGEND_VEHICLE
26
42
  }
@@ -77,4 +93,39 @@ export default class MCSVehicle extends MCSCarrierHolder {
77
93
  super.postrender(ctx)
78
94
  // this.renderDirection(ctx)
79
95
  }
96
+
97
+ onchangeData(after: Properties, before: Properties): void {
98
+ super.onchangeData(after, before)
99
+
100
+ const { NodeId: beforeNodeId, NodeMachine: beforeNodeMachine } = before.data || {}
101
+ const { NodeId, NodeMachine } = after.data || {}
102
+
103
+ if (
104
+ NodeId === undefined ||
105
+ NodeMachine === undefined ||
106
+ (beforeNodeId === NodeId && beforeNodeMachine === NodeMachine) ||
107
+ (this.lastWaypoint && this.lastWaypoint.NodeId === NodeId && this.lastWaypoint.NodeMachine === NodeMachine)
108
+ ) {
109
+ return // not changed
110
+ }
111
+
112
+ const afterNode = findNode(this, NodeId, NodeMachine)
113
+ if (!afterNode) {
114
+ console.warn(`Node not found. NodeId: ${NodeId}, NodeMachine: ${NodeMachine}`)
115
+ return
116
+ }
117
+
118
+ const beforeNode = this.lastWaypoint
119
+ ? findNode(this, this.lastWaypoint.NodeId, this.lastWaypoint.NodeMachine)
120
+ : afterNode
121
+
122
+ // 실제 존재하는 노드 중 가장 최근에 이동한 노드
123
+ this.lastWaypoint = { NodeId, NodeMachine }
124
+
125
+ this.trigger('waypoint', {
126
+ from: beforeNode,
127
+ to: afterNode,
128
+ duration: 5000
129
+ })
130
+ }
80
131
  }
package/src/node.ts CHANGED
@@ -3,12 +3,24 @@
3
3
  */
4
4
 
5
5
  import { Component, Shape } from '@hatiolab/things-scene'
6
+ import MCSMachine from './mcs-machine'
6
7
 
7
8
  const NATURE = {
8
9
  mutable: false,
9
10
  resizable: false,
10
11
  rotatable: false,
11
- properties: []
12
+ properties: [
13
+ {
14
+ type: 'string',
15
+ name: 'NodeMachine',
16
+ label: 'node-machine'
17
+ },
18
+ {
19
+ type: 'nodes',
20
+ label: 'nodes',
21
+ name: 'nodes'
22
+ }
23
+ ]
12
24
  }
13
25
 
14
26
  export default class Node extends Shape {
@@ -65,6 +77,18 @@ export default class Node extends Shape {
65
77
  })
66
78
  }
67
79
 
80
+ get nodeMachine() {
81
+ if (this.state.NodeMachine) {
82
+ return this.state.NodeMachine
83
+ }
84
+
85
+ const parent = this.parent
86
+
87
+ if (parent && parent instanceof MCSMachine) {
88
+ return parent.state.id
89
+ }
90
+ }
91
+
68
92
  contains(x, y) {
69
93
  var { cx, cy } = this.state
70
94
 
package/src/oht-line.ts CHANGED
@@ -22,7 +22,7 @@ export default class OHTLine extends MCSTransport {
22
22
  }
23
23
 
24
24
  containable(component: Component) {
25
- return ['OHT'].includes(component.state.type)
25
+ return ['OHT', 'Node'].includes(component.state.type)
26
26
  }
27
27
 
28
28
  render(context: CanvasRenderingContext2D) {
@@ -42,5 +42,7 @@
42
42
  "label.shuttle-legend-name": "shuttle",
43
43
  "label.crane-legend-name": "crane",
44
44
  "label.shelf-legend-name": "shelf",
45
- "label.equipment-legend-name": "equipment"
45
+ "label.equipment-legend-name": "equipment",
46
+ "label.node-machine": "node machine",
47
+ "label.nodes": "nodes"
46
48
  }
@@ -44,5 +44,7 @@
44
44
  "label.shuttle-legend-name": "shuttle",
45
45
  "label.crane-legend-name": "crane",
46
46
  "label.shelf-legend-name": "shelf",
47
- "label.equipment-legend-name": "equipment"
47
+ "label.equipment-legend-name": "equipment",
48
+ "label.node-machine": "node machine",
49
+ "label.nodes": "nodes"
48
50
  }
@@ -45,5 +45,7 @@
45
45
  "label.shuttle-legend-name": "셔틀",
46
46
  "label.crane-legend-name": "크레인",
47
47
  "label.shelf-legend-name": "선반",
48
- "label.equipment-legend-name": "장비"
48
+ "label.equipment-legend-name": "장비",
49
+ "label.node-machine": "node machine",
50
+ "label.nodes": "nodes"
49
51
  }
@@ -44,5 +44,7 @@
44
44
  "label.shuttle-legend-name": "shuttle",
45
45
  "label.crane-legend-name": "crane",
46
46
  "label.shelf-legend-name": "shelf",
47
- "label.equipment-legend-name": "equipment"
47
+ "label.equipment-legend-name": "equipment",
48
+ "label.node-machine": "node machine",
49
+ "label.nodes": "nodes"
48
50
  }
@@ -44,5 +44,7 @@
44
44
  "label.shuttle-legend-name": "shuttle",
45
45
  "label.crane-legend-name": "crane",
46
46
  "label.shelf-legend-name": "shelf",
47
- "label.equipment-legend-name": "equipment"
47
+ "label.equipment-legend-name": "equipment",
48
+ "label.node-machine": "node machine",
49
+ "label.nodes": "nodes"
48
50
  }