写入功能完成
This commit is contained in:
@@ -52,7 +52,7 @@
|
||||
</div>
|
||||
|
||||
<div class="card-body">
|
||||
<!-- Plan-driven editable form / 按计划参数渲染可编辑表单(不使用模板) -->
|
||||
<!-- 可编辑表单:值 + OPC点位(点位可先为空,后续协商配置) -->
|
||||
<el-form :model="plan.params" label-position="top" size="mini">
|
||||
<el-row :gutter="10">
|
||||
<el-col
|
||||
@@ -65,6 +65,14 @@
|
||||
v-model="plan.params[item.key]"
|
||||
:placeholder="getPlaceholder(item.key)"
|
||||
/>
|
||||
|
||||
<!-- OPC address input / OPC点位输入框(可先不填) -->
|
||||
<el-input
|
||||
v-model="driveAddress[item.key]"
|
||||
size="mini"
|
||||
class="addr-input"
|
||||
placeholder="OPC address (optional)"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
@@ -90,34 +98,52 @@ import { getLastSuccess } from '@/api/l2/sendTemplate'
|
||||
|
||||
// Drive fields definition (English UI, Chinese comments) / 传动字段定义(英文界面,中文注释)
|
||||
// 说明:key 必须与 setupForm 字段一致(来自 plan/components/setupForm.vue)
|
||||
// Drive + Plan fields definition (show effect first; OPC address can be edited later)
|
||||
// 传动 + 计划字段定义(先把效果做出来;OPC点位后续可协商配置)
|
||||
const DRIVE_FIELDS = [
|
||||
{ key: 'porTension', label: 'Pay-off Reel Tension' },
|
||||
{ key: 'celTension', label: 'Entry Loop Tension' },
|
||||
{ key: 'cleanTension', label: 'Cleaning Section Tension' },
|
||||
{ key: 'furTension', label: 'Furnace Zone Tension' },
|
||||
{ key: 'towerTension', label: 'Cooling Tower Tension' },
|
||||
{ key: 'tmNoneTension', label: 'TM No Tension' },
|
||||
{ key: 'tmEntryTension', label: 'TM Entry Tension' },
|
||||
{ key: 'tmExitTension', label: 'TM Exit Tension' },
|
||||
{ key: 'tlNoneTension', label: 'TL No Tension' },
|
||||
{ key: 'tlExitTension', label: 'TL Exit Tension' },
|
||||
{ key: 'coatTension', label: 'Post-treatment Tension' },
|
||||
{ key: 'cxlTension', label: 'Exit Loop Tension' },
|
||||
{ key: 'trTension', label: 'Take-up Reel Tension' },
|
||||
// ---- Drive tension / 传动张力 ----
|
||||
{ key: 'porTension', label: 'Pay-off Reel Tension', source: 'setup' },
|
||||
{ key: 'celTension', label: 'Entry Loop Tension', source: 'setup' },
|
||||
{ key: 'cleanTension', label: 'Cleaning Section Tension', source: 'setup' },
|
||||
{ key: 'furTension', label: 'Furnace Zone Tension', source: 'setup' },
|
||||
{ key: 'towerTension', label: 'Cooling Tower Tension', source: 'setup' },
|
||||
{ key: 'tmNoneTension', label: 'TM No Tension', source: 'setup' },
|
||||
{ key: 'tmEntryTension', label: 'TM Entry Tension', source: 'setup' },
|
||||
{ key: 'tmExitTension', label: 'TM Exit Tension', source: 'setup' },
|
||||
{ key: 'tlNoneTension', label: 'TL No Tension', source: 'setup' },
|
||||
{ key: 'tlExitTension', label: 'TL Exit Tension', source: 'setup' },
|
||||
{ key: 'coatTension', label: 'Post-treatment Tension', source: 'setup' },
|
||||
{ key: 'cxlTension', label: 'Exit Loop Tension', source: 'setup' },
|
||||
{ key: 'trTension', label: 'Take-up Reel Tension', source: 'setup' },
|
||||
|
||||
{ key: 'tlElong', label: 'TL Elongation' },
|
||||
{ key: 'tlLvlMesh1', label: 'TL Leveling Roll Mesh 1' },
|
||||
{ key: 'tlLvlMesh2', label: 'TL Leveling Roll Mesh 2' },
|
||||
{ key: 'tlAcbMesh', label: 'TL Anti-crossbow Mesh' },
|
||||
// ---- TL / TM setup ----
|
||||
{ key: 'tlElong', label: 'TL Elongation', source: 'setup' },
|
||||
{ key: 'tlLvlMesh1', label: 'TL Leveling Roll Mesh 1', source: 'setup' },
|
||||
{ key: 'tlLvlMesh2', label: 'TL Leveling Roll Mesh 2', source: 'setup' },
|
||||
{ key: 'tlAcbMesh', label: 'TL Anti-crossbow Mesh', source: 'setup' },
|
||||
|
||||
{ key: 'tmBendforce', label: 'TM Bending Force' },
|
||||
{ key: 'tmAcrMesh', label: 'TM Anti-crimping Roll Mesh' },
|
||||
{ key: 'tmBrMesh', label: 'TM Anti-tremor Roll Mesh' },
|
||||
{ key: 'tmRollforce', label: 'TM Roll Force' }
|
||||
{ key: 'tmBendforce', label: 'TM Bending Force', source: 'setup' },
|
||||
{ key: 'tmAcrMesh', label: 'TM Anti-crimping Roll Mesh', source: 'setup' },
|
||||
{ key: 'tmBrMesh', label: 'TM Anti-tremor Roll Mesh', source: 'setup' },
|
||||
{ key: 'tmRollforce', label: 'TM Roll Force', source: 'setup' },
|
||||
|
||||
// ---- Plan (from listPlan response) / 计划参数(来自 listPlan 返回)----
|
||||
{ key: 'entryWidth', label: 'Entry Width', source: 'plan' },
|
||||
{ key: 'entryThick', label: 'Entry Thick', source: 'plan' },
|
||||
{ key: 'entryWeight', label: 'Entry Weight', source: 'plan' },
|
||||
{ key: 'entryLength', label: 'Entry Length', source: 'plan' },
|
||||
|
||||
{ key: 'steelGrade', label: 'Steel Grade', source: 'plan' },
|
||||
|
||||
{ key: 'spmElongation', label: 'SPM Elongation', source: 'plan' },
|
||||
{ key: 'spmRollforce', label: 'SPM Roll Force', source: 'plan' },
|
||||
{ key: 'spmBendingForce', label: 'SPM Bending Force', source: 'plan' },
|
||||
|
||||
{ key: 'yieldPoint', label: 'Yield Point', source: 'plan' }
|
||||
]
|
||||
|
||||
// OPC address mapping (must align with back-end OpcMessageIdsManager.pdiSetupIds) / OPC点位映射(需与后端一致)
|
||||
// 中文注释:这里用“字段名->OPC地址”的方式直接组装发送items
|
||||
// OPC address mapping / OPC点位映射
|
||||
// 说明:此处后续可协商配置;当前允许在页面上编辑(默认可为空)
|
||||
const DRIVE_ADDRESS = {
|
||||
porTension: 'ns=2;s=ProcessCGL.PLCLine.L2Setup.tensionPorBR1',
|
||||
celTension: 'ns=2;s=ProcessCGL.PLCLine.L2Setup.tensionBR3',
|
||||
@@ -151,7 +177,9 @@ export default {
|
||||
loading: false,
|
||||
lastSuccess: null,
|
||||
plans: [],
|
||||
driveFields: DRIVE_FIELDS
|
||||
driveFields: DRIVE_FIELDS,
|
||||
// 可编辑的 OPC 点位(默认从常量拷贝;你也可以后续改成从后端/本地存储加载)
|
||||
driveAddress: { ...DRIVE_ADDRESS }
|
||||
}
|
||||
},
|
||||
created() {
|
||||
@@ -167,7 +195,8 @@ export default {
|
||||
|
||||
// plans / 获取计划
|
||||
const planRes = await listPlan({ status: 'NEW,READY,ONLINE,PRODUCING' })
|
||||
const planList = planRes.rows || []
|
||||
// 兼容后端返回结构:既可能是 {rows: []} 也可能是 {data: []}
|
||||
const planList = (planRes && (planRes.rows || planRes.data)) || []
|
||||
|
||||
const tasks = planList.map(async (p) => {
|
||||
let setup = {}
|
||||
@@ -180,14 +209,27 @@ export default {
|
||||
|
||||
const params = {}
|
||||
this.driveFields.forEach(f => {
|
||||
const fromSetup = setup[f.key]
|
||||
const fromPlan = p ? p[f.key] : undefined
|
||||
const fromSetup = setup ? setup[f.key] : undefined
|
||||
const fromLast = this.lastSuccess?.values?.[f.key]
|
||||
if (fromSetup !== undefined && fromSetup !== null && String(fromSetup) !== '') {
|
||||
params[f.key] = String(fromSetup)
|
||||
} else if (fromLast !== undefined && fromLast !== null) {
|
||||
params[f.key] = String(fromLast)
|
||||
|
||||
// 优先级:setup(如果字段来自setup) / plan(如果字段来自plan) -> lastSuccess -> ''
|
||||
if (f.source === 'plan') {
|
||||
if (fromPlan !== undefined && fromPlan !== null && String(fromPlan) !== '') {
|
||||
params[f.key] = String(fromPlan)
|
||||
} else if (fromLast !== undefined && fromLast !== null) {
|
||||
params[f.key] = String(fromLast)
|
||||
} else {
|
||||
params[f.key] = ''
|
||||
}
|
||||
} else {
|
||||
params[f.key] = ''
|
||||
if (fromSetup !== undefined && fromSetup !== null && String(fromSetup) !== '') {
|
||||
params[f.key] = String(fromSetup)
|
||||
} else if (fromLast !== undefined && fromLast !== null) {
|
||||
params[f.key] = String(fromLast)
|
||||
} else {
|
||||
params[f.key] = ''
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -252,18 +294,24 @@ export default {
|
||||
try {
|
||||
const items = this.driveFields.map(f => ({
|
||||
paramCode: f.key,
|
||||
address: DRIVE_ADDRESS[f.key],
|
||||
// OPC点位允许为空:为空则本次不发送该字段(先做效果,后续再配置)
|
||||
address: this.driveAddress[f.key],
|
||||
valueRaw: String(plan.params[f.key] || ''),
|
||||
setTime: new Date()
|
||||
})).filter(it => !!it.address)
|
||||
|
||||
if (!items.length) {
|
||||
this.$message.warning('OPC点位为空:当前没有可发送的字段(请先在输入框里填写点位)')
|
||||
return
|
||||
}
|
||||
|
||||
const dto = {
|
||||
deviceName: 'CGL_LINE_1',
|
||||
groups: [
|
||||
{
|
||||
groupNo: 1,
|
||||
groupType: 'DRIVE',
|
||||
groupName: `Drive Params for ${plan.steelGrade || ''}`,
|
||||
groupName: `Drive/Plan Params for ${plan.steelGrade || ''}`,
|
||||
items
|
||||
}
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user