@fmsim/machine 3.0.0-beta.5 → 3.0.0-beta.7

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": "3.0.0-beta.5",
5
+ "version": "3.0.0-beta.7",
6
6
  "main": "dist/index.js",
7
7
  "module": "dist/index.js",
8
8
  "things-scene": true,
@@ -26,8 +26,8 @@
26
26
  "migration": "things-factory-migration"
27
27
  },
28
28
  "dependencies": {
29
- "@fmsim/api": "^3.0.0-beta.2",
30
- "@fmsim/figure-capabilities": "^3.0.0-beta.5",
29
+ "@fmsim/api": "^3.0.0-beta.7",
30
+ "@fmsim/figure-capabilities": "^3.0.0-beta.7",
31
31
  "@hatiolab/figure-model": "^0.12.0",
32
32
  "@hatiolab/things-scene": "^10.1.90",
33
33
  "@operato/slot-layout-editor": "^10.29.33",
@@ -59,5 +59,5 @@
59
59
  "prettier --write"
60
60
  ]
61
61
  },
62
- "gitHead": "8a40b51a4db5f5b73a2f18dd4f7455eb5f9518fe"
62
+ "gitHead": "3d79ed4f842108b845268b5d4b229927ff77700f"
63
63
  }
@@ -103,12 +103,19 @@ export function LVVehicle<T extends new (...args: any[]) => any>(Base: T) {
103
103
  }
104
104
 
105
105
  /**
106
- * `fmsim.CarrierHolder` on a figure: carriers change hands at the transport surface, so a holder that takes them on its
106
+ * `fmsim.CarrierHolder` on a figure: its carriers stand in its places -- a gripper, a lift plate, a fork's tiers -- and
107
+ * go with them; one without places keeps them in a pile on its top. Carriers change hands at the transport surface, so a
108
+ * holder that takes them on its
107
109
  * top stands that tall when nothing else says -- as fmsim's own AGV and shuttle do. A machine's height wins (whichever is
108
110
  * worn over the other); a holder that hangs them or hangs itself from the ceiling (an OHT) keeps its own.
109
111
  */
