|
@@ -30,39 +30,60 @@
|
|
|
</NInputNumber>
|
|
</NInputNumber>
|
|
|
</ParamSection>
|
|
</ParamSection>
|
|
|
|
|
|
|
|
- <!-- 6 可配采样通道 -->
|
|
|
|
|
- <ParamSection :id="6" :preview="cfgHex" :disabled="!cfgValid" @add="add(6, cfgHex)">
|
|
|
|
|
|
|
+ <!-- 6 可配采样通道:读到几个通道就并列几行,可逐行加入或一键全部加入 -->
|
|
|
|
|
+ <div class="flex flex-col gap-2">
|
|
|
<div class="flex flex-wrap gap-2 items-center">
|
|
<div class="flex flex-wrap gap-2 items-center">
|
|
|
- <NInputNumber v-model:value="cfg.index" :min="1" :max="255" :show-button="false" class="w-24">
|
|
|
|
|
- <template #prefix>
|
|
|
|
|
- 索引
|
|
|
|
|
- </template>
|
|
|
|
|
- </NInputNumber>
|
|
|
|
|
- <NSelect v-model:value="cfg.mode" :options="modeOptions" class="w-40" />
|
|
|
|
|
- <NSelect v-if="cfg.mode === 0" v-model:value="cfg.addrType" :options="ADDR_TYPES" class="w-36" />
|
|
|
|
|
- <NInput
|
|
|
|
|
- v-model:value="cfg.value"
|
|
|
|
|
- class="w-40"
|
|
|
|
|
- :placeholder="cfg.mode === 0 ? '地址 8 位 hex' : '代码 4 位 hex'"
|
|
|
|
|
- :status="cfg.value && !cfgValid ? 'error' : undefined"
|
|
|
|
|
- />
|
|
|
|
|
|
|
+ <span class="font-bold">{{ functionName(6) }} (6)</span>
|
|
|
|
|
+ <span class="text-xs text-gray-500">索引 1 对应设备列表第 1 项</span>
|
|
|
|
|
+ <div class="flex-1" />
|
|
|
|
|
+ <NButton size="small" @click="addCfgRow">新增一行</NButton>
|
|
|
|
|
+ <NButton size="small" :disabled="changedRows.length === 0" @click="addRows(changedRows)">
|
|
|
|
|
+ 加入已修改({{ changedRows.length }})
|
|
|
|
|
+ </NButton>
|
|
|
|
|
+ <NButton size="small" type="primary" :disabled="validRows.length === 0" @click="addRows(cfgRows)">
|
|
|
|
|
+ 全部加入待提交({{ validRows.length }})
|
|
|
|
|
+ </NButton>
|
|
|
</div>
|
|
</div>
|
|
|
- </ParamSection>
|
|
|
|
|
- <div v-if="deviceCfgs" class="text-xs">
|
|
|
|
|
- <div class="text-gray-500 mb-1">设备当前可配采样通道(点击带入表单):</div>
|
|
|
|
|
- <div class="flex flex-wrap gap-2">
|
|
|
|
|
- <NTag
|
|
|
|
|
- v-for="(c, i) in deviceCfgs"
|
|
|
|
|
- :key="i"
|
|
|
|
|
- size="small"
|
|
|
|
|
- :type="cfg.index === i + 1 ? 'info' : 'default'"
|
|
|
|
|
- class="cursor-pointer"
|
|
|
|
|
- @click="loadCfg(i + 1)"
|
|
|
|
|
|
|
+ <!-- 每个通道两行:上面一行为输入框(不换行,宽度不足时横向滚动),下面一行为 setVal 预览和设备当前值 -->
|
|
|
|
|
+ <div class="overflow-x-auto">
|
|
|
|
|
+ <div
|
|
|
|
|
+ v-for="(row, i) in cfgRows"
|
|
|
|
|
+ :key="row.key"
|
|
|
|
|
+ class="mb-1 p-2 rounded min-w-full w-max"
|
|
|
|
|
+ :class="rowState(row).changed ? 'bg-blue-500/10' : 'bg-gray-500/5'"
|
|
|
>
|
|
>
|
|
|
- 索引 {{ i + 1 }}:{{ configDesc(c) }}
|
|
|
|
|
- </NTag>
|
|
|
|
|
- <span v-if="deviceCfgs.length === 0" class="text-gray-500">无</span>
|
|
|
|
|
|
|
+ <div class="flex flex-nowrap gap-2 items-center">
|
|
|
|
|
+ <NInputNumber v-model:value="row.index" :min="1" :max="255" :show-button="false" class="shrink-0 w-24">
|
|
|
|
|
+ <template #prefix>
|
|
|
|
|
+ 索引
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </NInputNumber>
|
|
|
|
|
+ <NSelect v-model:value="row.mode" :options="modeOptions" class="shrink-0 w-40" />
|
|
|
|
|
+ <NSelect v-if="row.mode === 0" v-model:value="row.addrType" :options="ADDR_TYPES" class="shrink-0 w-36" />
|
|
|
|
|
+ <!-- 代码方式没有地址类型,占位保持各行列对齐 -->
|
|
|
|
|
+ <div v-else class="shrink-0 w-36" />
|
|
|
|
|
+ <NInput
|
|
|
|
|
+ v-model:value="row.value"
|
|
|
|
|
+ class="shrink-0"
|
|
|
|
|
+ style="width: 9rem"
|
|
|
|
|
+ :placeholder="row.mode === 0 ? '地址8位(16进制)' : '代码4位(16进制)'"
|
|
|
|
|
+ :status="row.value && !rowState(row).valid ? 'error' : undefined"
|
|
|
|
|
+ />
|
|
|
|
|
+ <div class="shrink-0 w-16">
|
|
|
|
|
+ <NTag v-if="rowState(row).duplicate" size="small" type="error">索引重复</NTag>
|
|
|
|
|
+ <NTag v-else-if="row.original && rowState(row).changed" size="small" type="info">已修改</NTag>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="flex-1" />
|
|
|
|
|
+ <NButton size="small" class="shrink-0" :disabled="!rowState(row).valid" @click="addRows([row])">加入</NButton>
|
|
|
|
|
+ <NButton size="small" class="shrink-0" quaternary @click="cfgRows.splice(i, 1)">删除</NButton>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="text-xs text-gray-500 mt-1 flex flex-wrap gap-x-6">
|
|
|
|
|
+ <span class="font-mono">setVal = {{ rowHex(row) || '-' }}</span>
|
|
|
|
|
+ <span v-if="row.original">设备当前:{{ describeSetVal(6, row.original) }}</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
</div>
|
|
</div>
|
|
|
|
|
+ <div v-if="cfgRows.length === 0" class="text-xs text-gray-500">无可配采样通道,可点“新增一行”</div>
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
<!-- 原始模式 -->
|
|
<!-- 原始模式 -->
|
|
@@ -70,7 +91,7 @@
|
|
|
<NCollapseItem title="高级:原始模式(直接输入十六进制 setVal)" name="raw">
|
|
<NCollapseItem title="高级:原始模式(直接输入十六进制 setVal)" name="raw">
|
|
|
<div class="flex gap-2 items-center">
|
|
<div class="flex gap-2 items-center">
|
|
|
<NSelect v-model:value="raw.id" :options="codeOptions" class="w-48" />
|
|
<NSelect v-model:value="raw.id" :options="codeOptions" class="w-48" />
|
|
|
- <NInput v-model:value="raw.val" placeholder="setVal 十六进制" class="w-64" :status="raw.val && !isHex(raw.val) ? 'error' : undefined" />
|
|
|
|
|
|
|
+ <NInput v-model:value="raw.val" placeholder="setVal 十六进制" style="width: 16rem" :status="raw.val && !isHex(raw.val) ? 'error' : undefined" />
|
|
|
<NButton :disabled="!raw.val || !isHex(raw.val)" @click="add(raw.id, raw.val.toUpperCase())">加入</NButton>
|
|
<NButton :disabled="!raw.val || !isHex(raw.val)" @click="add(raw.id, raw.val.toUpperCase())">加入</NButton>
|
|
|
</div>
|
|
</div>
|
|
|
</NCollapseItem>
|
|
</NCollapseItem>
|
|
@@ -205,17 +226,49 @@ const modeOptions = [
|
|
|
{ value: 0, label: '默认方式(地址)' },
|
|
{ value: 0, label: '默认方式(地址)' },
|
|
|
{ value: 1, label: '代码方式' },
|
|
{ value: 1, label: '代码方式' },
|
|
|
]
|
|
]
|
|
|
-const cfg = reactive({ index: 1 as number | null, mode: 0, addrType: 1, value: '' })
|
|
|
|
|
-const cfgValid = computed(() => {
|
|
|
|
|
- if (!cfg.index) return false
|
|
|
|
|
- return cfg.mode === 0 ? HEX8_RE.test(cfg.value) : HEX4_RE.test(cfg.value)
|
|
|
|
|
-})
|
|
|
|
|
-const cfgHex = computed(() => {
|
|
|
|
|
- if (!cfgValid.value || !cfg.index) return ''
|
|
|
|
|
- const typeByte = (cfg.mode << 4) | (cfg.mode === 0 ? cfg.addrType : 0)
|
|
|
|
|
- const data = cfg.mode === 0 ? cfg.value : `${cfg.value}0000`
|
|
|
|
|
- return (hex(cfg.index, 1) + hex(typeByte, 1) + data).toUpperCase()
|
|
|
|
|
-})
|
|
|
|
|
|
|
+/** 可配采样通道编辑行;original 为设备当前值(setVal 格式),用于判断是否修改 */
|
|
|
|
|
+interface CfgRow {
|
|
|
|
|
+ key: number
|
|
|
|
|
+ index: number | null
|
|
|
|
|
+ mode: number
|
|
|
|
|
+ addrType: number
|
|
|
|
|
+ value: string
|
|
|
|
|
+ original?: string
|
|
|
|
|
+}
|
|
|
|
|
+let cfgRowKey = 0
|
|
|
|
|
+const cfgRows = ref<CfgRow[]>([{ key: ++cfgRowKey, index: 1, mode: 0, addrType: 1, value: '' }])
|
|
|
|
|
+
|
|
|
|
|
+function rowHex(row: CfgRow) {
|
|
|
|
|
+ const valid = row.index && (row.mode === 0 ? HEX8_RE.test(row.value) : HEX4_RE.test(row.value))
|
|
|
|
|
+ if (!valid || !row.index) return ''
|
|
|
|
|
+ const typeByte = (row.mode << 4) | (row.mode === 0 ? row.addrType : 0)
|
|
|
|
|
+ const data = row.mode === 0 ? row.value : `${row.value}0000`
|
|
|
|
|
+ return (hex(row.index, 1) + hex(typeByte, 1) + data).toUpperCase()
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function rowState(row: CfgRow) {
|
|
|
|
|
+ const h = rowHex(row)
|
|
|
|
|
+ const duplicate = row.index != null && cfgRows.value.filter((r) => r.index === row.index).length > 1
|
|
|
|
|
+ return { valid: h !== '' && !duplicate, duplicate, changed: h !== '' && h !== row.original }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const validRows = computed(() => cfgRows.value.filter((r) => rowState(r).valid))
|
|
|
|
|
+const changedRows = computed(() => validRows.value.filter((r) => rowState(r).changed))
|
|
|
|
|
+
|
|
|
|
|
+function addCfgRow() {
|
|
|
|
|
+ const next = Math.max(0, ...cfgRows.value.map((r) => r.index ?? 0)) + 1
|
|
|
|
|
+ cfgRows.value.push({ key: ++cfgRowKey, index: Math.min(next, 255), mode: 0, addrType: 1, value: '' })
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/** 把多行加入待提交;无效行跳过并提示 */
|
|
|
|
|
+function addRows(rows: CfgRow[]) {
|
|
|
|
|
+ let skipped = 0
|
|
|
|
|
+ for (const r of rows) {
|
|
|
|
|
+ if (rowState(r).valid) add(6, rowHex(r))
|
|
|
|
|
+ else skipped++
|
|
|
|
|
+ }
|
|
|
|
|
+ if (skipped > 0) message.warning(`${skipped} 行内容无效或索引重复,未加入`)
|
|
|
|
|
+}
|
|
|
|
|
|
|
|
const codeOptions = FUNCTION_CODES.filter((f) => f.page === 'param').map((f) => ({ value: f.id, label: `${f.id} ${f.name}` }))
|
|
const codeOptions = FUNCTION_CODES.filter((f) => f.page === 'param').map((f) => ({ value: f.id, label: `${f.id} ${f.name}` }))
|
|
|
const raw = reactive({ id: 5, val: '' })
|
|
const raw = reactive({ id: 5, val: '' })
|
|
@@ -255,19 +308,18 @@ const readIds = ref<number[]>([5, 6]) // 默认全选
|
|
|
const deviceFreq = ref<number>()
|
|
const deviceFreq = ref<number>()
|
|
|
const deviceCfgs = ref<FaultRecordConfigurableChannel[]>()
|
|
const deviceCfgs = ref<FaultRecordConfigurableChannel[]>()
|
|
|
|
|
|
|
|
-/** 索引 1 对应列表第 1 项 */
|
|
|
|
|
-function loadCfg(index: number) {
|
|
|
|
|
- const c = deviceCfgs.value?.[index - 1]
|
|
|
|
|
- cfg.index = index
|
|
|
|
|
- if (!c) return
|
|
|
|
|
|
|
+/** 设备通道 → 编辑行;索引 1 对应列表第 1 项 */
|
|
|
|
|
+function rowFromDevice(index: number, c: FaultRecordConfigurableChannel): CfgRow {
|
|
|
const { mode, addrType } = cfgParts(c)
|
|
const { mode, addrType } = cfgParts(c)
|
|
|
- cfg.mode = mode
|
|
|
|
|
- if (mode === 1) {
|
|
|
|
|
- cfg.value = hex(c.address >>> 16, 2)
|
|
|
|
|
- } else {
|
|
|
|
|
- cfg.addrType = addrType
|
|
|
|
|
- cfg.value = hex(c.address, 4)
|
|
|
|
|
|
|
+ const row: CfgRow = {
|
|
|
|
|
+ key: ++cfgRowKey,
|
|
|
|
|
+ index,
|
|
|
|
|
+ mode,
|
|
|
|
|
+ addrType: mode === 0 ? addrType : 1,
|
|
|
|
|
+ value: mode === 1 ? hex(c.address >>> 16, 2) : hex(c.address, 4),
|
|
|
}
|
|
}
|
|
|
|
|
+ row.original = rowHex(row)
|
|
|
|
|
+ return row
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/** 从 cmd=0 结果中取出采样频率和可配采样通道 */
|
|
/** 从 cmd=0 结果中取出采样频率和可配采样通道 */
|
|
@@ -289,7 +341,10 @@ function applyCurrent(d: FaultRecordOpDetail) {
|
|
|
}
|
|
}
|
|
|
if (cur.cfgs) {
|
|
if (cur.cfgs) {
|
|
|
deviceCfgs.value = cur.cfgs
|
|
deviceCfgs.value = cur.cfgs
|
|
|
- loadCfg(cfg.index ?? 1)
|
|
|
|
|
|
|
+ // 读到几个通道就并列几行;没有通道时保留一行空白可手动填写
|
|
|
|
|
+ cfgRows.value = cur.cfgs.length > 0
|
|
|
|
|
+ ? cur.cfgs.map((c, i) => rowFromDevice(i + 1, c))
|
|
|
|
|
+ : [{ key: ++cfgRowKey, index: 1, mode: 0, addrType: 1, value: '' }]
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -327,6 +382,7 @@ watch(
|
|
|
() => {
|
|
() => {
|
|
|
deviceFreq.value = undefined
|
|
deviceFreq.value = undefined
|
|
|
deviceCfgs.value = undefined
|
|
deviceCfgs.value = undefined
|
|
|
|
|
+ cfgRows.value = [{ key: ++cfgRowKey, index: 1, mode: 0, addrType: 1, value: '' }]
|
|
|
},
|
|
},
|
|
|
)
|
|
)
|
|
|
|
|
|