agentworkshop 0.7.39 → 0.7.41
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/.AgentWorkShop/prompts/host-tools.json +55 -0
- package/.output/.AgentWorkShop/prompts/host-tools.json +55 -0
- package/.output/nitro.json +1 -1
- package/.output/public/_nuxt/88GOowgO2.js +1 -0
- package/.output/public/_nuxt/{BNT8gYU92.js → BC9wjTJK2.js} +2 -2
- package/.output/public/_nuxt/{CY9LbPCI.js → BN16aRdQ.js} +1 -1
- package/.output/public/_nuxt/BWVhZntO.js +1 -0
- package/.output/public/_nuxt/{HBLJ-wei.js → BXxH0UQE.js} +1 -1
- package/.output/public/_nuxt/{zIGh7R49.js → BneN7fbu.js} +1 -1
- package/.output/public/_nuxt/BsROmY89.js +1 -0
- package/.output/public/_nuxt/{DUrecWPx.js → C0ZrS1KL.js} +2 -2
- package/.output/public/_nuxt/{C_OLzD09.js → C4oW1HsE.js} +1 -1
- package/.output/public/_nuxt/{CUDwzgLV.js → C4s_niy-.js} +9 -9
- package/.output/public/_nuxt/CFxtLwjp.js +1 -0
- package/.output/public/_nuxt/{Cec0Ua3F.js → CS50LB_l.js} +1 -1
- package/.output/public/_nuxt/CssgYAAL2.js +1 -0
- package/.output/public/_nuxt/D6mbFA-a.js +1 -0
- package/.output/public/_nuxt/D98NH8kG.js +1 -0
- package/.output/public/_nuxt/DGIuvyRN2.js +1 -0
- package/.output/public/_nuxt/DGbd-UiG.js +1 -0
- package/.output/public/_nuxt/{CojCTLKD.js → DGj-WEBg.js} +1 -1
- package/.output/public/_nuxt/{DDShH71m.js → DS8qj8EL.js} +1 -1
- package/.output/public/_nuxt/DTFCy6h-.js +1 -0
- package/.output/public/_nuxt/{DrYV5i68.js → DUjW5Yq5.js} +1 -1
- package/.output/public/_nuxt/{45zuyAso.js → D_OTvQza.js} +1 -1
- package/.output/public/_nuxt/Dap5PmTL.js +2 -0
- package/.output/public/_nuxt/Dqw1lpr-.js +1 -0
- package/.output/public/_nuxt/DvLxILQP.js +1 -0
- package/.output/public/_nuxt/IREZ1eC2.js +1 -0
- package/.output/public/_nuxt/TownView.f-YOdXRs.css +1 -0
- package/.output/public/_nuxt/_id_.D9ymtHB5.css +1 -0
- package/.output/public/_nuxt/_id_.jf-_8E_c.css +1 -0
- package/.output/public/_nuxt/_wsId_.B4WiqkIS.css +1 -0
- package/.output/public/_nuxt/agents.ZD0rVdHE.css +1 -0
- package/.output/public/_nuxt/aml.BN8ZEW4h.css +1 -0
- package/.output/public/_nuxt/builds/latest.json +1 -1
- package/.output/public/_nuxt/builds/meta/78a295bb-2072-48a4-8b46-6d545fdae743.json +1 -0
- package/.output/public/_nuxt/ca4TJ3Op.js +1 -0
- package/.output/public/_nuxt/channel-templates.BOtahzPi.css +1 -0
- package/.output/public/_nuxt/daq.DjSgY94l.css +1 -0
- package/.output/public/_nuxt/dcw.8Y7FqCVN.css +1 -0
- package/.output/public/_nuxt/default.J32SgqFY.css +1 -0
- package/.output/public/_nuxt/entry.bdm0tv6M.css +1 -0
- package/.output/public/_nuxt/eqq3wWQF.js +18 -0
- package/.output/public/_nuxt/logs.BpKzmKsh.css +1 -0
- package/.output/public/_nuxt/monitor.D9pe7qN6.css +1 -0
- package/.output/public/_nuxt/pages.D7aSDlUI.css +1 -0
- package/.output/public/_nuxt/permissions.DwTVlFe6.css +1 -0
- package/.output/public/_nuxt/plugins.sb4nDGqr.css +1 -0
- package/.output/public/_nuxt/{Cbq-mryP.js → qr6R6Ha1.js} +1 -1
- package/.output/public/_nuxt/settings.BTgR-NiE.css +1 -0
- package/.output/public/_nuxt/teams.CxnxKVVs.css +1 -0
- package/.output/public/_nuxt/tokens.BFY7qv4f.css +1 -0
- package/.output/public/_nuxt/{town.DKIAhKnx.css → town.BBXZmeoP.css} +1 -1
- package/.output/public/_nuxt/users.DLgZ1D0h.css +1 -0
- package/.output/public/_nuxt/workshop.CR_QzeAN.css +1 -0
- package/.output/public/_nuxt/{BzHEilTt.js → xh3_vYcu.js} +1 -1
- package/.output/public/_nuxt/yF0aYMF1.js +4 -0
- package/.output/server/chunks/_/entity.mjs.map +1 -1
- package/.output/server/chunks/_/nitro.mjs +2355 -1362
- package/.output/server/chunks/_/nitro.mjs.map +1 -1
- package/.output/server/chunks/build/{TownView-jVazYOjN.mjs → TownView--otsnr_7.mjs} +290 -208
- package/.output/server/chunks/build/TownView--otsnr_7.mjs.map +1 -0
- package/.output/server/chunks/build/{_id_-CfVosNg3.mjs → _id_-Brv-JSvr.mjs} +142 -141
- package/.output/server/chunks/build/_id_-Brv-JSvr.mjs.map +1 -0
- package/.output/server/chunks/build/{_id_-Hwr4rZpr.mjs → _id_-D9pl1OZy.mjs} +38 -36
- package/.output/server/chunks/build/_id_-D9pl1OZy.mjs.map +1 -0
- package/.output/server/chunks/build/{_wsId_-BVg3bB77.mjs → _wsId_-BASfDf0s.mjs} +295 -261
- package/.output/server/chunks/build/{_wsId_-BVg3bB77.mjs.map → _wsId_-BASfDf0s.mjs.map} +1 -1
- package/.output/server/chunks/build/{agents-Bvin73QY.mjs → agents-CLQqJF2y.mjs} +21 -27
- package/.output/server/chunks/build/agents-CLQqJF2y.mjs.map +1 -0
- package/.output/server/chunks/build/{aml-BCSJCdX7.mjs → aml-_7FdFL2p.mjs} +109 -109
- package/.output/server/chunks/build/{aml-BCSJCdX7.mjs.map → aml-_7FdFL2p.mjs.map} +1 -1
- package/.output/server/chunks/build/api-error-dul30BYQ.mjs.map +1 -1
- package/.output/server/chunks/build/{channel-templates-BTq8570n.mjs → channel-templates-p6SZNjlM.mjs} +19 -19
- package/.output/server/chunks/build/{channel-templates-BTq8570n.mjs.map → channel-templates-p6SZNjlM.mjs.map} +1 -1
- package/.output/server/chunks/build/{daq-C03Uu5UE.mjs → daq-DkMvD8sg.mjs} +193 -126
- package/.output/server/chunks/build/daq-DkMvD8sg.mjs.map +1 -0
- package/.output/server/chunks/build/{dcw-6vEYVeje.mjs → dcw-C1jgVJkO.mjs} +46 -46
- package/.output/server/chunks/build/{dcw-6vEYVeje.mjs.map → dcw-C1jgVJkO.mjs.map} +1 -1
- package/.output/server/chunks/build/{default-CZmZFpqH.mjs → default-BE4r0FZN.mjs} +152 -58
- package/.output/server/chunks/build/{default-CZmZFpqH.mjs.map → default-BE4r0FZN.mjs.map} +1 -1
- package/.output/server/chunks/build/{logs-B-xhuqln.mjs → logs--dByNyhw.mjs} +35 -35
- package/.output/server/chunks/build/{logs-B-xhuqln.mjs.map → logs--dByNyhw.mjs.map} +1 -1
- package/.output/server/chunks/build/{monitor-CyfDt4nR.mjs → monitor-C0okQaXz.mjs} +33 -33
- package/.output/server/chunks/build/{monitor-CyfDt4nR.mjs.map → monitor-C0okQaXz.mjs.map} +1 -1
- package/.output/server/chunks/build/{pages-BO7yZsXA.mjs → pages-B0bUWnC6.mjs} +35 -19
- package/.output/server/chunks/build/pages-B0bUWnC6.mjs.map +1 -0
- package/.output/server/chunks/build/{permissions-B52oOmaO.mjs → permissions-68SsNjex.mjs} +12 -12
- package/.output/server/chunks/build/{permissions-B52oOmaO.mjs.map → permissions-68SsNjex.mjs.map} +1 -1
- package/.output/server/chunks/build/{plugins-CUWmMdyt.mjs → plugins-lGlUr1lj.mjs} +17 -17
- package/.output/server/chunks/build/{plugins-CUWmMdyt.mjs.map → plugins-lGlUr1lj.mjs.map} +1 -1
- package/.output/server/chunks/build/{settings-DoQ2Oq3P.mjs → settings-Ddd1qx2Z.mjs} +50 -47
- package/.output/server/chunks/build/settings-Ddd1qx2Z.mjs.map +1 -0
- package/.output/server/chunks/build/{teams-2kz3CusY.mjs → teams-MEDLZdMn.mjs} +38 -34
- package/.output/server/chunks/build/teams-MEDLZdMn.mjs.map +1 -0
- package/.output/server/chunks/build/{tokens-BbWLqtVz.mjs → tokens-C7fTM5C2.mjs} +14 -14
- package/.output/server/chunks/build/{tokens-BbWLqtVz.mjs.map → tokens-C7fTM5C2.mjs.map} +1 -1
- package/.output/server/chunks/build/{town-BOl6hs9B.mjs → town-CoQdblbZ.mjs} +38 -12
- package/.output/server/chunks/build/town-CoQdblbZ.mjs.map +1 -0
- package/.output/server/chunks/build/{useDaqStream-CIBaiGAl.mjs → useDaqStream-DfCFpb3p.mjs} +9 -3
- package/.output/server/chunks/build/useDaqStream-DfCFpb3p.mjs.map +1 -0
- package/.output/server/chunks/build/{useDcwStream-CBbiYnWe.mjs → useDcwStream-UPiX-3iO.mjs} +11 -2
- package/.output/server/chunks/build/useDcwStream-UPiX-3iO.mjs.map +1 -0
- package/.output/server/chunks/build/useResponsive-BKDMGTHs.mjs +49 -0
- package/.output/server/chunks/build/useResponsive-BKDMGTHs.mjs.map +1 -0
- package/.output/server/chunks/build/{users-fohSmF-J.mjs → users-Cfd5-l2-.mjs} +6 -6
- package/.output/server/chunks/build/{users-fohSmF-J.mjs.map → users-Cfd5-l2-.mjs.map} +1 -1
- package/.output/server/chunks/build/{workshop-DX5-ldc4.mjs → workshop-BL5bhfs0.mjs} +15 -15
- package/.output/server/chunks/build/{workshop-DX5-ldc4.mjs.map → workshop-BL5bhfs0.mjs.map} +1 -1
- package/.output/server/chunks/build/{workspaces-BuydxM-3.mjs → workspaces-Bgz5Ekwp.mjs} +18 -3
- package/.output/server/chunks/build/workspaces-Bgz5Ekwp.mjs.map +1 -0
- package/.output/server/chunks/routes/api/system/config-groups/_id_.patch.mjs.map +1 -1
- package/.output/server/chunks/routes/api/system/index.patch.mjs.map +1 -1
- package/.output/server/chunks/routes/api/system/index.post.mjs.map +1 -1
- package/.output/server/chunks/routes/api/system/settings/events.get.mjs.map +1 -1
- package/.output/server/chunks/routes/api/workshop/agent-tools/invoke.post.mjs +6 -2
- package/.output/server/chunks/routes/api/workshop/agent-tools/invoke.post.mjs.map +1 -1
- package/.output/server/chunks/routes/api/workshop/aml/datasets/_id_.get.mjs +2 -1
- package/.output/server/chunks/routes/api/workshop/aml/datasets/_id_.get.mjs.map +1 -1
- package/.output/server/chunks/routes/api/workshop/channel-templates/_id/instantiate.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/channels.post.mjs +3 -3
- package/.output/server/chunks/routes/api/workshop/daq/index.post.mjs.map +1 -1
- package/.output/server/chunks/routes/api/workshop/daq/templates/_key_.delete.mjs.map +1 -1
- package/.output/server/chunks/routes/api/workshop/daq/templates/_key_.patch.mjs.map +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/_id/write.post.mjs +3 -2
- package/.output/server/chunks/routes/api/workshop/dcw/_id/write.post.mjs.map +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/_id_.delete.mjs +2 -1
- package/.output/server/chunks/routes/api/workshop/dcw/_id_.delete.mjs.map +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/index2.post.mjs +8 -6
- package/.output/server/chunks/routes/api/workshop/dcw/index2.post.mjs.map +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/index3.post.mjs +5 -5
- package/.output/server/chunks/routes/api/workshop/dcw/index3.post.mjs.map +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/index4.get.mjs +14 -4
- package/.output/server/chunks/routes/api/workshop/dcw/index4.get.mjs.map +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/index4.post.mjs +4 -4
- package/.output/server/chunks/routes/api/workshop/dcw/index4.post.mjs.map +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/index5.get.mjs +4 -3
- package/.output/server/chunks/routes/api/workshop/dcw/index5.get.mjs.map +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/index5.post.mjs +31 -0
- package/.output/server/chunks/routes/api/workshop/dcw/index5.post.mjs.map +1 -0
- package/.output/server/chunks/routes/api/workshop/dcw/index6.get.mjs +28 -0
- package/.output/server/chunks/routes/api/workshop/dcw/index6.get.mjs.map +1 -0
- package/.output/server/chunks/routes/api/workshop/dcw/lines/_id/start.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/lines/_id/stop.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/lines/_id_.patch.mjs.map +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/params/_id/read.post.mjs +32 -0
- package/.output/server/chunks/routes/api/workshop/dcw/params/_id/read.post.mjs.map +1 -0
- package/.output/server/chunks/routes/api/workshop/dcw/params/_id/write.post.mjs +34 -0
- package/.output/server/chunks/routes/api/workshop/dcw/params/_id/write.post.mjs.map +1 -0
- package/.output/server/chunks/routes/api/workshop/dcw/params/_id_.delete.mjs +33 -0
- package/.output/server/chunks/routes/api/workshop/dcw/params/_id_.delete.mjs.map +1 -0
- package/.output/server/chunks/routes/api/workshop/dcw/params/_id_.patch.mjs +34 -0
- package/.output/server/chunks/routes/api/workshop/dcw/params/_id_.patch.mjs.map +1 -0
- package/.output/server/chunks/routes/api/workshop/dcw/templates/_key_.delete.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/templates/_key_.patch.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/harnesses/_id/providers.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/harnesses.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/hitl/pending.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/hitl/respond.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/index.put.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/index5.get.mjs +3 -1
- package/.output/server/chunks/routes/api/workshop/index5.get.mjs.map +1 -1
- package/.output/server/chunks/routes/api/workshop/index6.get.mjs +5 -1
- package/.output/server/chunks/routes/api/workshop/index6.get.mjs.map +1 -1
- package/.output/server/chunks/routes/api/workshop/index9.get.mjs +3 -1
- package/.output/server/chunks/routes/api/workshop/index9.get.mjs.map +1 -1
- package/.output/server/chunks/routes/api/workshop/plugins/_name/disable.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/plugins/_name/enable.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/scene/layouts/_channelId_.delete.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/scene/layouts/_channelId_.put.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/workspaces/_id/channel-templates/_tplId_.post.mjs +1 -1
- package/.output/server/chunks/routes/renderer.mjs +3 -3
- package/.output/server/chunks/virtual/entry.mjs +51 -25
- package/.output/server/chunks/virtual/precomputed.mjs +1 -1
- package/.output/server/package.json +1 -1
- package/README-zh.md +203 -39
- package/README.md +739 -591
- package/app/app.vue +9 -3
- package/app/assets/css/main.css +517 -7
- package/app/components/AppHeader.vue +98 -7
- package/app/components/AppSidebar.vue +117 -11
- package/app/components/AwChart.vue +2 -1
- package/app/components/workshop/AgentInspectorDrawer.vue +6 -6
- package/app/components/workshop/AgentLanesView.vue +52 -1
- package/app/components/workshop/AssetLibrary.vue +4 -2
- package/app/components/workshop/ChannelSessionList.vue +4 -2
- package/app/components/workshop/Composer.vue +96 -0
- package/app/components/workshop/DaqNodeRow.vue +19 -0
- package/app/components/workshop/DaqTemplateIcon.vue +33 -0
- package/app/components/workshop/DeviceTwinPanel.vue +37 -35
- package/app/components/workshop/EventBlock.vue +3 -3
- package/app/components/workshop/InspectorPanel.vue +47 -11
- package/app/components/workshop/LiveAgentBar.vue +34 -0
- package/app/components/workshop/TaskBoardView.vue +83 -33
- package/app/components/workshop/TranscriptTimeline.vue +56 -0
- package/app/components/workshop/blocks/ClusterError.vue +1 -1
- package/app/components/workshop/blocks/ClusterRoute.vue +3 -1
- package/app/components/workshop/blocks/ClusterStatus.vue +2 -2
- package/app/components/workshop/blocks/ClusterStream.vue +8 -2
- package/app/components/workshop/town/ModelPreview3D.vue +1 -1
- package/app/components/workshop/town/TownView.vue +507 -82
- package/app/composables/useResponsive.ts +88 -0
- package/app/composables/useRouteMeta.ts +3 -0
- package/app/composables/workshop/useDaqStream.ts +7 -4
- package/app/composables/workshop/useDcwStream.ts +8 -2
- package/app/composables/workshop/useVisibleInterval.ts +2 -2
- package/app/composables/workshop/useWorkshopApi.ts +1 -1
- package/app/layouts/default.vue +46 -2
- package/app/pages/aml/index.vue +712 -616
- package/app/pages/daq/[id].vue +12 -7
- package/app/pages/daq/index.vue +216 -13
- package/app/pages/dcw/[id].vue +117 -32
- package/app/pages/dcw/index.vue +83 -23
- package/app/pages/index.vue +35 -1
- package/app/pages/logs/index.vue +147 -13
- package/app/pages/monitor.vue +56 -12
- package/app/pages/permissions/index.vue +34 -0
- package/app/pages/plugins/index.vue +35 -4
- package/app/pages/settings.vue +90 -21
- package/app/pages/tokens.vue +23 -0
- package/app/pages/town.vue +50 -5
- package/app/pages/users.vue +23 -0
- package/app/pages/workshop/agents.vue +92 -2
- package/app/pages/workshop/channel-templates.vue +53 -0
- package/app/pages/workshop/index.vue +14 -0
- package/app/pages/workshop/teams.vue +66 -6
- package/app/pages/workshop/w/[wsId].vue +202 -14
- package/app/plugins/aw-plugins.client.ts +13 -8
- package/app/stores/app.ts +33 -1
- package/app/stores/workshop/user.ts +1 -1
- package/app/stores/workshop/workspaces.ts +18 -2
- package/app/utils/api-error.ts +1 -1
- package/cli/commands/build.mjs +8 -1
- package/docker-compose.yml +6 -0
- package/docs/cli.en.md +2 -2
- package/docs/cli.md +2 -2
- package/docs/plugins.en.md +33 -1
- package/docs/plugins.md +29 -1
- package/docs/sdk.en.md +1 -1
- package/docs/sdk.md +1 -1
- package/i18n/locales/en.ts +2 -0
- package/i18n/locales/zh-CN.ts +2 -0
- package/nuxt.config.ts +31 -0
- package/package.json +11 -2
- package/scripts/_dbg-param-map-e2e.ts +242 -0
- package/scripts/_dbg-param-map-rest-smoke.mjs +73 -0
- package/scripts/_dbg-param-real-scenario.mjs +153 -0
- package/scripts/_loop-biax-campaign.mjs +124 -0
- package/scripts/api-live-e2e.mjs +40 -8
- package/scripts/capture-walkthrough.mjs +232 -0
- package/scripts/render-walkthrough-figure.mjs +46 -0
- package/scripts/seed-walkthrough-fixture.mjs +143 -0
- package/scripts/test-plugin-hardening.mjs +8 -0
- package/scripts/test-serial-bridge.mjs +418 -0
- package/scripts/ui/admin-shots.mjs +46 -0
- package/scripts/ui/audit-layout.mjs +301 -0
- package/scripts/ui/bump-microfont.mjs +39 -0
- package/scripts/ui/e2e-interactive.mjs +328 -0
- package/scripts/ui/lib.mjs +198 -0
- package/scripts/ui/linkcheck2.mjs +37 -0
- package/scripts/ui/live-render-check.mjs +72 -0
- package/scripts/ui/md-lint.mjs +80 -0
- package/scripts/ui/perf-audit.mjs +132 -0
- package/scripts/ui/readme-assets.mjs +30 -0
- package/scripts/ui/readme-shots.mjs +42 -0
- package/scripts/ui/record.mjs +225 -0
- package/scripts/ui/seed-admin.mjs +68 -0
- package/scripts/ui/seed-demo.mjs +83 -0
- package/scripts/ui/seed-industrial-demo.mjs +171 -0
- package/scripts/ui/shot-hero.mjs +25 -0
- package/scripts/ui/shots.mjs +75 -0
- package/scripts/ui/sim-ui-shot.mjs +16 -0
- package/scripts/ui/verify-nav.mjs +51 -0
- package/scripts/ui/verify-responsive.mjs +285 -0
- package/server/api/system/config-groups/[id].patch.ts +1 -1
- package/server/api/system/config-groups/index.post.ts +1 -1
- package/server/api/system/settings/events.get.ts +1 -1
- package/server/api/system/settings/index.patch.ts +1 -1
- package/server/api/workshop/agent-tools/invoke.post.ts +16 -2
- package/server/api/workshop/aml/datasets/[id].get.ts +8 -1
- package/server/api/workshop/daq/index.get.ts +3 -1
- package/server/api/workshop/daq/infra/reconnect.post.ts +1 -1
- package/server/api/workshop/daq/templates/[key].delete.ts +1 -0
- package/server/api/workshop/daq/templates/[key].patch.ts +1 -0
- package/server/api/workshop/daq/templates/index.post.ts +1 -0
- package/server/api/workshop/dcw/[id]/write.post.ts +10 -2
- package/server/api/workshop/dcw/[id].delete.ts +5 -2
- package/server/api/workshop/dcw/index.get.ts +5 -0
- package/server/api/workshop/dcw/lines/[id].patch.ts +1 -1
- package/server/api/workshop/dcw/params/[id]/read.post.ts +17 -0
- package/server/api/workshop/dcw/params/[id]/write.post.ts +21 -0
- package/server/api/workshop/dcw/params/[id].delete.ts +19 -0
- package/server/api/workshop/dcw/params/[id].patch.ts +23 -0
- package/server/api/workshop/dcw/params/index.get.ts +27 -0
- package/server/api/workshop/dcw/params/index.post.ts +25 -0
- package/server/api/workshop/permissions/index.get.ts +3 -1
- package/server/plugins/aw-plugins.ts +23 -10
- package/server/plugins/dev-stability-guard.ts +3 -1
- package/server/plugins/system-config.ts +6 -1
- package/server/plugins-builtin/serial-bridge/client.mjs +210 -0
- package/server/plugins-builtin/serial-bridge/drivers.mjs +409 -0
- package/server/plugins-builtin/serial-bridge/i18n.json +62 -0
- package/server/plugins-builtin/serial-bridge/index.mjs +247 -0
- package/server/plugins-builtin/serial-bridge/lib.mjs +208 -0
- package/server/plugins-builtin/serial-bridge/rtu.mjs +168 -0
- package/server/services/system-config.ts +28 -9
- package/server/services/workshop/agents/adapters/one-shot-cli-agent.ts +2 -1
- package/server/services/workshop/agents/agent-interface.ts +8 -6
- package/server/services/workshop/agents/base-agent.ts +18 -4
- package/server/services/workshop/agents/claude-agent.ts +7 -3
- package/server/services/workshop/agents/codex-agent.ts +3 -3
- package/server/services/workshop/agents/dsh-agent.ts +3 -3
- package/server/services/workshop/agents/goose-agent.ts +0 -1
- package/server/services/workshop/agents/harness-terminal.ts +14 -1
- package/server/services/workshop/agents/hermes-agent.ts +4 -2
- package/server/services/workshop/agents/host-tool-bridge.ts +5 -1
- package/server/services/workshop/agents/industrial-context.ts +4 -1
- package/server/services/workshop/agents/industrial-tools.ts +164 -6
- package/server/services/workshop/agents/omp-agent.ts +15 -9
- package/server/services/workshop/agents/opencode-agent.ts +47 -21
- package/server/services/workshop/agents/qwen-agent.ts +10 -5
- package/server/services/workshop/aml/entity.ts +3 -1
- package/server/services/workshop/aml/python-runtime.ts +3 -2
- package/server/services/workshop/daq/bus/mqtt.adapter.ts +8 -4
- package/server/services/workshop/daq/daq-controller.ts +25 -5
- package/server/services/workshop/daq/daq-runtime.ts +5 -3
- package/server/services/workshop/daq/drivers.ts +141 -11
- package/server/services/workshop/daq/host-ports.ts +3 -2
- package/server/services/workshop/daq/storage/timescale.adapter.ts +3 -1
- package/server/services/workshop/db/channel.repo.ts +4 -0
- package/server/services/workshop/db/ops.repo.ts +5 -2
- package/server/services/workshop/db/retention.ts +8 -3
- package/server/services/workshop/dcw/dcw-controller.ts +147 -16
- package/server/services/workshop/dcw/dcw-product.repo.ts +38 -1
- package/server/services/workshop/dcw/dcw-runtime.ts +3 -2
- package/server/services/workshop/dcw/drivers.ts +90 -4
- package/server/services/workshop/dcw/param-conversion.ts +78 -0
- package/server/services/workshop/dcw/param-limits.ts +142 -0
- package/server/services/workshop/dcw/param-map.repo.ts +305 -0
- package/server/services/workshop/dcw/plugin-bridge.ts +45 -0
- package/server/services/workshop/dcw/recipe-rollback-manager.ts +13 -7
- package/server/services/workshop/plugins/host.mjs +30 -0
- package/server/services/workshop/runtime/agent-runtime.ts +4 -0
- package/server/services/workshop/runtime/manager.ts +9 -2
- package/server/services/workshop/settings.ts +4 -2
- package/server/types/pg.d.ts +49 -0
- package/server/utils/stability-severity.ts +13 -2
- package/shared/config/groups.mjs +22 -1
- package/shared/config/home.d.mts +28 -0
- package/shared/daq-protocol.ts +15 -0
- package/shared/dcw-protocol.ts +136 -0
- package/uno.config.ts +12 -1
- package/.output/public/_nuxt/8NMOXZOV.js +0 -1
- package/.output/public/_nuxt/BFkgkW8u.js +0 -4
- package/.output/public/_nuxt/C5gUkRwn.js +0 -1
- package/.output/public/_nuxt/CM12FuB42.js +0 -1
- package/.output/public/_nuxt/CZiw7sEV.js +0 -1
- package/.output/public/_nuxt/Cc_9VzD42.js +0 -1
- package/.output/public/_nuxt/CdPzkOMV.js +0 -1
- package/.output/public/_nuxt/CmsOtOAA2.js +0 -1
- package/.output/public/_nuxt/D1YDxZpZ.js +0 -1
- package/.output/public/_nuxt/D20Yamr4.js +0 -1
- package/.output/public/_nuxt/DAe0h2MB.js +0 -1
- package/.output/public/_nuxt/DmqknNvV.js +0 -18
- package/.output/public/_nuxt/DnUuIR83.js +0 -1
- package/.output/public/_nuxt/S_fVQwJK.js +0 -1
- package/.output/public/_nuxt/TownView.0oLiQgTC.css +0 -1
- package/.output/public/_nuxt/_id_.D-SYuyZb.css +0 -1
- package/.output/public/_nuxt/_id_.hma_BU6T.css +0 -1
- package/.output/public/_nuxt/_wsId_.B12v3PhY.css +0 -1
- package/.output/public/_nuxt/agents.BV70ioCv.css +0 -1
- package/.output/public/_nuxt/ahgEJu2R.js +0 -1
- package/.output/public/_nuxt/aml.COEKNNqK.css +0 -1
- package/.output/public/_nuxt/builds/meta/4457d63f-0866-4cea-b320-04a321f24795.json +0 -1
- package/.output/public/_nuxt/channel-templates.C-M5nKcC.css +0 -1
- package/.output/public/_nuxt/daq.B3265ZDn.css +0 -1
- package/.output/public/_nuxt/dcw.DLYZqEhT.css +0 -1
- package/.output/public/_nuxt/default.BXI_fZUi.css +0 -1
- package/.output/public/_nuxt/entry.BB-Eeh-J.css +0 -1
- package/.output/public/_nuxt/logs.CqFyMrJw.css +0 -1
- package/.output/public/_nuxt/monitor.B--1dPzs.css +0 -1
- package/.output/public/_nuxt/pages.ooEzrMm9.css +0 -1
- package/.output/public/_nuxt/permissions.B1pjGChZ.css +0 -1
- package/.output/public/_nuxt/plugins.5e5chZ67.css +0 -1
- package/.output/public/_nuxt/pz2JDAM7.js +0 -2
- package/.output/public/_nuxt/settings.Dm3M0RLy.css +0 -1
- package/.output/public/_nuxt/teams.BC75EGnR.css +0 -1
- package/.output/public/_nuxt/tokens.xRb1fSPU.css +0 -1
- package/.output/public/_nuxt/users.CXnP2E08.css +0 -1
- package/.output/public/_nuxt/workshop.Jk5HxWPE.css +0 -1
- package/.output/server/chunks/build/TownView-jVazYOjN.mjs.map +0 -1
- package/.output/server/chunks/build/_id_-CfVosNg3.mjs.map +0 -1
- package/.output/server/chunks/build/_id_-Hwr4rZpr.mjs.map +0 -1
- package/.output/server/chunks/build/agents-Bvin73QY.mjs.map +0 -1
- package/.output/server/chunks/build/daq-C03Uu5UE.mjs.map +0 -1
- package/.output/server/chunks/build/pages-BO7yZsXA.mjs.map +0 -1
- package/.output/server/chunks/build/settings-DoQ2Oq3P.mjs.map +0 -1
- package/.output/server/chunks/build/teams-2kz3CusY.mjs.map +0 -1
- package/.output/server/chunks/build/town-BOl6hs9B.mjs.map +0 -1
- package/.output/server/chunks/build/useDaqStream-CIBaiGAl.mjs.map +0 -1
- package/.output/server/chunks/build/useDcwStream-CBbiYnWe.mjs.map +0 -1
- package/.output/server/chunks/build/workspaces-BuydxM-3.mjs.map +0 -1
- /package/.output/public/_nuxt/{36oeS8_R2.js → 36oeS8_R.js} +0 -0
- /package/.output/public/_nuxt/{CYWVXZTS.js → CYWVXZTS2.js} +0 -0
package/docs/plugins.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
> 本文是唯一权威参考;`docs/site/plugins/` 的分页文档与本文同源。
|
|
7
7
|
>
|
|
8
8
|
> 适用版本:插件系统 v2 自 **v0.7.29** 起可用(前端组件注入 / 插件设置 / 插件 i18n /
|
|
9
|
-
> 运行时服务面);本文档对应当前版本 **v0.7.
|
|
9
|
+
> 运行时服务面);本文档对应当前版本 **v0.7.41**。`ctx.sdkVersion` 当前为 `0.3.0`。
|
|
10
10
|
|
|
11
11
|
## 一、目录结构与快速开始
|
|
12
12
|
|
|
@@ -207,10 +207,37 @@ async setup(ctx) {
|
|
|
207
207
|
| 事件 | `ctx.events.on(type, fn)` / `ctx.events.off(type, fn)` | scene 实时事件糖:内部订阅 `event:<type>`(见 §7) |
|
|
208
208
|
| 路径 | `ctx.paths.home / configRoot / dataDir`;`ctx.dataDir` | `ctx.dataDir` 即本插件 KV 目录 `<配置根>/data/plugins/<插件名>` |
|
|
209
209
|
| 数采 | `ctx.daq.registerDriver / registerProcessor / registerTemplate / onFrame / onSample / query / nodes` | 驱动、下沉处理器、节点模板注册 + 时序查询 + 节点元数据快照 |
|
|
210
|
+
| 写控 | `ctx.dcw.registerWriteDriver(driver)` | 写驱动注入(与数采读驱动对称):契约 `{ kind, available(), write(input), test(cfg), read?(input), meta? }`;热重载停用即失效 |
|
|
210
211
|
| 工具 | `ctx.omp.registerTool({ name, label?, description, parameters, roles?, handler })` | Agent 工具注入;`roles` 仅 `'lead'` 与 `'worker'`(见 §10) |
|
|
211
212
|
| 服务 | `ctx.services.names()` / `.get(name)` / `.provide(name, getter)` | 运行时对象面:`get('daq' \| 'lines' \| 'channels' \| 'plugins')`;`provide` 自动加 `<插件名>.` 前缀 |
|
|
212
213
|
| 权限 | `ctx.permissions.lineMode / visibleLineIds / listGrants / setGrants` | 产线授权查询与管理 |
|
|
213
214
|
|
|
215
|
+
### 通信协议插件(自定义接口连接方式)
|
|
216
|
+
|
|
217
|
+
`ctx.daq.registerDriver` / `ctx.dcw.registerWriteDriver` 是**协议接入点**:串口、其他 PLC 协议、
|
|
218
|
+
私有总线的连接实现都从这里注入,主链路(采样循环 / 写控网关 / 前端表单)零改动。
|
|
219
|
+
|
|
220
|
+
```js
|
|
221
|
+
ctx.daq.registerDriver({
|
|
222
|
+
kind: 'serial', // 自定义协议 kind(与内置冲突时插件覆盖并告警)
|
|
223
|
+
async available() { return true }, // 协议栈包探测(缺失时 UI 显示「未安装」)
|
|
224
|
+
async sample({ driverConfig }) { return 25.3 }, // 读数(标量或 v2 帧信封)
|
|
225
|
+
async test(driverConfig) { return { ok: true, message: '连接成功', sampleValue: 25.3 } },
|
|
226
|
+
meta: { // 自描述(前端「添加节点」下拉 + 动态参数表单)
|
|
227
|
+
label: '串口 Serial(RS-232/485)',
|
|
228
|
+
status: 'real',
|
|
229
|
+
configFields: [{ key: 'path', label: '串口路径', type: 'string', required: true, placeholder: 'COM3' }],
|
|
230
|
+
},
|
|
231
|
+
})
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
要点:
|
|
235
|
+
- 驱动目录经 `GET /api/workshop/daq`(读)与 `GET /api/workshop/dcw`(写)的 `drivers` 字段下发,
|
|
236
|
+
前端数采/写控页据此渲染插件协议与参数表单(带 ⌁ 徽标);不带 `meta` 的驱动只出现在可用性里;
|
|
237
|
+
- 热重载先清空全部插件驱动再重装载:**停用/卸载的协议驱动立即失效**,不会残留到重启;
|
|
238
|
+
- 内置示例:`server/plugins-builtin/serial-bridge`(串口 Modbus RTU + ASCII 行协议,含读/写驱动、
|
|
239
|
+
`health`/`ports`/`probe` API、前端面板与 `serial_ports` Agent 工具)——新协议插件建议以它为模板。
|
|
240
|
+
|
|
214
241
|
### KV 落在哪里
|
|
215
242
|
|
|
216
243
|
`<configRoot>/data/plugins/<插件名>/kv.json` —— **不在插件目录里**。写入以内存为准,200ms 防抖后 `tmp + rename` 原子落盘;关机与热重载时取消挂起的防抖并同步落盘。`ctx.kv` 只有 `get / set / all / bump` 四个方法。
|
|
@@ -521,6 +548,7 @@ export default {
|
|
|
521
548
|
- **浏览器新启用插件注入延迟 ≤15s**(WS 可用时即时)。
|
|
522
549
|
- **`server:close` / `onDispose` 依赖优雅关闭信号**,Windows 强杀进程不触发。
|
|
523
550
|
- **`aw plugin list` 只做目录扫描**,不反映装载失败与运行态健康。
|
|
551
|
+
- **协议插件的原生栈与宿主同进程**:serial-bridge 的 RTU 帧机为自研实现(单 serialport 原生栈;弃用 modbus-serial —— 其包装层在 dev 模式实测触发 V8 原生崩溃)。已知边界:**`aw dev` 进程内,串口事务「写→超时→关闭」序列可能触发 bindings-cpp 的 V8 原生崩溃**(HandleScope fatal,JS 层不可捕获,与具体串口无关);**生产模式(`aw start`)不受影响 —— 已实测同一序列在生产下诚实返回超时且服务存活**,纯打开(ASCII 探针)在 dev 下同样正常。开发期连接真实硬件请知悉此风险;对稳定性要求极高的部署可把串口网关放进独立进程(参考 plc-node-simulator 的子进程模式)。
|
|
524
552
|
|
|
525
553
|
## 十三、发布前验证清单
|
|
526
554
|
|
package/docs/sdk.en.md
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
**SDK version constants**: server-side `SDK_VERSION = '0.3.0'`, browser-side `CLIENT_SDK_VERSION = '0.3.0'`.
|
|
9
9
|
These two constants are the **interface version of the SDK itself** and evolve **independently**
|
|
10
|
-
from the npm package version (the `version` field in `package.json`, currently 0.7.
|
|
10
|
+
from the npm package version (the `version` field in `package.json`, currently 0.7.41) —
|
|
11
11
|
them being out of sync is expected. Never use `SDK_VERSION` to infer the package version, or the reverse.
|
|
12
12
|
**Module format**: ESM only (only `import` / `export`; there is no CJS entry).
|
|
13
13
|
**Types**: `sdk/index.d.mts` and `sdk/client.d.mts` ship with the package, so TypeScript projects
|
package/docs/sdk.md
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
> 零第三方运行时依赖,Node ≥ 23.4 与现代浏览器双端可用。
|
|
6
6
|
|
|
7
7
|
**SDK 版本常量**:服务端 `SDK_VERSION = '0.3.0'`,浏览器端 `CLIENT_SDK_VERSION = '0.3.0'`。
|
|
8
|
-
这两个常量是 **SDK 自身接口的版本号**,与 npm 包版本(`package.json` 的 `version`,当前 0.7.
|
|
8
|
+
这两个常量是 **SDK 自身接口的版本号**,与 npm 包版本(`package.json` 的 `version`,当前 0.7.41)
|
|
9
9
|
**各自独立演进** —— 常数不同步是预期行为,不要拿 `SDK_VERSION` 去推断包版本,反之亦然。
|
|
10
10
|
**模块形态**:ESM only(只有 `import` / `export`,没有 CJS 入口)。
|
|
11
11
|
**类型**:`sdk/index.d.mts`、`sdk/client.d.mts` 随包发布,TS 项目零配置获得提示(覆盖范围见 §7)。
|
package/i18n/locales/en.ts
CHANGED
package/i18n/locales/zh-CN.ts
CHANGED
package/nuxt.config.ts
CHANGED
|
@@ -221,6 +221,37 @@ export default defineNuxtConfig({
|
|
|
221
221
|
},
|
|
222
222
|
},
|
|
223
223
|
|
|
224
|
+
vite: {
|
|
225
|
+
build: {
|
|
226
|
+
/**
|
|
227
|
+
* rolldown 的构建期检查开关。
|
|
228
|
+
*
|
|
229
|
+
* pluginTimings 会在"插件钩子占了构建时间大头"时打一条 WARN。本项目装了
|
|
230
|
+
* unocss / antd / i18n / unplugin-icons 等一堆构建期插件,这条 WARN 每次必现 ——
|
|
231
|
+
* 它是**构建自身的耗时画像**,不是代码质量问题,也无法通过改我们的代码消除
|
|
232
|
+
* (unocss 的 renderChunk 调用 93 次、占 ~19%,这是它的工作方式)。
|
|
233
|
+
*
|
|
234
|
+
* 只关这一项。其余 checks(命名空间当函数调用、配置冲突、无用的内置特性替代等)
|
|
235
|
+
* 都是**真的可能在抓 bug** 的检查,保持开启。
|
|
236
|
+
*/
|
|
237
|
+
rolldownOptions: {
|
|
238
|
+
checks: { pluginTimings: false },
|
|
239
|
+
},
|
|
240
|
+
/**
|
|
241
|
+
* 客户端单块体积阈值。
|
|
242
|
+
*
|
|
243
|
+
* 默认的 500kB 对本项目不适用:three.js(数字孪生,~1.35MB)与
|
|
244
|
+
* echarts(~650kB)是重型第三方库的固有体积,而且都已经是**路由级动态导入**
|
|
245
|
+
* ——/town 才加载 three,只有真正进入孪生页的用户才付这份下载。
|
|
246
|
+
* 继续拆分的收益很小(它们本身就是单个不可再分的库)。
|
|
247
|
+
*
|
|
248
|
+
* 阈值提到 1500kB 的用意不是"把告警关掉",而是让这条告警重新变得**有意义**:
|
|
249
|
+
* 一旦某天我们自己的代码或某个新依赖把这个数字推过 1.5MB,它照样会报。
|
|
250
|
+
*/
|
|
251
|
+
chunkSizeWarningLimit: 1500,
|
|
252
|
+
},
|
|
253
|
+
},
|
|
254
|
+
|
|
224
255
|
typescript: {
|
|
225
256
|
strict: true,
|
|
226
257
|
typeCheck: false,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agentworkshop",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.41",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "AgentWorkShop 软件开发系统 - 基于 Nuxt 4 的配置驱动运行范式",
|
|
6
6
|
"license": "PolyForm-Noncommercial-1.0.0",
|
|
@@ -80,6 +80,13 @@
|
|
|
80
80
|
"prepare": "husky",
|
|
81
81
|
"game:test": "tsx scripts/test-agent-brain.ts && tsx scripts/test-game-session.ts",
|
|
82
82
|
"test:api-live": "node scripts/api-live-e2e.mjs",
|
|
83
|
+
"ui:seed": "node scripts/ui/seed-demo.mjs",
|
|
84
|
+
"ui:shots": "node scripts/ui/shots.mjs",
|
|
85
|
+
"ui:audit": "node scripts/ui/audit-layout.mjs",
|
|
86
|
+
"ui:verify": "node scripts/ui/verify-responsive.mjs",
|
|
87
|
+
"ui:verify-nav": "node scripts/ui/verify-nav.mjs",
|
|
88
|
+
"readme:lint": "node scripts/ui/md-lint.mjs README.md README-zh.md",
|
|
89
|
+
"readme:assets": "node scripts/ui/readme-assets.mjs",
|
|
83
90
|
"game:verify": "node scripts/verify-game.mjs",
|
|
84
91
|
"game:build-assets": "node scripts/build-player-sheet.mjs",
|
|
85
92
|
"game:build-wuwa": "node scripts/build-wuwa-assets.mjs",
|
|
@@ -138,7 +145,8 @@
|
|
|
138
145
|
"zod": "^3.24.1"
|
|
139
146
|
},
|
|
140
147
|
"optionalDependencies": {
|
|
141
|
-
"onnxruntime-node": "^1.20.1"
|
|
148
|
+
"onnxruntime-node": "^1.20.1",
|
|
149
|
+
"serialport": "^13.0.0"
|
|
142
150
|
},
|
|
143
151
|
"devDependencies": {
|
|
144
152
|
"@commitlint/cli": "^19.6.1",
|
|
@@ -146,6 +154,7 @@
|
|
|
146
154
|
"@nuxt/eslint": "^1.0.0",
|
|
147
155
|
"@types/js-yaml": "^4.0.9",
|
|
148
156
|
"@types/node": "^26.2.0",
|
|
157
|
+
"@types/pg": "^8.23.1",
|
|
149
158
|
"@types/three": "^0.185.4",
|
|
150
159
|
"eslint": "^9.17.0",
|
|
151
160
|
"husky": "^9.1.7",
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 工艺参数映射层 E2E(真实 PLC 模拟器连通):
|
|
3
|
+
* ①节点创建自动生成工艺参数映射 + 参数面不透出 PLC 寻址细节
|
|
4
|
+
* ②真实 Modbus TCP 从站(dev-plc-simulator,一阶惯性温度回路)参数写→回读 roundtrip
|
|
5
|
+
* ③参数基准限界联锁(常驻层) ④产品限界 + 配方窗口分层联锁(产线运行期)
|
|
6
|
+
* ⑤配方下发同样受限(run.results 记失败) ⑥Agent param_control/param_read(绑定/越权/消歧)
|
|
7
|
+
* ⑦节点删除级联清理映射 ⑧运行中 plc-node-simulator(16040)导出配置连通性交叉验证
|
|
8
|
+
* 数据目录 AW_HOME 隔离,不触碰检出内 .AgentWorkShop。
|
|
9
|
+
* 运行: npx tsx --tsconfig .nuxt/tsconfig.server.json scripts/_dbg-param-map-e2e.ts
|
|
10
|
+
*/
|
|
11
|
+
import { mkdtempSync } from 'node:fs'
|
|
12
|
+
import { tmpdir } from 'node:os'
|
|
13
|
+
import { join } from 'node:path'
|
|
14
|
+
// @ts-expect-error 既有验证脚本助手(无类型声明)
|
|
15
|
+
import { startPlcSim } from './_lib-plc-sim-child.mjs'
|
|
16
|
+
|
|
17
|
+
// 进程级隔离:配置根落在一次性目录(须在业务模块 import 前设置)
|
|
18
|
+
const awHome = mkdtempSync(join(tmpdir(), 'aw-param-map-e2e-'))
|
|
19
|
+
process.env.AW_MODE = 'home'
|
|
20
|
+
process.env.AW_HOME = awHome
|
|
21
|
+
process.env.NO_PROXY = '127.0.0.1,localhost'
|
|
22
|
+
|
|
23
|
+
let failures = 0
|
|
24
|
+
const check = (name: string, ok: boolean, detail = ''): void => {
|
|
25
|
+
console.log(` ${ok ? 'PASS' : 'FAIL'} ${name}${detail ? ` — ${detail}` : ''}`)
|
|
26
|
+
if (!ok) failures += 1
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// ===== 真实 Modbus 从站(scripts/dev-plc-simulator.mjs:40021 温度SP / 40001 温度PV,float32 大端) =====
|
|
30
|
+
const SIM_PORT = 15040
|
|
31
|
+
const sim = startPlcSim(SIM_PORT)
|
|
32
|
+
const simReady: Promise<boolean> = sim.ready
|
|
33
|
+
|
|
34
|
+
// ===== 服务层导入(隔离根内) =====
|
|
35
|
+
const { getDcwController } = await import('../server/services/workshop/dcw/dcw-controller')
|
|
36
|
+
const { getDcwParamRepo } = await import('../server/services/workshop/dcw/param-map.repo')
|
|
37
|
+
const { getDcwLineRepo } = await import('../server/services/workshop/dcw/dcw-line.repo')
|
|
38
|
+
const { getDcwProductRepo } = await import('../server/services/workshop/dcw/dcw-product.repo')
|
|
39
|
+
const { getDcwRecipeRepo } = await import('../server/services/workshop/dcw/dcw-recipe.repo')
|
|
40
|
+
const { getAgentNodeBindingRepo } = await import('../server/services/workshop/agents/node-bindings.repo')
|
|
41
|
+
const { toolParamControl, toolParamRead } = await import('../server/services/workshop/agents/industrial-tools')
|
|
42
|
+
|
|
43
|
+
const ctrl = getDcwController()
|
|
44
|
+
const paramRepo = getDcwParamRepo()
|
|
45
|
+
|
|
46
|
+
/** 捕获 AppError 的消息与状态码 */
|
|
47
|
+
async function errOf(fn: () => Promise<unknown> | unknown): Promise<{ status: number, message: string }> {
|
|
48
|
+
try {
|
|
49
|
+
await fn()
|
|
50
|
+
return { status: 0, message: '(未抛错)' }
|
|
51
|
+
}
|
|
52
|
+
catch (err) {
|
|
53
|
+
const e = err as { status?: number, statusCode?: number, message?: string }
|
|
54
|
+
return { status: e.status ?? e.statusCode ?? 500, message: e.message ?? String(err) }
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
try {
|
|
59
|
+
console.log('\n--- ① 节点创建 → 自动工艺参数映射(参数面语义) ---')
|
|
60
|
+
if (!await simReady) throw new Error(`Modbus 模拟器未就绪(${SIM_PORT},可能端口被占)`)
|
|
61
|
+
console.log(` 模拟器就绪 @ 127.0.0.1:${SIM_PORT}`)
|
|
62
|
+
const line = getDcwLineRepo().create({ name: '参数映射验证线' })
|
|
63
|
+
const node = ctrl.create({
|
|
64
|
+
templateRef: 'dcw-temp-sp',
|
|
65
|
+
name: 'PP验证·烘箱温度',
|
|
66
|
+
driver: 'modbus-tcp',
|
|
67
|
+
driverConfig: { host: '127.0.0.1', port: SIM_PORT, unitId: 1, register: 40021, dataType: 'float32', byteOrder: 'big' },
|
|
68
|
+
min: 0,
|
|
69
|
+
max: 300,
|
|
70
|
+
readIntervalMs: 0,
|
|
71
|
+
lineId: line.id,
|
|
72
|
+
})
|
|
73
|
+
const prow = paramRepo.byNode(node.id)
|
|
74
|
+
check('节点创建自动生成工艺参数映射', !!prow && prow.key === 'temp-sp')
|
|
75
|
+
const pview0 = paramRepo.listViews().find(p => p.nodeId === node.id)
|
|
76
|
+
check('参数视图为语义面(无 driverConfig/寄存器/数据类型/字节序)', !!pview0
|
|
77
|
+
&& !('driverConfig' in (pview0 ?? {}))
|
|
78
|
+
&& !JSON.stringify(pview0).includes('register')
|
|
79
|
+
&& !JSON.stringify(pview0).includes('float32')
|
|
80
|
+
&& !JSON.stringify(pview0).includes('byteOrder'),
|
|
81
|
+
JSON.stringify(pview0).slice(0, 120))
|
|
82
|
+
check('参数视图携带驱动类别(仅类别)与产线归属', pview0?.driver === 'modbus-tcp' && pview0?.lineId === line.id)
|
|
83
|
+
const pid = pview0!.id
|
|
84
|
+
|
|
85
|
+
console.log('\n--- ② 真实 Modbus 参数写→回读 roundtrip(工程量纲直写) ---')
|
|
86
|
+
const w1 = await ctrl.writeParam(pid, 180, { source: 'manual', actor: 'e2e', actorName: 'E2E' })
|
|
87
|
+
check('参数写入成功(180℃)', w1.ok, w1.message)
|
|
88
|
+
check('PLC 同址回读一致(标定后物理值)', w1.readback != null && Math.abs((w1.readback ?? 0) - 180) < 0.5, `readback=${w1.readback}`)
|
|
89
|
+
check('执行节点设定值回填', ctrl.byId(node.id)?.value != null && Math.abs((ctrl.byId(node.id)!.value ?? 0) - 180) < 0.5)
|
|
90
|
+
await ctrl.readParam(pid).then((r) => {
|
|
91
|
+
check('参数读取成功(PLC 当前值)', r.ok && Math.abs((r.value ?? 0) - 180) < 0.5, `value=${r.value}`)
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
console.log('\n--- ③ 工艺参数基准限界(常驻层) ---')
|
|
95
|
+
paramRepo.update(pid, { min: 100, max: 250 })
|
|
96
|
+
const eHi = await errOf(() => ctrl.writeParam(pid, 260))
|
|
97
|
+
check('超出基准限界 260 → 拒绝且点名参数层', eHi.status === 400 && eHi.message.includes('基准限界'), eHi.message.slice(0, 90))
|
|
98
|
+
const eLo = await errOf(() => ctrl.writeParam(pid, 90))
|
|
99
|
+
check('低于基准限界 90 → 拒绝', eLo.status === 400 && eLo.message.includes('基准限界'))
|
|
100
|
+
const w2 = await ctrl.writeParam(pid, 200)
|
|
101
|
+
check('限界内 200 → 通过', w2.ok)
|
|
102
|
+
const eNode = await errOf(() => ctrl.write(node.id, 260))
|
|
103
|
+
check('节点面直写越参数限界 → 同样拒绝(同一咽喉点)', eNode.status === 400 && eNode.message.includes('基准限界'))
|
|
104
|
+
|
|
105
|
+
console.log('\n--- ④ 产品限界 + 配方窗口(产线运行期分层联锁) ---')
|
|
106
|
+
const eBadKey = await errOf(() => ctrl.createProduct({ name: 'PP验证产品·坏键', lineId: line.id, paramLimits: { 'no-such-param': { min: 1, max: 2 } } }))
|
|
107
|
+
check('产品限界引用不存在的参数键 → 400(防静默失效)', eBadKey.status === 400 && eBadKey.message.includes('no-such-param'))
|
|
108
|
+
const eBadRange = await errOf(() => ctrl.createProduct({ name: 'PP验证产品·坏区间', lineId: line.id, paramLimits: { 'temp-sp': { min: 200, max: 150 } } }))
|
|
109
|
+
check('产品限界 min>max → 400', eBadRange.status === 400)
|
|
110
|
+
const product = ctrl.createProduct({
|
|
111
|
+
name: 'PP验证产品',
|
|
112
|
+
lineId: line.id,
|
|
113
|
+
paramLimits: { 'temp-sp': { min: 150, max: 210 } },
|
|
114
|
+
})
|
|
115
|
+
check('产品限界设定成功(150~210)', (product.paramLimits?.['temp-sp']?.max ?? 0) === 210)
|
|
116
|
+
const recipe = ctrl.createRecipe({
|
|
117
|
+
productId: product.id,
|
|
118
|
+
name: 'PP验证配方',
|
|
119
|
+
params: [{ nodeId: node.id, value: 180, min: 160, max: 220 }],
|
|
120
|
+
})
|
|
121
|
+
const run = await ctrl.lineStart(line.id, recipe.id)
|
|
122
|
+
check('产线开跑(批次激活)', !!run.id)
|
|
123
|
+
const eProduct = await errOf(() => ctrl.writeParam(pid, 215))
|
|
124
|
+
check('215 在参数限界内但超产品上限 210 → 拒绝且点名产品层', eProduct.status === 400 && eProduct.message.includes('产品'), eProduct.message.slice(0, 90))
|
|
125
|
+
const eRecipe = await errOf(() => ctrl.writeParam(pid, 155))
|
|
126
|
+
check('155 在产品限界内但低于配方下限 160 → 拒绝且点名配方层', eRecipe.status === 400 && eRecipe.message.includes('配方'), eRecipe.message.slice(0, 90))
|
|
127
|
+
const w3 = await ctrl.writeParam(pid, 170)
|
|
128
|
+
check('170 四层交集内 → 通过', w3.ok)
|
|
129
|
+
const eNodeGate = await errOf(() => ctrl.write(node.id, 215))
|
|
130
|
+
check('节点面直写 215 → 产品层同样拦截', eNodeGate.status === 400 && eNodeGate.message.includes('产品'))
|
|
131
|
+
|
|
132
|
+
console.log('\n--- ⑤ 配方下发同样受限(run.results 记失败;产线保持运行) ---')
|
|
133
|
+
ctrl.updateRecipe(recipe.id, { params: [{ nodeId: node.id, value: 215, min: 160, max: 220 }] }, { by: 'user', actor: 'e2e', actorName: 'E2E', description: '压到产品上限外验证下发拦截' })
|
|
134
|
+
const run2 = await ctrl.applyRecipe(recipe.id)
|
|
135
|
+
const r2 = run2.results[0] as { ok: boolean, message: string }
|
|
136
|
+
check('配方参数 215 超产品上限 210 → 下发失败入档(不阻塞批次)', r2.ok === false && r2.message.includes('产品'), r2.message.slice(0, 90))
|
|
137
|
+
ctrl.updateRecipe(recipe.id, { params: [{ nodeId: node.id, value: 180, min: 160, max: 220 }] }, { by: 'user', actor: 'e2e', actorName: 'E2E', description: '回退验证值' })
|
|
138
|
+
const run3 = await ctrl.applyRecipe(recipe.id)
|
|
139
|
+
check('配方参数 180 限界内 → 下发成功', (run3.results[0] as { ok: boolean }).ok === true)
|
|
140
|
+
// 跨配方下发不误伤:配方乙自身窗口 [150,210] 值 155,低于活动批次配方甲窗口下限 160
|
|
141
|
+
// —— 配方下发路径跳过配方层(自身窗口保存时已校验),产品/参数层照常 → 应放行
|
|
142
|
+
const recipeB = ctrl.createRecipe({ productId: product.id, name: 'PP验证配方·乙', params: [{ nodeId: node.id, value: 155, min: 150, max: 210 }] })
|
|
143
|
+
const runB = await ctrl.applyRecipe(recipeB.id)
|
|
144
|
+
const rB = runB.results[0] as { ok: boolean, message: string }
|
|
145
|
+
check('运行中应用配方乙(155 在甲窗口外、乙窗口内)→ 不被旧批次窗口误伤', rB.ok === true, rB.message.slice(0, 90))
|
|
146
|
+
|
|
147
|
+
console.log('\n--- ⑥ Agent 参数面(param_control/param_read;批次运行中) ---')
|
|
148
|
+
const AGENT = 'agent-pp-verify'
|
|
149
|
+
getAgentNodeBindingRepo().bind(AGENT, node.id, 'dcw', 'auto')
|
|
150
|
+
const t1 = await toolParamControl(AGENT, { param: 'temp-sp', value: 175 })
|
|
151
|
+
check('param_control 按 key 下发成功(语义回包含参数名/有效区间)', !t1.isError && t1.text.includes('工艺参数') && t1.text.includes('有效写入区间'), t1.text.slice(0, 100))
|
|
152
|
+
const t2 = await toolParamControl(AGENT, { param: 'temp-sp', value: 215 })
|
|
153
|
+
check('param_control 超产品限界 → 拒绝(文案含产品层)', t2.isError === true && t2.text.includes('产品'), t2.text.slice(0, 100))
|
|
154
|
+
const t3 = await toolParamControl(AGENT, { param: pid, value: 168 })
|
|
155
|
+
check('param_control 按 pp-id 下发成功', !t3.isError)
|
|
156
|
+
const t4 = await toolParamRead(AGENT, { param: 'temp-sp' })
|
|
157
|
+
check('param_read 返回读数 + 限界区间', !t4.isError && t4.text.includes('PLC 读数'), t4.text.slice(0, 80))
|
|
158
|
+
const t5 = await toolParamControl('agent-nobody', { param: 'temp-sp', value: 170 })
|
|
159
|
+
check('未绑定 Agent → 拒绝', t5.isError === true)
|
|
160
|
+
// 跨产线:第二条线同模板节点,key 产线内唯一
|
|
161
|
+
const line2 = getDcwLineRepo().create({ name: '参数映射验证线·乙' })
|
|
162
|
+
const node2 = ctrl.create({ templateRef: 'dcw-speed-sp', name: 'PP验证乙·速度', driver: 'mock', driverConfig: { key: `pp-e2e-2-${Date.now()}` }, readIntervalMs: 0, lineId: line2.id })
|
|
163
|
+
check('第二产线节点自动映射(speed-sp)', paramRepo.byNode(node2.id)?.key === 'speed-sp')
|
|
164
|
+
const t6 = await toolParamControl(AGENT, { param: 'speed-sp', value: 300 })
|
|
165
|
+
check('未绑定节点的参数 → 拒绝(权限沿用节点绑定)', t6.isError === true, t6.text.slice(0, 80))
|
|
166
|
+
|
|
167
|
+
console.log('\n--- ⑦ 级联:节点删除 → 参数映射自动清理 ---')
|
|
168
|
+
ctrl.remove(node2.id)
|
|
169
|
+
check('执行节点删除后映射移除', paramRepo.byNode(node2.id) == null)
|
|
170
|
+
|
|
171
|
+
console.log('\n--- ⑧ 运行中 plc-node-simulator(16040)连通性交叉验证 ---')
|
|
172
|
+
try {
|
|
173
|
+
const res = await fetch('http://127.0.0.1:4010/api/nodes', { signal: AbortSignal.timeout(3000) }).then(r => r.json()) as { data?: Array<{ id: string, protocol: string }> }
|
|
174
|
+
const mbNode = (res.data ?? []).find(n => n.protocol === 'modbus-tcp')
|
|
175
|
+
if (mbNode) {
|
|
176
|
+
const exp = await fetch(`http://127.0.0.1:4010/api/nodes/${mbNode.id}/export`, { signal: AbortSignal.timeout(3000) }).then(r => r.json()) as { data?: { items?: Array<{ driver: string, driverConfig: Record<string, unknown> }> } }
|
|
177
|
+
const cfg = exp.data?.items?.[0]?.driverConfig
|
|
178
|
+
const test = await ctrl.testDriver('modbus-tcp', cfg as Record<string, unknown>)
|
|
179
|
+
check('五协议模拟器导出配置 → 网关连通测试通过', test.ok === true, JSON.stringify(cfg))
|
|
180
|
+
}
|
|
181
|
+
else {
|
|
182
|
+
check('五协议模拟器连通性交叉验证', false, '无 modbus-tcp 设备(模拟器未启动或无预设)')
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
catch (err) {
|
|
186
|
+
check('五协议模拟器连通性交叉验证', false, `模拟器不可达:${err instanceof Error ? err.message : String(err)}`)
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
console.log('\n--- ⑨ 标准转换模式:线级双向换算验证(原始寄存器字核对) ---')
|
|
190
|
+
const ModbusRTU = (await import('modbus-serial')).default
|
|
191
|
+
const raw = new ModbusRTU()
|
|
192
|
+
await raw.connectTCP('127.0.0.1', { port: SIM_PORT })
|
|
193
|
+
raw.setID(1)
|
|
194
|
+
// float32 直写模式:速度SP 40023(2 寄存器,大端)
|
|
195
|
+
const f32 = ctrl.createParamMapping({
|
|
196
|
+
key: 'oven_f32', templateRef: 'dcw-speed-sp', name: '转换验证·float32', lineId: line.id,
|
|
197
|
+
access: { host: '127.0.0.1', port: SIM_PORT, unitId: 1, register: 40023, conversion: { mode: 'float32' } },
|
|
198
|
+
})
|
|
199
|
+
check('access 一键建映射:自动建节点+参数面+float32 模式留存', !!f32.nodeId && f32.conversion?.mode === 'float32', `${f32.id} → ${f32.nodeId}`)
|
|
200
|
+
check('映射视图仍不透出 driverConfig', !JSON.stringify(f32).includes('driverConfig'))
|
|
201
|
+
await ctrl.writeParam(f32.id, 320)
|
|
202
|
+
const w32 = await raw.readHoldingRegisters(40023 - 40001, 2)
|
|
203
|
+
const buf32 = Buffer.alloc(4)
|
|
204
|
+
buf32.writeUInt16BE(w32.data[0]!, 0)
|
|
205
|
+
buf32.writeUInt16BE(w32.data[1]!, 2)
|
|
206
|
+
check('float32 直写:PLC 寄存器字 = float32BE(320)', Math.abs(buf32.readFloatBE(0) - 320) < 0.01, `words=[${w32.data}]`)
|
|
207
|
+
// int16 线性标定模式:eng 0~50 ↔ raw 0~500,寄存器 40025(1 寄存器)
|
|
208
|
+
const i16 = ctrl.createParamMapping({
|
|
209
|
+
key: 'oven_i16', templateRef: 'dcw-tension-sp', name: '转换验证·int16标定', lineId: line.id,
|
|
210
|
+
access: { host: '127.0.0.1', port: SIM_PORT, unitId: 1, register: 40025, conversion: { mode: 'int16-scaled', engMin: 0, engMax: 50, rawMin: 0, rawMax: 500 } },
|
|
211
|
+
})
|
|
212
|
+
await ctrl.writeParam(i16.id, 22)
|
|
213
|
+
const w16 = await raw.readHoldingRegisters(40025 - 40001, 1)
|
|
214
|
+
check('int16 线性标定写:eng 22 → 原始字 220(0.1 分辨率)', w16.data[0] === 220, `word=${w16.data[0]}`)
|
|
215
|
+
// 读方向:原始字 300 → 工程值 30.0(接收同样自动换算)
|
|
216
|
+
await raw.writeRegister(40025 - 40001, 300)
|
|
217
|
+
const rd16 = await ctrl.readParam(i16.id)
|
|
218
|
+
check('int16 线性标定读:原始字 300 → 工程值 30', rd16.ok && Math.abs((rd16.value ?? 0) - 30) < 0.01, `value=${rd16.value}`)
|
|
219
|
+
// 非法转换模式在配置期即拒
|
|
220
|
+
const eBadConv = await errOf(() => ctrl.createParamMapping({
|
|
221
|
+
key: 'bad-conv', templateRef: 'dcw-temp-sp', lineId: line.id,
|
|
222
|
+
access: { host: '127.0.0.1', register: 40021, conversion: { mode: 'int16-scaled' } as never },
|
|
223
|
+
}))
|
|
224
|
+
check('int16 标定缺量程 → 配置期 400 拒绝', eBadConv.status === 400, eBadConv.message.slice(0, 80))
|
|
225
|
+
raw.close()
|
|
226
|
+
|
|
227
|
+
console.log('\n--- 清理 ---')
|
|
228
|
+
// removeLine 自带活动批次窗口停止;lineStop 为同步抛错方法,勿在此直调
|
|
229
|
+
await ctrl.removeLine(line.id, { purge: true })
|
|
230
|
+
await ctrl.removeLine(line2.id, { purge: true })
|
|
231
|
+
check('产线 purge 后参数映射全部清理', paramRepo.listViews().filter(p => [line.id, line2.id].includes(p.lineId)).length === 0)
|
|
232
|
+
}
|
|
233
|
+
catch (err) {
|
|
234
|
+
failures += 1
|
|
235
|
+
console.error('\nE2E 异常中断:', err)
|
|
236
|
+
}
|
|
237
|
+
finally {
|
|
238
|
+
sim.kill()
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
console.log(`\n${failures === 0 ? '✅ 全部通过' : `❌ ${failures} 项失败`} (AW_HOME=${awHome})`)
|
|
242
|
+
process.exit(failures === 0 ? 0 : 1)
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 工艺参数映射 REST 冒烟(真实 dev server):
|
|
3
|
+
* ①params 列表(登录态 + 产线可见性过滤) ②建节点自动生成参数面(REST 视图无寄存器细节)
|
|
4
|
+
* ③param 写/读路由 ④param 基准限界 → 越界 400 ⑤产品限界落库 → 运行期产品层拦截
|
|
5
|
+
* ⑥清理(产线 purge)。mock 驱动,不依赖外部模拟器。
|
|
6
|
+
* 运行: node scripts/_dbg-param-map-rest-smoke.mjs(ROOT/E2E_USER/E2E_PASS 可覆盖)
|
|
7
|
+
*/
|
|
8
|
+
const ROOT = process.env.E2E_ROOT ?? 'http://127.0.0.1:3001'
|
|
9
|
+
let failed = 0
|
|
10
|
+
const check = (name, ok, detail = '') => { console.log(` ${ok ? 'PASS' : 'FAIL'} ${name}${detail ? ` — ${detail}` : ''}`); if (!ok) failed++ }
|
|
11
|
+
|
|
12
|
+
/** 认证:AW_E2E_TOKEN 直用(隔离实例首注册 token);否则 E2E_USER/E2E_PASS 登录(docs/full-test-plan.md 测试账号) */
|
|
13
|
+
let __token = process.env.AW_E2E_TOKEN
|
|
14
|
+
if (!__token) {
|
|
15
|
+
const login = await fetch(`${ROOT}/api/users/login`, { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ email: process.env.E2E_USER ?? 'admin@awshop.local', password: process.env.E2E_PASS ?? 'admin123' }) }).then(r => r.json())
|
|
16
|
+
__token = login.data?.token
|
|
17
|
+
check('登录', !!__token, login.message ?? '')
|
|
18
|
+
}
|
|
19
|
+
else {
|
|
20
|
+
check('使用 AW_E2E_TOKEN', true)
|
|
21
|
+
}
|
|
22
|
+
const H = { authorization: `Bearer ${__token}`, 'content-type': 'application/json' }
|
|
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
|
+
try {
|
|
26
|
+
// ① 列表路由
|
|
27
|
+
const list = await j('/api/workshop/dcw/params?limits=1')
|
|
28
|
+
check('GET /dcw/params 路由可用(含 limits 剖面)', Array.isArray(list.data?.params) && list.data.limits != null, `params=${list.data?.params?.length}`)
|
|
29
|
+
|
|
30
|
+
// ② 建线 + 建节点(mock)→ 自动参数面
|
|
31
|
+
const line = (await j('/api/workshop/dcw/lines', 'POST', { name: `PP冒烟线-${Date.now().toString(36)}` })).data?.line
|
|
32
|
+
check('创建产线', !!line?.id)
|
|
33
|
+
const node = (await j('/api/workshop/dcw', 'POST', { templateRef: 'dcw-pressure-sp', name: 'PP冒烟·熔体压力', driver: 'mock', driverConfig: { key: `pp-rest-${Date.now()}` }, readIntervalMs: 0, lineId: line.id })).data?.node
|
|
34
|
+
check('创建执行节点(mock)', !!node?.id)
|
|
35
|
+
const after = await j('/api/workshop/dcw/params')
|
|
36
|
+
const pv = after.data.params.find(p => p.nodeId === node.id)
|
|
37
|
+
check('节点创建自动生成参数面(pressure-sp)', pv?.key === 'pressure-sp', pv?.id)
|
|
38
|
+
check('REST 参数视图不含寄存器/数据类型细节', !JSON.stringify(pv).includes('register') && !JSON.stringify(pv).includes('dataType'))
|
|
39
|
+
|
|
40
|
+
// ③④ 参数写/读 + 基准限界
|
|
41
|
+
const w1 = (await j(`/api/workshop/dcw/params/${pv.id}/write`, 'POST', { value: 0.9 })).data?.outcome
|
|
42
|
+
check('param write 0.9MPa 成功(mock 回读一致)', w1?.ok === true, w1?.message)
|
|
43
|
+
const rd = (await j(`/api/workshop/dcw/params/${pv.id}/read`, 'POST', {})).data?.read
|
|
44
|
+
check('param read 返回物理值', rd?.ok === true && Math.abs((rd.value ?? 0) - 0.9) < 0.01, `value=${rd?.value}`)
|
|
45
|
+
await j(`/api/workshop/dcw/params/${pv.id}`, 'PATCH', { min: 0.7, max: 1.1 })
|
|
46
|
+
const w2 = await j(`/api/workshop/dcw/params/${pv.id}/write`, 'POST', { value: 1.15 })
|
|
47
|
+
check('越基准限界 1.15 → 400 点名参数层', w2.code === 'VALIDATION_ERROR' && w2.message?.includes('基准限界'), w2.message?.slice(0, 80))
|
|
48
|
+
|
|
49
|
+
// ⑤ 产品限界 → 运行期产品层拦截
|
|
50
|
+
const product = (await j('/api/workshop/dcw/products', 'POST', { name: 'PP冒烟产品', lineId: line.id, paramLimits: { 'pressure-sp': { min: 0.8, max: 1.0 } } })).data?.product
|
|
51
|
+
check('产品限界落库(0.8~1.0)', product?.paramLimits?.['pressure-sp']?.max === 1.0)
|
|
52
|
+
const recipe = (await j('/api/workshop/dcw/recipes', 'POST', { productId: product.id, name: 'PP冒烟配方', params: [{ nodeId: node.id, value: 0.9, min: 0.75, max: 1.05 }] })).data?.recipe
|
|
53
|
+
const start = await j(`/api/workshop/dcw/lines/${line.id}/start`, 'POST', { recipeId: recipe.id })
|
|
54
|
+
check('产线开跑', !!start.data?.run?.id, start.message)
|
|
55
|
+
const w3 = await j(`/api/workshop/dcw/params/${pv.id}/write`, 'POST', { value: 1.05 })
|
|
56
|
+
check('1.05 超产品上限 1.0 → 400 点名产品层', w3.code === 'VALIDATION_ERROR' && w3.message?.includes('产品'), w3.message?.slice(0, 80))
|
|
57
|
+
const w4 = (await j(`/api/workshop/dcw/params/${pv.id}/write`, 'POST', { value: 0.95 })).data?.outcome
|
|
58
|
+
check('0.95 交集内 → 通过', w4?.ok === true)
|
|
59
|
+
await j(`/api/workshop/dcw/lines/${line.id}/stop`, 'POST', {})
|
|
60
|
+
|
|
61
|
+
// ⑥ 清理
|
|
62
|
+
const del = await j(`/api/workshop/dcw/lines/${line.id}?purge=1`, 'DELETE')
|
|
63
|
+
check('产线 purge 清理', del.code === 0 || del.removed != null || del.data != null, JSON.stringify(del).slice(0, 80))
|
|
64
|
+
const afterDel = await j('/api/workshop/dcw/params')
|
|
65
|
+
check('purge 后参数面同步清理', !afterDel.data.params.some(p => p.id === pv.id))
|
|
66
|
+
}
|
|
67
|
+
catch (err) {
|
|
68
|
+
failed += 1
|
|
69
|
+
console.error('冒烟异常中断:', err)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
console.log(`\n${failed === 0 ? '✅ REST 冒烟全部通过' : `❌ ${failed} 项失败`}`)
|
|
73
|
+
process.exit(failed === 0 ? 0 : 1)
|