@jnrs/lingshu-smart 2.2.46 → 3.0.0
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/AGENTS.md +281 -348
- package/dist/components/debug/DataLogPanel.vue.d.ts +29 -3
- package/dist/components/debug/PerformanceStats.vue.d.ts +1 -1
- package/dist/components/debug/ScriptRunner/ConfigPanel.vue.d.ts +1 -2
- package/dist/components/debug/ScriptRunner/StepsPanel.vue.d.ts +4 -2
- package/dist/components/debug/ScriptRunner/index.vue.d.ts +11 -0
- package/dist/components/debug/ScriptRunner/script.d.ts +140 -41
- package/dist/components/debug/ScriptRunner/scriptFile.d.ts +92 -0
- package/dist/components/debug/WsProxy/index.vue.d.ts +10 -8
- package/dist/components/debug/WsProxy/proxy.d.ts +114 -53
- package/dist/components/editor/ControlBar/CaseManger.vue.d.ts +18 -2
- package/dist/components/editor/ControlBar/CaseTitleEdit.vue.d.ts +2 -2
- package/dist/components/editor/ControlBar/PersistDialog.vue.d.ts +9 -0
- package/dist/components/index.js +8419 -6045
- package/dist/components/preview/index.vue.d.ts +6852 -4876
- package/dist/controller/AppController.d.ts +75 -4
- package/dist/controller/SocketDispatcher.d.ts +59 -0
- package/dist/fullscreen-BA3oea8O.js +53 -0
- package/dist/index-C6Liz-8o.js +1238 -0
- package/dist/index.js +36 -27
- package/dist/lingshu-smart/cases.json +5890 -14828
- package/dist/lingshu-smart/mockSocket.json +263 -0
- package/dist/lingshu-smart/wsProxyConfig.json +343 -720
- package/dist/playground/ViewPageDemo.vue.d.ts +14412 -10438
- package/dist/stores/index.js +1 -1
- package/dist/stores/useAppStore.d.ts +20570 -14641
- package/dist/stores/useDataStore.d.ts +21139 -14587
- package/dist/stores/useDebugStore.d.ts +14 -2
- package/dist/stores/useEditorStore.d.ts +3 -3
- package/dist/types/app.types.d.ts +1 -1
- package/dist/types/components/config.types.d.ts +16 -0
- package/dist/types/components/operating.types.d.ts +28 -19
- package/dist/types/enums.d.ts +22 -2
- package/dist/types/guards.d.ts +4 -1
- package/dist/types/index.d.ts +1 -0
- package/dist/types/models/config.types.d.ts +41 -9
- package/dist/types/models/operating.types.d.ts +64 -74
- package/dist/types/socket.types.d.ts +275 -0
- package/dist/ui/abstract/ModelAbs.d.ts +42 -2
- package/dist/ui/components/Resource.d.ts +0 -1
- package/dist/ui/components/StationSlot.d.ts +8 -2
- package/dist/ui/components/StationSlotGroup.d.ts +28 -3
- package/dist/ui/models/DoubleJointRobot.d.ts +16 -4
- package/dist/ui/models/MachineTool.d.ts +6 -2
- package/dist/ui/models/MaterialTrack.d.ts +14 -2
- package/dist/ui/models/Pallet.d.ts +29 -9
- package/dist/ui/models/Rail.d.ts +9 -2
- package/dist/ui/models/ResourceOperationWindow.d.ts +2 -1
- package/dist/ui/models/ResourceStore.d.ts +7 -5
- package/dist/ui/models/Rgv.d.ts +6 -6
- package/dist/ui/models/ShuttleStation.d.ts +14 -4
- package/dist/ui/models/SingleJointRobot.d.ts +14 -4
- package/dist/ui/utils/resolveStationSlotLayers.d.ts +20 -0
- package/dist/ui/utils/resolveTypeStyle.d.ts +13 -0
- package/dist/useAppStore-DASwAOBy.js +7223 -0
- package/dist/utils/fullscreen.d.ts +9 -0
- package/dist/utils/index.d.ts +1 -0
- package/dist/vite.d.ts +15 -0
- package/dist/vite.js +39 -0
- package/package.json +6 -2
- package/dist/components/editor/OptionCard/Equipment/stationSlot/StationSlotGroupBoth.vue.d.ts +0 -2
- package/dist/guards-aLOXCeCF.js +0 -35
- package/dist/index-Dsgrnpn0.js +0 -1186
- package/dist/useAppStore-CVyLAMtL.js +0 -6125
- /package/dist/components/editor/OptionCard/Equipment/{stationSlot/StationSlotGroup.vue.d.ts → models/ResourceStore.vue.d.ts} +0 -0
package/AGENTS.md
CHANGED
|
@@ -38,7 +38,7 @@ const saveCases = (data?: ICase[]) => {
|
|
|
38
38
|
/* 持久化到后端 */
|
|
39
39
|
}
|
|
40
40
|
const handlePreview = (currentCase?: ICase) => {
|
|
41
|
-
window.open(`/preview/${currentCase?.
|
|
41
|
+
window.open(`/preview/${currentCase?.id}`)
|
|
42
42
|
}
|
|
43
43
|
const handleExit = () => {
|
|
44
44
|
/* 退出逻辑 */
|
|
@@ -56,25 +56,23 @@ const handleExit = () => {
|
|
|
56
56
|
| --------------- | ----------------------------------- | --------------------------- | --------------------------------------------------- |
|
|
57
57
|
| `loadCases` | `(id?: string) => Promise<ICase[]>` | 编辑器初始化 / 方案管理弹窗 | **必填**。返回方案列表,组件内部写入 `useDataStore` |
|
|
58
58
|
| `saveCases` | `(cases?: ICase[]) => void` | 点击保存按钮 | 将当前全部方案提交后端 |
|
|
59
|
-
| `handlePreview` | `(currentCase?: ICase) => void` | 点击预览按钮 | 通常跳转至预览页并携带 `
|
|
59
|
+
| `handlePreview` | `(currentCase?: ICase) => void` | 点击预览按钮 | 通常跳转至预览页并携带 `id` |
|
|
60
60
|
| `handleExit` | `() => void` | 点击退出按钮 | 返回上一页或关闭窗口 |
|
|
61
61
|
|
|
62
|
-
**注意:**
|
|
62
|
+
**注意:** 编辑模式不涉及消息协议。设备运转动画仅在预览看板中通过 `feedSocketMessage` 经 `SocketDispatcher` 驱动。
|
|
63
63
|
|
|
64
64
|
---
|
|
65
65
|
|
|
66
66
|
### ViewPageDemo.vue — 预览/运行模式
|
|
67
67
|
|
|
68
|
-
**职责:**
|
|
68
|
+
**职责:** 加载并展示指定方案、响应用户交互、接收符合《socket-operating-api.md》的标准 Socket 协议消息(`envelope + payload`)并驱动设备动画。
|
|
69
69
|
|
|
70
70
|
**核心结构:**
|
|
71
71
|
|
|
72
72
|
```vue
|
|
73
73
|
<script setup lang="ts">
|
|
74
74
|
import { ref, computed, onMounted } from 'vue'
|
|
75
|
-
import type { ICase, ILayer,
|
|
76
|
-
import { EnumModel } from '@/types'
|
|
77
|
-
import { transRuntimeToPixel, transPercentToPixel } from '@/utils'
|
|
75
|
+
import type { ICase, ILayer, InteractiveUi } from '@/types'
|
|
78
76
|
import LingshuSmartPreview from '@/components/preview/index.vue'
|
|
79
77
|
|
|
80
78
|
const lingshuSmartPreviewRef = ref()
|
|
@@ -97,29 +95,33 @@ const onDragEnd = (layer: ILayer, data: InteractiveUi) => {
|
|
|
97
95
|
/* 拖拽结束 */
|
|
98
96
|
}
|
|
99
97
|
|
|
100
|
-
//
|
|
101
|
-
const
|
|
102
|
-
lingshuSmartPreviewRef.value?.
|
|
98
|
+
// WebSocket 消息入口 — 将原始消息直接喂给协议分发器(见下文「Socket 协议接入指南」)
|
|
99
|
+
const onSocketMessage = (rawMessage: any) => {
|
|
100
|
+
lingshuSmartPreviewRef.value?.feedSocketMessage(rawMessage)
|
|
103
101
|
}
|
|
104
102
|
|
|
105
|
-
//
|
|
106
|
-
const
|
|
107
|
-
|
|
103
|
+
// 订阅协议分发器回调(回执 command_ack / 透传 alarm_push 等,可选)
|
|
104
|
+
const setHandlers = () => {
|
|
105
|
+
lingshuSmartPreviewRef.value?.setSocketHandlers({
|
|
106
|
+
onAck: (ack) => console.log('[command_ack]', ack?.payload),
|
|
107
|
+
onRaw: (raw) => console.log('[透传]', raw?.envelope?.type)
|
|
108
|
+
})
|
|
108
109
|
}
|
|
109
110
|
|
|
110
111
|
onMounted(() => {
|
|
111
|
-
// 从 URL 解析方案
|
|
112
|
+
// 从 URL 解析方案 ID,例如 /preview/abc-123
|
|
112
113
|
const VIEW_PATH = '/preview/'
|
|
113
114
|
if (location.pathname.includes(VIEW_PATH)) {
|
|
114
115
|
currentCaseId.value = location.pathname.split(VIEW_PATH)[1]
|
|
115
116
|
}
|
|
117
|
+
setHandlers()
|
|
116
118
|
})
|
|
117
119
|
</script>
|
|
118
120
|
|
|
119
121
|
<template>
|
|
120
122
|
<LingshuSmartPreview
|
|
121
123
|
ref="lingshuSmartPreviewRef"
|
|
122
|
-
:case-
|
|
124
|
+
:case-id="currentCaseId"
|
|
123
125
|
:actions="{ loadCases, onTap, onMenuTap, onDragEnd }"
|
|
124
126
|
/>
|
|
125
127
|
</template>
|
|
@@ -136,330 +138,248 @@ onMounted(() => {
|
|
|
136
138
|
|
|
137
139
|
**Preview 组件 expose:**
|
|
138
140
|
|
|
139
|
-
| 属性/方法
|
|
140
|
-
|
|
|
141
|
-
| `currentCase`
|
|
142
|
-
| `
|
|
141
|
+
| 属性/方法 | 类型 | 说明 |
|
|
142
|
+
| ------------------- | ---------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------- |
|
|
143
|
+
| `currentCase` | `ICase \| null` | 当前加载的方案 |
|
|
144
|
+
| `feedSocketMessage` | `(msg: unknown) => void` | **推荐**。下发一条标准信封协议消息(`envelope + payload`),经 `SocketDispatcher` 自动分发驱动画布 |
|
|
145
|
+
| `setSocketHandlers` | `(handlers: { onAck?: (ack) => void; onRaw?: (raw) => void }) => void` | 订阅回执 `command_ack` 与透传(`alarm_push` / `runtime_status_push`)回调 |
|
|
146
|
+
| `operating` | `(data: ILayerOperating) => void` | 旧链路内部 `operating` 引擎,仅 WsProxy 等内部组件可选使用 |
|
|
147
|
+
| `resetModels` | `() => void` | 一键复位画布(所有模型按初始配置重建,含资源清空) |
|
|
143
148
|
|
|
144
149
|
**Props:**
|
|
145
150
|
|
|
146
|
-
| 属性 | 类型 | 默认值 | 说明
|
|
147
|
-
| --------------- | ---------------- | --------- |
|
|
148
|
-
| `
|
|
149
|
-
| `actions` | `PreviewActions` | `{}` | 事件与数据加载回调
|
|
150
|
-
| `appHittable` | `boolean` | `true` | 画布是否可交互
|
|
151
|
-
| `appFill` | `string` | `#424242` | 容器背景色
|
|
152
|
-
| `hideSceneFill` | `boolean` | `false` | 是否隐藏场景背景
|
|
151
|
+
| 属性 | 类型 | 默认值 | 说明 |
|
|
152
|
+
| --------------- | ---------------- | --------- | ------------------------- |
|
|
153
|
+
| `caseId` | `string` | — | **必填**。要展示的方案 ID |
|
|
154
|
+
| `actions` | `PreviewActions` | `{}` | 事件与数据加载回调 |
|
|
155
|
+
| `appHittable` | `boolean` | `true` | 画布是否可交互 |
|
|
156
|
+
| `appFill` | `string` | `#424242` | 容器背景色 |
|
|
157
|
+
| `hideSceneFill` | `boolean` | `false` | 是否隐藏场景背景 |
|
|
153
158
|
|
|
154
159
|
---
|
|
155
160
|
|
|
156
|
-
## 📡
|
|
161
|
+
## 📡 Socket 协议接入指南
|
|
162
|
+
|
|
163
|
+
预览看板通过 WebSocket(或 SSE、轮询等)接收后端推送,将原始消息**直接交给** **`feedSocketMessage()`**。画布内置的 `SocketDispatcher` 负责解析、去重、校验并驱动设备动画,业务侧无需自行分支映射。
|
|
157
164
|
|
|
158
|
-
|
|
165
|
+
> 协议规范以《socket-operating-api.md》为准(v3.5),本文仅摘录接入要点。
|
|
159
166
|
|
|
160
167
|
### 数据流
|
|
161
168
|
|
|
162
169
|
```
|
|
163
|
-
WebSocket
|
|
164
|
-
→
|
|
165
|
-
→
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
170
|
+
WebSocket 原始消息(envelope + payload)
|
|
171
|
+
→ feedSocketMessage(rawMessage) // 无需手动解析
|
|
172
|
+
→ SocketDispatcher.dispatch
|
|
173
|
+
├─ 识别传输层心跳 { type: 'ping' | 'pong' },不进业务流程
|
|
174
|
+
├─ 校验信封(envelope.id / type / payload 缺失则丢弃)
|
|
175
|
+
├─ 按 envelope.id 去重(重复丢弃 + warn 日志)
|
|
176
|
+
└─ 按 envelope.type 分发
|
|
177
|
+
├─ device_init → 全量初始化设备状态 + 初始资源实体
|
|
178
|
+
├─ device_command → 增量驱动单设备状态(灯/进度/出入口/position 等)
|
|
179
|
+
├─ resource_command→ 资源池生命周期:add / batch_add / transfer / remove / batch_remove
|
|
180
|
+
├─ alarm_push → 设备告警高亮(resolved 解除)
|
|
181
|
+
├─ runtime_status_push → 无模型,透传 onRaw 供全局 UI 处理
|
|
182
|
+
└─ scene_reset → 画布全部模型重建 + 资源清空 + 告警解除
|
|
183
|
+
→ AppController / ResourcePool → ModelAbs.handleOperation → 子组件动画
|
|
184
|
+
→ 处理完毕产出回执 command_ack(0 / 400 / 404 / 409 / 500),经 onAck 回调交给宿主业务;是否经 WebSocket 回推后端由业务按需决定(可选)
|
|
169
185
|
```
|
|
170
186
|
|
|
171
|
-
###
|
|
172
|
-
|
|
173
|
-
参考 `src/playground/mockWebSocketData.ts`,推荐外层结构:
|
|
187
|
+
### 消息格式约定(envelope + payload)
|
|
174
188
|
|
|
175
189
|
```typescript
|
|
176
|
-
interface
|
|
177
|
-
|
|
178
|
-
type:
|
|
179
|
-
|
|
190
|
+
interface MessageEnvelope {
|
|
191
|
+
id: string // 唯一 ID(雪花算法 Long 型字符串),画布据此去重
|
|
192
|
+
type: EnvelopeType // device_init | device_command | resource_command
|
|
193
|
+
// | alarm_push | runtime_status_push | scene_reset | command_ack
|
|
194
|
+
timestamp: string // ISO 8601
|
|
195
|
+
case_id?: string
|
|
180
196
|
}
|
|
181
|
-
```
|
|
182
|
-
|
|
183
|
-
**入口校验模板:**
|
|
184
197
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
console.warn('无效的 WebSocket 消息格式', rawMessage)
|
|
189
|
-
return
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
const { type, payload } = rawMessage
|
|
193
|
-
|
|
194
|
-
if (type === 'update') {
|
|
195
|
-
switch (payload.type) {
|
|
196
|
-
case 'Resource':
|
|
197
|
-
/* 资源增删 */ break
|
|
198
|
-
case 'MachineTool':
|
|
199
|
-
/* 机床状态 */ break
|
|
200
|
-
case 'ResourceStore':
|
|
201
|
-
/* 资源库/随行托 */ break
|
|
202
|
-
case 'SingleJointRobot':
|
|
203
|
-
/* 单关节机器人 */ break
|
|
204
|
-
case 'DoubleJointRobot':
|
|
205
|
-
/* 双关节机器人 */ break
|
|
206
|
-
case 'Rgv':
|
|
207
|
-
/* RGV */ break
|
|
208
|
-
case 'LoadingStation':
|
|
209
|
-
/* 装载站 */ break
|
|
210
|
-
case 'MarkingMachine':
|
|
211
|
-
/* 打标机 */ break
|
|
212
|
-
case 'CleaningMachine':
|
|
213
|
-
/* 清洗机 */ break
|
|
214
|
-
case 'Rail':
|
|
215
|
-
/* 轨道拖链 */ break
|
|
216
|
-
}
|
|
217
|
-
}
|
|
198
|
+
interface SocketMessage<T> {
|
|
199
|
+
envelope: MessageEnvelope
|
|
200
|
+
payload: T
|
|
218
201
|
}
|
|
219
202
|
```
|
|
220
203
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
`operating()` 通过三级匹配定位画布上的设备:`className → id → 工位号`:
|
|
224
|
-
|
|
225
|
-
- `id` 作为前后端对于设备的唯一识别字段
|
|
226
|
-
- `rawData` 为后端接口返回的原始数据 payload,其中包含 id 属性,如果设备注册了交互事件,则该对象会存在于回调函数的返回值中,供"点击设备后弹窗展示设备详情"等业务需求使用
|
|
227
|
-
- `className` 使用 `EnumModel` 枚举值,例如 `EnumModel.MACHINE_TOOL`
|
|
228
|
-
- 资源增删时,`addType` / `removeType` 为目标设备的 `className`,`addId` / `removeId` 为目标设备 `id`
|
|
229
|
-
- **工位号匹配(三级)**:当同一 `id` 的设备(如 `ResourceStore`)在画布上被拆分为多个 UI(不同工位号范围)时,`businessMap` 以数组存储同 `className + id` 的多个模型,再通过 `resource.index`(工位号)匹配到具体 UI。模型通过 `containsStationIndex` 方法判断是否包含该工位号。仅有一个模型时直接使用,无需工位号匹配
|
|
230
|
-
|
|
231
|
-
匹配失败时控制台输出:`模型操作失败: 模型未匹配到`。
|
|
232
|
-
|
|
233
|
-
### 各业务场景映射示例
|
|
234
|
-
|
|
235
|
-
#### 1. 资源增删(`payload.type === 'Resource'`)
|
|
236
|
-
|
|
237
|
-
Resource 不在编辑器中进行维护,是根据业务数据动态添加或删除的一类 UI 图形,如“托盘”、“刀具”。
|
|
238
|
-
同一消息可同时包含新增与移除操作。
|
|
204
|
+
**入口模板(顺带订阅回执,可选):**
|
|
239
205
|
|
|
240
206
|
```typescript
|
|
241
|
-
//
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
className: payload.addType, // 如 EnumModel.MACHINE_TOOL
|
|
245
|
-
id: payload.addId,
|
|
246
|
-
rawData: payload,
|
|
247
|
-
resource: {
|
|
248
|
-
handleType: 'add',
|
|
249
|
-
resourceType: payload.resourceType, // EnumResourceType.PALLET | TOOL
|
|
250
|
-
name: payload.name,
|
|
251
|
-
color: payload.status === 3 ? 'green' : '#424242',
|
|
252
|
-
index: payload.addSiteNumber, // 工位槽索引
|
|
253
|
-
rawData: payload
|
|
254
|
-
}
|
|
255
|
-
})
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
// 从设备工位移除资源
|
|
259
|
-
if (payload.removeType && payload.removeId) {
|
|
260
|
-
operating({
|
|
261
|
-
className: payload.removeType,
|
|
262
|
-
id: payload.removeId,
|
|
263
|
-
rawData: payload,
|
|
264
|
-
resource: {
|
|
265
|
-
handleType: 'remove',
|
|
266
|
-
resourceType: payload.resourceType,
|
|
267
|
-
index: payload.removeSiteNumber,
|
|
268
|
-
rawData: payload
|
|
269
|
-
}
|
|
270
|
-
})
|
|
207
|
+
// WebSocket onmessage → 直接透传,无需解析
|
|
208
|
+
const onSocketMessage = (rawMessage: any) => {
|
|
209
|
+
lingshuSmartPreviewRef.value?.feedSocketMessage(rawMessage)
|
|
271
210
|
}
|
|
272
|
-
```
|
|
273
211
|
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
className: EnumModel.MACHINE_TOOL,
|
|
279
|
-
id: payload.id,
|
|
280
|
-
rawData: payload,
|
|
281
|
-
light: {
|
|
282
|
-
color: payload.status === 2 ? 'green' : '#424242',
|
|
283
|
-
blinking: payload.status !== 2,
|
|
284
|
-
rotating: payload.status === 2
|
|
285
|
-
},
|
|
286
|
-
progress: {
|
|
287
|
-
percent: payload.percent, // 0 ~ 1
|
|
288
|
-
textContent: payload.status === 2 ? '运行中' : '待机中'
|
|
289
|
-
},
|
|
290
|
-
entrance: {
|
|
291
|
-
isIn: payload.isIn,
|
|
292
|
-
isOut: payload.isOut
|
|
293
|
-
},
|
|
294
|
-
stationRotatable: payload.stationRotatable // false 横向 / true 纵向
|
|
212
|
+
// 订阅回执 command_ack 与透传消息(可选,用于联调与全局 UI)
|
|
213
|
+
lingshuSmartPreviewRef.value?.setSocketHandlers({
|
|
214
|
+
onAck: (ack) => console.log('[command_ack]', ack?.payload),
|
|
215
|
+
onRaw: (raw) => console.log('[透传]', raw?.envelope?.type)
|
|
295
216
|
})
|
|
296
217
|
```
|
|
297
218
|
|
|
298
|
-
|
|
219
|
+
### 核心语义
|
|
299
220
|
|
|
300
|
-
|
|
301
|
-
operating({
|
|
302
|
-
className: EnumModel.RESOURCE_STORE,
|
|
303
|
-
id: payload.id,
|
|
304
|
-
rawData: payload,
|
|
305
|
-
light: {
|
|
306
|
-
color: payload.status === 2 ? 'green' : '#424242',
|
|
307
|
-
blinking: payload.status === 2
|
|
308
|
-
},
|
|
309
|
-
x: transPercentToPixel(payload.x, 2, 'width') // 沿轨道移动时需转换
|
|
310
|
-
})
|
|
311
|
-
```
|
|
221
|
+
- **字段缺失即跳过(防抖动)**:`device_command` / `resource_command` 中未携带的字段保持当前状态;携带的字段(哪怕为 0 / false / 空串)必须按值设置。因此高频位置消息可只传变化字段,如仅 `position.x`
|
|
312
222
|
|
|
313
|
-
|
|
223
|
+
- **`device_init`** **为全量覆盖**:先复位该设备(缺失字段重置默认值、工位资源清空),再应用显式字段
|
|
314
224
|
|
|
315
|
-
|
|
316
|
-
operating({
|
|
317
|
-
className: EnumModel.SINGLE_JOINT_ROBOT,
|
|
318
|
-
id: payload.id,
|
|
319
|
-
rawData: payload,
|
|
320
|
-
light: {
|
|
321
|
-
color: payload.status === 2 ? 'green' : '#424242',
|
|
322
|
-
blinking: payload.status === 2
|
|
323
|
-
},
|
|
324
|
-
x: transPercentToPixel(payload.x, 2, 'width'),
|
|
325
|
-
j1: payload.j1, // 一级关节角度(度)
|
|
326
|
-
currentFixture: payload.currentFixture // EnumResourceType.PALLET | TOOL
|
|
327
|
-
})
|
|
328
|
-
```
|
|
225
|
+
- **资源为独立实体**:由画布对象资源池管理,按 `resource_id` 全局索引,工位退化为纯位置槽(三级坐标:设备 → 层 → 工位)
|
|
329
226
|
|
|
330
|
-
|
|
227
|
+
- **已满校验(静态强校验)**:资源类型与目标工位声明类型不匹配回执 `409`;设备/工位未找到回执 `404`;参数错误回执 `400`;成功回执 `0`;异常回执 `500`
|
|
331
228
|
|
|
332
|
-
|
|
333
|
-
operating({
|
|
334
|
-
className: EnumModel.DOUBLE_JOINT_ROBOT,
|
|
335
|
-
id: payload.id,
|
|
336
|
-
rawData: payload,
|
|
337
|
-
light: {
|
|
338
|
-
color: payload.status === 0 ? '#f30' : '#424242'
|
|
339
|
-
},
|
|
340
|
-
currentFixture: payload.currentFixture,
|
|
341
|
-
x: transPercentToPixel(payload.x, 2, 'width'),
|
|
342
|
-
j1: payload.j1,
|
|
343
|
-
j2: payload.j2
|
|
344
|
-
})
|
|
345
|
-
```
|
|
229
|
+
- **字段全层级 snake_case**:`device_type` / `device_id` / `device_station` / `current_fixture` 等
|
|
346
230
|
|
|
347
|
-
|
|
231
|
+
### 设备类型枚举(device_type)
|
|
348
232
|
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
233
|
+
| 枚举值 | 说明 |
|
|
234
|
+
| --------------------------- | ------------ |
|
|
235
|
+
| `machine_tool` | 机床 |
|
|
236
|
+
| `loading_station` | 装载站 |
|
|
237
|
+
| `shuttle_station` | 接驳站 |
|
|
238
|
+
| `material_track` | 料道 |
|
|
239
|
+
| `marking_machine` | 打标机 |
|
|
240
|
+
| `cleaning_machine` | 清洗机 |
|
|
241
|
+
| `single_joint_robot` | 单关节机器人 |
|
|
242
|
+
| `double_joint_robot` | 双关节机器人 |
|
|
243
|
+
| `rgv` | RGV |
|
|
244
|
+
| `resource_store` | 资源库 |
|
|
245
|
+
| `pallet` | 托盘 |
|
|
246
|
+
| `resource_operation_window` | 资源操作窗口 |
|
|
360
247
|
|
|
361
|
-
|
|
248
|
+
### 资源类型枚举(resource_type)
|
|
362
249
|
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
light: { color: payload.lightColor, blinking: payload.blinking },
|
|
369
|
-
progress: { percent: payload.percent, textContent: payload.textContent },
|
|
370
|
-
entrance: { isIn: payload.isIn, isOut: payload.isOut },
|
|
371
|
-
laserSiteNumberArray: payload.laserSiteNumberArray // { site, active }[]
|
|
372
|
-
})
|
|
373
|
-
```
|
|
250
|
+
| 值 | 资源 |
|
|
251
|
+
| ----------- | ---- |
|
|
252
|
+
| `workpiece` | 工件 |
|
|
253
|
+
| `tool` | 刀具 |
|
|
254
|
+
| `auxiliary` | 辅料 |
|
|
374
255
|
|
|
375
|
-
|
|
256
|
+
### 各消息示例
|
|
376
257
|
|
|
377
|
-
|
|
378
|
-
operating({
|
|
379
|
-
className: EnumModel.CLEANING_MACHINE,
|
|
380
|
-
id: payload.id,
|
|
381
|
-
rawData: payload,
|
|
382
|
-
light: { color: payload.lightColor, blinking: payload.blinking },
|
|
383
|
-
progress: { percent: payload.percent, textContent: payload.textContent },
|
|
384
|
-
entrance: { isIn: payload.isIn, isOut: payload.isOut }
|
|
385
|
-
})
|
|
386
|
-
```
|
|
258
|
+
#### 1. `device_command` — 驱动单设备状态(增量)
|
|
387
259
|
|
|
388
|
-
|
|
260
|
+
```json
|
|
261
|
+
{
|
|
262
|
+
"envelope": { "id": "msg_xxx", "type": "device_command", "timestamp": "2026-08-31T10:30:00.000Z" },
|
|
263
|
+
"payload": {
|
|
264
|
+
"device_type": "single_joint_robot",
|
|
265
|
+
"device_id": "RBT-001",
|
|
266
|
+
"light": { "color": "#66DC7A", "blinking": true },
|
|
267
|
+
"position": { "x": 0.6, "j1": 45 },
|
|
268
|
+
"current_fixture": "tool"
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
```
|
|
389
272
|
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
}
|
|
273
|
+
#### 2. `device_init` — 设备初始化(全量)
|
|
274
|
+
|
|
275
|
+
```json
|
|
276
|
+
{
|
|
277
|
+
"envelope": { "id": "msg_xxx", "type": "device_init", "timestamp": "2026-08-31T10:30:00.000Z" },
|
|
278
|
+
"payload": {
|
|
279
|
+
"devices": [
|
|
280
|
+
{
|
|
281
|
+
"device_type": "machine_tool",
|
|
282
|
+
"device_id": "CNC-001",
|
|
283
|
+
"light": { "color": "green", "blinking": false },
|
|
284
|
+
"resources": [{ "resource_type": "workpiece", "resource_id": "PRD-001", "name": "毛坯1", "device_station": 1 }]
|
|
285
|
+
}
|
|
286
|
+
]
|
|
287
|
+
}
|
|
288
|
+
}
|
|
401
289
|
```
|
|
402
290
|
|
|
403
|
-
####
|
|
291
|
+
#### 3. `resource_command` — 资源生命周期
|
|
292
|
+
|
|
293
|
+
```json
|
|
294
|
+
// add(进场)
|
|
295
|
+
{ "envelope": { "id": "m1", "type": "resource_command", "timestamp": "..." },
|
|
296
|
+
"payload": { "operation": "add", "resource_id": "PRD-003", "resource_type": "workpiece",
|
|
297
|
+
"name": "毛坯3", "color": "#F2C34F",
|
|
298
|
+
"to": { "device_type": "machine_tool", "device_id": "CNC-001", "device_station": 1 } } }
|
|
299
|
+
|
|
300
|
+
// transfer(流转)
|
|
301
|
+
{ "envelope": { "id": "m2", "type": "resource_command", "timestamp": "..." },
|
|
302
|
+
"payload": { "operation": "transfer", "resource_id": "PRD-001", "resource_type": "workpiece",
|
|
303
|
+
"from": { "device_type": "machine_tool", "device_id": "CNC-001", "device_station": 1 },
|
|
304
|
+
"to": { "device_type": "resource_store", "device_id": "RS-001", "device_station": 3 } } }
|
|
305
|
+
|
|
306
|
+
// remove(离场)
|
|
307
|
+
{ "envelope": { "id": "m3", "type": "resource_command", "timestamp": "..." },
|
|
308
|
+
"payload": { "operation": "remove", "resource_id": "PRD-001" } }
|
|
309
|
+
|
|
310
|
+
// batch_add(批量进场) / batch_remove(批量离场)
|
|
311
|
+
{ "envelope": { "id": "m4", "type": "resource_command", "timestamp": "..." },
|
|
312
|
+
"payload": { "operation": "batch_add", "resources": [ { "resource_id": "PRD-4", "resource_type": "workpiece",
|
|
313
|
+
"to": { "device_type": "machine_tool", "device_id": "CNC-001", "device_station": 2 } } ] } }
|
|
314
|
+
```
|
|
404
315
|
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
316
|
+
#### 4. `alarm_push` — 全局告警推送(设备高亮)
|
|
317
|
+
|
|
318
|
+
```json
|
|
319
|
+
{
|
|
320
|
+
"envelope": { "id": "m5", "type": "alarm_push", "timestamp": "..." },
|
|
321
|
+
"payload": {
|
|
322
|
+
"alarm_id": "ALM-001",
|
|
323
|
+
"device_type": "machine_tool",
|
|
324
|
+
"device_id": "CNC-001",
|
|
325
|
+
"device_name": "CNC-001",
|
|
326
|
+
"alarm_level": "critical",
|
|
327
|
+
"alarm_type": "threshold_exceeded",
|
|
328
|
+
"alarm_message": "主轴温度过高",
|
|
329
|
+
"current_value": 72.5,
|
|
330
|
+
"threshold_value": 70,
|
|
331
|
+
"status": "pending",
|
|
332
|
+
"create_time": 1776230000
|
|
333
|
+
}
|
|
334
|
+
}
|
|
413
335
|
```
|
|
414
336
|
|
|
415
|
-
|
|
337
|
+
- `status: 'pending'` 点亮设备告警高亮;同一 `alarm_id` 以 `status: 'resolved'` 重推 → 解除高亮
|
|
416
338
|
|
|
417
|
-
|
|
339
|
+
- 该消息同样透传给 `onRaw`,供业务弹窗等场景使用
|
|
418
340
|
|
|
419
|
-
|
|
420
|
-
import { transRuntimeToPixel } from '@jnrs/lingshu-smart'
|
|
341
|
+
#### 5. `scene_reset` — 场景复位
|
|
421
342
|
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
const pixelY = transRuntimeToPixel(payload.y, 1, 'height')
|
|
343
|
+
```json
|
|
344
|
+
{ "envelope": { "id": "m6", "type": "scene_reset", "timestamp": "..." }, "payload": {} }
|
|
425
345
|
```
|
|
426
346
|
|
|
427
|
-
###
|
|
347
|
+
### 坐标与数值约定
|
|
428
348
|
|
|
429
|
-
|
|
349
|
+
| 约定项 | 说明 |
|
|
350
|
+
| -------- | ----------------------------------------------------------------------------- |
|
|
351
|
+
| 序号体系 | 工位 `device_station` 与层 `device_layer` 均 **1-based** |
|
|
352
|
+
| 比例值 | 进度、货叉位移、设备位移 `position.x/y/z` 使用比例值 \[0,1],画布自动换算像素 |
|
|
353
|
+
| 角度值 | 关节/旋转角度 `position.j1/j2` 单位为 **度** |
|
|
354
|
+
| 时间格式 | `envelope.timestamp` 用 ISO 8601;payload 内业务时间戳用 Unix **秒** |
|
|
430
355
|
|
|
431
|
-
|
|
432
|
-
import { transPercentToPixel } from '@jnrs/lingshu-smart'
|
|
356
|
+
### 心跳与重连
|
|
433
357
|
|
|
434
|
-
|
|
435
|
-
const pixelX = transPercentToPixel(payload.x, 2, 'width')
|
|
436
|
-
const pixelY = transPercentToPixel(payload.y, 1, 'height')
|
|
437
|
-
```
|
|
358
|
+
- 前端定时发送 `{ "type": "ping" }`,后端回复 `{ "type": "pong" }`;心跳为传输层消息,**不包信封**,画布识别后直接忽略不进业务流程
|
|
438
359
|
|
|
439
|
-
|
|
360
|
+
- 断线后指数退避重连:初始 1s,每次翻倍,上限 30s
|
|
440
361
|
|
|
441
362
|
### WebSocket 接入示例
|
|
442
363
|
|
|
443
364
|
```typescript
|
|
444
365
|
onMounted(() => {
|
|
445
|
-
const ws = new WebSocket(
|
|
366
|
+
const ws = new WebSocket(`wss://your-server/ws?token=${token}`)
|
|
446
367
|
ws.onmessage = (event) => {
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
}
|
|
368
|
+
onSocketMessage(JSON.parse(event.data))
|
|
369
|
+
}
|
|
370
|
+
ws.onclose = () => {
|
|
371
|
+
/* 断线重连(建议指数退避) */
|
|
452
372
|
}
|
|
453
373
|
})
|
|
454
374
|
```
|
|
455
375
|
|
|
456
|
-
本地调试可复用 `mockWebSocketData.ts` 中的 `firstBatchMessages` / `secondBatchMessages`。
|
|
457
|
-
|
|
458
376
|
---
|
|
459
377
|
|
|
460
378
|
## 📊 ILayerOperating 类型参考
|
|
461
379
|
|
|
462
|
-
`ILayerOperating`
|
|
380
|
+
`ILayerOperating` 是设备层运转状态的**联合类型**,为画布 `operating()` 内部引擎的入参。所有子类型均继承基类 `IModelOperating`,并通过 `className` 字段区分具体设备。
|
|
381
|
+
|
|
382
|
+
> **业务接入说明:** 业务侧**不应直接构造** **`ILayerOperating`**。`SocketDispatcher` 已把标准协议消息(`envelope + payload`)转换为该内部类型并交由 `operating()` 驱动画布。下表仅用于理解内部驱动原理,或为 WsProxy 等内部组件的桥接逻辑提供类型参考。
|
|
463
383
|
|
|
464
384
|
### 基类 IModelOperating
|
|
465
385
|
|
|
@@ -491,42 +411,41 @@ type ILayerOperating =
|
|
|
491
411
|
|
|
492
412
|
### 子类型一览
|
|
493
413
|
|
|
494
|
-
| 类型 | className | 特有属性
|
|
495
|
-
| ---------------------------- | -------------------- |
|
|
496
|
-
| `IMachineToolOperating` | `MACHINE_TOOL` | `light?`, `progress?`, `entrance?`, `
|
|
497
|
-
| `ILoadingStationOperating` | `LOADING_STATION` | `light?`, `progress?`, `entrance
|
|
498
|
-
| `IShuttleStationOperating` | `SHUTTLE_STATION` | `light?`, `layerLightColor?`, `layerLightBlinking?`, `layerVisible?`
|
|
499
|
-
| `IMarkingMachineOperating` | `MARKING_MACHINE` | `light?`, `progress?`, `entrance?`, `laserSiteNumberArray?`
|
|
500
|
-
| `ICleaningMachineOperating` | `CLEANING_MACHINE` | `light?`, `progress?`, `entrance?`, `isWorking?`
|
|
501
|
-
| `ISingleJointRobotOperating` | `SINGLE_JOINT_ROBOT` | `light?`, `x?`, `y?`, `j1?`, `currentFixture?`, `resource?`
|
|
502
|
-
| `IDoubleJointRobotOperating` | `DOUBLE_JOINT_ROBOT` | `light?`, `x?`, `y?`, `j1?`, `j2?`, `currentFixture?`, `resource?`
|
|
503
|
-
| `IRGVOperating` | `RGV` | `light?`, `x?`, `j1?`, `forkXY?`, `forkZ?`, `resource?`
|
|
504
|
-
| `IRailOperating` | `RAIL` | `x?`, `botId?`
|
|
505
|
-
| `IResourceStoreOperating` | `RESOURCE_STORE` | `light?`, `x?`, `resource?`, `stationColor?`
|
|
506
|
-
| `IPalletOperating` | `PALLET` | `light?`, `x?`, `y?`, `resource?`, `stationColor?`
|
|
414
|
+
| 类型 | className | 特有属性 | 子组件能力 | 典型 payload.type |
|
|
415
|
+
| ---------------------------- | -------------------- | --------------------------------------------------------------------- | -------------------------------------------------------------- | ------------------ |
|
|
416
|
+
| `IMachineToolOperating` | `MACHINE_TOOL` | `light?`, `progress?`, `entrance?`, `station_rotation?`, `isWorking?` | 状态灯(含旋转)、进度条、出入口、工位槽、工作状态(玻璃呼吸) | `MachineTool` |
|
|
417
|
+
| `ILoadingStationOperating` | `LOADING_STATION` | `light?`, `progress?`, `entrance?` | 状态灯(无旋转)、进度条、出入口、工位槽 | `LoadingStation` |
|
|
418
|
+
| `IShuttleStationOperating` | `SHUTTLE_STATION` | `light?`, `layerLightColor?`, `layerLightBlinking?`, `layerVisible?` | 状态灯、多工位层状态灯/显隐 | `ShuttleStation` |
|
|
419
|
+
| `IMarkingMachineOperating` | `MARKING_MACHINE` | `light?`, `progress?`, `entrance?`, `laserSiteNumberArray?` | 状态灯、进度条、出入口、工位槽、激光工位(电报节奏闪动) | `MarkingMachine` |
|
|
420
|
+
| `ICleaningMachineOperating` | `CLEANING_MACHINE` | `light?`, `progress?`, `entrance?`, `isWorking?` | 状态灯、进度条、出入口、工位槽、工作状态(波浪流动) | `CleaningMachine` |
|
|
421
|
+
| `ISingleJointRobotOperating` | `SINGLE_JOINT_ROBOT` | `light?`, `x?`, `y?`, `j1?`, `currentFixture?`, `resource?` | 双状态灯、轨道位移、关节旋转、夹具切换、双工位槽 | `SingleJointRobot` |
|
|
422
|
+
| `IDoubleJointRobotOperating` | `DOUBLE_JOINT_ROBOT` | `light?`, `x?`, `y?`, `j1?`, `j2?`, `currentFixture?`, `resource?` | 双状态灯、轨道位移、双关节旋转、夹具切换、双工位槽 | `DoubleJointRobot` |
|
|
423
|
+
| `IRGVOperating` | `RGV` | `light?`, `x?`, `j1?`, `forkXY?`, `forkZ?`, `resource?` | 双状态灯、轨道位移、旋转、货叉 XY/Z、双工位槽 | `Rgv` |
|
|
424
|
+
| `IRailOperating` | `RAIL` | `x?`, `botId?` | 拖链动画(关联 botId 的设备移动时拖链跟随) | `Rail` |
|
|
425
|
+
| `IResourceStoreOperating` | `RESOURCE_STORE` | `light?`, `x?`, `resource?`, `stationColor?` | 状态灯、轨道位移、托盘/刀具工位槽、按工位序改背景色 | `ResourceStore` |
|
|
426
|
+
| `IPalletOperating` | `PALLET` | `light?`, `x?`, `y?`, `resource?`, `stationColor?` | 状态灯、X/Y 位移、托盘/刀具工位槽、按工位序改背景色 | `Pallet` |
|
|
507
427
|
|
|
508
428
|
### 子类型字段详情
|
|
509
429
|
|
|
510
430
|
#### IMachineToolOperating
|
|
511
431
|
|
|
512
|
-
| 属性 | 类型 | 说明
|
|
513
|
-
| ------------------ | -------------------- |
|
|
514
|
-
| `light` | `ILightOperating` | 支持 `rotating`(运行中旋转灯效)
|
|
515
|
-
| `progress` | `IProgressOperating` | 加工进度 0
|
|
516
|
-
| `entrance` | `IEntranceOperating` | 物料入口/出口动画
|
|
517
|
-
| `
|
|
432
|
+
| 属性 | 类型 | 说明 |
|
|
433
|
+
| ------------------ | -------------------- | ----------------------------------------------------------- |
|
|
434
|
+
| `light` | `ILightOperating` | 支持 `rotating`(运行中旋转灯效) |
|
|
435
|
+
| `progress` | `IProgressOperating` | 加工进度 0\~1 |
|
|
436
|
+
| `entrance` | `IEntranceOperating` | 物料入口/出口动画 |
|
|
437
|
+
| `station_rotation` | `number` | 工位旋转增量(度):叠加在机床配置的工位容器方向角之上 |
|
|
518
438
|
| `isWorking` | `boolean` | 工作状态:`true` 时工位容器玻璃背景呼吸闪动(默认 `false`) |
|
|
519
|
-
| `resource` | `IResourceOperating` | 托盘等资源增删
|
|
439
|
+
| `resource` | `IResourceOperating` | 托盘等资源增删 |
|
|
520
440
|
|
|
521
441
|
#### ILoadingStationOperating
|
|
522
442
|
|
|
523
|
-
| 属性
|
|
524
|
-
|
|
|
525
|
-
| `light`
|
|
526
|
-
| `progress`
|
|
527
|
-
| `entrance`
|
|
528
|
-
| `
|
|
529
|
-
| `resource` | `IResourceOperating` | 工位槽资源 |
|
|
443
|
+
| 属性 | 类型 | 说明 |
|
|
444
|
+
| ---------- | ----------------------------------- | -------------- |
|
|
445
|
+
| `light` | `Omit<ILightOperating, 'rotating'>` | 不支持旋转灯效 |
|
|
446
|
+
| `progress` | `IProgressOperating` | 装载进度 |
|
|
447
|
+
| `entrance` | `IEntranceOperating` | 出入口状态 |
|
|
448
|
+
| `resource` | `IResourceOperating` | 工位槽资源 |
|
|
530
449
|
|
|
531
450
|
#### IShuttleStationOperating
|
|
532
451
|
|
|
@@ -541,13 +460,13 @@ type ILayerOperating =
|
|
|
541
460
|
|
|
542
461
|
#### IMarkingMachineOperating
|
|
543
462
|
|
|
544
|
-
| 属性 | 类型 | 说明
|
|
545
|
-
| ---------------------- | ----------------------------------- |
|
|
546
|
-
| `light` | `Omit<ILightOperating, 'rotating'>` | 状态灯
|
|
547
|
-
| `progress` | `IProgressOperating` | 打标进度
|
|
548
|
-
| `entrance` | `IEntranceOperating` | 出入口
|
|
463
|
+
| 属性 | 类型 | 说明 |
|
|
464
|
+
| ---------------------- | ----------------------------------- | --------------------------------------------------------- |
|
|
465
|
+
| `light` | `Omit<ILightOperating, 'rotating'>` | 状态灯 |
|
|
466
|
+
| `progress` | `IProgressOperating` | 打标进度 |
|
|
467
|
+
| `entrance` | `IEntranceOperating` | 出入口 |
|
|
549
468
|
| `laserSiteNumberArray` | `ILaserSiteState[]` | 各激光工位打标状态(`active` 时点亮并按无序电报节奏闪动) |
|
|
550
|
-
| `resource` | `IResourceOperating` | 工位槽资源
|
|
469
|
+
| `resource` | `IResourceOperating` | 工位槽资源 |
|
|
551
470
|
|
|
552
471
|
**ILaserSiteState:**
|
|
553
472
|
|
|
@@ -558,13 +477,13 @@ type ILayerOperating =
|
|
|
558
477
|
|
|
559
478
|
#### ICleaningMachineOperating
|
|
560
479
|
|
|
561
|
-
| 属性
|
|
562
|
-
|
|
|
563
|
-
| `light`
|
|
564
|
-
| `progress`
|
|
565
|
-
| `entrance`
|
|
480
|
+
| 属性 | 类型 | 说明 |
|
|
481
|
+
| ----------- | ----------------------------------- | ------------------------------------------------------------- |
|
|
482
|
+
| `light` | `Omit<ILightOperating, 'rotating'>` | 状态灯 |
|
|
483
|
+
| `progress` | `IProgressOperating` | 清洗进度 |
|
|
484
|
+
| `entrance` | `IEntranceOperating` | 出入口 |
|
|
566
485
|
| `isWorking` | `boolean` | 工作状态:`true` 时工位容器内显示波浪流动效果(默认 `false`) |
|
|
567
|
-
| `resource`
|
|
486
|
+
| `resource` | `IResourceOperating` | 工位槽资源 |
|
|
568
487
|
|
|
569
488
|
#### ISingleJointRobotOperating
|
|
570
489
|
|
|
@@ -591,14 +510,14 @@ type ILayerOperating =
|
|
|
591
510
|
|
|
592
511
|
#### IRGVOperating
|
|
593
512
|
|
|
594
|
-
| 属性 | 类型 | 说明
|
|
595
|
-
| ---------- | -------------------- |
|
|
596
|
-
| `light` | `ILightOperating` | 双灯同步更新
|
|
597
|
-
| `x` | `number` |
|
|
598
|
-
| `j1` | `number` |
|
|
599
|
-
| `forkXY` | `number` | 货叉进出移动比例 [−1,1] |
|
|
600
|
-
| `forkZ` | `number` | 货叉升降比例 [0,1] |
|
|
601
|
-
| `resource` | `IResourceOperating` | 托盘/刀具工位槽
|
|
513
|
+
| 属性 | 类型 | 说明 |
|
|
514
|
+
| ---------- | -------------------- | -------------------------- |
|
|
515
|
+
| `light` | `ILightOperating` | 双灯同步更新 |
|
|
516
|
+
| `x` | `number` | 沿轨道位移(像素)\[0,1] |
|
|
517
|
+
| `j1` | `number` | 车体旋转角度(度)\[0,360] |
|
|
518
|
+
| `forkXY` | `number` | 货叉进出移动比例 \[−1,1] |
|
|
519
|
+
| `forkZ` | `number` | 货叉升降比例 \[0,1] |
|
|
520
|
+
| `resource` | `IResourceOperating` | 托盘/刀具工位槽 |
|
|
602
521
|
|
|
603
522
|
#### IRailOperating
|
|
604
523
|
|
|
@@ -630,12 +549,12 @@ type ILayerOperating =
|
|
|
630
549
|
|
|
631
550
|
被 `ILayerOperating` 各字段引用的子组件操作类型,定义于 `src/types/components/operating.types.ts`:
|
|
632
551
|
|
|
633
|
-
| 类型 | 用途 | 字段
|
|
634
|
-
| -------------------- | ---------- |
|
|
635
|
-
| `ILightOperating` | 状态灯 | `color`(必填), `blinking?`, `rotating?`
|
|
636
|
-
| `IProgressOperating` | 进度条 | `percent`(0
|
|
637
|
-
| `IEntranceOperating` | 出入口 | `isIn`, `isOut`
|
|
638
|
-
| `IResourceOperating` | 工位槽资源 | `
|
|
552
|
+
| 类型 | 用途 | 字段 |
|
|
553
|
+
| -------------------- | ---------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
554
|
+
| `ILightOperating` | 状态灯 | `color`(必填), `blinking?`, `rotating?` |
|
|
555
|
+
| `IProgressOperating` | 进度条 | `percent`(0\~1), `textContent` |
|
|
556
|
+
| `IEntranceOperating` | 出入口 | `isIn`, `isOut` |
|
|
557
|
+
| `IResourceOperating` | 工位槽资源 | `resource_type`, `resource_id?`, `name?`, `resource_color?`(显式颜色优先,未下发时按 `resource_state` 字典映射), `device_station`, `handle_type`(`'add'\|'remove'`), `raw_data` |
|
|
639
558
|
|
|
640
559
|
---
|
|
641
560
|
|
|
@@ -672,10 +591,11 @@ type ILayerOperating =
|
|
|
672
591
|
|
|
673
592
|
**EnumResourceType — 资源类型**
|
|
674
593
|
|
|
675
|
-
| 枚举值
|
|
676
|
-
|
|
|
677
|
-
| `
|
|
678
|
-
| `TOOL`
|
|
594
|
+
| 枚举值 | 说明 |
|
|
595
|
+
| ----------- | ---- |
|
|
596
|
+
| `WORKPIECE` | 工件 |
|
|
597
|
+
| `TOOL` | 刀具 |
|
|
598
|
+
| `AUXILIARY` | 辅料 |
|
|
679
599
|
|
|
680
600
|
#### 核心类型
|
|
681
601
|
|
|
@@ -695,7 +615,7 @@ type ILayerOperating =
|
|
|
695
615
|
|
|
696
616
|
| 属性 | 类型 | 必填 | 说明 |
|
|
697
617
|
| ---------- | ---------- | ---- | -------- |
|
|
698
|
-
| `
|
|
618
|
+
| `id` | `string` | ✓ | 唯一标识 |
|
|
699
619
|
| `isActive` | `boolean` | ✓ | 是否激活 |
|
|
700
620
|
| `title` | `string` | ✓ | 标题 |
|
|
701
621
|
| `scene` | `IScene` | ✓ | 场景 |
|
|
@@ -716,13 +636,14 @@ type ILayerOperating =
|
|
|
716
636
|
|
|
717
637
|
## 📝 注意事项
|
|
718
638
|
|
|
719
|
-
1. **编辑器 vs 预览:** 编辑模式配置 `EditorActions`;预览模式配置 `PreviewActions` + `
|
|
720
|
-
2.
|
|
721
|
-
3. **坐标系:**
|
|
722
|
-
4. **资源消息:** `
|
|
723
|
-
5.
|
|
724
|
-
6.
|
|
725
|
-
7.
|
|
639
|
+
1. **编辑器 vs 预览:** 编辑模式配置 `EditorActions`;预览模式配置 `PreviewActions`,并通过 `feedSocketMessage` 下发标准信封协议消息(`envelope + payload`)驱动画布。画布内置 `SocketDispatcher` 自动分发,业务侧无需自行解析 `payload.type`。
|
|
640
|
+
2. **设备 ID 匹配:** 协议消息中 `device_id` 必须与方案图层 `rawData.id` 一致,否则回执 `404` 且不驱动(在前端层面该值即为编辑器组件中业务数据配置中必填项 id 的值,需根据后端预先提供的设备 id 在编辑器中进行配置)
|
|
641
|
+
3. **坐标系:** 协议 `position.x/y/z` 为比例值 \[0,1],画布按场景自动换算为像素;`j1 / j2 / fork_xy / fork_z` 为角度/比例原值直接透传
|
|
642
|
+
4. **资源消息:** `resource_command` 不再映射到独立图层,资源为独立实体,由画布对象资源池按 `resource_id` 管理,操作工位的纯位置槽
|
|
643
|
+
5. **扩展工位(合并编号):** 托盘图层配置 `extendHostId`(宿主设备业务 ID)后,托盘工位并入宿主设备的合并编号空间——资源寻址宿主越界时按「宿主工位总数 + 前序扩展托盘工位总数」顺延落到扩展托盘(404/409 以实际承接工位判定);点击扩展托盘工位时 `onTap` 的 `layer` 为宿主设备、`data.index` 为合并编号(附 `extensionOf` / `localIndex` 溯源字段)。详见 socket-operating-api.md 3.6
|
|
644
|
+
6. **引擎生命周期:** 组件卸载时自动 `destroyContainer`;手动集成需自行销毁
|
|
645
|
+
7. **类型安全:** 所有 API 均导出 TypeScript 类型,推荐 `import type { EnvelopeType, SocketMessage, DeviceCommandPayload, ResourceCommandPayload, EnumDeviceType, EnumResourceType } from '@jnrs/lingshu-smart'`
|
|
646
|
+
8. **调试模式:** 500ms 内点击主容器(`#lingshu-smart`)10 次可打开调试配置面板,支持运行时配置日志等级、阴影/动画开关、FPS 限制、交互禁用、性能统计。配置通过 `localStorage` 持久化,详见 README「调试功能」章节。
|
|
726
647
|
|
|
727
648
|
---
|
|
728
649
|
|
|
@@ -730,18 +651,19 @@ type ILayerOperating =
|
|
|
730
651
|
|
|
731
652
|
### 架构
|
|
732
653
|
|
|
733
|
-
| 模块 | 职责
|
|
734
|
-
| ------------------------------------------ |
|
|
735
|
-
| `utils/logger.ts` | 统一日志工具,支持 tag 着色、LogLevel 过滤、`operating` 追踪级别
|
|
736
|
-
| `utils/debug-flags.ts` | 模块级动画标志位,供 UI 组件直接导入控制循环动画
|
|
737
|
-
| `utils/performance-stats.ts` | 性能统计工具,记录运行时长和 WebSocket 数据接收量
|
|
738
|
-
| `stores/useDebugStore.ts` | 全局设置 Pinia store(facade):响应式读写 `useDataStore.globalSetting` / `
|
|
739
|
-
| `composables/useDebugEasterEgg.ts` | 调试触发检测:500ms 内 10 次点击打开面板
|
|
740
|
-
| `components/debug/GlobalSetting.vue` | 全局设置对话框 UI(原 DebugPanel,含语言/日志等级/FPS/阴影/动画等开关)
|
|
741
|
-
| `components/debug/ScriptRunner/` | Socket
|
|
742
|
-
| `components/debug/WsProxy/` | WebSocket 代理器:声明式字段映射将后端 socket
|
|
743
|
-
| `components/debug/WsProxy/WsProxyPage.vue` | `WsProxy` 独立路由页面组件:以独立 URL
|
|
744
|
-
| `controller/
|
|
654
|
+
| 模块 | 职责 |
|
|
655
|
+
| ------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | --- |
|
|
656
|
+
| `utils/logger.ts` | 统一日志工具,支持 tag 着色、LogLevel 过滤、`operating` 追踪级别 |
|
|
657
|
+
| `utils/debug-flags.ts` | 模块级动画标志位,供 UI 组件直接导入控制循环动画 |
|
|
658
|
+
| `utils/performance-stats.ts` | 性能统计工具,记录运行时长和 WebSocket 数据接收量 |
|
|
659
|
+
| `stores/useDebugStore.ts` | 全局设置 Pinia store(facade):响应式读写 `useDataStore.globalSetting` / `scriptsConfig`(脚本按方案分组;其 `scriptSteps` / `scriptLooping` 基于「激活方案」,组件持有明确方案上下文时建议直接用 `useDataStore` 按 caseId 读写),持久化统一在 `@jnrs/lingshu-smart` | |
|
|
660
|
+
| `composables/useDebugEasterEgg.ts` | 调试触发检测:500ms 内 10 次点击打开面板 |
|
|
661
|
+
| `components/debug/GlobalSetting.vue` | 全局设置对话框 UI(原 DebugPanel,含语言/日志等级/FPS/阴影/动画等开关) |
|
|
662
|
+
| `components/debug/ScriptRunner/` | Socket 脚本模拟器:脚本步骤编排、断点调试,生成标准信封协议消息(`envelope + payload`,含 device_init / device_command / resource_command / alarm_push / scene_reset 等模板);设备下拉支持自定义/全部复位(resetAll)/资源操作,产物经 `feedSocketMessage` 直接驱动画布;脚本**按方案分组**存储(`ScriptCaseData[]`:caseId / caseName / steps / looping,与 `wsProxyConfig` 对称,经 `currentCase` prop 确定归属);支持**一键烧录**(二次确认后写入 `public/lingshu-smart/mockSocket.json`,开发服务器接口 `POST /lingshu-smart/__burn-mock-socket`,宿主无需改动)与**导入/导出** JSON 文件;弹窗打开时按「文件优先 + 未烧录改动确认」懒加载当前方案(见 `scriptFile.ts`) |
|
|
663
|
+
| `components/debug/WsProxy/` | WebSocket 代理器:声明式字段映射将后端 socket 结构桥接为标准信封协议消息(`envelope + payload`,snake_case)并经 `feedSocketMessage` 驱动画布,标准信封消息直接透传;支持连接/断开/重连(指数退避)、心跳 ping/pong、日志、配置导出/导入、场景像素换算(percentToPixel 百分比/ runtimeToPixel 运行时坐标)、standalone 独立标签页模式 / remote 远程接收模式(BroadcastChannel 跨标签页流转数据);操作规则含 single/resource/global/resetAll(ResetAll 接收源类型值 `resetAll` 后生成全部复位) |
|
|
664
|
+
| `components/debug/WsProxy/WsProxyPage.vue` | `WsProxy` 独立路由页面组件:以独立 URL/标签页运行代理器配置,将标准协议消息/状态广播给预览页驱动图形运转 |
|
|
665
|
+
| `controller/SocketDispatcher.ts` | Socket 协议分发器:解析 `envelope + payload`,识别心跳、按 `envelope.id` 去重、按类型分发(device_init / device_command / resource_command / alarm_push / runtime_status_push / scene_reset),对象资源池管理资源生命周期,处理完毕回执 `command_ack`(0/400/404/409/500) |
|
|
666
|
+
| `controller/AppController.ts` | 画布控制器:提供 `toggleShadows` / `toggleAnimations` / `setFps` / `resetAllModels` / `setAlarmHighlight` 等方法 |
|
|
745
667
|
|
|
746
668
|
### 日志系统
|
|
747
669
|
|
|
@@ -755,9 +677,13 @@ logger.warn('警告') // [WARN:MyModule] 警告(浏览器默认样式)
|
|
|
755
677
|
```
|
|
756
678
|
|
|
757
679
|
- `log`/`info`/`operating` 级别:使用彩色单徽章(`LEVEL:Tag` 合并显示)
|
|
680
|
+
|
|
758
681
|
- `warn`/`error` 级别:纯文本前缀 `[LEVEL:Tag]`
|
|
682
|
+
|
|
759
683
|
- `operating` 级别:用于追踪 UI 模型操作指令,在 `ModelAbs.handleOperation` 和 `ComponentAbs.handleOperation` 中自动埋点
|
|
684
|
+
|
|
760
685
|
- 日志等级优先级:`none` < `error` < `warn` < `info` < `log` < `operating`
|
|
686
|
+
|
|
761
687
|
- 默认:开发环境 `log`(全开),生产环境 `warn`
|
|
762
688
|
|
|
763
689
|
### 调试配置项
|
|
@@ -772,18 +698,25 @@ logger.warn('警告') // [WARN:MyModule] 警告(浏览器默认样式)
|
|
|
772
698
|
| 性能统计 | — | `trackWebSocketMessage()` |
|
|
773
699
|
|
|
774
700
|
- **动画开关**:通过标志位控制循环动画(灯闪烁、入口通道动画等),关闭后保留静态状态(如灯颜色保持不变),不影响一次性动画(如机器人摆臂、进度条动画)
|
|
701
|
+
|
|
775
702
|
- **阴影开关**:递归遍历所有元素切换 `shadow`/`innerShadow` 的 `visible` 属性,同时处理 `ShadowCanvas` 元素(`createShadow` 工厂创建的 Canvas 阴影)的 `visible` 属性
|
|
776
703
|
|
|
777
704
|
### localStorage 存储
|
|
778
705
|
|
|
779
706
|
统一 key 为 `lingshu-smart`,debug 配置存储在 `debug` 子对象中,与 `useDataStore` 的持久化数据共享同一 key。
|
|
780
707
|
|
|
708
|
+
脚本数据存于该 key 的 `scriptsConfig` 字段(**按方案分组**的 `ScriptCaseData[]`,与 `wsProxyConfig` 平级);旧版全局字段 `scriptRunner` / `scriptLooping` 在读取时自动归入当前激活方案(无 `isActive` 标记时兜底取第一个方案),随后删除。
|
|
709
|
+
|
|
710
|
+
另有独立小 key `@jnrs/lingshu-smart/mock-socket-baseline`:记录**各方案**脚本与 `mockSocket.json` 的「已同步基线」hash,值为 `{ [caseId]: hash }`(`scriptFile.ts` 的 `readBaselineHash` / `writeBaselineHash`),用于检出该方案未烧录的脚本改动——仅在文件加载 / 烧录 / 导入成功后更新,用户手动编辑不更新。
|
|
711
|
+
|
|
781
712
|
---
|
|
782
713
|
|
|
783
714
|
## 🛠 技术栈
|
|
784
715
|
|
|
785
716
|
- Vue 3 + TypeScript + Pinia
|
|
717
|
+
|
|
786
718
|
- LeaferJS 2D 图形引擎
|
|
719
|
+
|
|
787
720
|
- Element Plus UI
|
|
788
721
|
|
|
789
722
|
---
|