agentworkshop 0.6.0 → 0.6.1
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/builds/latest.json +1 -1
- package/.output/public/_nuxt/builds/meta/221de779-a113-4232-b9a1-31a614f5e3c7.json +1 -0
- package/.output/server/chunks/_/nitro.mjs +679 -676
- package/.output/server/package.json +1 -1
- package/package.json +1 -1
- package/scripts/_dbg-plugin-tools-inject.ts +80 -0
- package/scripts/_dbg-plugin-verify-e2e.mjs +123 -0
- package/server/data/daqs.json +32 -8
- package/server/data/dcw-rollback.json +1 -1
- package/server/data/dcw-runs.json +82 -36
- package/server/data/dcw-writes.json +48 -0
- package/server/data/dcws.json +4 -4
- package/server/services/workshop/daq/daq-controller.ts +3 -3
- package/.output/public/_nuxt/builds/meta/a0ac51ef-51c0-4ff9-8484-7c90d8dd10ba.json +0 -1
package/package.json
CHANGED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 插件扩展验证 C(omp 工具注入,协议层真机):
|
|
3
|
+
* ①插件工具并入角色工具面 → ②真实 omp --mode rpc 子进程接受含插件工具的
|
|
4
|
+
* set_host_tools(协议层注入生效) → ③handleHostTool 分发到插件 handler。
|
|
5
|
+
* 运行: npx tsx --tsconfig .nuxt/tsconfig.server.json scripts/_dbg-plugin-tools-inject.ts
|
|
6
|
+
*/
|
|
7
|
+
import { spawn } from 'node:child_process'
|
|
8
|
+
import { registerPluginTool, listPluginTools } from '../server/services/workshop/agents/plugin-tools'
|
|
9
|
+
import { hostToolsForRole, OmpRpcAgentImpl } from '../server/services/workshop/agents/omp-agent'
|
|
10
|
+
|
|
11
|
+
let failures = 0
|
|
12
|
+
const check = (name: string, ok: boolean, detail = ''): void => {
|
|
13
|
+
console.log(` ${ok ? 'PASS' : 'FAIL'} ${name}${detail ? ` — ${detail}` : ''}`)
|
|
14
|
+
if (!ok) failures += 1
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// ===== ① 插件工具注册(模拟插件 setup 的注册调用)=====
|
|
18
|
+
registerPluginTool('live-verify', {
|
|
19
|
+
name: 'plugin_verify_probe',
|
|
20
|
+
description: '插件验证探针:返回插件链路状态',
|
|
21
|
+
parameters: { type: 'object', properties: { ping: { type: 'string' } } },
|
|
22
|
+
handler: args => ({ text: `probe-ok:${String(args.ping ?? '')}` }),
|
|
23
|
+
})
|
|
24
|
+
check('插件工具注册进注册表', listPluginTools().has('plugin_verify_probe'))
|
|
25
|
+
check('hostToolsForRole(worker) 含插件工具', hostToolsForRole('worker').some(t => t.name === 'plugin_verify_probe'))
|
|
26
|
+
|
|
27
|
+
// ===== ② 真实 omp 子进程:set_host_tools 含插件工具 → 协议接受 =====
|
|
28
|
+
console.log('\n--- 真实 omp 子进程注入 ---')
|
|
29
|
+
{
|
|
30
|
+
const tools = hostToolsForRole('worker')
|
|
31
|
+
const child = spawn('omp', ['--mode', 'rpc'], { stdio: ['pipe', 'pipe', 'pipe'], windowsHide: true })
|
|
32
|
+
let buf = ''
|
|
33
|
+
let sent = false
|
|
34
|
+
let accepted: unknown = null
|
|
35
|
+
const result = await new Promise<unknown>((resolve) => {
|
|
36
|
+
const timer = setTimeout(() => resolve('timeout'), 30_000)
|
|
37
|
+
child.stdout!.setEncoding('utf-8')
|
|
38
|
+
child.stdout!.on('data', (chunk: string) => {
|
|
39
|
+
buf += chunk
|
|
40
|
+
let idx: number
|
|
41
|
+
while ((idx = buf.indexOf('\n')) >= 0) {
|
|
42
|
+
const line = buf.slice(0, idx)
|
|
43
|
+
buf = buf.slice(idx + 1)
|
|
44
|
+
if (!line.trim()) continue
|
|
45
|
+
try {
|
|
46
|
+
const frame = JSON.parse(line)
|
|
47
|
+
if (frame.type === 'ready' && !sent) {
|
|
48
|
+
sent = true
|
|
49
|
+
// 注入含插件工具的工具面(与 omp-agent ensureClient 同一命令)
|
|
50
|
+
child.stdin!.write(`${JSON.stringify({ id: 'req_tools', type: 'set_host_tools', tools })}\n`)
|
|
51
|
+
}
|
|
52
|
+
else if (frame.type === 'response' && frame.id === 'req_tools') {
|
|
53
|
+
clearTimeout(timer)
|
|
54
|
+
accepted = frame
|
|
55
|
+
resolve(frame)
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
catch { /* 非 JSON 行忽略 */ }
|
|
59
|
+
}
|
|
60
|
+
})
|
|
61
|
+
child.on('exit', () => resolve(accepted ?? 'exit'))
|
|
62
|
+
})
|
|
63
|
+
child.kill()
|
|
64
|
+
const resp = accepted as { success?: boolean } | null
|
|
65
|
+
check('真实 omp 接受含插件工具的 set_host_tools', !!resp && resp.success === true, JSON.stringify(accepted).slice(0, 120))
|
|
66
|
+
void result
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// ===== ③ handleHostTool 分发(真实 OmpRpcAgentImpl 实例)=====
|
|
70
|
+
console.log('\n--- 分发执行 ---')
|
|
71
|
+
{
|
|
72
|
+
const impl = new OmpRpcAgentImpl({ agentId: 'ag-verify', name: '验证员', role: 'worker', channelId: 'ch-v' })
|
|
73
|
+
const handle = (impl as unknown as Record<string, (req: unknown) => Promise<{ text: string, isError?: boolean }>>).handleHostTool.bind(impl)
|
|
74
|
+
const out = await handle({ toolName: 'plugin_verify_probe', toolCallId: 't1', arguments: { ping: 'e2e' } })
|
|
75
|
+
check('handleHostTool 分发到插件 handler', out.text === 'probe-ok:e2e', out.text)
|
|
76
|
+
await impl.dispose()
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
console.log(failures === 0 ? '\nTOOLS INJECT ALL PASS' : `\nTOOLS INJECT FAILED(${failures})`)
|
|
80
|
+
process.exit(failures === 0 ? 0 : 1)
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 插件扩展全栈验证 A(数据管线 + 前端 + 孪生):
|
|
3
|
+
* ①插件模板+插件驱动建节点 → 产线开跑 → mock 一次性 vector 采样
|
|
4
|
+
* ②sink 加工断言(×2 确定性签名:argmax=6、points[6]≈1.08、points[0]≈0.90、verifyTag=1)
|
|
5
|
+
* ③DB 事实(REST frames = Timescale 读取)
|
|
6
|
+
* ④/daq 列表页实时值变化(浏览器)
|
|
7
|
+
* ⑤/town 数字孪生右轨面板实时变化(浏览器)
|
|
8
|
+
* 产出 .e2e-plugin-ctx.json 供 Agent 读库段使用;节点保留(--cleanup=1 时清理)。
|
|
9
|
+
* 运行: node scripts/_dbg-plugin-verify-e2e.mjs
|
|
10
|
+
*/
|
|
11
|
+
import puppeteer from 'puppeteer-core'
|
|
12
|
+
import { writeFileSync, readFileSync } from 'node:fs'
|
|
13
|
+
|
|
14
|
+
const ROOT = process.env.E2E_ROOT ?? 'http://127.0.0.1:3000'
|
|
15
|
+
const CLEANUP = process.env.CLEANUP === '1'
|
|
16
|
+
const sleep = ms => new Promise(r => setTimeout(r, ms))
|
|
17
|
+
let failed = 0
|
|
18
|
+
const check = (name, cond, detail = '') => { console.log(` ${cond ? 'PASS' : 'FAIL'} ${name}${detail ? ` — ${detail}` : ''}`); if (!cond) failed++ }
|
|
19
|
+
|
|
20
|
+
const login = await fetch(`${ROOT}/api/users/login`, { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ email: 'zhangwei@awshop.io', password: 'Awshop@123' }) }).then(r => r.json())
|
|
21
|
+
const H = { authorization: `Bearer ${login.data.token}`, 'content-type': 'application/json' }
|
|
22
|
+
const cookie = { name: 'token', value: login.data.token, domain: '127.0.0.1', path: '/' }
|
|
23
|
+
const j = (u, m = 'GET', b) => fetch(ROOT + u, { method: m, headers: H, body: b ? JSON.stringify(b) : undefined }).then(r => r.json())
|
|
24
|
+
|
|
25
|
+
// ===== ① 插件模板/驱动在目录 =====
|
|
26
|
+
const daq0 = (await j('/api/workshop/daq')).data
|
|
27
|
+
const tpl = daq0.templates.find(t => t.key === 'plug-verify-x2-profile')
|
|
28
|
+
check('①插件模板在目录(plugin 标记,vector)', !!tpl && tpl.plugin != null && tpl.signalKind === 'vector')
|
|
29
|
+
|
|
30
|
+
// ===== ② 开跑 + 插件模板 + 插件驱动建节点 =====
|
|
31
|
+
const d = (await j('/api/workshop/dcw')).data
|
|
32
|
+
const cand = d.lines.map(l => ({ line: l, recipe: d.recipes.find(r => r.lineId === l.id) })).find(x => x.recipe)
|
|
33
|
+
await j(`/api/workshop/dcw/lines/${cand.line.id}/stop`, 'POST').catch(() => {})
|
|
34
|
+
await sleep(800)
|
|
35
|
+
await j(`/api/workshop/dcw/lines/${cand.line.id}/start`, 'POST', { recipeId: cand.recipe.id })
|
|
36
|
+
const node = (await j('/api/workshop/daq', 'POST', {
|
|
37
|
+
templateRef: 'plug-verify-x2-profile',
|
|
38
|
+
driver: 'verify-burst',
|
|
39
|
+
lineId: cand.line.id,
|
|
40
|
+
name: '全栈验证 ×2 轮廓',
|
|
41
|
+
posX: 120, posZ: 120,
|
|
42
|
+
})).data.node
|
|
43
|
+
check('②节点创建(插件模板+插件驱动)', node?.driver === 'verify-burst', `driver=${node?.driver}`)
|
|
44
|
+
|
|
45
|
+
// ===== ③ 等帧 → sink 加工确定性断言 =====
|
|
46
|
+
let frames = []
|
|
47
|
+
for (let i = 0; i < 15; i++) {
|
|
48
|
+
await sleep(2000)
|
|
49
|
+
frames = (await j(`/api/workshop/daq/${node.id}/frames?limit=5`)).data.frames
|
|
50
|
+
if (frames.length >= 3) break
|
|
51
|
+
}
|
|
52
|
+
check('③帧入库(Rest = Timescale 事实源)', frames.length >= 3, `${frames.length} 帧`)
|
|
53
|
+
if (frames.length >= 3) {
|
|
54
|
+
const f = frames[0]
|
|
55
|
+
const pts = f.points ?? []
|
|
56
|
+
const argmax = pts.reduce((best, p, i) => (p > pts[best] ? i : best), 0)
|
|
57
|
+
check('一次性 vector:24 点完整轮廓', pts.length === 24, `len=${pts.length}`)
|
|
58
|
+
check('×2 标定签名:argmax=6(尖峰位)', argmax === 6, `argmax=${argmax}`)
|
|
59
|
+
check('×2 标定签名:points[6]∈[1.06,1.12](=2×0.54)', pts[6] >= 1.06 && pts[6] <= 1.12, String(pts[6]))
|
|
60
|
+
check('×2 标定签名:points[0]∈[0.88,0.92](=2×0.45)', pts[0] >= 0.88 && pts[0] <= 0.92, String(pts[0]))
|
|
61
|
+
check('插件处理器指纹:metrics.verifyTag=1', f.metrics.verifyTag === 1)
|
|
62
|
+
check('内置 derive-metric 在插件后执行:max=points[6]', Math.abs((f.metrics.max ?? 0) - pts[6]) < 0.001, `max=${f.metrics.max}`)
|
|
63
|
+
check('产线打标继承', f.lineId === cand.line.id)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// ===== ④/⑤ 浏览器:/daq 列表实时 + /town 孪生面板实时 =====
|
|
67
|
+
const twins = (await j('/api/workshop/device-twins')).data
|
|
68
|
+
const twinsList = twins.twins ?? twins
|
|
69
|
+
const device = twinsList.find(t => t.kind !== 'daq' && t.kind !== 'dcw')
|
|
70
|
+
if (device) await j(`/api/workshop/daq/${node.id}/bind`, 'POST', { deviceId: device.id })
|
|
71
|
+
|
|
72
|
+
const browser = await puppeteer.launch({ executablePath: 'C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe', headless: 'new', args: ['--no-sandbox', '--disable-gpu', '--window-size=1600,1000'] })
|
|
73
|
+
const page = await browser.newPage()
|
|
74
|
+
await page.setViewport({ width: 1600, height: 1000 })
|
|
75
|
+
await page.setCookie(cookie)
|
|
76
|
+
try {
|
|
77
|
+
// ④ /daq 列表页
|
|
78
|
+
await page.goto(`${ROOT}/daq`, { waitUntil: 'networkidle2', timeout: 90000 })
|
|
79
|
+
await sleep(5000)
|
|
80
|
+
const rowVal = () => page.evaluate(() => {
|
|
81
|
+
const row = [...document.querySelectorAll('tbody tr')].find(r => r.textContent?.includes('全栈验证'))
|
|
82
|
+
const m = row?.textContent?.match(/(\d+\.\d+)\s*mm/)
|
|
83
|
+
return m ? m[1] : null
|
|
84
|
+
})
|
|
85
|
+
const v1 = await rowVal()
|
|
86
|
+
await sleep(4500)
|
|
87
|
+
const v2 = await rowVal()
|
|
88
|
+
check('④/daq 列表页出现插件节点行', v1 != null, `v1=${v1}`)
|
|
89
|
+
check('④/daq 实时值变化(WS 帧驱动)', v1 != null && v2 != null && v1 !== v2, `${v1} → ${v2}`)
|
|
90
|
+
|
|
91
|
+
// ⑤ /town 数字孪生
|
|
92
|
+
await page.goto(`${ROOT}/town`, { waitUntil: 'domcontentloaded', timeout: 90000 })
|
|
93
|
+
await sleep(20000)
|
|
94
|
+
const panel = () => page.evaluate(() => {
|
|
95
|
+
const item = [...document.querySelectorAll('.twin-daq .daq-item')].find(e => e.textContent?.includes('标定轮廓'))
|
|
96
|
+
return { text: item?.textContent?.replace(/\s+/g, '') ?? '', val: item?.querySelector('b')?.textContent?.replace(/\s+/g, '') ?? null }
|
|
97
|
+
})
|
|
98
|
+
const p1 = await panel()
|
|
99
|
+
await sleep(5000)
|
|
100
|
+
const p2 = await panel()
|
|
101
|
+
check('⑤/town 孪生面板出现插件节点行(标定轮廓)', p1.text !== '', p1.text.slice(0, 60))
|
|
102
|
+
check('⑤/town 面板实时值变化(加工后量级)', p1.val != null && p2.val != null && p1.val !== p2.val, `${p1.val} → ${p2.val}`)
|
|
103
|
+
} finally {
|
|
104
|
+
await browser.close().catch(() => {})
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// ===== Agent 绑定(供读库段)=====
|
|
108
|
+
const agents = (await j('/api/workshop/agents')).data
|
|
109
|
+
const list = agents.agents ?? agents
|
|
110
|
+
const agent = list.find(a => a.enabled !== 0 && a.role === 'worker') ?? list[0]
|
|
111
|
+
await fetch(`${ROOT}/api/workshop/agent-tools/bindings`, { method: 'POST', headers: H, body: JSON.stringify({ agentId: agent.id, nodeId: node.id, kind: 'daq', mode: 'manual' }) }).then(r => r.json())
|
|
112
|
+
writeFileSync('.e2e-plugin-ctx.json', JSON.stringify({ nodeId: node.id, agentId: agent.id, lineId: cand.line.id }))
|
|
113
|
+
|
|
114
|
+
if (CLEANUP) {
|
|
115
|
+
await j(`/api/workshop/daq/${node.id}`, 'DELETE').catch(() => {})
|
|
116
|
+
await j(`/api/workshop/dcw/lines/${cand.line.id}/stop`, 'POST').catch(() => {})
|
|
117
|
+
console.log('(cleanup 模式:节点已删/线已停)')
|
|
118
|
+
} else {
|
|
119
|
+
console.log(`(节点 ${node.id} 与产线运行保留,供 Agent 段/浏览器复验;CLEANUP=1 可清理)`)
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
console.log(failed === 0 ? '\nSTACK-A ALL PASS' : `\nSTACK-A FAILED(${failed})`)
|
|
123
|
+
process.exit(failed === 0 ? 0 : 1)
|
package/server/data/daqs.json
CHANGED
|
@@ -64,9 +64,9 @@
|
|
|
64
64
|
"posX": 1695,
|
|
65
65
|
"posZ": 1135,
|
|
66
66
|
"lineId": "ln-af002514",
|
|
67
|
-
"value": 167
|
|
67
|
+
"value": 167,
|
|
68
68
|
"state": "ok",
|
|
69
|
-
"lastAt": "2026-09-
|
|
69
|
+
"lastAt": "2026-09-03T10:44:03.821Z",
|
|
70
70
|
"createdAt": "2026-08-27T12:39:08.110Z"
|
|
71
71
|
},
|
|
72
72
|
{
|
|
@@ -308,9 +308,9 @@
|
|
|
308
308
|
"deviceBindingId": null,
|
|
309
309
|
"driverConfig": {},
|
|
310
310
|
"lineId": "ln-af002514",
|
|
311
|
-
"value":
|
|
311
|
+
"value": 315,
|
|
312
312
|
"state": "ok",
|
|
313
|
-
"lastAt": "2026-09-
|
|
313
|
+
"lastAt": "2026-09-03T10:44:03.821Z",
|
|
314
314
|
"createdAt": "2026-08-28T09:37:38.518Z"
|
|
315
315
|
},
|
|
316
316
|
{
|
|
@@ -330,9 +330,9 @@
|
|
|
330
330
|
"deviceBindingId": null,
|
|
331
331
|
"driverConfig": {},
|
|
332
332
|
"lineId": "ln-af002514",
|
|
333
|
-
"value": 166.
|
|
333
|
+
"value": 166.3,
|
|
334
334
|
"state": "ok",
|
|
335
|
-
"lastAt": "2026-09-
|
|
335
|
+
"lastAt": "2026-09-03T10:44:03.821Z",
|
|
336
336
|
"createdAt": "2026-08-29T03:30:57.463Z"
|
|
337
337
|
},
|
|
338
338
|
{
|
|
@@ -1012,9 +1012,9 @@
|
|
|
1012
1012
|
"posX": 2395,
|
|
1013
1013
|
"posZ": 1135,
|
|
1014
1014
|
"lineId": "ln-af002514",
|
|
1015
|
-
"value":
|
|
1015
|
+
"value": 171,
|
|
1016
1016
|
"state": "ok",
|
|
1017
|
-
"lastAt": "2026-09-
|
|
1017
|
+
"lastAt": "2026-09-03T10:44:04.350Z",
|
|
1018
1018
|
"createdAt": "2026-08-31T04:07:19.038Z"
|
|
1019
1019
|
},
|
|
1020
1020
|
{
|
|
@@ -3006,5 +3006,29 @@
|
|
|
3006
3006
|
"state": "ok",
|
|
3007
3007
|
"lastAt": "2026-09-02T15:29:49.039Z",
|
|
3008
3008
|
"createdAt": "2026-09-02T11:55:28.275Z"
|
|
3009
|
+
},
|
|
3010
|
+
{
|
|
3011
|
+
"id": "dn-2b91d255",
|
|
3012
|
+
"templateRef": "plug-verify-x2-profile",
|
|
3013
|
+
"name": "全栈验证 ×2 轮廓",
|
|
3014
|
+
"driver": "mock",
|
|
3015
|
+
"enabled": true,
|
|
3016
|
+
"intervalMs": null,
|
|
3017
|
+
"publishIntervalMs": null,
|
|
3018
|
+
"unit": "mm",
|
|
3019
|
+
"decimals": 4,
|
|
3020
|
+
"min": 0.4,
|
|
3021
|
+
"max": 0.6,
|
|
3022
|
+
"warnLow": 0.416,
|
|
3023
|
+
"warnHigh": 0.584,
|
|
3024
|
+
"deviceBindingId": "dev-mthzr991-ujuad",
|
|
3025
|
+
"driverConfig": {},
|
|
3026
|
+
"posX": 120,
|
|
3027
|
+
"posZ": 120,
|
|
3028
|
+
"lineId": "ln-af002514",
|
|
3029
|
+
"value": 0.9733,
|
|
3030
|
+
"state": "ok",
|
|
3031
|
+
"lastAt": "2026-09-03T10:44:03.821Z",
|
|
3032
|
+
"createdAt": "2026-09-03T10:17:59.182Z"
|
|
3009
3033
|
}
|
|
3010
3034
|
]
|
|
@@ -1,40 +1,4 @@
|
|
|
1
1
|
[
|
|
2
|
-
{
|
|
3
|
-
"id": "rr-f9620cd7",
|
|
4
|
-
"recipeId": "rc-d04a7e04",
|
|
5
|
-
"recipeName": "探针配方Q5",
|
|
6
|
-
"productId": "pd-5f4ba882",
|
|
7
|
-
"lineId": "ln-73c3b1e4",
|
|
8
|
-
"startedAt": "2026-08-30T01:49:02.645Z",
|
|
9
|
-
"endedAt": "2026-08-30T01:57:02.510Z",
|
|
10
|
-
"results": [
|
|
11
|
-
{
|
|
12
|
-
"templateRef": "dcw-temp-sp",
|
|
13
|
-
"nodeId": "dw-f0769f22",
|
|
14
|
-
"ok": true,
|
|
15
|
-
"message": "写入并回读一致:180 → raw 1200,回读 180(标定后物理值 180)",
|
|
16
|
-
"value": 180
|
|
17
|
-
}
|
|
18
|
-
]
|
|
19
|
-
},
|
|
20
|
-
{
|
|
21
|
-
"id": "rr-2339e74a",
|
|
22
|
-
"recipeId": "rc-0c5201dd",
|
|
23
|
-
"recipeName": "工具审计配方",
|
|
24
|
-
"productId": "pd-39f76bc4",
|
|
25
|
-
"lineId": "ln-c8f08cbd",
|
|
26
|
-
"startedAt": "2026-08-30T01:57:03.037Z",
|
|
27
|
-
"endedAt": "2026-08-30T01:57:21.695Z",
|
|
28
|
-
"results": [
|
|
29
|
-
{
|
|
30
|
-
"templateRef": "dcw-temp-sp",
|
|
31
|
-
"nodeId": "dw-2ed2e19a",
|
|
32
|
-
"ok": true,
|
|
33
|
-
"message": "Mock PLC 写入成功:180 → raw 180,回读一致(标定后物理值 180)",
|
|
34
|
-
"value": 180
|
|
35
|
-
}
|
|
36
|
-
]
|
|
37
|
-
},
|
|
38
2
|
{
|
|
39
3
|
"id": "rr-06f366cf",
|
|
40
4
|
"recipeId": "rc-84cbf4ae",
|
|
@@ -5411,5 +5375,87 @@
|
|
|
5411
5375
|
"max": 1
|
|
5412
5376
|
}
|
|
5413
5377
|
]
|
|
5378
|
+
},
|
|
5379
|
+
{
|
|
5380
|
+
"id": "rr-8b294482",
|
|
5381
|
+
"recipeId": "rc-7bf584db",
|
|
5382
|
+
"recipeName": "A-标准工艺",
|
|
5383
|
+
"productId": "pd-6348a595",
|
|
5384
|
+
"lineId": "ln-af002514",
|
|
5385
|
+
"startedAt": "2026-09-03T10:17:58.886Z",
|
|
5386
|
+
"endedAt": "2026-09-03T10:41:13.654Z",
|
|
5387
|
+
"results": [
|
|
5388
|
+
{
|
|
5389
|
+
"templateRef": "dcw-temp-sp",
|
|
5390
|
+
"nodeId": "dw-322b1978",
|
|
5391
|
+
"ok": true,
|
|
5392
|
+
"message": "Mock PLC 写入成功:182 → raw 182,回读一致(标定后物理值 182)",
|
|
5393
|
+
"value": 182
|
|
5394
|
+
},
|
|
5395
|
+
{
|
|
5396
|
+
"templateRef": "dcw-pressure-sp",
|
|
5397
|
+
"nodeId": "dw-e92bb0e7",
|
|
5398
|
+
"ok": true,
|
|
5399
|
+
"message": "Mock PLC 写入成功:0.92 → raw 0.92,回读一致(标定后物理值 0.92)",
|
|
5400
|
+
"value": 0.92
|
|
5401
|
+
}
|
|
5402
|
+
],
|
|
5403
|
+
"paramsSnapshot": [
|
|
5404
|
+
{
|
|
5405
|
+
"nodeId": "dw-322b1978",
|
|
5406
|
+
"templateRef": "dcw-temp-sp",
|
|
5407
|
+
"value": 182,
|
|
5408
|
+
"min": 176,
|
|
5409
|
+
"max": 188
|
|
5410
|
+
},
|
|
5411
|
+
{
|
|
5412
|
+
"nodeId": "dw-e92bb0e7",
|
|
5413
|
+
"templateRef": "dcw-pressure-sp",
|
|
5414
|
+
"value": 0.92,
|
|
5415
|
+
"min": 0.85,
|
|
5416
|
+
"max": 1
|
|
5417
|
+
}
|
|
5418
|
+
]
|
|
5419
|
+
},
|
|
5420
|
+
{
|
|
5421
|
+
"id": "rr-b1f6156d",
|
|
5422
|
+
"recipeId": "rc-7bf584db",
|
|
5423
|
+
"recipeName": "A-标准工艺",
|
|
5424
|
+
"productId": "pd-6348a595",
|
|
5425
|
+
"lineId": "ln-af002514",
|
|
5426
|
+
"startedAt": "2026-09-03T10:41:14.527Z",
|
|
5427
|
+
"endedAt": "2026-09-03T10:44:04.580Z",
|
|
5428
|
+
"results": [
|
|
5429
|
+
{
|
|
5430
|
+
"templateRef": "dcw-temp-sp",
|
|
5431
|
+
"nodeId": "dw-322b1978",
|
|
5432
|
+
"ok": true,
|
|
5433
|
+
"message": "Mock PLC 写入成功:182 → raw 182,回读一致(标定后物理值 182)",
|
|
5434
|
+
"value": 182
|
|
5435
|
+
},
|
|
5436
|
+
{
|
|
5437
|
+
"templateRef": "dcw-pressure-sp",
|
|
5438
|
+
"nodeId": "dw-e92bb0e7",
|
|
5439
|
+
"ok": true,
|
|
5440
|
+
"message": "Mock PLC 写入成功:0.92 → raw 0.92,回读一致(标定后物理值 0.92)",
|
|
5441
|
+
"value": 0.92
|
|
5442
|
+
}
|
|
5443
|
+
],
|
|
5444
|
+
"paramsSnapshot": [
|
|
5445
|
+
{
|
|
5446
|
+
"nodeId": "dw-322b1978",
|
|
5447
|
+
"templateRef": "dcw-temp-sp",
|
|
5448
|
+
"value": 182,
|
|
5449
|
+
"min": 176,
|
|
5450
|
+
"max": 188
|
|
5451
|
+
},
|
|
5452
|
+
{
|
|
5453
|
+
"nodeId": "dw-e92bb0e7",
|
|
5454
|
+
"templateRef": "dcw-pressure-sp",
|
|
5455
|
+
"value": 0.92,
|
|
5456
|
+
"min": 0.85,
|
|
5457
|
+
"max": 1
|
|
5458
|
+
}
|
|
5459
|
+
]
|
|
5414
5460
|
}
|
|
5415
5461
|
]
|
|
@@ -10690,5 +10690,53 @@
|
|
|
10690
10690
|
"message": "Mock PLC 写入成功:0.92 → raw 0.92,回读一致(标定后物理值 0.92)",
|
|
10691
10691
|
"recipeRunId": "rr-a6881136",
|
|
10692
10692
|
"at": "2026-09-03T01:13:27.665Z"
|
|
10693
|
+
},
|
|
10694
|
+
{
|
|
10695
|
+
"id": "wh-72cc0d94",
|
|
10696
|
+
"nodeId": "dw-322b1978",
|
|
10697
|
+
"nodeName": "温度设定器(示例)",
|
|
10698
|
+
"param": "烘箱温度设定",
|
|
10699
|
+
"eng": 182,
|
|
10700
|
+
"raw": 182,
|
|
10701
|
+
"ok": true,
|
|
10702
|
+
"message": "Mock PLC 写入成功:182 → raw 182,回读一致(标定后物理值 182)",
|
|
10703
|
+
"recipeRunId": "rr-8b294482",
|
|
10704
|
+
"at": "2026-09-03T10:17:58.888Z"
|
|
10705
|
+
},
|
|
10706
|
+
{
|
|
10707
|
+
"id": "wh-2daf0cf4",
|
|
10708
|
+
"nodeId": "dw-e92bb0e7",
|
|
10709
|
+
"nodeName": "压力设定器(示例)",
|
|
10710
|
+
"param": "熔体压力设定",
|
|
10711
|
+
"eng": 0.92,
|
|
10712
|
+
"raw": 0.92,
|
|
10713
|
+
"ok": true,
|
|
10714
|
+
"message": "Mock PLC 写入成功:0.92 → raw 0.92,回读一致(标定后物理值 0.92)",
|
|
10715
|
+
"recipeRunId": "rr-8b294482",
|
|
10716
|
+
"at": "2026-09-03T10:17:58.993Z"
|
|
10717
|
+
},
|
|
10718
|
+
{
|
|
10719
|
+
"id": "wh-d865e72d",
|
|
10720
|
+
"nodeId": "dw-322b1978",
|
|
10721
|
+
"nodeName": "温度设定器(示例)",
|
|
10722
|
+
"param": "烘箱温度设定",
|
|
10723
|
+
"eng": 182,
|
|
10724
|
+
"raw": 182,
|
|
10725
|
+
"ok": true,
|
|
10726
|
+
"message": "Mock PLC 写入成功:182 → raw 182,回读一致(标定后物理值 182)",
|
|
10727
|
+
"recipeRunId": "rr-b1f6156d",
|
|
10728
|
+
"at": "2026-09-03T10:41:14.529Z"
|
|
10729
|
+
},
|
|
10730
|
+
{
|
|
10731
|
+
"id": "wh-ef8ea3a1",
|
|
10732
|
+
"nodeId": "dw-e92bb0e7",
|
|
10733
|
+
"nodeName": "压力设定器(示例)",
|
|
10734
|
+
"param": "熔体压力设定",
|
|
10735
|
+
"eng": 0.92,
|
|
10736
|
+
"raw": 0.92,
|
|
10737
|
+
"ok": true,
|
|
10738
|
+
"message": "Mock PLC 写入成功:0.92 → raw 0.92,回读一致(标定后物理值 0.92)",
|
|
10739
|
+
"recipeRunId": "rr-b1f6156d",
|
|
10740
|
+
"at": "2026-09-03T10:41:14.679Z"
|
|
10693
10741
|
}
|
|
10694
10742
|
]
|
package/server/data/dcws.json
CHANGED
|
@@ -16,8 +16,8 @@
|
|
|
16
16
|
"posZ": 1360,
|
|
17
17
|
"lineId": "ln-af002514",
|
|
18
18
|
"value": 182,
|
|
19
|
-
"lastAckAt": "2026-09-
|
|
20
|
-
"lastWriteAt": "2026-09-
|
|
19
|
+
"lastAckAt": "2026-09-03T10:41:14.529Z",
|
|
20
|
+
"lastWriteAt": "2026-09-03T10:41:14.529Z",
|
|
21
21
|
"state": "ok",
|
|
22
22
|
"lastError": null,
|
|
23
23
|
"createdAt": "2026-08-29T03:30:57.513Z"
|
|
@@ -60,8 +60,8 @@
|
|
|
60
60
|
"posZ": 1360,
|
|
61
61
|
"lineId": "ln-af002514",
|
|
62
62
|
"value": 0.92,
|
|
63
|
-
"lastAckAt": "2026-09-
|
|
64
|
-
"lastWriteAt": "2026-09-
|
|
63
|
+
"lastAckAt": "2026-09-03T10:41:14.679Z",
|
|
64
|
+
"lastWriteAt": "2026-09-03T10:41:14.679Z",
|
|
65
65
|
"state": "ok",
|
|
66
66
|
"lastError": null,
|
|
67
67
|
"createdAt": "2026-08-29T03:30:57.533Z"
|
|
@@ -25,7 +25,7 @@ import { createLogger } from '../logger'
|
|
|
25
25
|
import { randomUUID } from 'node:crypto'
|
|
26
26
|
import { daqKeyFromRef, normalizeDataTransform, normalizeSignalKind, DAQ_DRIVERS, type AepDaqControllerState, type AepDaqFrame, type AepDaqReading, type AepDaqNodeChange, type DaqDriverKind, type DaqNodeView, type DataTransform, type DriverTestResult } from '../../../../shared/daq-protocol'
|
|
27
27
|
import { AppError, ErrorCodes } from '../../../utils/errors'
|
|
28
|
-
import { normalizeDriverKind, resolveDaqDriver, probeDriverAvailability, type DaqFrameSample } from './drivers'
|
|
28
|
+
import { normalizeDriverKind, resolveDaqDriver, probeDriverAvailability, listPluginDrivers, type DaqFrameSample } from './drivers'
|
|
29
29
|
import { findDaqTemplate } from './daq-templates'
|
|
30
30
|
import { DaqNode } from './daq-node'
|
|
31
31
|
import { DaqNodeRuntime, type DaqRuntimeHost } from './daq-runtime'
|
|
@@ -725,8 +725,8 @@ class DaqController {
|
|
|
725
725
|
}
|
|
726
726
|
|
|
727
727
|
/** 后端能力自描述(meta 用) */
|
|
728
|
-
backends(): { tsdb: string, queue: string, objectstore: string, drivers: typeof DAQ_DRIVERS } {
|
|
729
|
-
return { tsdb: getTsdb().backend, queue: g_queueBackend ?? 'inproc', objectstore: getObjectStore().backend, drivers: DAQ_DRIVERS }
|
|
728
|
+
backends(): { tsdb: string, queue: string, objectstore: string, pluginDrivers: string[], drivers: typeof DAQ_DRIVERS } {
|
|
729
|
+
return { tsdb: getTsdb().backend, queue: g_queueBackend ?? 'inproc', objectstore: getObjectStore().backend, pluginDrivers: listPluginDrivers(), drivers: DAQ_DRIVERS }
|
|
730
730
|
}
|
|
731
731
|
|
|
732
732
|
// ---------- 控制器全局 ----------
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"id":"a0ac51ef-51c0-4ff9-8484-7c90d8dd10ba","timestamp":1788400076932,"prerendered":[]}
|