agentworkshop 0.7.21 → 0.7.23
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/.output/nitro.json +1 -1
- package/.output/public/_nuxt/BJbhwS5o.js +1 -0
- package/.output/public/_nuxt/Bo8t5fkJ.js +1 -0
- package/.output/public/_nuxt/{DBXhDLgl.js → BuiGDfMB.js} +1 -1
- package/.output/public/_nuxt/{QT54MQV9.js → C53drffV.js} +1 -1
- package/.output/public/_nuxt/{BA3XzwiD.js → CEHmSufQ.js} +2 -2
- package/.output/public/_nuxt/Dc32T41C.js +1 -0
- package/.output/public/_nuxt/DtzZqC0z.js +1 -0
- package/.output/public/_nuxt/{BOJaHO5V.js → Ko9mMsJj.js} +3 -3
- package/.output/public/_nuxt/{TownView.BHLKAjEu.css → TownView.zu2rU5nS.css} +1 -1
- package/.output/public/_nuxt/_id_.C-YXjn8Q.css +1 -0
- package/.output/public/_nuxt/_id_.MbzdFygI.css +1 -0
- package/.output/public/_nuxt/_wsId_.BqWBlY7X.css +1 -0
- package/.output/public/_nuxt/builds/latest.json +1 -1
- package/.output/public/_nuxt/builds/meta/1adc96e0-8bbd-4a79-ac7e-b6952556fef8.json +1 -0
- package/.output/public/_nuxt/daq.3OKj58HH.css +1 -0
- package/.output/public/_nuxt/dbi5bHkY.js +2 -0
- package/.output/public/_nuxt/dcw.rXH2fc2Z.css +1 -0
- package/.output/public/_nuxt/entry.OR_psZHp.css +1 -0
- package/.output/public/_nuxt/{DsOkkMX3.js → jyQyulh-.js} +1 -1
- package/.output/public/_nuxt/logs.DMOM-iHe.css +1 -0
- package/.output/public/_nuxt/{B-E8fXAO.js → n3vMFfrv.js} +1 -1
- package/.output/server/chunks/_/nitro.mjs +842 -717
- package/.output/server/chunks/_/nitro.mjs.map +1 -1
- package/.output/server/chunks/build/{TownView-ql2eHZOg.mjs → TownView-DxeQe1Bl.mjs} +144 -143
- package/.output/server/chunks/build/{TownView-ql2eHZOg.mjs.map → TownView-DxeQe1Bl.mjs.map} +1 -1
- package/.output/server/chunks/build/{_id_-CwR5oLu9.mjs → _id_-DQDqj-cZ.mjs} +33 -35
- package/.output/server/chunks/build/_id_-DQDqj-cZ.mjs.map +1 -0
- package/.output/server/chunks/build/{_id_-Bs-euJPu.mjs → _id_-DY6v6sqB.mjs} +138 -137
- package/.output/server/chunks/build/_id_-DY6v6sqB.mjs.map +1 -0
- package/.output/server/chunks/build/{_wsId_-B3aB9bIp.mjs → _wsId_-C2O5zZsI.mjs} +6 -6
- package/.output/server/chunks/build/{_wsId_-B3aB9bIp.mjs.map → _wsId_-C2O5zZsI.mjs.map} +1 -1
- package/.output/server/chunks/build/{daq-epWL_sC7.mjs → daq-B-3xCp_2.mjs} +146 -118
- package/.output/server/chunks/build/daq-B-3xCp_2.mjs.map +1 -0
- package/.output/server/chunks/build/{dcw-CBPaFj2f.mjs → dcw-BL2dhuz7.mjs} +43 -43
- package/.output/server/chunks/build/{dcw-CBPaFj2f.mjs.map → dcw-BL2dhuz7.mjs.map} +1 -1
- package/.output/server/chunks/build/{logs-Bz1p6x_5.mjs → logs-Dto_umFY.mjs} +44 -38
- package/.output/server/chunks/build/logs-Dto_umFY.mjs.map +1 -0
- package/.output/server/chunks/build/{pages-Dq7_H4FK.mjs → pages-D9tOoJsf.mjs} +2 -2
- package/.output/server/chunks/build/{pages-Dq7_H4FK.mjs.map → pages-D9tOoJsf.mjs.map} +1 -1
- package/.output/server/chunks/build/{town-CstiKxf_.mjs → town-Dsh0jlwZ.mjs} +3 -3
- package/.output/server/chunks/build/{town-CstiKxf_.mjs.map → town-Dsh0jlwZ.mjs.map} +1 -1
- package/.output/server/chunks/build/{useDaqStream-BOOZ-p-l.mjs → useDaqStream-DkRL3tEW.mjs} +9 -1
- package/.output/server/chunks/build/{useDaqStream-BOOZ-p-l.mjs.map → useDaqStream-DkRL3tEW.mjs.map} +1 -1
- package/.output/server/chunks/routes/api/workshop/daq/_id/bind.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/daq/_id/bind.post.mjs.map +1 -1
- package/.output/server/chunks/routes/api/workshop/daq/_id/bindings.put.mjs +34 -0
- package/.output/server/chunks/routes/api/workshop/daq/_id/bindings.put.mjs.map +1 -0
- package/.output/server/chunks/routes/api/workshop/dcw/_id/bind.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/_id/bind.post.mjs.map +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/_id/bindings.put.mjs +33 -0
- package/.output/server/chunks/routes/api/workshop/dcw/_id/bindings.put.mjs.map +1 -0
- package/.output/server/chunks/routes/renderer.mjs +1 -1
- package/.output/server/chunks/virtual/entry.mjs +16 -8
- package/.output/server/chunks/virtual/precomputed.mjs +1 -1
- package/.output/server/package.json +1 -1
- package/app/assets/css/main.css +306 -161
- package/app/components/workshop/blocks/ClusterArtifact.vue +0 -16
- package/app/components/workshop/town/TownView.vue +7082 -7076
- package/app/composables/workshop/useDaqStream.ts +10 -0
- package/app/pages/daq/[id].vue +919 -937
- package/app/pages/daq/index.vue +67 -43
- package/app/pages/dcw/[id].vue +6 -5
- package/app/pages/dcw/index.vue +1 -30
- package/app/pages/logs/index.vue +99 -87
- package/i18n/locales/en.ts +3 -0
- package/i18n/locales/zh-CN.ts +5 -0
- package/package.json +148 -148
- package/scripts/_dbg-device-bind-e2e.mjs +129 -0
- package/scripts/_dbg-logs-ui-e2e.py +125 -0
- package/server/api/workshop/daq/[id]/bind.post.ts +5 -2
- package/server/api/workshop/daq/[id]/bindings.put.ts +22 -0
- package/server/api/workshop/dcw/[id]/bind.post.ts +4 -2
- package/server/api/workshop/dcw/[id]/bindings.put.ts +21 -0
- package/server/data/daqs.json +405 -100
- package/server/data/dcw-lines.json +7 -0
- package/server/data/dcw-rollback.json +21 -10
- package/server/data/dcw-runs.json +50 -54
- package/server/data/dcw-writes.json +24 -0
- package/server/data/dcws.json +505 -216
- package/server/data/line-runs.json +18 -18
- package/server/services/workshop/agents/industrial-context.ts +231 -222
- package/server/services/workshop/daq/daq-controller.ts +76 -23
- package/server/services/workshop/daq/daq-node.ts +11 -1
- package/server/services/workshop/dcw/dcw-controller.ts +50 -2
- package/server/services/workshop/dcw/dcw-node.ts +10 -1
- package/shared/daq-protocol.ts +2 -0
- package/shared/dcw-protocol.ts +2 -0
- package/.output/public/_nuxt/B_C3GLsD.js +0 -1
- package/.output/public/_nuxt/Ba7YofCb.js +0 -2
- package/.output/public/_nuxt/C_2bTK0C.js +0 -1
- package/.output/public/_nuxt/CgT5wMfz.js +0 -1
- package/.output/public/_nuxt/D4CLp1hc.js +0 -1
- package/.output/public/_nuxt/_id_.DIxEKj88.css +0 -1
- package/.output/public/_nuxt/_id_.IbmpkhH4.css +0 -1
- package/.output/public/_nuxt/_wsId_.BFyCuUwS.css +0 -1
- package/.output/public/_nuxt/builds/meta/5d344836-03a5-4f47-a0a7-fd30192f5a8f.json +0 -1
- package/.output/public/_nuxt/daq.6IrhVVfy.css +0 -1
- package/.output/public/_nuxt/dcw.rpqpOYQ8.css +0 -1
- package/.output/public/_nuxt/entry.DRUzDwmX.css +0 -1
- package/.output/public/_nuxt/logs.BarkrvUp.css +0 -1
- package/.output/server/chunks/build/_id_-Bs-euJPu.mjs.map +0 -1
- package/.output/server/chunks/build/_id_-CwR5oLu9.mjs.map +0 -1
- package/.output/server/chunks/build/daq-epWL_sC7.mjs.map +0 -1
- package/.output/server/chunks/build/logs-Bz1p6x_5.mjs.map +0 -1
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
"productId": "pd-6348a595",
|
|
8
8
|
"productName": "0.8mm 光学膜",
|
|
9
9
|
"startedAt": "2026-09-03T11:18:01.213Z",
|
|
10
|
-
"taggedSamples":
|
|
10
|
+
"taggedSamples": 62397
|
|
11
11
|
},
|
|
12
12
|
{
|
|
13
13
|
"lineId": "ln-9a60a0cd",
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"productId": "pd-e712875d",
|
|
18
18
|
"productName": "闭环验证产品",
|
|
19
19
|
"startedAt": "2026-09-03T16:03:27.304Z",
|
|
20
|
-
"taggedSamples":
|
|
20
|
+
"taggedSamples": 1199
|
|
21
21
|
},
|
|
22
22
|
{
|
|
23
23
|
"lineId": "ln-b33a3105",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"productId": "pd-16eecb35",
|
|
28
28
|
"productName": "闭环验证产品",
|
|
29
29
|
"startedAt": "2026-09-03T16:39:22.962Z",
|
|
30
|
-
"taggedSamples":
|
|
30
|
+
"taggedSamples": 1199
|
|
31
31
|
},
|
|
32
32
|
{
|
|
33
33
|
"lineId": "ln-1aedc305",
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"productId": "pd-9c755781",
|
|
38
38
|
"productName": "闭环验证产品",
|
|
39
39
|
"startedAt": "2026-09-03T16:44:33.199Z",
|
|
40
|
-
"taggedSamples":
|
|
40
|
+
"taggedSamples": 1199
|
|
41
41
|
},
|
|
42
42
|
{
|
|
43
43
|
"lineId": "ln-758658dc",
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
"productId": "pd-835aed90",
|
|
48
48
|
"productName": "闭环验证产品",
|
|
49
49
|
"startedAt": "2026-09-03T17:12:52.205Z",
|
|
50
|
-
"taggedSamples":
|
|
50
|
+
"taggedSamples": 1199
|
|
51
51
|
},
|
|
52
52
|
{
|
|
53
53
|
"lineId": "ln-1b318320",
|
|
@@ -57,7 +57,7 @@
|
|
|
57
57
|
"productId": "pd-db432902",
|
|
58
58
|
"productName": "探针产品",
|
|
59
59
|
"startedAt": "2026-09-04T05:08:10.103Z",
|
|
60
|
-
"taggedSamples":
|
|
60
|
+
"taggedSamples": 23170
|
|
61
61
|
},
|
|
62
62
|
{
|
|
63
63
|
"lineId": "ln-6a34fbc7",
|
|
@@ -67,7 +67,7 @@
|
|
|
67
67
|
"productId": "pd-10cfc2b3",
|
|
68
68
|
"productName": "全协议产品-mtnynfph",
|
|
69
69
|
"startedAt": "2026-09-05T05:47:26.453Z",
|
|
70
|
-
"taggedSamples":
|
|
70
|
+
"taggedSamples": 82806
|
|
71
71
|
},
|
|
72
72
|
{
|
|
73
73
|
"lineId": "ln-419463e0",
|
|
@@ -77,7 +77,7 @@
|
|
|
77
77
|
"productId": "pd-b9b15b64",
|
|
78
78
|
"productName": "全协议产品-mtnzg090",
|
|
79
79
|
"startedAt": "2026-09-05T06:09:36.931Z",
|
|
80
|
-
"taggedSamples":
|
|
80
|
+
"taggedSamples": 82751
|
|
81
81
|
},
|
|
82
82
|
{
|
|
83
83
|
"lineId": "ln-3548f325",
|
|
@@ -87,7 +87,7 @@
|
|
|
87
87
|
"productId": "pd-fd822802",
|
|
88
88
|
"productName": "runp3-prod",
|
|
89
89
|
"startedAt": "2026-09-05T07:23:51.972Z",
|
|
90
|
-
"taggedSamples":
|
|
90
|
+
"taggedSamples": 9563
|
|
91
91
|
},
|
|
92
92
|
{
|
|
93
93
|
"lineId": "ln-c3a7ba01",
|
|
@@ -107,7 +107,7 @@
|
|
|
107
107
|
"productId": "pd-d547a376",
|
|
108
108
|
"productName": "pp-prod",
|
|
109
109
|
"startedAt": "2026-09-05T09:47:55.526Z",
|
|
110
|
-
"taggedSamples":
|
|
110
|
+
"taggedSamples": 10880
|
|
111
111
|
},
|
|
112
112
|
{
|
|
113
113
|
"lineId": "ln-1acc1a0c",
|
|
@@ -117,7 +117,7 @@
|
|
|
117
117
|
"productId": "pd-3adf854f",
|
|
118
118
|
"productName": "lx8-prod",
|
|
119
119
|
"startedAt": "2026-09-06T02:22:23.209+08:00",
|
|
120
|
-
"taggedSamples":
|
|
120
|
+
"taggedSamples": 10111
|
|
121
121
|
},
|
|
122
122
|
{
|
|
123
123
|
"lineId": "ln-917490c1",
|
|
@@ -127,7 +127,7 @@
|
|
|
127
127
|
"productId": "pd-75752beb",
|
|
128
128
|
"productName": "lx8-prod",
|
|
129
129
|
"startedAt": "2026-09-06T02:24:50.582+08:00",
|
|
130
|
-
"taggedSamples":
|
|
130
|
+
"taggedSamples": 9970
|
|
131
131
|
},
|
|
132
132
|
{
|
|
133
133
|
"lineId": "ln-24dedf82",
|
|
@@ -137,7 +137,7 @@
|
|
|
137
137
|
"productId": "pd-f0436841",
|
|
138
138
|
"productName": "模拟器产品-mtpsdote",
|
|
139
139
|
"startedAt": "2026-09-06T20:27:23.270+08:00",
|
|
140
|
-
"taggedSamples":
|
|
140
|
+
"taggedSamples": 23004
|
|
141
141
|
},
|
|
142
142
|
{
|
|
143
143
|
"lineId": "ln-9d5f8b3d",
|
|
@@ -147,7 +147,7 @@
|
|
|
147
147
|
"productId": "pd-8f6910a4",
|
|
148
148
|
"productName": "模拟器产品-mtpt1b1w",
|
|
149
149
|
"startedAt": "2026-09-06T20:45:45.440+08:00",
|
|
150
|
-
"taggedSamples":
|
|
150
|
+
"taggedSamples": 23884
|
|
151
151
|
},
|
|
152
152
|
{
|
|
153
153
|
"lineId": "ln-62776cae",
|
|
@@ -157,7 +157,7 @@
|
|
|
157
157
|
"productId": "pd-e2b76cd7",
|
|
158
158
|
"productName": "模拟器产品-mtptla26",
|
|
159
159
|
"startedAt": "2026-09-06T21:01:14.334+08:00",
|
|
160
|
-
"taggedSamples":
|
|
160
|
+
"taggedSamples": 20313
|
|
161
161
|
},
|
|
162
162
|
{
|
|
163
163
|
"lineId": "ln-34c7c3c4",
|
|
@@ -167,7 +167,7 @@
|
|
|
167
167
|
"productId": "pd-e883e141",
|
|
168
168
|
"productName": "模拟器产品-mtpy8ms3",
|
|
169
169
|
"startedAt": "2026-09-06T23:11:26.035+08:00",
|
|
170
|
-
"taggedSamples":
|
|
170
|
+
"taggedSamples": 25382
|
|
171
171
|
},
|
|
172
172
|
{
|
|
173
173
|
"lineId": "ln-b6fb7977",
|
|
@@ -197,7 +197,7 @@
|
|
|
197
197
|
"productId": "pd-616de77a",
|
|
198
198
|
"productName": "模拟产品-lv3ros",
|
|
199
199
|
"startedAt": "2026-09-07T00:43:16.369+08:00",
|
|
200
|
-
"taggedSamples":
|
|
200
|
+
"taggedSamples": 8136
|
|
201
201
|
},
|
|
202
202
|
{
|
|
203
203
|
"lineId": "ln-0d66fdf4",
|
|
@@ -207,6 +207,6 @@
|
|
|
207
207
|
"productId": "pd-7b25186a",
|
|
208
208
|
"productName": "mh-prod-mhqc88",
|
|
209
209
|
"startedAt": "2026-09-07T01:17:49.558+08:00",
|
|
210
|
-
"taggedSamples":
|
|
210
|
+
"taggedSamples": 1385
|
|
211
211
|
}
|
|
212
212
|
]
|
|
@@ -1,222 +1,231 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* IndustrialContext —— Agent 工业语义上下文服务(**数据驱动,零硬编码**)。
|
|
3
|
-
*
|
|
4
|
-
* 语义来源优先级(逐层覆盖,全部为用户可编辑数据):
|
|
5
|
-
* 节点 semantics(节点级备注)> 模板 semantics(模板编辑)> 结构化字段自动描述
|
|
6
|
-
* (量程/单位/预警带/采样周期由节点与模板元数据合成)。
|
|
7
|
-
* 上下文 = 节点语义卡 × 产线工况 × 关联设备 —— 只描述「该 Agent 实际绑定的节点」。
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
import { getAgentNodeBindingRepo } from './node-bindings.repo'
|
|
11
|
-
import { getDcwController } from '../dcw/dcw-controller'
|
|
12
|
-
import { getDcwLineRepo } from '../dcw/dcw-line.repo'
|
|
13
|
-
import { getActiveLineRun } from '../dcw/line-run'
|
|
14
|
-
import { findDcwTemplate } from '../dcw/dcw-templates'
|
|
15
|
-
import { getDaqNodeRepo } from '../daq/daq-node.repo'
|
|
16
|
-
import { findDaqTemplate } from '../daq/daq-templates'
|
|
17
|
-
import { getDeviceTwinRepo } from '../assets/device-twin.repo'
|
|
18
|
-
import { getRecipeRollBackManager } from '../dcw/recipe-rollback-manager'
|
|
19
|
-
|
|
20
|
-
/** 从属设备描述(名称/状态/实时遥测;数据驱动) */
|
|
21
|
-
function describeTwin(bindId: string | null | undefined): string | null {
|
|
22
|
-
if (!bindId) return null
|
|
23
|
-
const t = getDeviceTwinRepo().findById(bindId)
|
|
24
|
-
if (!t) return null
|
|
25
|
-
return `${t.name}(id=${t.id},state=${t.state},telemetry=${JSON.stringify(t.telemetry)})`
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
function
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
const
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
const
|
|
68
|
-
|
|
69
|
-
const
|
|
70
|
-
const
|
|
71
|
-
|
|
72
|
-
const
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
if (
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
)
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
steps.push(
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
1
|
+
/**
|
|
2
|
+
* IndustrialContext —— Agent 工业语义上下文服务(**数据驱动,零硬编码**)。
|
|
3
|
+
*
|
|
4
|
+
* 语义来源优先级(逐层覆盖,全部为用户可编辑数据):
|
|
5
|
+
* 节点 semantics(节点级备注)> 模板 semantics(模板编辑)> 结构化字段自动描述
|
|
6
|
+
* (量程/单位/预警带/采样周期由节点与模板元数据合成)。
|
|
7
|
+
* 上下文 = 节点语义卡 × 产线工况 × 关联设备 —— 只描述「该 Agent 实际绑定的节点」。
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { getAgentNodeBindingRepo } from './node-bindings.repo'
|
|
11
|
+
import { getDcwController } from '../dcw/dcw-controller'
|
|
12
|
+
import { getDcwLineRepo } from '../dcw/dcw-line.repo'
|
|
13
|
+
import { getActiveLineRun } from '../dcw/line-run'
|
|
14
|
+
import { findDcwTemplate } from '../dcw/dcw-templates'
|
|
15
|
+
import { getDaqNodeRepo } from '../daq/daq-node.repo'
|
|
16
|
+
import { findDaqTemplate } from '../daq/daq-templates'
|
|
17
|
+
import { getDeviceTwinRepo } from '../assets/device-twin.repo'
|
|
18
|
+
import { getRecipeRollBackManager } from '../dcw/recipe-rollback-manager'
|
|
19
|
+
|
|
20
|
+
/** 从属设备描述(名称/状态/实时遥测;数据驱动) */
|
|
21
|
+
function describeTwin(bindId: string | null | undefined): string | null {
|
|
22
|
+
if (!bindId) return null
|
|
23
|
+
const t = getDeviceTwinRepo().findById(bindId)
|
|
24
|
+
if (!t) return null
|
|
25
|
+
return `${t.name}(id=${t.id},state=${t.state},telemetry=${JSON.stringify(t.telemetry)})`
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** 多对多绑定:节点全部绑定设备的描述(deviceIds 权威;legacy 别名兜底) */
|
|
29
|
+
function describeTwins(node: { deviceIds?: string[], deviceBindingId?: string | null }): string[] {
|
|
30
|
+
const ids = node.deviceIds?.length ? node.deviceIds : (node.deviceBindingId ? [node.deviceBindingId] : [])
|
|
31
|
+
return ids.map(id => describeTwin(id)).filter((x): x is string => Boolean(x))
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** 数控节点语义卡:模板 semantics(或节点覆盖)× 实时数据合成 */
|
|
35
|
+
function dcwSemanticCard(nodeId: string, mode: string): string | null {
|
|
36
|
+
const node = getDcwController().byId(nodeId)
|
|
37
|
+
if (!node) return null
|
|
38
|
+
const tpl = findDcwTemplate(node.templateKey)
|
|
39
|
+
const sem = (node.semantics ?? tpl?.semantics ?? '').trim()
|
|
40
|
+
const line = node.lineId ? getDcwLineRepo().byId(node.lineId) : undefined
|
|
41
|
+
const run = node.lineId ? getActiveLineRun(node.lineId) : null
|
|
42
|
+
const recipe = run ? getDcwController().listRecipes().find(r => r.id === run.recipeId) : undefined
|
|
43
|
+
const param = recipe?.params.find(p => p.nodeId === nodeId)
|
|
44
|
+
const twinDesc = describeTwins(node).join(' | ') || null
|
|
45
|
+
const step = Math.max(10 ** -node.decimals, (node.max - node.min) * 0.02)
|
|
46
|
+
|
|
47
|
+
const lines = [
|
|
48
|
+
`#### ◆ ${node.name} [id=${nodeId}]`,
|
|
49
|
+
`- 物理量: ${tpl?.ch ?? node.templateKey},单位 ${node.unit},精度 ${node.decimals} 位小数`,
|
|
50
|
+
]
|
|
51
|
+
if (sem) lines.push(`- 工艺语义: ${sem}`)
|
|
52
|
+
lines.push(`- 安全量程: [${node.min}, ${node.max}] ${node.unit}(硬联锁,越界即拒)`)
|
|
53
|
+
if (param && (param.min != null || param.max != null)) {
|
|
54
|
+
lines.push(`- 活动配方「${recipe!.name}」工艺窗口: [${param.min ?? '-∞'}, ${param.max ?? '+∞'}] ${node.unit}(软联锁,配方目标值 ${param.value}${node.unit ?? ''})`)
|
|
55
|
+
}
|
|
56
|
+
else if (run) {
|
|
57
|
+
lines.push(`- 活动配方「${run.recipeName}」未对本节点设窗口(全局量程生效)`)
|
|
58
|
+
}
|
|
59
|
+
lines.push(`- 当前设定: ${node.value != null ? `${node.value}${node.unit}` : '未下发'},状态 ${node.state},所属产线「${line?.name ?? '未分配'}」`)
|
|
60
|
+
if (twinDesc) lines.push(`- 从属设备: ${twinDesc};你的写入经 PLC 下发后反映到该设备的物理行为`)
|
|
61
|
+
lines.push(`- 操作守则: 单次调幅建议 ≤${Number(step.toFixed(node.decimals))}${node.unit}(量程 2%);下发后等待工艺响应再评估;目标值必须落在窗口内;驱动 ${node.driver};${mode === 'manual' ? '**手动确认模式**,每次下发会请求用户批准,请在下发前说明理由' : '自动模式,直接执行'}`)
|
|
62
|
+
return lines.join('\n')
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** 数采节点语义卡 */
|
|
66
|
+
function daqSemanticCard(nodeId: string, mode: string): string | null {
|
|
67
|
+
const node = getDaqNodeRepo().byId(nodeId)
|
|
68
|
+
if (!node) return null
|
|
69
|
+
const tpl = findDaqTemplate(node.templateKey)
|
|
70
|
+
const sem = (node.semantics ?? tpl?.semantics ?? '').trim()
|
|
71
|
+
const line = node.lineId ? getDcwLineRepo().byId(node.lineId) : undefined
|
|
72
|
+
const run = node.lineId ? getActiveLineRun(node.lineId) : null
|
|
73
|
+
const recipe = run ? getDcwController().listRecipes().find(r => r.id === run.recipeId) : undefined
|
|
74
|
+
const win = recipe?.daqWindows?.find(w => w.nodeId === nodeId)
|
|
75
|
+
const twinDesc = describeTwins(node).join(' | ') || null
|
|
76
|
+
const fresh = node.lastAt ? Math.round((Date.now() - Date.parse(node.lastAt)) / 1000) : null
|
|
77
|
+
|
|
78
|
+
const lines = [
|
|
79
|
+
`#### ◇ ${node.name} [id=${nodeId}]`,
|
|
80
|
+
`- 物理量: ${tpl?.ch ?? node.templateKey},单位 ${node.unit},正常量程 [${node.min}, ${node.max}] ${node.unit}`,
|
|
81
|
+
]
|
|
82
|
+
if (sem) lines.push(`- 采集语义: ${sem}`)
|
|
83
|
+
if (node.warnLow != null || node.warnHigh != null) {
|
|
84
|
+
lines.push(`- 预警带: [${node.warnLow ?? '-∞'}, ${node.warnHigh ?? '+∞'}] ${node.unit}(出带=warn,越量程=alarm)`)
|
|
85
|
+
}
|
|
86
|
+
if (win) lines.push(`- 活动配方「${recipe!.name}」监控窗口: [${win.min ?? '-∞'}, ${win.max ?? '+∞'}] ${node.unit}(实时值越限 → 节点标红 + 孪生告警)`)
|
|
87
|
+
lines.push(`- 实时值: ${node.value != null ? `${node.value}${node.unit}` : '暂无数据'}${fresh != null ? `( ${fresh}s 前更新)` : ''},状态 ${node.state},采样 ${node.effectiveInterval(1000)}ms,驱动 ${node.driver},所属产线「${line?.name ?? '未分配'}」`)
|
|
88
|
+
if (twinDesc) lines.push(`- 从属设备: ${twinDesc};量测的是该设备的真实过程量`)
|
|
89
|
+
lines.push(`- 数据获取: daq_query(node_id='${nodeId}');${mode === 'manual' ? '手动确认模式' : '查询自动执行'}`)
|
|
90
|
+
return lines.join('\n')
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/** 工具:my_industrial_nodes 的语义卡视图 */
|
|
94
|
+
export function nodeSemanticCards(agentId: string): { text: string, stale: number } {
|
|
95
|
+
const repo = getAgentNodeBindingRepo()
|
|
96
|
+
const bindings = repo.byAgent(agentId)
|
|
97
|
+
const cards: string[] = []
|
|
98
|
+
let stale = 0
|
|
99
|
+
for (const b of bindings) {
|
|
100
|
+
const card = b.kind === 'dcw' ? dcwSemanticCard(b.nodeId, b.mode) : daqSemanticCard(b.nodeId, b.mode)
|
|
101
|
+
if (card) cards.push(card)
|
|
102
|
+
else stale++
|
|
103
|
+
}
|
|
104
|
+
return { text: cards.join('\n\n'), stale }
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/** 产线工况简报(注入每次回合 prompt;只描述 Agent 绑定节点所在的产线) */
|
|
108
|
+
export function buildIndustrialContext(agentId: string): string {
|
|
109
|
+
const bindings = getAgentNodeBindingRepo().byAgent(agentId)
|
|
110
|
+
const dcwIds = bindings.filter(b => b.kind === 'dcw').map(b => b.nodeId)
|
|
111
|
+
const daqIds = bindings.filter(b => b.kind === 'daq').map(b => b.nodeId)
|
|
112
|
+
if (dcwIds.length === 0 && daqIds.length === 0) return ''
|
|
113
|
+
|
|
114
|
+
const lineIds = [...new Set([
|
|
115
|
+
...dcwIds.map(id => getDcwController().byId(id)?.lineId ?? ''),
|
|
116
|
+
...daqIds.map(id => getDaqNodeRepo().byId(id)?.lineId ?? ''),
|
|
117
|
+
])].filter(Boolean)
|
|
118
|
+
|
|
119
|
+
const sections: string[] = []
|
|
120
|
+
for (const lineId of lineIds) {
|
|
121
|
+
const line = getDcwLineRepo().byId(lineId)
|
|
122
|
+
if (!line) continue
|
|
123
|
+
const run = getActiveLineRun(lineId)
|
|
124
|
+
const lines: string[] = [`### 产线「${line.name}」(id=${lineId},光晕色 ${line.color})`]
|
|
125
|
+
if (run) {
|
|
126
|
+
const elapsedMin = Math.round((Date.now() - Date.parse(run.startedAt)) / 60_000)
|
|
127
|
+
lines.push(`- 工况:**运行中**,批次 ${run.runId.slice(0, 8)},产品「${run.productName}」× 配方「${run.recipeName}」,已运行 ${elapsedMin} 分钟,已采集 ${run.taggedSamples} 打标样本`)
|
|
128
|
+
const recipe = getDcwController().listRecipes().find(r => r.id === run.recipeId)
|
|
129
|
+
if (recipe) {
|
|
130
|
+
const params = recipe.params
|
|
131
|
+
.map((p) => {
|
|
132
|
+
const n = getDcwController().byId(p.nodeId)
|
|
133
|
+
const win = (p.min != null || p.max != null) ? ` 窗口 ${p.min ?? '-∞'}~${p.max ?? '+∞'}${n?.unit ?? ''}` : ''
|
|
134
|
+
return `${n?.name ?? p.nodeId}=${p.value}${n?.unit ?? ''}${win}`
|
|
135
|
+
})
|
|
136
|
+
.join(';')
|
|
137
|
+
lines.push(`- 配方工艺参数: ${params}`)
|
|
138
|
+
const wins = (recipe.daqWindows ?? [])
|
|
139
|
+
.map((w) => {
|
|
140
|
+
const n = getDaqNodeRepo().byId(w.nodeId)
|
|
141
|
+
return `${n?.name ?? w.nodeId} ∈ [${w.min ?? '-∞'}, ${w.max ?? '+∞'}]${n?.unit ?? ''}`
|
|
142
|
+
})
|
|
143
|
+
.join(';')
|
|
144
|
+
if (wins) lines.push(`- 数采监控窗口(越限即报警): ${wins}`)
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
lines.push('- 工况:**停线**(无活动批次;写入仅受节点全局量程约束,数采暂停)')
|
|
149
|
+
}
|
|
150
|
+
const twinNames = new Set<string>()
|
|
151
|
+
for (const id of [...dcwIds, ...daqIds]) {
|
|
152
|
+
for (const desc of [
|
|
153
|
+
...describeTwins(getDcwController().byId(id) ?? {}),
|
|
154
|
+
...describeTwins(getDaqNodeRepo().byId(id) ?? {}),
|
|
155
|
+
]) {
|
|
156
|
+
if (desc) twinNames.add(desc)
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
if (twinNames.size > 0) lines.push(`- 关联设备孪生: ${Array.from(twinNames).join(' | ')}`)
|
|
160
|
+
// 当前报警透出(只报本 Agent 绑定的数采节点):越限即报警是工况里最需要
|
|
161
|
+
// Agent 优先感知的事实 —— 不注入的话,Agent 只能靠 daq_query 事后发现
|
|
162
|
+
const alarms = daqIds
|
|
163
|
+
.map(id => getDaqNodeRepo().byId(id))
|
|
164
|
+
.filter(n => n && n.lineId === lineId && n.state === 'alarm')
|
|
165
|
+
if (alarms.length > 0) {
|
|
166
|
+
lines.push(`- **当前报警**: ${alarms.map(n => `${n.name} 实时 ${n.value ?? '?'}${n.unit} 处于报警态(越量程或越配方监控窗口)`)},应优先判读(设定-响应滞后 vs 真实异常)并处置`)
|
|
167
|
+
}
|
|
168
|
+
sections.push(lines.join('\n'))
|
|
169
|
+
}
|
|
170
|
+
if (sections.length === 0) return ''
|
|
171
|
+
return `## 产线工况简报(实时;每次回合自动注入)\n${sections.join('\n\n')}`
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* 工业调控作业环(prompt 层纪律注入;有工业绑定才注入,零硬编码)。
|
|
176
|
+
* 与节点语义卡(my_industrial_nodes 结果)互补:语义卡在工具调用后可见,
|
|
177
|
+
* 作业环保证 Agent 在**任何回合开头**就知道调控方法论。
|
|
178
|
+
* 七步闭环(v2.1):观察 → 理解 → 假设 → 设定 → 判定 → 回退/保持 → 复盘;
|
|
179
|
+
* 每次下发自动开优化记录、判定入册、回退可审计 —— 闭环由系统记账,Agent 负责判断质量。
|
|
180
|
+
*/
|
|
181
|
+
export function industrialLoopGuide(agentId: string): string {
|
|
182
|
+
const bindings = getAgentNodeBindingRepo().byAgent(agentId)
|
|
183
|
+
if (bindings.length === 0) return ''
|
|
184
|
+
const hasDcw = bindings.some(b => b.kind === 'dcw')
|
|
185
|
+
const hasManual = bindings.some(b => b.kind === 'dcw' && b.mode === 'manual')
|
|
186
|
+
const steps: string[] = [
|
|
187
|
+
'1. 观察:daq_query 获取绑定节点的真实时序数据(支持按产品/配方/时间窗过滤),结合返回的工况判读理解当前状态。',
|
|
188
|
+
'2. 理解:my_industrial_nodes 读取节点语义卡 —— 物理量含义/单位/安全量程/活动配方工艺窗口/单次调幅步进,以及调控闭环状态(进行中的优化记录/上次良好值/最近判定)。',
|
|
189
|
+
]
|
|
190
|
+
if (hasDcw) {
|
|
191
|
+
steps.push(
|
|
192
|
+
'3. 假设:调参前先声明假设(写入 dcw_control 的 hypothesis 参数)—— 目标值 + 预期效果 + 观察计划;目标必须落在「安全量程 ∩ 活动配方工艺窗口」内,优先小步幅(≤量程 2%)、单向逼近。',
|
|
193
|
+
'4. 设定:dcw_control 下发(自动开优化记录);等待工艺响应(热惯性/传动惯量),勿连续大幅调整。',
|
|
194
|
+
'5. 判定:daq_query 复测窗口数据后,dcw_judge 落判定 —— keep(已验证经验)/ rollback(判应回退)/ uncertain(证据不足)。未判定前再下发,旧记录会被标记 superseded;判定必须引用具体数值与时间窗证据。',
|
|
195
|
+
'6. 回退/保持:判 rollback 后用 dcw_rollback(record_id) 执行回退(判定不自动改 PLC),回退后 daq_query 复测确认恢复;回退冷却期内禁止同向重写。收敛异常时分析根因或上报,而非盲目加码。',
|
|
196
|
+
'7. 复盘:dcw_journal 查看该节点参数变更史与判定结论;keep 的记录是已验证经验,rollback 的记录写进结论修正下次假设 —— 禁止重复已失败的调参方向。',
|
|
197
|
+
)
|
|
198
|
+
}
|
|
199
|
+
else {
|
|
200
|
+
steps.push('3. 判读:你是量测侧 —— 数据越过配方监控窗口会触发节点报警与孪生告警;判读时区分「同线数控设定-响应滞后」与「真实过程异常」,结论引用具体数值与时间窗。')
|
|
201
|
+
}
|
|
202
|
+
if (hasManual) {
|
|
203
|
+
steps.push('8. 权限边界:手动确认模式的节点,下发前必须说明理由并等待用户批准(用户备注会随结果返回,请响应其关切);自动模式节点直接执行,但同样受联锁约束;其判定回退会推请用户确认。')
|
|
204
|
+
}
|
|
205
|
+
steps.push(`${steps.length + 1}. 数值口径:工具返回的采集/设定值均为经标定钩子处理后的真实物理量纲;引用数值时带上单位与时间,便于人工复核。`)
|
|
206
|
+
const history = recentOptimizationNotes(agentId)
|
|
207
|
+
return `## 工业调控作业环(你的节点操作方法论,每回合生效)\n${steps.join('\n')}${history}`
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/** 经验注入:该 Agent 绑定数控节点的最近优化记录结论(keep/rollback 统计),跨任务积累 */
|
|
211
|
+
function recentOptimizationNotes(agentId: string): string {
|
|
212
|
+
try {
|
|
213
|
+
const rb = getRecipeRollBackManager()
|
|
214
|
+
const dcwIds = getAgentNodeBindingRepo().byAgent(agentId).filter(b => b.kind === 'dcw').map(b => b.nodeId)
|
|
215
|
+
if (dcwIds.length === 0) return ''
|
|
216
|
+
const lines: string[] = []
|
|
217
|
+
for (const nodeId of dcwIds.slice(0, 3)) {
|
|
218
|
+
const records = rb.records({ nodeId, limit: 20 })
|
|
219
|
+
if (records.length === 0) continue
|
|
220
|
+
const keep = records.filter(r => r.judge?.verdict === 'keep').length
|
|
221
|
+
const roll = records.filter(r => r.judge?.verdict === 'rollback' || r.status === 'rolled-back').length
|
|
222
|
+
const last = records.find(r => r.judge)
|
|
223
|
+
lines.push(`- ${records[0]!.nodeName}:近 ${records.length} 次调控(keep ${keep}/rollback ${roll})${last?.judge ? `,最近判定 ${last.judge.verdict}(${last.judge.reason.slice(0, 60)})` : ''}`)
|
|
224
|
+
}
|
|
225
|
+
if (lines.length === 0) return ''
|
|
226
|
+
return `\n\n## 优化经验台账(你绑定节点的近期调控结论;避免重复已失败方向)\n${lines.join('\n')}`
|
|
227
|
+
}
|
|
228
|
+
catch {
|
|
229
|
+
return ''
|
|
230
|
+
}
|
|
231
|
+
}
|