1074 lines
39 KiB
Vue
1074 lines
39 KiB
Vue
<template>
|
||
<div id="southdirection-box">
|
||
<div class="device-config">
|
||
<h2>设备配置</h2>
|
||
<div class="btn-list">
|
||
<div class="btn-list-left">
|
||
<el-button type="primary" @click="addDeviceConfig">新增配置</el-button>
|
||
<!-- <el-button type="danger" @click="getDeviceConfig">删除</el-button>-->
|
||
</div>
|
||
<div class="btn-list-right">
|
||
<el-button type="primary" style="margin-right: 10px" @click="exportDeviceConfigXlsx">导出</el-button>
|
||
<!-- <el-button type="primary" @click="getDeviceConfig">导入</el-button>-->
|
||
<el-upload action="#" :auto-upload="false" :on-change="handleFileChange" :show-file-list="false" style="margin-right: 10px">
|
||
<el-button type="primary">导入</el-button>
|
||
</el-upload>
|
||
<el-button type="primary" @click="getDeviceConfig">获取配置</el-button>
|
||
<el-button type="primary" @click="setDeviceConfig">设置配置</el-button>
|
||
</div>
|
||
</div>
|
||
<div class="table-box">
|
||
<el-table
|
||
ref="deviceConfigTableRef"
|
||
highlight-current-row
|
||
:data="deviceConfigList"
|
||
border
|
||
height="187"
|
||
:style="{ width: tabelBox + 'px', background: '#f2f2f2' }"
|
||
empty-text="配置为空"
|
||
row-key="index"
|
||
@row-click="deviceConfigClick"
|
||
>
|
||
<el-table-column type="index" label="序号" align="center" width="60" />
|
||
<el-table-column prop="equipId" label="设备ID">
|
||
<template #default="scope">
|
||
<el-input v-model="scope.row.equipId" placeholder="请输入设备ID" />
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column prop="equipName" label="设备名">
|
||
<template #default="scope">
|
||
<el-input v-model="scope.row.equipName" placeholder="请输入设备名" />
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column prop="protocol.libName" label="协议类型">
|
||
<template #default="scope">
|
||
<el-select v-model="scope.row.protocol.libName" placeholder="请选择协议类型">
|
||
<el-option v-for="item in protocolList" :key="item.value" :label="item.label" :value="item.value" />
|
||
</el-select>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column prop="interfaceParams.link" label="设备连接" />
|
||
<el-table-column prop="protocol.params.linkPar" label="设备地址">
|
||
<template #default="scope">
|
||
<el-input v-model="scope.row.protocol.params.linkPar" placeholder="请输入设备地址" />
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="高级选项" width="208">
|
||
<template #default="scope">
|
||
<el-button link type="primary" size="small" @click="openConnectModel(scope.row)">连接配置</el-button>
|
||
<el-button link type="primary" size="small" @click="openCollectionModel(scope.row)">采集配置</el-button>
|
||
<el-button link type="danger" size="small" @click="delDeviceConfig(scope.$index)">删除</el-button>
|
||
</template>
|
||
</el-table-column>
|
||
</el-table>
|
||
</div>
|
||
</div>
|
||
<div class="data-config">
|
||
<h2>数据源配置 {{ activeDeviceConfig.equipId }}</h2>
|
||
<div class="btn-list">
|
||
<div class="btn-list-left">
|
||
<el-button type="primary" @click="addDeviceDataConfig">新增配置</el-button>
|
||
<!-- <el-button type="danger" @click="getDeviceConfig">删除</el-button>-->
|
||
</div>
|
||
<div class="btn-list-right">
|
||
<el-button type="primary" style="margin-right: 10px" @click="exportDeviceDataConfigXlsx">导出</el-button>
|
||
<el-upload action="#" :auto-upload="false" :on-change="handleFileChange" :show-file-list="false">
|
||
<el-button type="primary">导入</el-button>
|
||
</el-upload>
|
||
</div>
|
||
</div>
|
||
<div class="table-box">
|
||
<el-table
|
||
ref="deviceDataTableRef"
|
||
:data="activeDeviceConfig.properties"
|
||
border
|
||
size="small"
|
||
height="100%"
|
||
:style="{ width: tabelBox + 'px' }"
|
||
empty-text="配置为空"
|
||
>
|
||
<el-table-column type="index" label="序号" align="center" width="60" fixed />
|
||
<el-table-column prop="id" label="数据ID" fixed align="center">
|
||
<template #default="scope">
|
||
<el-input v-model="scope.row.id" placeholder="请输入数据ID" />
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column prop="dataName" label="数据名" fixed align="center">
|
||
<template #default="scope">
|
||
<el-input v-model="scope.row.dataName" placeholder="请输入数据名" />
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column prop="dataAddress" label="数据地址" align="center">
|
||
<template #default="scope">
|
||
<el-input v-model="scope.row.dataAddress" placeholder="请输入数据地址" />
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column prop="dataDesc" label="描述" align="center">
|
||
<template #default="scope">
|
||
<el-input v-model="scope.row.dataDesc" placeholder="请输入描述" />
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column prop="dataDisplay" label="数据类型" width="120" align="center">
|
||
<template #default="scope">
|
||
<el-select v-model="scope.row.dataDisplay" placeholder="请选择数据类型" @change="changedataDisplay($event, scope.$index)">
|
||
<el-option v-for="item in dataTypeArray" :key="item.type" :value="item.type">{{ item.type }}</el-option>
|
||
<!-- <el-option value="bool">bool</el-option>-->
|
||
<!-- <el-option value="int8">int8</el-option>-->
|
||
<!-- <el-option value="uint8">uint8</el-option>-->
|
||
<!-- <el-option value="int16">int16</el-option>-->
|
||
<!-- <el-option value="uint16">uint16</el-option>-->
|
||
<!-- <el-option value="float">float</el-option>-->
|
||
<!-- <el-option value="int32">int32</el-option>-->
|
||
<!-- <el-option value="uint32">uint32</el-option>-->
|
||
<!-- <el-option value="int64">int64</el-option>-->
|
||
<!-- <el-option value="uint64">uint64</el-option>-->
|
||
<!-- <el-option value="double">double</el-option>-->
|
||
<!-- <el-option value="char">char</el-option>-->
|
||
<!-- <el-option value="string">string</el-option>-->
|
||
<!-- <el-option value="int16_AB">int16_AB</el-option>-->
|
||
<!-- <el-option value="int16_BA">int16_BA</el-option>-->
|
||
<!-- <el-option value="uint16_AB">uint16_AB</el-option>-->
|
||
<!-- <el-option value="uint16_BA">uint16_BA</el-option>-->
|
||
<!-- <el-option value="float_ABCD">float_ABCD</el-option>-->
|
||
<!-- <el-option value="float_BADC">float_BADC</el-option>-->
|
||
<!-- <el-option value="float_CDAB">float_CDAB</el-option>-->
|
||
<!-- <el-option value="float_DCBA">float_DCBA</el-option>-->
|
||
<!-- <el-option value="int32_ABCD">int32_ABCD</el-option>-->
|
||
<!-- <el-option value="int32_BADC">int32_BADC</el-option>-->
|
||
<!-- <el-option value="int32_CDAB">int32_CDAB</el-option>-->
|
||
<!-- <el-option value="int32_DCBA">int32_DCBA</el-option>-->
|
||
<!-- <el-option value="uint32_ABCD">uint32_ABCD</el-option>-->
|
||
<!-- <el-option value="uint32_BADC">uint32_BADC</el-option>-->
|
||
<!-- <el-option value="uint32_CDAB">uint32_CDAB</el-option>-->
|
||
<!-- <el-option value="uint32_DCBA">uint32_DCBA</el-option>-->
|
||
</el-select>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column prop="datalen" label="长度(字节)" width="120" align="center">
|
||
<template #default="scope">
|
||
<el-input v-model="scope.row.datalen" type="number" :disabled="!scope.row.datalenShow" placeholder="请输入长度(字节)" />
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column prop="dataUnit" label="单位" width="70" align="center">
|
||
<template #default="scope">
|
||
<el-input v-model="scope.row.dataUnit" placeholder="请输入单位" />
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column prop="authority" label="读写权限" width="90" align="center">
|
||
<template #default="scope">
|
||
<el-select v-model="scope.row.authority" placeholder="请选择读写权限">
|
||
<el-option value="RW">RW</el-option>
|
||
<el-option value="OR">OR</el-option>
|
||
<el-option value="OW">OW</el-option>
|
||
</el-select>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column prop="proportion" label="比例" width="70" align="center">
|
||
<template #default="scope">
|
||
<el-input v-model="scope.row.proportion" placeholder="请输入比例" />
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column prop="offset" label="偏置" width="60" align="center">
|
||
<template #default="scope">
|
||
<el-input v-model="scope.row.offset" placeholder="请输入偏置" />
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column prop="northCalcParam.decimal.numBits" label="小数位" width="60" align="center">
|
||
<template #default="scope">
|
||
<el-input v-model="scope.row.northCalcParam.decimal.numBits" placeholder="请输入小数位" />
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column prop="northCalcParam.decimal.way" label="取整" align="center">
|
||
<template #default="scope">
|
||
<el-select v-model="scope.row.northCalcParam.decimal.way" placeholder="请选择取整">
|
||
<el-option value="rounding" label="四舍五入"></el-option>
|
||
<el-option value="flooring" label="全舍"></el-option>
|
||
<el-option value="ceiling" label="全入"></el-option>
|
||
</el-select>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column prop="driveFlag.isConclude_R" label="块读" width="50" align="center">
|
||
<template #default="scope">
|
||
<!-- <el-select v-model="scope.row.driveFlag.isConclude_R" placeholder="请选择块读">-->
|
||
<!-- <el-option value="true">Yes</el-option>-->
|
||
<!-- <el-option value="false">No</el-option>-->
|
||
<!-- </el-select>-->
|
||
<el-switch v-model="scope.row.driveFlag.isConclude_R" />
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column prop="driveFlag.isConclude_W" label="块写" width="50" align="center">
|
||
<template #default="scope">
|
||
<!-- <el-select v-model="scope.row.driveFlag.isConclude_W" placeholder="请选择块写">-->
|
||
<!-- <el-option value="true">Yes</el-option>-->
|
||
<!-- <el-option value="false">No</el-option>-->
|
||
<!-- </el-select>-->
|
||
<el-switch v-model="scope.row.driveFlag.isConclude_W" />
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column prop="taskId" label="任务ID" align="center">
|
||
<template #default="scope">
|
||
<el-select v-model="scope.row.taskId" placeholder="请选择任务ID" @change="changeTaskId($event, scope.$index)">
|
||
<el-option v-for="(item, index) in taskList" :key="index" :value="item.taskId" :label="item.taskId"></el-option>
|
||
</el-select>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column prop="uploadMode" label="上报策略" align="center">
|
||
<template #default="scope">
|
||
<!-- <el-select v-model="scope.row.uploadMode" placeholder="请选择上报策略">-->
|
||
<!-- <el-option :value="item.uploadMode" v-for="(item,index) in taskList" :key="index" :label="item.uploadMode"></el-option>-->
|
||
<!-- </el-select>-->
|
||
<el-autocomplete
|
||
v-model="scope.row.uploadMode"
|
||
:fetch-suggestions="querySearch"
|
||
clearable
|
||
class="inline-input w-50"
|
||
placeholder="请选择上报策略"
|
||
/>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="操作" width="55" fixed="right" align="center">
|
||
<template #default="scope">
|
||
<el-button link type="danger" size="small" @click.stop="delDeviceDataConfig(scope.$index)">删除</el-button>
|
||
</template>
|
||
</el-table-column>
|
||
</el-table>
|
||
</div>
|
||
</div>
|
||
|
||
<el-dialog v-model="collectionModelShow" title="采集配置" width="500" :before-close="handleClose">
|
||
<el-form ref="collectionRef" :model="collectionForm" label-width="auto" status-icon>
|
||
<el-form-item label="启动">
|
||
<el-switch v-model="collectionForm.collectConf.isRunCmd" />
|
||
</el-form-item>
|
||
<el-row>
|
||
<el-col :span="12">
|
||
<el-form-item label="轮询周期(ms)">
|
||
<el-input v-model="collectionForm.collectConf.pollingCycleTime_ms" type="number" placeholder="请输入轮询周期(ms)" />
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="12" class="text-center">
|
||
<el-form-item label="读通信最大时长(ms)">
|
||
<el-input v-model="collectionForm.collectConf.allowTimeOut.read" type="number" placeholder="请输入读通信最大时长(ms)" />
|
||
</el-form-item>
|
||
</el-col>
|
||
</el-row>
|
||
<el-row>
|
||
<el-col :span="12">
|
||
<el-form-item label="通信间隔(ms)">
|
||
<el-input v-model="collectionForm.collectConf.intervalTime_ms" type="number" placeholder="请输入通信间隔(ms)" />
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="12" class="text-center">
|
||
<el-form-item label="写通信最大时长(ms)">
|
||
<el-input v-model="collectionForm.collectConf.allowTimeOut.write" type="number" placeholder="请输入写通信最大时长(ms)" />
|
||
</el-form-item>
|
||
</el-col>
|
||
</el-row>
|
||
<el-form-item label="异常允许最大次数">
|
||
<el-input v-model="collectionForm.collectConf.abnormalMax" type="number" placeholder="请输入异常允许最大次数" />
|
||
</el-form-item>
|
||
<el-form-item label="读归纳布尔量最大长度">
|
||
<el-input v-model="collectionForm.inductionParam.maxLenBlock_read.boolArea" type="number" placeholder="请输入读归纳布尔量最大长度" />
|
||
</el-form-item>
|
||
<el-form-item label="读归纳字节最大长度">
|
||
<el-input v-model="collectionForm.inductionParam.maxLenBlock_read.charArea" type="number" placeholder="请输入读归纳字节最大长度" />
|
||
</el-form-item>
|
||
<el-form-item label="写归纳布尔量最大长度">
|
||
<el-input v-model="collectionForm.inductionParam.maxLenBlock_write.boolArea" type="number" placeholder="请输入写归纳布尔量最大长度" />
|
||
</el-form-item>
|
||
<el-form-item label="写归纳字节最大长度">
|
||
<el-input v-model="collectionForm.inductionParam.maxLenBlock_write.charArea" type="number" placeholder="请输入写归纳字节最大长度" />
|
||
</el-form-item>
|
||
</el-form>
|
||
<template #footer>
|
||
<div class="dialog-footer">
|
||
<el-button @click="collectionModelShow = false">取消</el-button>
|
||
<el-button type="primary" @click="editCollection"> 确认 </el-button>
|
||
</div>
|
||
</template>
|
||
</el-dialog>
|
||
<!-- 串口连接配置 -->
|
||
<el-dialog v-model="dlt645ModelShow" title="连接配置" width="500">
|
||
<el-form ref="dlt645Ref" :model="dlt645Form" label-width="auto" status-icon>
|
||
<el-form-item label="端口" prop="port" required>
|
||
<el-select v-model="dlt645Form.port" placeholder="选择端口" style="flex: 1">
|
||
<el-option v-for="(item, index) in serialportList" :key="index" :label="item" :value="item" />
|
||
</el-select>
|
||
<el-button style="margin-left: 10px" type="primary" @click="getSerialPortList">获取端口列表</el-button>
|
||
</el-form-item>
|
||
<el-form-item label="波特率" prop="baudrate" required>
|
||
<el-select v-model="dlt645Form.baudrate" placeholder="选择波特率">
|
||
<el-option label="4800" value="4800" />
|
||
<el-option label="9600" value="9600" />
|
||
<el-option label="19200" value="19200" />
|
||
<el-option label="43000" value="43000" />
|
||
<el-option label="56000" value="56000" />
|
||
<el-option label="115200" value="115200" />
|
||
</el-select>
|
||
</el-form-item>
|
||
<el-form-item label="数据位" prop="databits" required>
|
||
<el-select v-model="dlt645Form.databits" placeholder="选择数据位">
|
||
<el-option label="5" value="5" />
|
||
<el-option label="6" value="6" />
|
||
<el-option label="7" value="7" />
|
||
<el-option label="8" value="8" />
|
||
</el-select>
|
||
</el-form-item>
|
||
<el-form-item label="停止位" prop="stopbits" required>
|
||
<el-select v-model="dlt645Form.stopbits" placeholder="选择停止位">
|
||
<el-option label="1" value="1" />
|
||
<el-option label="1.5" value="15" />
|
||
<el-option label="2" value="2" />
|
||
</el-select>
|
||
</el-form-item>
|
||
<el-form-item label="校验位" prop="parity" required>
|
||
<el-select v-model="dlt645Form.parity" placeholder="选择校验位">
|
||
<el-option label="None" value="N" />
|
||
<el-option label="Odd" value="O" />
|
||
<el-option label="Even" value="E" />
|
||
<!-- <el-option label="Mark" value="M" />-->
|
||
<!-- <el-option label="Space" value="S" />-->
|
||
</el-select>
|
||
</el-form-item>
|
||
</el-form>
|
||
<template #footer>
|
||
<div class="dialog-footer">
|
||
<el-button @click="dlt645ModelShow = false">取消</el-button>
|
||
<el-button type="primary" @click="editDlt645(dlt645Ref)"> 确认 </el-button>
|
||
</div>
|
||
</template>
|
||
</el-dialog>
|
||
<!-- TCP连接配置 -->
|
||
<el-dialog v-model="tcpModelShow" title="连接配置" width="500">
|
||
<el-form ref="tcpRef" :model="tcpForm" :rules="tcpFormRules" label-width="auto" status-icon>
|
||
<el-form-item label="设备地址" prop="link" required>
|
||
<el-input v-model="tcpForm.link" placeholder="请输入ip地址+端口:如192.168.0.1:80" />
|
||
</el-form-item>
|
||
</el-form>
|
||
<template #footer>
|
||
<div class="dialog-footer">
|
||
<el-button @click="tcpModelShow = false">取消</el-button>
|
||
<el-button type="primary" @click="editTcp(tcpRef)"> 确认 </el-button>
|
||
</div>
|
||
</template>
|
||
</el-dialog>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { reactive, ref, onMounted, onUnmounted, watch } from 'vue';
|
||
import { ElMessage, ElMessageBox } from 'element-plus';
|
||
import { guidGenerator } from '/@/utils/function.js';
|
||
// 导入依赖
|
||
import FileSaver from 'file-saver';
|
||
import * as XLSX from 'xlsx';
|
||
import dayjs from 'dayjs';
|
||
import { getDeviceConf, getSerialPortDataList, setDeviceConf } from '/@/api/gateway/southdirection';
|
||
|
||
const dataTypeArray = ref([
|
||
{ type: 'bool', datalen: 1, show: false },
|
||
{ type: 'int8', datalen: 1, show: false },
|
||
{ type: 'uint8', datalen: 1, show: false },
|
||
{ type: 'int16', datalen: 2, show: false },
|
||
{ type: 'uint16', datalen: 2, show: false },
|
||
{ type: 'float', datalen: 4, show: false },
|
||
{ type: 'int32', datalen: 4, show: false },
|
||
{ type: 'uint32', datalen: 4, show: false },
|
||
{ type: 'int64', datalen: 8, show: false },
|
||
{ type: 'uint64', datalen: 8, show: false },
|
||
{ type: 'double', datalen: 8, show: false },
|
||
{ type: 'char', datalen: 1, show: false },
|
||
{ type: 'string', datalen: null, show: true },
|
||
{ type: 'int16_AB', datalen: 2, show: false },
|
||
{ type: 'int16_BA', datalen: 2, show: false },
|
||
{ type: 'uint16_AB', datalen: 2, show: false },
|
||
{ type: 'uint16_BA', datalen: 2, show: false },
|
||
{ type: 'float_ABCD', datalen: 4, show: false },
|
||
{ type: 'float_BADC', datalen: 4, show: false },
|
||
{ type: 'float_CDAB', datalen: 4, show: false },
|
||
{ type: 'float_DCBA', datalen: 4, show: false },
|
||
{ type: 'int32_ABCD', datalen: 4, show: false },
|
||
{ type: 'int32_BADC', datalen: 4, show: false },
|
||
{ type: 'int32_CDAB', datalen: 4, show: false },
|
||
{ type: 'int32_DCBA', datalen: 4, show: false },
|
||
{ type: 'uint32_ABCD', datalen: 4, show: false },
|
||
{ type: 'uint32_BADC', datalen: 4, show: false },
|
||
{ type: 'uint32_CDAB', datalen: 4, show: false },
|
||
{ type: 'uint32_DCBA', datalen: 4, show: false },
|
||
]);
|
||
|
||
//上报策略
|
||
const uploadModeList = ref([
|
||
{ value: 'timer 10', link: 'timer 10' },
|
||
{ value: 'change 10', link: 'change 10' },
|
||
{ value: 'timer 10 || change 10', link: 'timer 10 || change 10' },
|
||
{ value: 'label LD200 up', link: 'label LD200 up' },
|
||
]);
|
||
|
||
const querySearch = (queryString, cb) => {
|
||
const results = queryString ? uploadModeList.value.filter(createFilter(queryString)) : uploadModeList.value;
|
||
cb(results);
|
||
};
|
||
const createFilter = (queryString) => {
|
||
return (restaurant) => {
|
||
return restaurant.value.toLowerCase().indexOf(queryString.toLowerCase()) === 0;
|
||
};
|
||
};
|
||
//北向任务列表(后面换请求获取)
|
||
const taskList = ref([]);
|
||
const deviceConfigTableRef = ref();
|
||
const deviceDataTableRef = ref();
|
||
const tabelBox = ref(884);
|
||
const deviceConfigList = ref([]);
|
||
|
||
const protocolList = ref([
|
||
{ label: 'ModbusTCP', value: 'ModbusTCP' },
|
||
{ label: 'DLT645_2007', value: 'DLT645_2007' },
|
||
{ label: 'gycan', value: 'gycan' },
|
||
]);
|
||
//新增配置
|
||
const addDeviceConfig = () => {
|
||
let newDeviceConfig = {
|
||
id: guidGenerator(),
|
||
collectConf: {
|
||
// 数据采集配置
|
||
abnormalMax: 3, // 异常最大次数
|
||
allowTimeOut: { read: 2000, write: 2000 }, // 读写超时设置
|
||
intervalTime_ms: 100, // 采集间隔时间
|
||
isBlockRead: true, // 是否块读取
|
||
isBlockWrite: true, // 是否块写入
|
||
isRunCmd: true, // 是否运行命令
|
||
pollingCycleTime_ms: 1000, // 轮询周期时间
|
||
},
|
||
conclude: { isUse: false, libName: '' }, // 结论库设置
|
||
equipId: '', // 设备ID
|
||
equipName: '', // 设备名称
|
||
inductionParam: {
|
||
// 感应参数设置
|
||
maxLenBlock_read: { boolArea: -1, charArea: -1 }, // 读取最大长度
|
||
maxLenBlock_write: { boolArea: -1, charArea: -1 }, // 写入最大长度
|
||
},
|
||
interfaceParams: { link: '', physicalInterface: '' }, // 接口参数
|
||
properties: [],
|
||
protocol: { libName: '', params: { linkPar: '' } }, // 通信协议设置
|
||
};
|
||
console.log('deviceConfigList.value', deviceConfigTableRef.value);
|
||
deviceConfigList.value.push(newDeviceConfig);
|
||
// activeDeviceConfig.value=newDeviceConfig;
|
||
};
|
||
|
||
//删除配置
|
||
const delDeviceConfig = (index) => {
|
||
console.log('当前点击的行号', index);
|
||
ElMessageBox.confirm('确认要删除该设备配置吗?', '告警', {
|
||
confirmButtonText: '确认',
|
||
cancelButtonText: '取消',
|
||
type: 'warning',
|
||
})
|
||
.then(() => {
|
||
deviceConfigList.value.splice(index, 1);
|
||
ElMessage({
|
||
type: 'success',
|
||
message: '删除成功',
|
||
});
|
||
})
|
||
.catch(() => {
|
||
ElMessage({
|
||
type: 'info',
|
||
message: '取消删除',
|
||
});
|
||
});
|
||
};
|
||
|
||
const getDeviceConfig = () => {
|
||
console.log('获取配置');
|
||
getDeviceConf({})
|
||
.then((response) => {
|
||
// 成功响应后的处理
|
||
console.log(response); // 打印响应数据
|
||
if (response.code == 0) {
|
||
//缓存上下行主题
|
||
deviceConfigList.value = response.data.equips || [];
|
||
taskList.value = response.data.tasks || [];
|
||
if (deviceConfigList.value.length) {
|
||
if (deviceConfigList.value.length > ActiveRowIndex.value) {
|
||
activeDeviceConfig.value = deviceConfigList.value[ActiveRowIndex.value];
|
||
} else {
|
||
activeDeviceConfig.value = deviceConfigList.value[0];
|
||
}
|
||
} else {
|
||
addDeviceConfig();
|
||
}
|
||
} else {
|
||
ElMessage.error(response.message);
|
||
}
|
||
})
|
||
.catch((error) => {
|
||
// 错误处理
|
||
console.error(error); // 打印错误信息
|
||
ElMessage.error(error);
|
||
});
|
||
};
|
||
|
||
const dataFormat = (data) => {
|
||
console.log('数据格式化', deviceConfigList.value);
|
||
let nowtaskList =
|
||
taskList.value.map((item) => {
|
||
item.dataIdEntire = [];
|
||
return item;
|
||
}) || [];
|
||
if (nowtaskList.length > 0) {
|
||
deviceConfigList.value.forEach((item) => {
|
||
item.properties.forEach((property) => {
|
||
let taskId = property.taskId;
|
||
nowtaskList.forEach((task) => {
|
||
if (task.taskId === taskId) {
|
||
task.dataIdEntire.push({
|
||
equipId: item.equipId,
|
||
idInfo: [{ id: property.id, uploadMode: property.uploadMode }],
|
||
});
|
||
}
|
||
});
|
||
});
|
||
});
|
||
}
|
||
|
||
return {
|
||
equips: data,
|
||
tasks: nowtaskList,
|
||
};
|
||
};
|
||
|
||
const setDeviceConfig = () => {
|
||
console.log('提交配置', deviceConfigList.value);
|
||
let obj = dataFormat(deviceConfigList.value);
|
||
console.log('数据格式化后', obj);
|
||
setDeviceConf(obj)
|
||
.then((response) => {
|
||
// 成功响应后的处理
|
||
console.log(response); // 打印响应数据
|
||
if (response.code == 0) {
|
||
ElMessage.success('保存成功');
|
||
} else {
|
||
ElMessage.error(response.message);
|
||
}
|
||
})
|
||
.catch((error) => {
|
||
// 错误处理
|
||
console.error(error); // 打印错误信息
|
||
ElMessage.error(error);
|
||
});
|
||
};
|
||
|
||
//当选中索引
|
||
const ActiveRowIndex = ref(0);
|
||
|
||
const collectionModelShow = ref(false);
|
||
const collectionRef = ref();
|
||
const collectionForm = ref({
|
||
collectConf: {
|
||
// 数据采集配置
|
||
abnormalMax: 3, // 异常最大次数
|
||
allowTimeOut: { read: 2000, write: 2000 }, // 读写超时设置
|
||
intervalTime_ms: 100, // 采集间隔时间
|
||
isBlockRead: true, // 是否块读取
|
||
isBlockWrite: true, // 是否块写入
|
||
isRunCmd: true, // 是否运行命令
|
||
pollingCycleTime_ms: 1000, // 轮询周期时间
|
||
},
|
||
inductionParam: {
|
||
// 感应参数设置
|
||
maxLenBlock_read: { boolArea: -1, charArea: -1 }, // 读取最大长度
|
||
maxLenBlock_write: { boolArea: -1, charArea: -1 }, // 写入最大长度
|
||
},
|
||
});
|
||
//打开采集配置弹窗
|
||
const openCollectionModel = (row) => {
|
||
console.log('当前采集', row);
|
||
collectionForm.value = {
|
||
collectConf: row.collectConf,
|
||
inductionParam: row.inductionParam,
|
||
};
|
||
collectionModelShow.value = true;
|
||
};
|
||
|
||
const editCollection = () => {
|
||
deviceConfigList.value[ActiveRowIndex.value].collectConf = collectionForm.value.collectConf;
|
||
deviceConfigList.value[ActiveRowIndex.value].inductionParam = collectionForm.value.inductionParam;
|
||
collectionModelShow.value = false;
|
||
};
|
||
|
||
//连接配置
|
||
|
||
//串口配置
|
||
const dlt645ModelShow = ref(false);
|
||
const dlt645Ref = ref();
|
||
const serialportList = ref([]);
|
||
const dlt645Form = ref({
|
||
port: '',
|
||
baudrate: 115200,
|
||
databits: 8,
|
||
stopbits: 1,
|
||
parity: 'N',
|
||
});
|
||
//获取端口列表
|
||
const getSerialPortList = (msgShow = true) => {
|
||
getSerialPortDataList({})
|
||
.then((response) => {
|
||
// 成功响应后的处理
|
||
if (response.code === 0) {
|
||
serialportList.value = response.data.list || [];
|
||
if (msgShow) {
|
||
ElMessage.success('获取端口列表成功');
|
||
}
|
||
} else {
|
||
ElMessage.error(response.message);
|
||
}
|
||
})
|
||
.catch((error) => {
|
||
// 错误处理
|
||
ElMessage.error(error);
|
||
});
|
||
};
|
||
|
||
const editDlt645 = (formEl) => {
|
||
if (!formEl) return;
|
||
formEl.validate((valid) => {
|
||
if (valid) {
|
||
deviceConfigList.value[ActiveRowIndex.value].interfaceParams.link =
|
||
dlt645Form.value.port +
|
||
',' +
|
||
dlt645Form.value.baudrate +
|
||
',' +
|
||
dlt645Form.value.databits +
|
||
',' +
|
||
dlt645Form.value.stopbits +
|
||
',' +
|
||
dlt645Form.value.parity;
|
||
deviceConfigList.value[ActiveRowIndex.value].interfaceParams.physicalInterface = dlt645Form.value.port;
|
||
dlt645ModelShow.value = false;
|
||
}
|
||
});
|
||
};
|
||
|
||
//TCP配置
|
||
const tcpModelShow = ref(false);
|
||
const tcpRef = ref();
|
||
const tcpForm = ref({
|
||
link: '192.168.0.1:80',
|
||
});
|
||
|
||
const tcpFormRules = reactive({
|
||
link: [
|
||
{ required: true, message: 'ip地址+端口', trigger: 'blur' },
|
||
{ pattern: /^([0-9]{1,3}\.){3}[0-9]{1,3}:[0-9]{1,5}$/, message: '格式错误格式应该是ip地址+端口如:192.168.0.1:80', trigger: 'blur' },
|
||
],
|
||
});
|
||
|
||
//打开采集配置弹窗
|
||
const openConnectModel = (row) => {
|
||
console.log('当前采集', row.protocol.libName);
|
||
if (!row.protocol.libName) {
|
||
ElMessage.error('请先选择通信类型');
|
||
} else if (row.protocol.libName === 'DLT645_2007' || row.protocol.libName === 'gycan') {
|
||
if (row.interfaceParams.link) {
|
||
tcpForm.value.port = row.interfaceParams.link.split(',')[0];
|
||
tcpForm.value.baudrate = row.interfaceParams.link.split(',')[1];
|
||
tcpForm.value.databits = row.interfaceParams.link.split(',')[2];
|
||
tcpForm.value.stopbits = row.interfaceParams.link.split(',')[3];
|
||
tcpForm.value.parity = row.interfaceParams.link.split(',')[4];
|
||
} else {
|
||
tcpForm.value = {
|
||
port: '',
|
||
baudrate: 115200,
|
||
databits: 8,
|
||
stopbits: 1,
|
||
parity: 'N',
|
||
};
|
||
}
|
||
// getSerialPortList()
|
||
dlt645ModelShow.value = true;
|
||
} else if (row.protocol.libName === 'ModbusTCP') {
|
||
tcpForm.value.link = row.interfaceParams.link;
|
||
tcpModelShow.value = true;
|
||
} else {
|
||
ElMessage.error('暂不支持该通信类型');
|
||
}
|
||
};
|
||
|
||
const editTcp = (formEl) => {
|
||
if (!formEl) return;
|
||
formEl.validate((valid) => {
|
||
if (valid) {
|
||
deviceConfigList.value[ActiveRowIndex.value].interfaceParams.link = tcpForm.value.link;
|
||
tcpModelShow.value = false;
|
||
}
|
||
});
|
||
};
|
||
|
||
const activeDeviceConfig = ref({
|
||
collectConf: {
|
||
// 数据采集配置
|
||
abnormalMax: 3, // 异常最大次数
|
||
allowTimeOut: { read: 2000, write: 2000 }, // 读写超时设置
|
||
intervalTime_ms: 100, // 采集间隔时间
|
||
isBlockRead: true, // 是否块读取
|
||
isBlockWrite: true, // 是否块写入
|
||
isRunCmd: true, // 是否运行命令
|
||
pollingCycleTime_ms: 1000, // 轮询周期时间
|
||
},
|
||
conclude: { isUse: false, libName: '' }, // 结论库设置
|
||
equipId: 'dev102', // 设备ID
|
||
equipName: 'dev102', // 设备名称
|
||
inductionParam: {
|
||
// 感应参数设置
|
||
maxLenBlock_read: { boolArea: -1, charArea: -1 }, // 读取最大长度
|
||
maxLenBlock_write: { boolArea: -1, charArea: -1 }, // 写入最大长度
|
||
},
|
||
interfaceParams: { link: '', physicalInterface: '' }, // 接口参数
|
||
properties: [
|
||
// 设备属性项,可包含多个属性
|
||
{
|
||
authority: 'RW', // 读写权限
|
||
dataAddress: '', // 数据地址
|
||
dataDesc: '', // 数据描述
|
||
dataDisplay: 'int16', // 数据展示格式
|
||
dataName: '', // 数据名称
|
||
dataUnit: '', // 数据单位
|
||
driveFlag: { isConclude_R: true, isConclude_W: true }, // 驱动标志
|
||
id: '', // 属性ID
|
||
northCalcParam: {
|
||
// 北向计算参数
|
||
decimal: { numBits: 1, way: 'rounding' }, // 小数处理方式
|
||
offset: '0', // 偏移量
|
||
proportion: '1', // 比例
|
||
},
|
||
offset: '0', // 偏移量
|
||
proportion: '1', // 比例
|
||
taskId: '', // 任务ID
|
||
uploadMode: '', // 上报策略
|
||
},
|
||
],
|
||
protocol: { libName: 'ModbusTCP', params: { linkPar: '' } }, // 通信协议设置
|
||
});
|
||
|
||
//添加数据源配置
|
||
const addDeviceDataConfig = () => {
|
||
let newDeviceDataConfig = {
|
||
authority: 'RW', // 读写权限
|
||
dataAddress: '', // 数据地址
|
||
dataDesc: '', // 数据描述
|
||
dataDisplay: 'int16', // 数据展示格式
|
||
datalen: 2, // 数据长度
|
||
datalenShow: false, // 数据长度是否可编辑
|
||
dataName: '', // 数据名称
|
||
dataUnit: '', // 数据单位
|
||
driveFlag: { isConclude_R: true, isConclude_W: true }, // 驱动标志
|
||
id: '', // 属性ID
|
||
northCalcParam: {
|
||
// 北向计算参数
|
||
decimal: { numBits: 1, way: 'rounding' }, // 小数处理方式
|
||
offset: '0', // 偏移量
|
||
proportion: '1', // 比例
|
||
},
|
||
offset: '0', // 偏移量
|
||
proportion: '1', // 比例
|
||
taskId: '', // 任务ID
|
||
uploadMode: '', // 上报策略
|
||
};
|
||
activeDeviceConfig.value.properties.push(newDeviceDataConfig);
|
||
};
|
||
|
||
const changedataDisplay = (e, index) => {
|
||
let dataTypeObj = dataTypeArray.value.filter((item) => item.type === e);
|
||
activeDeviceConfig.value.properties[index].datalen = dataTypeObj[0].datalen;
|
||
activeDeviceConfig.value.properties[index].datalenShow = dataTypeObj[0].show;
|
||
};
|
||
|
||
const changeTaskId = (e, index) => {
|
||
let taskObj = taskList.value.filter((item) => item.taskId === e);
|
||
activeDeviceConfig.value.properties[index].uploadMode = taskObj[0].uploadMode;
|
||
};
|
||
|
||
//删除数据源配置
|
||
const delDeviceDataConfig = (index) => {
|
||
ElMessageBox.confirm('确认要删除该数据源配置吗?', '告警', {
|
||
confirmButtonText: '确认',
|
||
cancelButtonText: '取消',
|
||
type: 'warning',
|
||
})
|
||
.then(() => {
|
||
activeDeviceConfig.value.properties.splice(index, 1);
|
||
ElMessage({
|
||
type: 'success',
|
||
message: '删除成功',
|
||
});
|
||
})
|
||
.catch(() => {
|
||
ElMessage({
|
||
type: 'info',
|
||
message: '取消删除',
|
||
});
|
||
});
|
||
};
|
||
|
||
//选择设备配置
|
||
const deviceConfigClick = (e, event, column) => {
|
||
activeDeviceConfig.value = e;
|
||
ActiveRowIndex.value = deviceConfigList.value.indexOf(e);
|
||
};
|
||
|
||
//更新表格宽度
|
||
const handleResize = () => {
|
||
deviceDataTableRef.value.doLayout();
|
||
tabelBox.value = window.innerWidth - 180 - 10;
|
||
};
|
||
|
||
const exportDeviceConfigXlsx = () => {
|
||
const workbook = XLSX.utils.book_new();
|
||
deviceConfigList.value.map((item) => {
|
||
let dataArr = [];
|
||
dataArr = item.properties.map((val) => {
|
||
return {
|
||
equipId: item.equipId,
|
||
...val,
|
||
isConclude_R: val.driveFlag.isConclude_R,
|
||
isConclude_W: val.driveFlag.isConclude_W,
|
||
offset: val.northCalcParam.offset,
|
||
proportion: val.northCalcParam.proportion,
|
||
numBits: val.northCalcParam.decimal.numBits,
|
||
way: val.northCalcParam.decimal.way,
|
||
equipName: item.equipName,
|
||
protocol: item.protocol.libName,
|
||
link: item.interfaceParams.link,
|
||
linkPar: item.protocol.params.linkPar,
|
||
};
|
||
});
|
||
// 将 JSON 数据转换为工作表
|
||
let worksheet = XLSX.utils.json_to_sheet(dataArr);
|
||
// 创建工作簿并添加工作表
|
||
XLSX.utils.book_append_sheet(workbook, worksheet, '南向' + item.equipId);
|
||
});
|
||
// 将工作簿写入二进制数组
|
||
const selIn = XLSX.write(workbook, { bookType: 'xlsx', bookSST: true, type: 'array' });
|
||
try {
|
||
FileSaver.saveAs(new Blob([selIn], { type: 'application/octet-stream' }), '网关南向采集配置' + dayjs().format('YYYY-MM-DD HH-mm-ss') + '.xlsx');
|
||
} catch (e) {
|
||
if (typeof console !== 'undefined') console.log(e, selIn);
|
||
}
|
||
return selIn;
|
||
};
|
||
|
||
const exportDeviceDataConfigXlsx = () => {
|
||
const workbook = XLSX.utils.book_new();
|
||
let dataArr = [];
|
||
dataArr = activeDeviceConfig.value.properties.map((val) => {
|
||
return {
|
||
equipId: activeDeviceConfig.value.equipId,
|
||
...val,
|
||
isConclude_R: val.driveFlag.isConclude_R,
|
||
isConclude_W: val.driveFlag.isConclude_W,
|
||
offset: val.northCalcParam.offset,
|
||
proportion: val.northCalcParam.proportion,
|
||
numBits: val.northCalcParam.decimal.numBits,
|
||
way: val.northCalcParam.decimal.way,
|
||
equipName: activeDeviceConfig.value.equipName,
|
||
libName: activeDeviceConfig.value.protocol.libName,
|
||
link: activeDeviceConfig.value.interfaceParams.link,
|
||
linkPar: activeDeviceConfig.value.protocol.params.linkPar,
|
||
};
|
||
});
|
||
// 将 JSON 数据转换为工作表
|
||
let worksheet = XLSX.utils.json_to_sheet(dataArr);
|
||
// 创建工作簿并添加工作表
|
||
XLSX.utils.book_append_sheet(workbook, worksheet, '南向' + activeDeviceConfig.value.equipId);
|
||
// 将工作簿写入二进制数组
|
||
const selIn = XLSX.write(workbook, { bookType: 'xlsx', bookSST: true, type: 'array' });
|
||
try {
|
||
FileSaver.saveAs(
|
||
new Blob([selIn], { type: 'application/octet-stream' }),
|
||
'网关南向采集数据源配置' + dayjs().format('YYYY-MM-DD HH-mm-ss') + '.xlsx'
|
||
);
|
||
} catch (e) {
|
||
if (typeof console !== 'undefined') console.log(e, selIn);
|
||
}
|
||
return selIn;
|
||
};
|
||
|
||
const handleFileChange = (file, fileList) => {
|
||
const rawFile = file.raw;
|
||
if (!rawFile) {
|
||
ElMessage.error('请选择一个文件');
|
||
return;
|
||
}
|
||
|
||
const reader = new FileReader();
|
||
reader.onload = (e) => {
|
||
const data = new Uint8Array(e.target.result);
|
||
const workbook = XLSX.read(data, { type: 'array' });
|
||
|
||
// 解析所有工作表
|
||
const sheets = {};
|
||
workbook.SheetNames.forEach((sheetName) => {
|
||
const sheet = workbook.Sheets[sheetName];
|
||
const json = XLSX.utils.sheet_to_json(sheet);
|
||
sheets[sheetName] = json;
|
||
});
|
||
|
||
for (var prop in sheets) {
|
||
console.log(prop, sheets[prop]);
|
||
let dataArr = sheets[prop];
|
||
if (dataArr.length > 0) {
|
||
let flag = true;
|
||
deviceConfigList.value.forEach((item) => {
|
||
if (item.equipId === dataArr[0].equipId) {
|
||
flag = false;
|
||
item.properties = dataArr.map((val) => {
|
||
return {
|
||
authority: val.authority, // 读写权限
|
||
dataAddress: val.dataAddress, // 数据地址
|
||
dataDesc: val.dataDesc, // 数据描述
|
||
dataDisplay: val.dataDisplay, // 数据展示格式
|
||
datalen: val.datalen,
|
||
datalenShow: val.datalenShow, // 数据长度是否可编辑
|
||
dataName: val.dataName, // 数据名称
|
||
dataUnit: val.dataUnit, // 数据单位
|
||
driveFlag: { isConclude_R: val.isConclude_R, isConclude_W: val.isConclude_W }, // 驱动标志
|
||
id: val.id, // 属性ID
|
||
northCalcParam: {
|
||
// 北向计算参数
|
||
decimal: { numBits: val.numBits, way: val.way }, // 小数处理方式
|
||
offset: val.offset, // 偏移量
|
||
proportion: val.proportion, // 比例
|
||
},
|
||
offset: val.offset, // 偏移量
|
||
proportion: val.proportion, // 比例
|
||
taskId: val.taskId, // 任务ID
|
||
uploadMode: val.uploadMode, // 上报策略
|
||
};
|
||
});
|
||
item.equipName = dataArr[0].equipName;
|
||
item.protocol.libName = dataArr[0].protocol;
|
||
item.interfaceParams.link = dataArr[0].link;
|
||
item.protocol.params.linkPar = dataArr[0].linkPar;
|
||
}
|
||
});
|
||
// 是否有新增
|
||
if (flag) {
|
||
let obj = {
|
||
id: guidGenerator(),
|
||
collectConf: {
|
||
// 数据采集配置
|
||
abnormalMax: 3, // 异常最大次数
|
||
allowTimeOut: { read: 2000, write: 2000 }, // 读写超时设置
|
||
intervalTime_ms: 100, // 采集间隔时间
|
||
isBlockRead: true, // 是否块读取
|
||
isBlockWrite: true, // 是否块写入
|
||
isRunCmd: true, // 是否运行命令
|
||
pollingCycleTime_ms: 1000, // 轮询周期时间
|
||
},
|
||
conclude: { isUse: false, libName: '' }, // 结论库设置
|
||
equipId: dataArr[0].equipId, // 设备ID
|
||
equipName: dataArr[0].equipName, // 设备名称
|
||
inductionParam: {
|
||
// 感应参数设置
|
||
maxLenBlock_read: { boolArea: -1, charArea: -1 }, // 读取最大长度
|
||
maxLenBlock_write: { boolArea: -1, charArea: -1 }, // 写入最大长度
|
||
},
|
||
interfaceParams: { link: dataArr[0].link, physicalInterface: '' }, // 接口参数
|
||
properties: [],
|
||
protocol: { libName: dataArr[0].protocol, params: { linkPar: dataArr[0].linkPar } }, // 通信协议设置
|
||
};
|
||
obj.properties = dataArr.map((val) => {
|
||
return {
|
||
authority: val.authority, // 读写权限
|
||
dataAddress: val.dataAddress, // 数据地址
|
||
dataDesc: val.dataDesc, // 数据描述
|
||
dataDisplay: val.dataDisplay, // 数据展示格式
|
||
datalen: val.datalen,
|
||
datalenShow: val.datalenShow, // 数据长度是否可编辑
|
||
dataName: val.dataName, // 数据名称
|
||
dataUnit: val.dataUnit, // 数据单位
|
||
driveFlag: { isConclude_R: val.isConclude_R, isConclude_W: val.isConclude_W }, // 驱动标志
|
||
id: val.id, // 属性ID
|
||
northCalcParam: {
|
||
// 北向计算参数
|
||
decimal: { numBits: val.numBits, way: val.way }, // 小数处理方式
|
||
offset: val.offset, // 偏移量
|
||
proportion: val.proportion, // 比例
|
||
},
|
||
offset: val.offset, // 偏移量
|
||
proportion: val.proportion, // 比例
|
||
taskId: val.taskId, // 任务ID
|
||
uploadMode: val.uploadMode, // 上报策略
|
||
};
|
||
});
|
||
deviceConfigList.value.push(obj);
|
||
}
|
||
}
|
||
}
|
||
|
||
// 你可以在这里处理解析后的数据
|
||
ElMessage.success('导入成功');
|
||
};
|
||
reader.readAsArrayBuffer(rawFile);
|
||
};
|
||
|
||
onMounted(() => {
|
||
window.addEventListener('resize', handleResize);
|
||
getDeviceConfig();
|
||
getSerialPortList(false);
|
||
handleResize();
|
||
});
|
||
|
||
onUnmounted(() => {
|
||
window.removeEventListener('resize', handleResize);
|
||
});
|
||
</script>
|
||
|
||
<style scoped lang="scss">
|
||
.el-table .double-clicked-row {
|
||
background-color: #f0f9eb;
|
||
}
|
||
:deep(.el-table .el-scrollbar) {
|
||
background: #fff;
|
||
}
|
||
#southdirection-box {
|
||
position: absolute;
|
||
width: 100%;
|
||
height: 100%;
|
||
.btn-list {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
padding: 10px 0;
|
||
.btn-list-right {
|
||
display: flex;
|
||
align-items: center;
|
||
}
|
||
}
|
||
.device-config {
|
||
position: relative;
|
||
overflow-y: auto;
|
||
}
|
||
.data-config {
|
||
position: relative;
|
||
height: calc(100% - 278px);
|
||
.table-box {
|
||
position: absolute;
|
||
width: 100%;
|
||
height: calc(100% - 162px);
|
||
}
|
||
}
|
||
.table-box {
|
||
//position: absolute;
|
||
//width: 100%;
|
||
}
|
||
}
|
||
</style>
|
||
<style>
|
||
.selected-row {
|
||
background-color: #55e800;
|
||
}
|
||
</style>
|