110
112
  export function FigureCarrierHolder<T extends new (...args: any[]) => any>(Base: T) {
111
113
  return class extends CarrierHolder(Base) {
114
+ /** Its carriers stand in its own places, which go with the parts that carry them (ADR-0167 decision 23) */
115
+ get fillsPlaces(): boolean {
116
+ return true
117
+ }
118
+
112
119
  get effectiveDepth(): number | undefined {
113
120
  const given = super.effectiveDepth
114
121
  if (given !== undefined) return given
@@ -6,6 +6,7 @@ import {
6
6
  ItemsDataBinding,
7
7
  Model,
8
8
  TRANSFER_SLOT_KEY,
9
+ drawingScaleFor,
9
10
  figureLegendSourcesChanged,
10
11
  itemTypeEntryOf,
11
12
  itemTypeTableOf,
@@ -16,7 +17,7 @@ import { colourAndEffect } from './features/legend-rows'
16
17
  import { HOLDER_ANCHOR, type ItemDims, type ResolvedSlot } from '@hatiolab/figure-model'
17
18
  import { carrierHolderProperties } from '@fmsim/figure-capabilities'
18
19
  import MCSUnit from './mcs-unit.js'
19
- import { incomingFromV2 } from './utils/load-address'
20
+ import { incomingFromV2, resolveOccupancy, type IncomingCarrier, type LoadLayout, type OccupancyResult } from './utils/load-address'
20
21
 
21
22
  /**
22
23
  * A holder's slots outside its layout (common item contract 3.4): every carrier of a holder without a layout stands in
@@ -99,15 +100,67 @@ export function CarrierHolder<T extends new (...args: any[]) => any>(Base: T) {
99
100
  }
100
101
 
101
102
  /**
102
- * One record per carrier the data names, bottom first: MULTICARRIERS in order, then the representative. Without a
103
- * layout they all stand in the pile. A carrier is told its own keys and whether to show its text.
103
+ * Whether carriers stand in the holder's own places (ADR-0167 decision 23): a figure holder's places -- a gripper, a
104
+ * lift plate, the tiers on a fork -- carry what stands on them as the figure moves. fmsim's own holders keep theirs: a
105
+ * pile, or the layout their data addresses (LoadLayoutHolder).
106
+ */
107
+ get fillsPlaces(): boolean {
108
+ return false
109
+ }
110
+
111
+ /** The holder's places as the last records took them: an addressed carrier keeps its slot while it is still there */
112
+ _placeOccupancy?: OccupancyResult
113
+
114
+ /**
115
+ * One record per carrier the data names, bottom first: MULTICARRIERS in order, then the representative. A carrier is
116
+ * told its own keys and whether to show its text. They stand in the pile, unless the holder fills its places.
104
117
  */
105
118
  carrierRecords(data: Record<string, any> | undefined): CarrierRecord[] {
106
- return incomingFromV2(data).map(c => ({
107
- slotId: PILE_SLOT,
119
+ const incoming = incomingFromV2(data)
120
+ const record = (c: IncomingCarrier, slotId: string): CarrierRecord => ({
121
+ slotId,
108
122
  itemId: c.name,
109
123
  itemState: this.carrierStateOf(c.record, c.record === data)
110
- }))
124
+ })
125
+ const places = this.fillsPlaces ? (((this as any).slots ?? []) as Array<{ id: string; maxCount?: number }>) : []
126
+ if (!places.length) return incoming.map(c => record(c, PILE_SLOT))
127
+
128
+ /* A carrier its data addresses (a layout's TIER/SLOT) stands at that address, as on a crane; a wrong address beside the holder. */
129
+ const layout = this._placeLayout
130
+ const occupancy = layout ? resolveOccupancy(layout, incoming, this._placeOccupancy) : undefined
131
+ this._placeOccupancy = occupancy
132
+ const slotOf = new Map<string, string>()
133
+ for (const [key, list] of occupancy?.slots ?? []) {
134
+ const [tier, slot] = key.split(':').map(Number)
135
+ for (const c of list) slotOf.set(c.name, `t${tier}_s${slot}`)
136
+ }
137
+ for (const c of occupancy?.addressError ?? []) slotOf.set(c.name, EXCEPTION_SLOT)
138
+
139
+ /* The others fill the places in their order, each up to what it takes; one still held keeps its place. What is left over stands beside the holder. */
140
+ const room = new Map(places.map(p => [p.id, p.maxCount ?? 1]))
141
+ for (const slotId of slotOf.values()) if (room.has(slotId)) room.set(slotId, room.get(slotId)! - 1)
142
+ const unplaced = incoming.filter(c => !slotOf.has(c.name))
143
+ const heldIn = new Map(((this as any)._items as any[]).map(item => [item.state?.id, item[TRANSFER_SLOT_KEY]] as const))
144
+ for (const c of unplaced) {
145
+ const kept = heldIn.get(c.name)
146
+ if (kept && (room.get(kept) ?? 0) > 0) {
147
+ slotOf.set(c.name, kept)
148
+ room.set(kept, room.get(kept)! - 1)
149
+ }
150
+ }
151
+ for (const c of unplaced) {
152
+ if (slotOf.has(c.name)) continue
153
+ const free = places.find(p => (room.get(p.id) ?? 0) > 0)
154
+ slotOf.set(c.name, free ? free.id : EXCEPTION_SLOT)
155
+ if (free) room.set(free.id, room.get(free.id)! - 1)
156
+ }
157
+ return incoming.map(c => record(c, slotOf.get(c.name)!))
158
+ }
159
+
160
+ /** The layout addresses are read by: the holder's, when it holds and the drawing's scale is known */
161
+ get _placeLayout(): LoadLayout | undefined {
162
+ const sel = (this as any).effectiveLayout
163
+ return sel?.status === 'valid' && drawingScaleFor(this as any).known ? (sel.layout as LoadLayout) : undefined
111
164
  }
112
165
 
113
166
  /** What a carrier is told: its own keys (the representative's are picked out of the holder data), and its text */
@@ -201,7 +254,9 @@ export function CarrierHolder<T extends new (...args: any[]) => any>(Base: T) {
201
254
  */
202
255
  itemShownDimsOf(_carrier: any, slot: ResolvedSlot, slotId: string): ItemDims | null {
203
256
  /* Elsewhere outside the layout, a holder that keeps carriers there says (the exception area: LoadLayoutHolder). */
204
- if (slotId === PILE_SLOT) {
257
+ /* A place that says no size (a gripper): the carrier is shown at its own (ADR-0167 decision 23). */
258
+ if (slotId !== PILE_SLOT && slotId !== EXCEPTION_SLOT && !slot?.allowed) return null
259
+ if (slotId === PILE_SLOT || slotId === EXCEPTION_SLOT) {
205
260
  const k = (this as any).mmPerSceneUnit
206
261
  const { width = 0, height = 0 } = (this as any).state
207
262
  const w = Math.abs(width) * PILE_PLAN_RATIO