feat(calibration): 重构标定功能并添加新配置项

- 重新设计了标定页面布局和功能
- 添加了操作员编号输入字段
- 新增了标定中和标定后检测的配置项
- 优化了设备列表和日志的显示方式
- 重构了日志处理和设备信息更新的逻辑
- 新增了漏电检测相关的配置项
This commit is contained in:
fhysy 2025-07-08 17:00:43 +08:00
parent 67b005f77c
commit a9a580ad29
3 changed files with 375 additions and 232 deletions

View File

@ -1,10 +1,14 @@
module.exports = { module.exports = {
//打包配置
// url: 'http://127.0.0.1:8000', // url: 'http://127.0.0.1:8000',
// wsUrl: 'ws://127.0.0.1:8000', // wsUrl: 'ws://127.0.0.1:8000',
// swichWsUrl: 'ws://127.0.0.1:8001', // swichWsUrl: 'ws://127.0.0.1:8001',
// serialPortUrl: 'http://127.0.0.1:8000', // serialPortUrl: 'http://127.0.0.1:8000',
//开发配置
url: 'http://192.168.1.17:8000', url: 'http://192.168.1.17:8000',
wsUrl: 'ws://192.168.1.17:8000', wsUrl: 'ws://192.168.1.17:8000',
swichWsUrl: 'ws://192.168.1.17:8001', swichWsUrl: 'ws://192.168.1.17:8001',

View File

@ -17,15 +17,15 @@
// } // }
export default { export default {
url: 'http://127.0.0.1:8888', // url: 'http://127.0.0.1:8000',
wsUrl: 'ws://127.0.0.1:8888', // wsUrl: 'ws://127.0.0.1:8000',
swichWsUrl: 'ws://127.0.0.1:8001', // swichWsUrl: 'ws://127.0.0.1:8001',
serialPortUrl: 'http://127.0.0.1:8888', // serialPortUrl: 'http://127.0.0.1:8000',
// url: 'http://192.168.1.17:8000', url: 'http://192.168.1.17:8000',
// wsUrl: 'ws://192.168.1.17:8000', wsUrl: 'ws://192.168.1.17:8000',
// swichWsUrl: 'ws://192.168.1.17:8001', swichWsUrl: 'ws://192.168.1.17:8001',
// serialPortUrl: 'http://192.168.1.17:8000', serialPortUrl: 'http://192.168.1.17:8000',
// 远程连接串口使用 // 远程连接串口使用
// serialPortUrl: 'http://120.77.172.42:7202', // serialPortUrl: 'http://120.77.172.42:7202',

View File

@ -6,7 +6,8 @@
<el-option v-for="(item, index) in schemeList" :key="index" :label="item.schemeName" :value="item.id"></el-option> <el-option v-for="(item, index) in schemeList" :key="index" :label="item.schemeName" :value="item.id"></el-option>
</el-select> </el-select>
<el-button icon="CaretRight" type="primary" :disabled="playState" @click="startExecution">开始执行</el-button> <el-button icon="CaretRight" type="primary" :disabled="playState" @click="startExecution">开始执行</el-button>
<el-button icon="RemoveFilled" type="danger" :disabled="!playState" @click="stopExecution">停止</el-button> <el-button icon="RemoveFilled" type="danger" @click="stopExecution">停止</el-button>
<el-input v-model="operatorCode" placeholder="请输入操作员编号" style="width: 200px; margin-left: 15px" />
</div> </div>
<div class="header-right"> <div class="header-right">
<el-button icon="RefreshRight" type="primary" :disabled="playState" @click="generateDeviceList">刷新列表</el-button> <el-button icon="RefreshRight" type="primary" :disabled="playState" @click="generateDeviceList">刷新列表</el-button>
@ -46,8 +47,25 @@
</div> </div>
</template> </template>
<div class="device-card-body"> <div class="device-card-body">
<div :class="['iconfont', 'icon-icon_duanluqi', 'circuit-breaker', device.result === 1 ? 'color-green' : device.result === -1 ? 'color-red' : '']"></div> <div v-if="device.activeStep" class="process-status" :class="device.stepStatus === -1 ? 'color-red' : device.stepStatus === 1 ? 'color-green' : 'color-orange'">{{ device.activeStep }}</div>
<p>芯片ID: {{ device.deviceSN || '无' }}</p> <div
v-if="device.connectStatus === 1"
:class="[
'iconfont',
'icon-icon_duanluqi',
'circuit-breaker',
device.result === 1 ? 'color-green' : device.result === -1 ? 'color-red' : device.stepStatus === -1 ? 'color-red' : device.stepStatus === 1 ? 'color-green' : 'color-orange'
]"
></div>
<div v-else :class="['iconfont', 'icon-icon_duanluqi', 'circuit-breaker', device.result === 1 ? 'color-green' : device.result === -1 ? 'color-red' : '']"></div>
<p>
芯片ID:
<span
><el-tooltip class="box-item" effect="dark" :content="device.cpuId" placement="top-start">
{{ device.cpuId || '无' }}
</el-tooltip></span
>
</p>
<p>设备端口: {{ device.devicePort || '无' }}</p> <p>设备端口: {{ device.devicePort || '无' }}</p>
<p> <p>
标定结果: 标定结果:
@ -57,11 +75,12 @@
</p> </p>
<p> <p>
结果描述: 结果描述:
<span <span v-if="device.resultTxt"
><el-tooltip class="box-item" effect="dark" :content="device.resultTxt" placement="top-start"> ><el-tooltip class="box-item" effect="dark" :content="device.resultTxt" placement="top-start">
{{ device.resultTxt || '无' }} {{ device.resultTxt || '无' }}
</el-tooltip></span </el-tooltip></span
> >
<span v-else>{{ device.resultTxt || '' }}</span>
</p> </p>
</div> </div>
</el-card> </el-card>
@ -70,23 +89,23 @@
<el-footer class="calibration-footer" :class="isOpenLog ? 'open-log' : ''"> <el-footer class="calibration-footer" :class="isOpenLog ? 'open-log' : ''">
<div id="log-box-main" class="log-box-main"> <div id="log-box-main" class="log-box-main">
<div class="log-list"> <div class="log-list">
<div v-for="(item, index) in logs" :key="index" class="log-item"> <div v-for="(item, index) in logs" :key="index" class="log-item">
<el-tooltip v-if="item.type == 'calibrate_error'" effect="light" content="错误信息" placement="top"> <el-tooltip v-if="item.type == 'calibrate_error'" effect="light" content="错误信息" placement="top">
<span class="iconfont icon-bug-fill" style="color: #ff3b2b"></span> <span class="iconfont icon-bug-fill" style="color: #ff3b2b"></span>
</el-tooltip> </el-tooltip>
<el-tooltip v-else-if="item.type == 'calibrate_result_error'" effect="light" content="标定错误" placement="top"> <el-tooltip v-else-if="item.type == 'calibrate_result_error'" effect="light" content="标定错误" placement="top">
<span class="iconfont icon-xwtubiaoku-15" style="color: #ff3b2b"></span> <span class="iconfont icon-xwtubiaoku-15" style="color: #ff3b2b"></span>
</el-tooltip> </el-tooltip>
<el-tooltip v-else-if="item.type == 'calibrate_result_success'" effect="light" content="标定成功" placement="top"> <el-tooltip v-else-if="item.type == 'calibrate_result_success'" effect="light" content="标定成功" placement="top">
<span class="iconfont icon-xwtubiaoku-13" style="color: #55e800"></span> <span class="iconfont icon-xwtubiaoku-13" style="color: #55e800"></span>
</el-tooltip> </el-tooltip>
<el-tooltip v-else effect="light" content="系统" placement="top"> <el-tooltip v-else effect="light" content="系统" placement="top">
<span class="iconfont icon-rizhi1"></span> <span class="iconfont icon-rizhi1"></span>
</el-tooltip> </el-tooltip>
<span class="log-item-txt">{{ item.time }} {{ item.msg }}</span> <span class="log-item-txt">{{ item.time }} {{ item.msg }}</span>
</div> </div>
</div> </div>
</div> </div>
<div class="calibration-footer-btn"> <div class="calibration-footer-btn">
<div class="btn-box"> <div class="btn-box">
@ -150,12 +169,12 @@
<!-- 方案详情对话框 --> <!-- 方案详情对话框 -->
<el-dialog v-model="schemeDetailVisible" :title="currentScheme.schemeName" width="95%" :destroy-on-close="true" align-center :close-on-click-modal="false"> <el-dialog v-model="schemeDetailVisible" :title="currentScheme.schemeName" width="95%" :destroy-on-close="true" align-center :close-on-click-modal="false">
<el-form :model="currentScheme" label-width="120px"> <el-form :model="currentScheme" label-width="100px">
<el-form-item label="方案名称"> <el-form-item label="方案名称">
<el-input v-model="currentScheme.schemeName" /> <el-input v-model="currentScheme.schemeName" />
</el-form-item> </el-form-item>
<el-row> <el-row>
<el-col :span="8"> <el-col :span="5">
<el-form-item label="系列"> <el-form-item label="系列">
<el-select v-model="currentScheme.series" placeholder="请选择系列"> <el-select v-model="currentScheme.series" placeholder="请选择系列">
<el-option label="B7" value="B7" /> <el-option label="B7" value="B7" />
@ -163,14 +182,14 @@
</el-select> </el-select>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="8"> <el-col :span="5">
<el-form-item label="框架"> <el-form-item label="框架">
<el-select v-model="currentScheme.framework" placeholder="请选择框架"> <el-select v-model="currentScheme.framework" placeholder="请选择框架">
<el-option label="100ZS" value="100ZS" /> <el-option label="100ZS" value="100ZS" />
</el-select> </el-select>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="8"> <el-col :span="5">
<el-form-item label="断路器类型"> <el-form-item label="断路器类型">
<el-select v-model="currentScheme.schemeType" placeholder="请选择断路器类型"> <el-select v-model="currentScheme.schemeType" placeholder="请选择断路器类型">
<el-option label="1p" value="1p" /> <el-option label="1p" value="1p" />
@ -180,6 +199,16 @@
</el-select> </el-select>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="4">
<el-form-item label="标定中检测">
<el-switch v-model="currentScheme.middleDetect" />
</el-form-item>
</el-col>
<el-col :span="4">
<el-form-item label="标定后检测">
<el-switch v-model="currentScheme.laterDetect" />
</el-form-item>
</el-col>
</el-row> </el-row>
<el-divider>断路器校准参数</el-divider> <el-divider>断路器校准参数</el-divider>
@ -197,9 +226,16 @@
</div> --> </div> -->
<el-divider>校准精度(%)</el-divider> <el-divider>校准精度(%)</el-divider>
<div class="accuracy-title">普通电流</div>
<div class="accuracy-params"> <div class="accuracy-params">
<el-form-item v-for="(value, key) in currentScheme.errorRange" :key="key" label-width="80px" :label="accuracyType[key]"> <el-form-item v-for="(value, key) in currentScheme.errorRange.range" :key="key" label-width="auto" :label="accuracyType[key]">
<el-input-number v-model="currentScheme.errorRange[key]" :min="0" :max="100" :precision="2" /> <el-input-number v-model="currentScheme.errorRange.range[key]" style="width: 120px" :min="0" :max="100" :precision="2" />
</el-form-item>
</div>
<div class="accuracy-title">小电流</div>
<div class="accuracy-params">
<el-form-item v-for="(value, key) in currentScheme.errorRange.smallRange" :key="key" label-width="auto" :label="accuracyType[key]">
<el-input-number v-model="currentScheme.errorRange.smallRange[key]" style="width: 120px" :min="0" :max="100" :precision="2" />
</el-form-item> </el-form-item>
</div> </div>
@ -254,6 +290,31 @@
</el-table-column> </el-table-column>
</el-table> </el-table>
</div> </div>
<!-- 新增漏电检测设置分组 -->
<el-divider>漏电检测设置</el-divider>
<el-row :gutter="20">
<el-col :span="6">
<el-form-item label="漏电检测">
<el-switch v-model="currentScheme.iLeakDetectObj.iLeakDetect" />
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="检测电流(mA)">
<el-input-number v-model="currentScheme.iLeakDetectObj.detectInput" :min="0" :step="1" style="width: 120px" />
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="漏电保护">
<el-switch v-model="currentScheme.iLeakDetectObj.iLeakProtect" />
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="保护电流(mA)">
<el-input-number v-model="currentScheme.iLeakDetectObj.protectInput" :min="0" :step="1" style="width: 120px" />
</el-form-item>
</el-col>
</el-row>
</el-form> </el-form>
<template #footer> <template #footer>
<span class="dialog-footer"> <span class="dialog-footer">
@ -268,7 +329,7 @@
<div class="scheme-drawer-content log-content"> <div class="scheme-drawer-content log-content">
<div class="device-info"> <div class="device-info">
<div class="device-info-item">设备: {{ activeDeviceInfo.id || '-' }}</div> <div class="device-info-item">设备: {{ activeDeviceInfo.id || '-' }}</div>
<div class="device-info-item">芯片ID: {{ activeDeviceInfo.deviceSN || '-' }}</div> <div class="device-info-item">芯片ID: {{ activeDeviceInfo.cpuId || '-' }}</div>
<div class="device-info-item">设备端口: {{ activeDeviceInfo.devicePort || '-' }}</div> <div class="device-info-item">设备端口: {{ activeDeviceInfo.devicePort || '-' }}</div>
<div class="device-info-item"> <div class="device-info-item">
标定结果: 标定结果:
@ -280,23 +341,58 @@
</div> </div>
<el-tabs v-model="logTabActive" class="demo-tabs" @tab-click="logTabChange"> <el-tabs v-model="logTabActive" class="demo-tabs" @tab-click="logTabChange">
<el-tab-pane label="标定日志" name="log"> <el-tab-pane label="标定日志" name="log">
<el-table :data="transposedLogList" style="width: 100%" size="small" scrollbar-always-on resizable empty-text="暂无日志"> <el-table :data="stepLogRows" style="width: 100%" size="small" scrollbar-always-on resizable empty-text="暂无日志">
<el-table-column prop="name" label="参数" fixed align="center" /> <el-table-column prop="step" label="步骤" fixed align="center" />
<el-table-column v-for="(item, index) in logList" :key="index" :label="item.name" align="center"> <el-table-column v-for="col in logParamColumns" :key="col.key" :prop="'paramMap.' + col.valueKey + '.actualError'" :label="col.valueKey" align="center">
<el-table-column prop="name" label="结果" align="center" min-width="80"> <template #header>
<template #default="scope"> <el-tooltip effect="dark" :content="(col.valueName || col.valueKey) + (col.valueUnit ? ' [' + col.valueUnit + ']' : '')" placement="top">
<span :style="{ color: scope.row.data[index]?.result != 1 ? 'red' : 'green' }">{{ scope.row.data[index]?.value === null?'': scope.row.data[index]?.value }}</span> <span>{{ col.valueKey }}</span>
<el-icon v-if="scope.row.data[index]?.result === 2" style="color: red"><Top /></el-icon> </el-tooltip>
<el-icon v-if="scope.row.data[index]?.result === 0" style="color: red"><Bottom /></el-icon> </template>
</template> <template #default="scope">
</el-table-column> <el-tooltip
<el-table-column label="范围" align="center"> effect="dark"
<template #default="scope"> :content="
<span style="margin-left: 8px; color: #888; font-size: 12px">{{ '差值百分比: ' +
scope.row.data[index]?.minValue && scope.row.data[index]?.maxValue ? scope.row.data[index]?.minValue + ' ~ ' + scope.row.data[index]?.maxValue : '-' (scope.row.paramMap[col.valueKey]?.actualError + '%' ?? '') +
}}</span> '\n' +
</template> '范围百分比: ' +
</el-table-column> (scope.row.paramMap[col.valueKey]?.expectedError + '%' ?? '') +
'\n' +
'源输出值: ' +
(scope.row.paramMap[col.valueKey]?.outputValue ?? '') +
'\n' +
'范围最小值: ' +
(scope.row.paramMap[col.valueKey]?.ExpectedMin ?? '') +
'\n' +
'范围最大值: ' +
(scope.row.paramMap[col.valueKey]?.expectedMax ?? '') +
'\n' +
'读取值: ' +
(scope.row.paramMap[col.valueKey]?.actualValue ?? '')
"
placement="top"
>
<span :style="{ color: scope.row.paramMap[col.valueKey]?.result === 1 ? 'green' : 'red' }">
{{
typeof scope.row.paramMap[col.valueKey]?.actualError === 'number'
? Number(scope.row.paramMap[col.valueKey].actualError.toFixed(6)).toString()
: scope.row.paramMap[col.valueKey]?.actualError
}}<span v-if="scope.row.paramMap[col.valueKey]">%</span>
<template v-if="scope.row.paramMap[col.valueKey]">
<el-icon v-if="scope.row.paramMap[col.valueKey].actualValue > scope.row.paramMap[col.valueKey].expectedMax" style="color: red"><Top /></el-icon>
<el-icon v-else-if="scope.row.paramMap[col.valueKey].actualValue < scope.row.paramMap[col.valueKey].ExpectedMin" style="color: red"><Bottom /></el-icon>
</template>
</span>
</el-tooltip>
</template>
</el-table-column>
<el-table-column prop="result" fixed="right" label="结果" align="center" width="80">
<template #default="scope">
<span :style="{ color: scope.row.result === 1 ? 'green' : 'red' }">
{{ scope.row.result === 1 ? '成功' : '失败' }}
</span>
</template>
</el-table-column> </el-table-column>
</el-table> </el-table>
</el-tab-pane> </el-tab-pane>
@ -315,7 +411,7 @@ import dayjs from 'dayjs';
import axios from 'axios'; import axios from 'axios';
// import config from '@renderer/util/config.js'; // import config from '@renderer/util/config.js';
const config = window.electron.readConfig(); const config = window.electron.readConfig();
console.log("当前获取config",config); console.log('当前获取config', config);
import { logWebSocketStore } from '@renderer/stores/logWebSocket.js'; import { logWebSocketStore } from '@renderer/stores/logWebSocket.js';
const webSocketStore = logWebSocketStore(); const webSocketStore = logWebSocketStore();
@ -327,9 +423,9 @@ const isScroll = ref(true);
const logs = ref([]); const logs = ref([]);
const version = ref('V1.0.0'); const version = ref('V1.0.0');
const logTabActive = ref('log'); const logTabActive = ref('log');
const operatorCode = ref('');
const loading = ref(true); const loading = ref(true);
const fullscreenLoading = ref(false)
const isOpenLog = ref(false); const isOpenLog = ref(false);
const logSocketStateHover = ref(false); const logSocketStateHover = ref(false);
@ -342,7 +438,6 @@ const activeScheme = ref();
const accuracyType = ref({ const accuracyType = ref({
voltage: '电压', voltage: '电压',
current: '电流', current: '电流',
smallCurrent: '小电流',
powerFactor: '功率因数', powerFactor: '功率因数',
activePower: '有功功率', activePower: '有功功率',
reactivePower: '无功功率', reactivePower: '无功功率',
@ -363,6 +458,13 @@ const defaultConfList = ref({
format: [] format: []
}); });
const defaultiLeakDetectObj = ref({
iLeakDetect: false,
detectInput: 100,
iLeakProtect: false,
protectInput: 100
});
const logVisible = ref(false); const logVisible = ref(false);
const logList = ref([]); const logList = ref([]);
@ -376,11 +478,13 @@ const activeDeviceIndex = ref(0);
const activeDeviceInfo = ref({ const activeDeviceInfo = ref({
id: 0, id: 0,
devicePort: '', devicePort: '',
deviceSN: '', cpuId: '',
connectStatus: 0, connectStatus: 0,
switch: 0, switch: 0,
result: 0, result: 0,
resultTxt: '' resultTxt: '',
stepStatus: 0,
activeStep: ''
}); });
const defaultPropList = ref([]); const defaultPropList = ref([]);
@ -390,20 +494,36 @@ const currentScheme = ref({
schemeType: '1p', schemeType: '1p',
series: 'B7', series: 'B7',
framework: '100ZS', framework: '100ZS',
middleDetect: true,
laterDetect: false,
configParam: defaultConfList, configParam: defaultConfList,
propertyParam: defaultPropList, propertyParam: defaultPropList,
errorRange: { errorRange: {
voltage: 0.5, range: {
current: 0.5, voltage: 0.5,
smallCurrent: 1.0, current: 0.5,
powerFactor: '0.5L', powerFactor: 0.5,
activePower: 0.5, activePower: 0.5,
reactivePower: 0.5, reactivePower: 0.5,
apparentPower: 0.5 apparentPower: 0.5
},
smallRange: {
voltage: 1,
current: 1,
powerFactor: 1,
activePower: 1,
reactivePower: 1,
apparentPower: 1
}
}, },
steps: [] steps: [],
iLeakDetectObj: defaultiLeakDetectObj
}); });
// 1. step
const logParamColumns = ref([]); // [{ valueKey, valueName, valueUnit }]
const stepLogRows = ref([]); // [{ step, paramMap: { valueKey: item }, result: 0/1 }]
const logTabChange = (tab, event) => { const logTabChange = (tab, event) => {
console.log(tab, event); console.log(tab, event);
}; };
@ -412,7 +532,7 @@ const selectDevice = async (item, index) => {
activeDeviceInfo.value = item; activeDeviceInfo.value = item;
activeDeviceIndex.value = index; activeDeviceIndex.value = index;
// //
await fetchDeviceLogs(item.id); await fetchDeviceLogs(item.cpuId);
logVisible.value = true; logVisible.value = true;
}; };
@ -421,7 +541,7 @@ const fetchDeviceLogs = async deviceId => {
logLoading.value = true; logLoading.value = true;
try { try {
const response = await axios.get(config.url + '/master/calibrate/result', { const response = await axios.get(config.url + '/master/calibrate/result', {
params: { devId: deviceId } params: { cpuId: deviceId }
}); });
if (response.data.code === 0) { if (response.data.code === 0) {
@ -437,103 +557,60 @@ const fetchDeviceLogs = async deviceId => {
} }
}; };
// // processLogData
const processLogData = apiData => { const processLogData = apiData => {
if (!apiData || !Array.isArray(apiData)) { if (!apiData || !Array.isArray(apiData)) {
logList.value = []; logParamColumns.value = [];
stepLogRows.value = [];
return; return;
} }
// activeScheme.propertyParam
const paramMap = new Map();
apiData.forEach(item => {
let valueName = item.valueName || item.valueKey;
let valueUnit = item.valueUnit || '';
if (activeScheme.value && Array.isArray(activeScheme.value.propertyParam)) {
const found = activeScheme.value.propertyParam.find(param => param.key === item.valueKey);
if (found) {
valueName = found.name || valueName;
valueUnit = found.unit || valueUnit;
}
}
const key = item.valueKey + (valueUnit ? `(${valueUnit})` : '');
if (!paramMap.has(key)) {
paramMap.set(key, {
valueKey: item.valueKey,
valueName,
valueUnit,
key: key
});
}
});
logParamColumns.value = Array.from(paramMap.values());
// // step
const stepGroups = {}; const stepGroups = {};
apiData.forEach(item => { apiData.forEach(item => {
if (!stepGroups[item.step]) { if (!stepGroups[item.step]) stepGroups[item.step] = [];
stepGroups[item.step] = [];
}
stepGroups[item.step].push(item); stepGroups[item.step].push(item);
}); });
//
// stepLogRows.value = Object.entries(stepGroups).map(([step, arr]) => {
const processedLogs = Object.keys(stepGroups).map(stepName => { // paramMap: { valueKey: item }
const stepData = stepGroups[stepName]; const paramMapRow = {};
const dataList = stepData.map(item => { arr.forEach(item => {
// activeScheme.propertyParam paramMapRow[item.valueKey] = item;
const paramInfo = activeScheme.value?.propertyParam?.find(param => param.key === item.valueKey || param.name === item.valueName);
// ExpectedMinexpectedMaxresult
const minValue = parseFloat(item.ExpectedMin);
const maxValue = parseFloat(item.expectedMax);
const outputValue = parseFloat(item.actualValue);
let result = 0; //
if (!isNaN(minValue) && !isNaN(maxValue) && !isNaN(outputValue)) {
// outputValue
if (outputValue >= minValue && outputValue <= maxValue) {
result = 1; //
} else if (outputValue > maxValue) {
result = 2; //
} else if (outputValue < minValue) {
result = 0; //
}
}
return {
id: item.id,
name: paramInfo?.name || item.valueName || item.valueKey,
value: item.actualValue,
unit: paramInfo?.unit || item.valueUnit,
accuracy: item.expectedError,
result: result,
minValue: item.ExpectedMin?.toString(),
maxValue: item.expectedMax?.toString()
};
}); });
//
const allOk = logParamColumns.value.every(col => paramMapRow[col.valueKey]?.result === 1);
return { return {
name: stepName, step,
dataList: dataList paramMap: paramMapRow,
result: allOk ? 1 : 0
}; };
}); });
logList.value = processedLogs;
}; };
// 便
const transposedLogList = computed(() => {
if (!logList.value.length) return [];
// valueKey
const allValueKeys = new Set();
logList.value.forEach(step => {
step.dataList.forEach(item => {
allValueKeys.add(item.valueKey || item.name);
});
});
// valueKey
const result = [];
allValueKeys.forEach(valueKey => {
// valueKey
const firstStepWithKey = logList.value.find(step =>
step.dataList.some(item => (item.valueKey || item.name) === valueKey)
);
const firstItem = firstStepWithKey?.dataList.find(item =>
(item.valueKey || item.name) === valueKey
);
const param = {
name: (firstItem?.name || valueKey) + (firstItem?.unit ? '(' + firstItem.unit + ')' : ''),
data: logList.value.map(step => {
// valueKey
const item = step.dataList.find(item => (item.valueKey || item.name) === valueKey);
return item || null; // null
})
};
result.push(param);
});
return result;
});
const schemeChange = e => { const schemeChange = e => {
if (e) { if (e) {
const scheme = schemeList.value.filter(item => item.id === e); const scheme = schemeList.value.filter(item => item.id === e);
@ -554,45 +631,44 @@ const tryParseJSON = data => {
// //
const throttledScroll = throttle(() => { const throttledScroll = throttle(() => {
logBoxRef.value.scrollTop = logBoxRef.value.scrollHeight; logBoxRef.value.scrollTop = logBoxRef.value.scrollHeight;
}, 100); }, 500);
const setDeviceInfo = msg => { const setDeviceInfo = msg => {
let deviceInfo = tryParseJSON(msg); let deviceInfo = tryParseJSON(msg);
let type = false; let type = false;
if(deviceInfo){ if (deviceInfo) {
devices.value.forEach((item,index) => { devices.value.forEach((item, index) => {
if (item.id === deviceInfo.id) { if (item.id === deviceInfo.id) {
// debugger // debugger
devices.value[index] = deviceInfo; devices.value[index] = deviceInfo;
// item=deviceInfo; // item=deviceInfo;
if(deviceInfo.result === -1){ if (deviceInfo.result === -1) {
type = 'calibrate_result_error'; type = 'calibrate_result_error';
}else if (deviceInfo.result === 1){ } else if (deviceInfo.result === 1) {
type = 'calibrate_result_success'; type = 'calibrate_result_success';
} }
} }
}) });
} }
return type; return type;
}; };
const getSocketMeassage = message => { const getSocketMeassage = message => {
// message = {data:{"msgType":"calibrate_result","data":"{\"id\":1,\"devicePort\":\"com3\",\"deviceSN\":\"SN\",\"connectStatus\":1,\"switch\":0,\"result\":-1,\"resultTxt\":\"\"}"}}; // message = {data:{"msgType":"calibrate_result","data":"{\"id\":1,\"switch\":0,\"result\":-1,\"switch\":0,\"result\":-1,\"switch\":0,\"result\":-1,\"switch\":0,\"result\":-1,\"switch\":0,\"result\":-1,\"switch\":0,\"result\":-1,\"switch\":0,\"result\":-1,\"switch\":0,\"result\":-1,\"switch\":0,\"result\":-1,\"switch\":0,\"result\":-1,\"switch\":0,\"result\":-1,\"switch\":0,\"result\":-1,\"devicePort\":\"com3\",\"cpuId\":\"SN\",\"connectStatus\":1,\"switch\":0,\"result\":-1,\"switch\":0,\"result\":-1,\"switch\":0,\"result\":-1,\"switch\":0,\"result\":-1,\"switch\":0,\"result\":-1,\"switch\":0,\"result\":-1,\"switch\":0,\"result\":-1,\"switch\":0,\"result\":-1,\"switch\":0,\"result\":-1,\"switch\":0,\"result\":-1,\"switch\":0,\"result\":-1,\"switch\":0,\"result\":-1,\"switch\":0,\"result\":-1,\"switch\":0,\"result\":-1,\"switch\":0,\"result\":-1,\"switch\":0,\"result\":-1,\"switch\":0,\"result\":-1,\"resultTxt\":\"\"}"}};
const msg = tryParseJSON(message.data); const msg = tryParseJSON(message.data);
console.log("msg",msg); console.log('msg', msg);
const newLog = { const newLog = {
time: dayjs().format('YYYY-MM-DD HH:mm:ss'), time: dayjs().format('YYYY-MM-DD HH:mm:ss'),
msg: msg.data ?? msg, msg: msg.data ?? msg,
type: msg.msgType ?? null type: msg.msgType ?? null
}; };
if(msg.msgType === 'calibrate_result'){ if (msg.msgType === 'calibrate_result') {
let resultType = setDeviceInfo(msg.data); let resultType = setDeviceInfo(msg.data);
if(resultType){ if (resultType) {
newLog.type = resultType; newLog.type = resultType;
} }
} }
// //
logs.value = [...logs.value.slice(-MAX_LOG_LENGTH + 1), newLog]; logs.value = [...logs.value.slice(-MAX_LOG_LENGTH + 1), newLog];
@ -649,13 +725,12 @@ onMounted(() => {
version.value = v; version.value = v;
}); });
} }
// generateMockDevices(); // generateMockDevices();
getSchemeDefaultConf(); getSchemeDefaultConf();
getSchemeDefaultProp(); getSchemeDefaultProp();
generateDeviceList(); generateDeviceList();
loadSchemes(); loadSchemes();
}); });
onUnmounted(() => { onUnmounted(() => {
@ -673,6 +748,12 @@ const startExecution = () => {
ElMessage.error('请先选择方案'); ElMessage.error('请先选择方案');
return; return;
} }
if (operatorCode.value.trim() === '') {
ElMessage.error('请先输入操作员编号');
return;
}
// socket
reconnectSocket();
const scheme = schemeList.value.filter(item => item.id === selectedScheme.value); const scheme = schemeList.value.filter(item => item.id === selectedScheme.value);
console.log(scheme); console.log(scheme);
ElMessage.success('开始执行方案: ' + scheme[0].schemeName); ElMessage.success('开始执行方案: ' + scheme[0].schemeName);
@ -681,10 +762,20 @@ const startExecution = () => {
// Add actual start logic here // Add actual start logic here
}; };
const stopExecution = () => { const stopExecution = async () => {
ElMessage.info('停止执行'); try {
const response = await axios.get(config.url + '/master/scheme/stop');
if (response.data.code === 0) {
// devices.value = response.data.data.result || [];
playState.value = false;
ElMessage.success('停止成功');
} else {
ElMessage.error(response.data.message || '停止失败');
}
} catch (error) {
ElMessage.error('停止失败');
}
playState.value = false; playState.value = false;
// Add actual stop logic here
}; };
const openSettings = () => { const openSettings = () => {
@ -757,18 +848,11 @@ const saveScheme = async () => {
Object.entries(accuracyType.value).forEach(([key, value]) => { Object.entries(accuracyType.value).forEach(([key, value]) => {
// 使 // 使
if (param.name.includes(value)) { if (param.name.includes(value)) {
// // bigRangesmallRange
if (key === 'current') { param.errorRange = {
param.errorRange = { bigRange: scheme.errorRange.range[key],
bigRange: scheme.errorRange[key], smallRange: scheme.errorRange.smallRange[key]
smallRange: scheme.errorRange.smallCurrent };
};
} else {
// defaultRange
param.errorRange = {
defaultRange: scheme.errorRange[key]
};
}
} }
}); });
}); });
@ -826,18 +910,30 @@ const addScheme = () => {
schemeType: '1p', schemeType: '1p',
series: 'B7', series: 'B7',
framework: '100ZS', framework: '100ZS',
middleDetect: true,
laterDetect: false,
configParam: defaultConfList, configParam: defaultConfList,
propertyParam: defaultPropList, propertyParam: defaultPropList,
errorRange: { errorRange: {
voltage: 0.5, range: {
current: 0.5, voltage: 0.5,
smallCurrent: 1.0, current: 0.5,
powerFactor: '0.5L', powerFactor: 0.5,
activePower: 0.5, activePower: 0.5,
reactivePower: 0.5, reactivePower: 0.5,
apparentPower: 0.5 apparentPower: 0.5
},
smallRange: {
voltage: 1,
current: 1,
powerFactor: 1,
activePower: 1,
reactivePower: 1,
apparentPower: 1
}
}, },
steps: [] steps: [],
iLeakDetectObj: defaultiLeakDetectObj
}; };
schemeDetailVisible.value = true; schemeDetailVisible.value = true;
}; };
@ -886,8 +982,8 @@ const generateDeviceList = async () => {
const response = await axios.get(config.url + '/master/device/list'); const response = await axios.get(config.url + '/master/device/list');
if (response.data.code === 0) { if (response.data.code === 0) {
devices.value = response.data.data.result || []; devices.value = response.data.data.result || [];
// //
// setDeviceInfo("{\"id\":1,\"devicePort\":\"com3\",\"deviceSN\":\"SN\",\"connectStatus\":1,\"switch\":0,\"result\":-1,\"resultTxt\":\"\"}") // setDeviceInfo("{\"id\":1,\"devicePort\":\"com3\",\"cpuId\":\"SN\",\"connectStatus\":1,\"switch\":0,\"result\":-1,\"resultTxt\":\"\"}")
} else { } else {
ElMessage.error(response.data.message || '刷新列表失败'); ElMessage.error(response.data.message || '刷新列表失败');
} }
@ -899,15 +995,15 @@ const generateDeviceList = async () => {
const playCalibration = async () => { const playCalibration = async () => {
try { try {
const response = await axios.get(config.url + '/master/scheme/start?schemeId=' + selectedScheme.value); const response = await axios.get(config.url + '/master/scheme/start?schemeId=' + selectedScheme.value + '&operatorCode=' + operatorCode.value);
if (response.data.code === 0) { if (response.data.code === 0) {
devices.value = response.data.data.result || []; // devices.value = response.data.data.result || [];
} else { } else {
ElMessage.error(response.data.message || '获取设备列表失败'); // ElMessage.error(response.data.message || '');
} }
} catch (error) { } catch (error) {
console.error('获取设备列表失败:', error); console.error('获取设备列表失败:', error);
ElMessage.error('获取设备列表失败'); // ElMessage.error('');
} }
}; };
</script> </script>
@ -969,7 +1065,7 @@ const playCalibration = async () => {
.calibration-main { .calibration-main {
flex-grow: 1; flex-grow: 1;
padding: 20px; padding: 20px 10px;
overflow-y: auto; overflow-y: auto;
} }
@ -995,9 +1091,37 @@ const playCalibration = async () => {
font-size: 14px; font-size: 14px;
} }
@keyframes dot-animation {
0% {
content: '.';
}
33% {
content: '..';
}
66% {
content: '...';
}
100% {
content: '.';
}
}
.device-card-body { .device-card-body {
font-size: 12px; font-size: 12px;
text-align: center; text-align: center;
position: relative;
.process-status {
position: absolute;
top: -6px;
left: 0;
width: 100%;
&.color-orange {
&::after {
content: '...';
animation: dot-animation 1.5s infinite;
}
}
}
.device-result { .device-result {
font-size: 14px; font-size: 14px;
font-weight: bold; font-weight: bold;
@ -1014,6 +1138,11 @@ const playCalibration = async () => {
/* Green for connected */ /* Green for connected */
} }
.color-orange {
color: #e6a23c;
/* Green for connected */
}
.color-red { .color-red {
color: #f56c6c; color: #f56c6c;
/* Red for disconnected */ /* Red for disconnected */
@ -1052,7 +1181,6 @@ const playCalibration = async () => {
.log-box-main { .log-box-main {
font-family: 'Courier New', Courier, monospace; font-family: 'Courier New', Courier, monospace;
padding: 5px;
font-size: 10px; font-size: 10px;
white-space: pre-wrap; white-space: pre-wrap;
flex-grow: 1; flex-grow: 1;
@ -1061,30 +1189,34 @@ const playCalibration = async () => {
background-color: #202020; background-color: #202020;
color: #dcdcdc; color: #dcdcdc;
scroll-behavior: smooth; scroll-behavior: smooth;
.log-list{
// padding: 5px;
.log-item {
padding: 0 5px;
word-wrap: break-word;
word-break: break-all;
font-size: 12px;
line-height: 18px;
font-weight: 500;
.log-item { .iconfont {
word-wrap: break-word; font-size: 14px;
word-break: break-all; line-height: 18px;
font-size: 12px; &.icon-icon_fuzhi {
line-height: 18px; line-height: 26px;
font-weight: 500; margin-left: 8px;
font-size: 16px;
color: #0066cc;
cursor: pointer;
}
}
.iconfont { .log-item-txt {
font-size: 14px; margin-left: 5px;
line-height: 18px;
&.icon-icon_fuzhi {
line-height: 26px;
margin-left: 8px;
font-size: 16px;
color: #0066cc;
cursor: pointer;
} }
} }
.log-item-txt{
margin-left: 5px;
}
} }
} }
.calibration-footer-btn { .calibration-footer-btn {
@ -1191,11 +1323,18 @@ const playCalibration = async () => {
gap: 5px; gap: 5px;
} }
.accuracy-title {
font-weight: bold;
margin-bottom: 10px;
font-size: 14px;
color: #000;
}
.device-params, .device-params,
.accuracy-params { .accuracy-params {
display: grid; display: grid;
grid-template-columns: repeat(4, 1fr); gap: 10px;
gap: 20px; grid-template-columns: repeat(6, 1fr);
} }
.calibration-steps { .calibration-steps {