fastbee/vue/src/views/iot/device/instruction-parsing.vue

1306 lines
56 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="instruction-parsing">
<el-row>
<el-col :span="20" class="left">
<div class="head">
<span style="color: #909399">{{ $t('device.instruction-parsing.830424-0') }}</span>
<span>{{ device.serialNumber }}</span>
<!-- <el-dropdown style="margin-left: auto" @command="handleFormatChange">-->
<!-- <span class="el-dropdown-link">-->
<!-- {{ format }}-->
<!-- <i class="el-icon-arrow-down el-icon&#45;&#45;right"></i>-->
<!-- </span>-->
<!-- <el-dropdown-menu slot="dropdown">-->
<!-- <el-dropdown-item v-for="(item, index) in formatList" :key="index" :command="item">{{ item }}</el-dropdown-item>-->
<!-- </el-dropdown-menu>-->
<!-- </el-dropdown>-->
</div>
<div class="content">
<div v-for="(item, index) in logList" :key="index" class="item-class" :class="{ 'send-class': item.type == 0, 'receive-class': item.type == 1, 'report-class': item.type == 2 }">
<div class="item-head">
<div>
{{ item.type == 0 ? $t('device.instruction-parsing.830424-1') : item.type == 1 ? $t('device.instruction-parsing.830424-2') : $t('device.instruction-parsing.830424-3') }}
</div>
<div class="head-time">{{ item.time }}</div>
<div class="analysis-btn right-btn" v-if="item.type != 0 && !item.analysis" v-loading="item.loading" @click.stop="analysisData(item, index)">
{{ $t('device.instruction-parsing.830424-4') }}
</div>
<div class="analysised right-btn" v-if="item.type != 0 && item.analysis">
{{ $t('device.instruction-parsing.830424-5') }}
</div>
</div>
<div class="item-content">
<div class="content-value">
<!-- 自动识别并展示所有可用格式 -->
<!-- JSON格式优先展示(如果是有效JSON) -->
<div v-if="isValidJson(item.valueAscii || item.value)" class="data-format-item json-item">
<span class="format-label">JSON:</span>
<pre class="json-value">{{ formatJsonValue(item.value, item.valueAscii) }}</pre>
</div>
<!-- 非JSON时展示HEX和ASCII -->
<template v-else>
<!-- HEX格式 -->
<div v-if="item.value" class="data-format-item">
<span class="format-label">HEX:</span>
<span class="hex-value">{{ item.value }}</span>
</div>
<!-- ASCII格式(如果有可读内容) -->
<div v-if="item.valueAscii && hasReadableAscii(item.valueAscii)" class="data-format-item ascii-item">
<span class="format-label">ASCII:</span>
<pre class="ascii-value">{{ item.valueAscii }}</pre>
</div>
</template>
</div>
</div>
<div class="analysis-data" v-if="item.type != 0 && item.analysis">
<div class="data-col left-col">
<div class="label">{{ $t('device.instruction-parsing.830424-6') }}</div>
<div class="value">{{ item.analysisVal.name || '--' }}</div>
</div>
<div class="data-col right-col">
<div class="label">{{ $t('device.instruction-parsing.830424-7') }}</div>
<div class="value">{{ item.analysisVal.id || '--' }}</div>
</div>
<div class="data-col left-col">
<div class="label">{{ $t('device.instruction-parsing.830424-8') }}</div>
<div class="value">{{ item.analysisVal.value || '--' }}</div>
</div>
<div class="data-col right-col" v-if="item.analysisList.length > 1">
<el-button type="text" size="mini" @click.stop="openMore(item)">{{ $t('device.instruction-parsing.830424-9') }}</el-button>
</div>
</div>
</div>
</div>
<div class="bottom">
<el-input :placeholder="$t('device.instruction-parsing.830424-10')" v-model="sendVal" class="input-with-select" size="medium" @keyup.enter.native="sendMessage">
<el-select v-model="dataType" slot="prepend" :placeholder="$t('device.instruction-parsing.830424-11')">
<el-option label="hex" value="hex"></el-option>
<el-option label="json" value="json"></el-option>
<el-option label="ascii" value="ascii"></el-option>
</el-select>
</el-input>
<el-button round type="primary" class="send-btn" size="mini" @click.stop="sendMessage">{{ $t('device.instruction-parsing.830424-14') }}</el-button>
</div>
</el-col>
<el-col :span="4" class="right">
<div class="head right-head">{{ $t('device.instruction-parsing.830424-15') }}</div>
<div class="content beautify-scroll-def">
<div v-for="(item, index) in quickParsing" :key="index"
class="quick-item"
:class="{ 'product-command': item.productId != null }"
@click.stop="quickClick(item)"
@contextmenu.prevent="onContextmenu($event, item)">
<!-- <el-tag v-if="item.productId != null" size="mini" type="success" style="margin-right: 4px;">产品</el-tag>-->
{{ item.name }}
</div>
</div>
<div class="right-bottom" @click.stop="openEdit">{{ $t('device.instruction-parsing.830424-16') }}</div>
</el-col>
</el-row>
<el-dialog :title="editName ? $t('device.instruction-parsing.830424-17') : $t('device.instruction-parsing.830424-18')" :visible.sync="editDialog" :width="editName ? '35%' : '45%'">
<div class="dialog-content beautify-scroll-def" v-show="!editName">
<el-form :model="createForm" label-position="top">
<el-row :gutter="40">
<!-- 从机地址 -->
<el-col :span="24">
<el-form-item :label="$t('device.instruction-parsing.830424-19')" prop="path">
<el-input v-model="createForm.path"></el-input>
</el-form-item>
</el-col>
<!-- 指令类型 -->
<el-col :span="12">
<el-form-item :label="$t('device.instruction-parsing.830424-20')" prop="start">
<el-select v-model="createForm.start" @change="changeNum" style="width: 275px">
<el-option :label="start.label" :value="start.value" v-for="start in startList" :key="start.value"></el-option>
</el-select>
</el-form-item>
</el-col>
<!-- 功能码 -->
<el-col :span="12">
<el-form-item :label="$t('device.instruction-parsing.830424-21')" prop="functionCode">
<el-select v-model="createForm.functionCode" @change="changeNum" :disabled="createForm.start == '0xFFAA'" style="width: 275px">
<el-option :label="functionCode.label" :value="functionCode.value" v-for="functionCode in functionCodeList" :key="functionCode.value"></el-option>
</el-select>
</el-form-item>
</el-col>
<!--起始寄存器地址-->
<el-col :span="12">
<el-form-item prop="startPath">
<div slot="label" class="form-item-label">
<div style="margin-right: auto">{{ $t('device.instruction-parsing.830424-22') }}</div>
<el-tooltip :content="createForm.startPathSwitch" placement="top">
<el-switch v-model="createForm.startPathSwitch" size="mini" active-color="#13ce66" inactive-color="#ff4949" active-value="Dec" inactive-value="Hex" />
</el-tooltip>
</div>
<el-input
style="width: 275px"
v-model="createForm.startPath"
type="number"
v-show="createForm.startPathSwitch == 'Dec'"
:min="0"
@change="
() => {
createForm.startPath16 = int2hex(createForm.startPath);
}
"
@input="
() => {
createForm.startPath16 = int2hex(createForm.startPath);
}
"
>
<div slot="append">0x{{ createForm.startPath16 }}</div>
</el-input>
<el-input
v-model="createForm.startPath16"
v-show="createForm.startPathSwitch != 'Dec'"
@input="
() => {
createForm.startPath = hex2int(createForm.startPath16);
}
"
>
<div slot="append">{{ createForm.startPath }}</div>
</el-input>
</el-form-item>
</el-col>
<!-- 个数或写值 -->
<el-col :span="12">
<!-- 个数 -->
<el-form-item :label="registerNumTitle" prop="registerNum" v-show="!['05', '06'].includes(createForm.functionCode)">
<el-input-number v-model="createForm.registerNum" controls-position="right" :min="0" @change="changeNum" style="width: 275px" />
</el-form-item>
<!-- 写值 -->
<el-form-item prop="setValue" v-show="['05', '06'].includes(createForm.functionCode)">
<div slot="label" class="form-item-label">
<div style="margin-right: auto">{{ registerNumTitle }}</div>
<el-tooltip :content="createForm.setValueSwitch" placement="top">
<el-switch v-model="createForm.setValueSwitch" size="mini" active-color="#13ce66" inactive-color="#ff4949" active-value="Dec" inactive-value="Hex" />
</el-tooltip>
</div>
<el-input
v-model="createForm.setValue"
type="number"
v-show="createForm.setValueSwitch == 'Dec'"
@change="
() => {
createForm.setValue16 = int2hex(createForm.setValue);
}
"
@input="
() => {
createForm.setValue16 = int2hex(createForm.setValue);
}
"
>
<div slot="append">0x{{ createForm.setValue16 }}</div>
</el-input>
<el-input
v-model="createForm.setValue16"
v-show="createForm.setValueSwitch != 'Dec'"
@input="
() => {
createForm.setValue = hex2int(createForm.setValue16);
}
"
>
<div slot="append">{{ createForm.setValue }}</div>
</el-input>
</el-form-item>
</el-col>
<!-- 批量写寄存器值 -->
<el-col :span="12" v-for="(item, index) in registerValList" :key="'register' + index" v-show="createForm.functionCode == '16'">
<el-form-item prop="registerValList">
<div slot="label" class="form-item-label">
<div style="margin-right: auto">#{{ index }} {{ $t('device.instruction-parsing.830424-23') }}</div>
<el-tooltip :content="item.switch" placement="top">
<el-switch
v-model="item.switch"
size="mini"
active-color="#13ce66"
@change="
() => {
refreshRegisterInpust(item, index);
}
"
inactive-color="#ff4949"
active-value="Dec"
inactive-value="Hex"
/>
</el-tooltip>
</div>
<el-input
v-model="item.value"
type="number"
v-show="item.switch == 'Dec'"
:min="0"
@change="
() => {
item.value16 = int2hex(item.value);
refreshRegisterInpust(item, index);
}
"
@input="
() => {
item.value16 = int2hex(item.value);
refreshRegisterInpust(item, index);
}
"
>
<div slot="append">0x{{ item.value16 }}</div>
</el-input>
<el-input
v-model="item.value16"
v-show="item.switch != 'Dec'"
@input="
() => {
item.value = hex2int(item.value16);
refreshRegisterInpust(item, index);
}
"
>
<div slot="append">{{ item.value }}</div>
</el-input>
</el-form-item>
</el-col>
<!-- 批量上报值 -->
<el-col :span="12" v-for="(item, index) in reportValList" :key="'report' + index" v-show="createForm.start == '0xFFAA'">
<el-form-item prop="reportValList">
<div slot="label" class="form-item-label">
<div style="margin-right: auto">#{{ index }} {{ $t('device.instruction-parsing.830424-24') }}</div>
<el-tooltip :content="item.switch" placement="top">
<el-switch
v-model="item.switch"
size="mini"
active-color="#13ce66"
@change="
() => {
refreshReportValList(item, index);
}
"
inactive-color="#ff4949"
active-value="Dec"
inactive-value="Hex"
/>
</el-tooltip>
</div>
<el-input
v-model="item.value"
type="number"
v-show="item.switch == 'Dec'"
:min="0"
@change="
() => {
item.value16 = int2hex(item.value);
refreshReportValList(item, index);
}
"
@input="
() => {
item.value16 = int2hex(item.value);
refreshReportValList(item, index);
}
"
>
<div slot="append">0x{{ item.value16 }}</div>
</el-input>
<el-input
v-model="item.value16"
v-show="item.switch != 'Dec'"
@input="
() => {
item.value = hex2int(item.value16);
refreshReportValList(item, index);
}
"
>
<div slot="append">{{ item.value }}</div>
</el-input>
</el-form-item>
</el-col>
<!-- 批量写线圈值 -->
<el-col :span="6" v-for="(item, index) in IOValList" :key="'IO' + index" v-show="createForm.functionCode == '15'">
<el-form-item prop="registerValList">
<div slot="label" class="form-item-label">
<div style="margin-right: auto">#{{ index }} {{ $t('device.instruction-parsing.830424-25') }}</div>
</div>
<el-switch
v-model="item.value"
active-value="1"
inactive-value="0"
@change="
() => {
refreshIOInpust(item, index);
}
"
/>
</el-form-item>
</el-col>
</el-row>
</el-form>
<div v-loading="createLoading">
<div class="create-title">
<el-button type="text" @click.stop="encode">{{ $t('device.instruction-parsing.830424-26') }}</el-button>
<div class="title-right">
<el-button type="" size="mini" :disabled="!canSend" @click="openEditName({ command: createCode })">{{ $t('device.instruction-parsing.830424-27') }}</el-button>
<el-button type="primary" :disabled="!canSend" size="mini" @click="copyText(createCode)">{{ $t('device.instruction-parsing.830424-28') }}</el-button>
</div>
</div>
<div class="create-code">{{ createCode }}</div>
</div>
</div>
<div v-show="editName" class="dialog-content">
<el-form :model="editNameForm">
<el-form-item :label="$t('device.instruction-parsing.830424-29')">
<el-input v-model="editNameForm.name" :placeholder="$t('device.instruction-parsing.830424-30')" style="width: 60%"></el-input>
</el-form-item>
<el-form-item :label="$t('device.instruction-parsing.830424-31')">
<el-input v-model="editNameForm.command" style="width: 60%"></el-input>
</el-form-item>
<el-form-item label="指令范围">
<el-radio-group v-model="editNameForm.isProductCommand">
<el-radio :label="true">产品指令(同类产品通用)</el-radio>
<el-radio :label="false">设备指令(仅本设备)</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
</div>
<div slot="footer" class="dialog-btn">
<el-button type="" size="mini" @click="editDialog = false">{{ $t('device.instruction-parsing.830424-32') }}</el-button>
<el-button type="primary" :disabled="!canSend" size="mini" @click="confrimBtn" v-loading="saveLoading">{{ $t('device.instruction-parsing.830424-33') }}</el-button>
</div>
</el-dialog>
<el-dialog :visible.sync="delDialog" :title="$t('device.instruction-parsing.830424-34')" width="30%">
<div style="padding: 20px">{{ $t('device.instruction-parsing.830424-35') }}{{ delItem.name }}{{ $t('device.instruction-parsing.830424-36') }}</div>
<div slot="footer" class="dialog-btn">
<el-button type="" size="mini" @click="delDialog = false">{{ $t('device.instruction-parsing.830424-37') }}</el-button>
<el-button type="primary" size="mini" @click="delQuick">{{ $t('device.instruction-parsing.830424-38') }}</el-button>
</div>
</el-dialog>
<el-dialog :title="$t('device.instruction-parsing.830424-39')" width="40%" :visible.sync="moreDialog">
<div class="dialog-content beautify-scroll-def">
<el-table :data="analysisList" height="400" border style="width: 100%">
<el-table-column type="index" :label="$t('device.instruction-parsing.830424-40')" />
<el-table-column prop="name" :label="$t('device.instruction-parsing.830424-41')"></el-table-column>
<el-table-column prop="id" :label="$t('device.instruction-parsing.830424-42')"></el-table-column>
<el-table-column prop="value" :label="$t('device.instruction-parsing.830424-43')"></el-table-column>
</el-table>
</div>
</el-dialog>
</div>
</template>
<script>
import { hex2int, int2hex, copyText, formatDate, deepClone } from '@/utils/common';
import { encode, decode, messagePost, addPreferences, editPreferences, delPreferences, preferencesList } from '@/api/iot/mqttTest';
export default {
props: {
device: {
type: Object,
default: null,
},
},
created() {
this.getPreferencesList();
},
watch: {
device() {
this.getPreferencesList();
},
},
computed: {
/**编辑寄存器个数显示的标题 */
registerNumTitle() {
switch (this.createForm.functionCode) {
case '01':
case '02':
case '15':
return this.$t('device.instruction-parsing.830424-44');
case '03':
case '04':
case '16':
return this.$t('device.instruction-parsing.830424-45');
case '05':
return this.$t('device.instruction-parsing.830424-46');
case '06':
return this.$t('device.instruction-parsing.830424-47');
}
},
},
data() {
return {
//选中的格式
format: 'Hex',
//格式列表
formatList: ['Hex', 'JSON', 'ASCII'],
//数据列表
logList: [],
//发送的命令
sendVal: '',
//发送的主题
topic: '/function/get',
dataType: 'hex',
//快捷指令数组
quickParsing: [],
//编辑框
editDialog: false,
//生成表单
createForm: {},
//功能码列表
functionCodeList: [
{
label: this.$t('device.instruction-parsing.830424-48'),
value: '01',
},
{
label: this.$t('device.instruction-parsing.830424-49'),
value: '02',
},
{
label: this.$t('device.instruction-parsing.830424-50'),
value: '03',
},
{
label: this.$t('device.instruction-parsing.830424-51'),
value: '04',
},
{
label: this.$t('device.instruction-parsing.830424-52'),
value: '05',
},
{
label: this.$t('device.instruction-parsing.830424-53'),
value: '06',
},
{
label: this.$t('device.instruction-parsing.830424-54'),
value: '15',
},
{
label: this.$t('device.instruction-parsing.830424-55'),
value: '16',
},
],
//上报下发列表
startList: [
{
label: this.$t('device.instruction-parsing.830424-56'),
value: '0xFFDD',
},
{
label: this.$t('device.instruction-parsing.830424-57'),
value: '0xFFAA',
},
],
//生成的指令码
createCode: '',
//批量写的寄存器值数组
registerValList: [],
//批量上报值数组
reportValList: [],
//批量写的线圈个数
IOValList: [],
//编辑名称
editName: false,
//编辑名称表单
editNameForm: {},
//生成刷新
createLoading: false,
//删除快捷指令
delDialog: false,
//选择删除的快捷指令
delItem: {},
//选择的解析组
analysisList: [],
//更多解析弹框
moreDialog: false,
//保存按钮loading
saveLoading: false,
canSend: false,
};
},
methods: {
/**处理updateData事件 */
handleUpdateData(params) {
const { serialNumber, productId, data } = params;
if (serialNumber == this.device.serialNumber) {
this.logList.push({
//0是下发,1是回复,2是上报
type: 2,
time: formatDate(new Date()),
value: data.sources, // HEX格式
valueAscii: data.sourcesAscii || '', // ASCII格式
loading: false,
analysis: false,
//解析的数据数组
analysisList: data.message,
});
}
},
/**处理格式切换 */
handleFormatChange(command) {
this.format = command;
},
/**格式化JSON值 */
formatJsonValue(hexValue, asciiValue) {
// 优先尝试从ASCII值解析JSON
if (asciiValue) {
try {
const json = JSON.parse(asciiValue);
return JSON.stringify(json, null, 2);
} catch (e) {
// ASCII不是JSON,继续尝试HEX
}
}
// 尝试从HEX值解析JSON
if (hexValue) {
try {
// 先尝试直接解析
const json = JSON.parse(hexValue);
return JSON.stringify(json, null, 2);
} catch (e) {
// 如果不是JSON字符串,尝试将HEX转为字符串再解析
try {
const hexStr = hexValue.replace(/\s+/g, '');
let jsonStr = '';
for (let i = 0; i < hexStr.length; i += 2) {
const hexByte = hexStr.substr(i, 2);
const charCode = parseInt(hexByte, 16);
if (charCode >= 32 && charCode <= 126) {
jsonStr += String.fromCharCode(charCode);
}
}
if (jsonStr) {
const json = JSON.parse(jsonStr);
return JSON.stringify(json, null, 2);
}
} catch (e2) {
// 转换失败
}
}
}
// 如果都不是JSON,返回提示信息
return '数据不是有效的JSON格式';
},
/**判断是否为有效的JSON */
isValidJson(value) {
if (!value) return false;
try {
const parsed = JSON.parse(value);
// 确保是对象或数组,而不是简单值
return typeof parsed === 'object' && parsed !== null;
} catch (e) {
return false;
}
},
/**判断ASCII是否包含可读内容 */
hasReadableAscii(asciiValue) {
if (!asciiValue) return false;
// 检查是否有可打印字符(排除纯控制字符)
const printableRegex = /[\x20-\x7E]/;
return printableRegex.test(asciiValue) && asciiValue.trim().length > 0;
},
/**打开编辑框 */
openEdit() {
this.resetCreateForm();
this.editName = false;
this.editDialog = true;
this.canSend = false;
},
/**打开更多解析框 */
openMore(item) {
this.analysisList = item.analysisList || [];
this.moreDialog = true;
},
/**打开编辑名字的框 */
openEditName(item) {
this.editNameForm = {
id: item.id,
name: item.name || '',
command: item.command,
isProductCommand: item.productId != null, // 通过 productId 判断是否为产品指令
};
this.editName = true;
this.editDialog = true;
},
/**重置编辑框 */
resetCreateForm() {
this.createForm = {
path: '01',
functionCode: '01',
startPath: 0,
startPath16: '0000',
registerNum: 1,
startPathSwitch: 'Dec',
setValue: 0,
setValue16: '0000',
setValueSwitch: 'Dec',
start: '0xFFDD',
};
this.createCode = '';
},
/**十进制变成十六进制 */
int2hex(str) {
return int2hex(str);
},
/**十六进制变成十进制 */
hex2int(str) {
return hex2int(str);
},
/**修改批量写的寄存器个数或者线圈个数 */
changeNum() {
//判断是不是上报
if (this.createForm.start == '0xFFAA') {
for (let index = 0; index < this.createForm.registerNum; index++) {
const item = this.reportValList[index];
if (!item) {
this.reportValList[index] = {
value: 0,
value16: '0000',
switch: 'Dec',
};
}
}
//如果编写数组大于他需要写的数字,就到写的个数
if (this.registerValList.length > this.createForm.reportValList) {
//多的个数
const num = this.reportValList.length - this.createForm.reportValList;
this.registerValList.splice(this.createForm.reportValList, num);
}
//上报功能码是03
this.createForm.functionCode = '03';
} else {
//批量写寄存器
if (this.createForm.functionCode == '16') {
for (let index = 0; index < this.createForm.registerNum; index++) {
const item = this.registerValList[index];
if (!item) {
this.registerValList[index] = {
value: 0,
value16: '0000',
switch: 'Dec',
};
}
}
//如果编写数组大于他需要写的数字,就到写的个数
if (this.registerValList.length > this.createForm.registerNum) {
//多的个数
const num = this.registerValList.length - this.createForm.registerNum;
this.registerValList.splice(this.createForm.registerNum, num);
}
}
//批量写线圈
if (this.createForm.functionCode == '15') {
for (let index = 0; index < this.createForm.registerNum; index++) {
const item = this.IOValList[index];
if (!item) {
this.IOValList[index] = {
value: '0',
};
}
}
//如果编写数组大于他需要写的数字,就到写的个数
if (this.IOValList.length > this.createForm.registerNum) {
//多的个数
const num = this.IOValList.length - this.createForm.registerNum;
this.IOValList.splice(this.createForm.registerNum, num);
}
}
}
},
/**刷新寄存器输入框 */
refreshRegisterInpust(item, index) {
this.$set(this.registerValList, index, item);
},
/**刷新上报读取设定的值 */
refreshReportValList(item, index) {
this.$set(this.reportValList, index, item);
},
/**刷新线圈值 */
refreshIOInpust(item, index) {
this.$set(this.IOValList, index, item);
},
/**确认按钮 */
async confrimBtn() {
if (this.editName) {
try {
this.saveLoading = true;
// 根据选择决定传 productId 还是 serialNumber
const isProductCommand = this.editNameForm.isProductCommand;
if (this.editNameForm.id) {
await editPreferences({
command: this.editNameForm.command,
name: this.editNameForm.name,
serialNumber: isProductCommand ? null : this.device.serialNumber,
productId: isProductCommand ? this.device.productId : null,
id: this.editNameForm.id,
});
} else {
await addPreferences({
command: this.editNameForm.command,
name: this.editNameForm.name,
serialNumber: isProductCommand ? null : this.device.serialNumber,
productId: isProductCommand ? this.device.productId : null,
});
}
this.$message({
type: 'success',
message: `${this.editNameForm.id ? this.$t('device.instruction-parsing.830424-58') : this.$t('device.instruction-parsing.830424-59') + this.$t('device.instruction-parsing.830424-60')}`,
});
this.getPreferencesList();
} catch (err) {
this.$message({
type: 'error',
message:
err.message || `${this.editNameForm.id ? this.$t('device.instruction-parsing.830424-58') : this.$t('device.instruction-parsing.830424-59') + this.$t('device.instruction-parsing.830424-61')}`,
});
}
} else {
this.sendVal = this.createCode;
}
this.saveLoading = false;
this.editDialog = false;
},
/**获取指令列表(产品指令+设备指令) */
async getPreferencesList() {
try {
const { rows } = await preferencesList({
serialNumber: this.device.serialNumber,
productId: this.device.productId
});
this.quickParsing = rows;
} catch (err) {
console.log('🚀 ~ getPreferencesList ~ err:', err);
}
},
/**快捷按钮 */
quickClick(item) {
this.sendVal = item.command;
},
/**复制 */
copyText(command) {
const res = copyText(command);
this.$message({
type: res.type,
message: res.message,
});
},
/**编码 */
async encode() {
try {
this.createLoading = true;
let params = {
slaveId: parseInt(this.createForm.path), //从机地址
address: this.createForm.startPath, //起始寄存器地址
code: parseInt(this.createForm.functionCode), //功能码
start: this.createForm.start,
protocolCode: this.device.protocolCode,
serialNumber: this.device.serialNumber,
};
//判断是不是上报
if (this.createForm.start == '0xFFAA') {
params.address = this.createForm.startPath;
params.bitCount = this.createForm.registerNum * 2;
const reportValList = this.reportValList.map((item) => {
return item.value;
});
params.data = reportValList;
} else {
switch (this.createForm.functionCode) {
case '01':
case '02':
case '03':
case '04':
//线圈个数/寄存器个数
params.count = this.createForm.registerNum;
break;
case '05':
case '06':
//线圈值/寄存器值
params.writeData = this.createForm.setValue;
break;
case '15':
//线圈个数/寄存器个数
params.count = this.createForm.registerNum;
//线圈值数组
const IOValList = this.IOValList.map((item) => {
return item.value;
});
params.bitString = IOValList.join('');
break;
case '16':
//线圈个数/寄存器个数
params.count = this.createForm.registerNum;
//寄存器值数组
const registerValList = this.registerValList.map((item) => {
return item.value;
});
params.tenWriteData = registerValList;
break;
}
}
const res = await encode(params);
this.createCode = res.msg;
} catch (err) {
this.$message({
type: 'error',
message: err.message || this.$t('device.instruction-parsing.830424-62'),
});
} finally {
this.createLoading = false;
this.canSend = true;
}
},
/**发送指令 */
sendMessage() {
console.log('🚀 ~ this.sendVal', this.dataType);
try {
if (this.sendVal) {
messagePost({
message: this.sendVal,
serialNumber: this.device.serialNumber,
topicName: this.topic,
dataType: this.dataType,
}).then((response) => {
this.logList.push({
//0是下发,1是回复,2是上报
type: 0,
time: formatDate(new Date()),
value: this.sendVal,
loading: false,
analysis: false,
//解析的数据数组
analysisList: [],
});
});
} else {
throw { message: this.$t('device.instruction-parsing.830424-63') };
}
} catch (err) {
this.$message({
type: 'error',
message: err.message || this.$t('device.instruction-parsing.830424-64'),
});
}
},
/**解析数据 */
analysisData(item, index) {
try {
item.loading = true;
item.analysis = true;
if (item.analysisList[0]) {
item.analysisVal = {
name: item.analysisList[0].name,
value: item.analysisList[0].value,
id: item.analysisList[0].id,
};
} else {
throw { message: this.$t('device.instruction-parsing.830424-65') };
}
} catch (err) {
this.$message({
type: 'error',
message: err.message || this.$t('device.instruction-parsing.830424-66'),
});
} finally {
item.loading = false;
}
},
/**右击快捷指令 */
onContextmenu(event, item) {
const contextMenuData = [
{
label: this.$t('device.instruction-parsing.830424-67'),
icon: 'iconfont el-icon-edit-outline',
onClick: () => {
this.editNameForm = deepClone(item);
this.editName = true;
this.editDialog = true;
},
},
{
label: this.$t('device.instruction-parsing.830424-68'),
icon: 'iconfont el-icon-delete',
onClick: () => {
this.delItem = item;
this.delDialog = true;
},
},
];
this.$contextmenu({
items: contextMenuData,
event, // 鼠标事件信息
zIndex: 3, // 菜单样式 z-index
minWidth: 230, // 主菜单最小宽度
});
return false;
},
/**删除指令 */
async delQuick() {
try {
await delPreferences(this.delItem);
this.$message({
type: 'success',
message: this.$t('device.instruction-parsing.830424-69'),
});
this.delDialog = false;
this.getPreferencesList();
} catch (err) {
this.$message({
type: 'error',
message: err.message || this.$t('device.instruction-parsing.830424-70'),
});
}
},
},
mounted() {
this.resetCreateForm();
// 先移除旧的监听器,避免重复绑定
this.$busEvent.$off('updateData', this.handleUpdateData);
// 绑定新的监听器
this.$busEvent.$on('updateData', this.handleUpdateData);
},
beforeDestroy() {
// 移除所有事件监听器
this.$busEvent.$off('updateData', this.handleUpdateData);
this.$busEvent.$off('updateMqttMessage');
},
};
</script>
<style lang="scss" scoped>
$border-color: #dcdfe6;
$right-btn-color: #1890ff;
.instruction-parsing {
border: 1px solid $border-color;
width: 90%;
margin: 0 10px;
.left {
border-right: 1px solid $border-color;
.bottom {
display: flex;
border-top: 1px solid $border-color;
position: relative;
height: 64px;
align-items: center;
::v-deep .el-select .el-input {
width: 80px;
}
::v-deep .el-input-group--prepend .el-input__inner {
border: 0px;
background-color: #fff;
}
::v-deep .el-input-group__prepend {
border: 0;
// border-right: 1px solid $border-color;
}
.send-btn {
position: absolute;
top: 50%;
transform: translateY(-50%);
right: 24px;
}
.input-with-select {
max-width: calc(100% - 80px);
}
}
}
.head {
line-height: 36px;
display: flex;
padding: 0 16px;
align-items: center;
font-size: 12px;
border-bottom: 1px solid $border-color;
.el-dropdown-link {
cursor: pointer;
}
.el-icon-arrow-down {
font-size: 12px;
}
}
.right {
display: flex;
flex-direction: column;
// 高度 = 头部36px + 内容700px + 底部64px
height: 800px;
.right-head {
font-size: 14px;
text-align: center;
width: 100%;
justify-content: center;
}
// 覆盖通用 .content 样式
.content.beautify-scroll-def {
flex: 1;
height: auto;
display: flex;
flex-direction: column;
justify-content: flex-end;
padding: 0;
}
.quick-item {
width: 80%;
margin: 0 auto 12px auto;
text-align: center;
line-height: 36px;
border: 1px solid $right-btn-color;
color: $right-btn-color;
cursor: pointer;
border-radius: 4px;
display: flex;
align-items: center;
justify-content: center;
padding: 0 8px;
box-sizing: border-box;
&:first-child {
margin-top: 12px;
}
&.product-command {
border-color: #67c23a;
color: #67c23a;
background-color: rgba(103, 194, 58, 0.05);
}
}
.right-bottom {
line-height: 64px;
background-color: $right-btn-color;
border-top: 1px solid $right-btn-color;
font-size: 24px;
color: #fff;
text-align: center;
cursor: pointer;
}
}
.content {
height: 700px;
width: 100%;
overflow: auto;
padding: 20px;
.send-class {
border-left: 4px solid #e6a23c;
margin-left: auto;
color: #e6a23c;
.head-time {
margin-left: auto !important;
color: #c0c4cc;
}
}
.receive-class {
border-left: 4px solid #67c23a;
color: #67c23a;
}
.report-class {
border-left: 4px solid #409eff;
color: #409eff;
}
.item-class {
width: 50%;
box-sizing: border-box;
border-radius: 4px;
padding: 0 8px;
background-color: #f2f6fc;
margin-bottom: 16px;
.item-head {
display: flex;
border-bottom: 1px solid $border-color;
font-size: 14px;
padding: 8px 0;
align-items: center;
.head-time {
margin-left: 8px;
color: #c0c4cc;
font-size: 12px;
margin-top: 3px;
}
.analysis-btn {
background: #1890ff;
color: #fff;
cursor: pointer;
}
.right-btn {
margin-left: auto;
padding: 8px 12px;
border-radius: 4px;
line-height: 12px;
font-size: 14px;
}
.analysised {
background: #909399;
color: #e4e7ed;
}
}
.item-content {
padding: 8px 0;
color: #303133;
font-size: 14px;
line-height: 20px;
display: flex;
align-items: flex-start;
.content-value {
max-width: 100%;
word-wrap: break-word;
width: 100%;
.data-format-item {
margin-bottom: 12px;
line-height: 24px;
&:last-child {
margin-bottom: 0;
}
&.ascii-item {
margin-top: 8px;
}
.format-label {
font-weight: 600;
color: #606266;
margin-right: 8px;
display: inline-block;
min-width: 60px;
vertical-align: top;
}
.hex-value {
word-break: break-all;
font-family: 'Courier New', monospace;
}
.ascii-value,
.json-value {
margin: 0;
padding: 8px;
background-color: #f5f7fa;
border-radius: 4px;
white-space: pre-wrap;
word-wrap: break-word;
font-family: 'Courier New', monospace;
font-size: 13px;
line-height: 1.6;
color: #303133;
display: block;
max-width: 100%;
overflow-x: auto;
}
}
}
}
.analysis-data {
margin-top: 12px;
font-size: 12px;
line-height: 18px;
width: 100%;
color: #303133;
font-weight: 600;
display: flex;
flex-wrap: wrap;
.data-col {
// width:100%;
display: flex;
margin-bottom: 10px;
.label {
text-align: right;
margin-right: 12px;
color: #c0c4cc;
font-weight: 400;
}
}
.left-col {
width: 60%;
.label {
width: 64px;
}
}
.right-col {
margin-left: auto;
}
}
}
}
::v-deep .el-dialog__body {
border-top: 1px solid $border-color;
// border-bottom: 1px solid $border-color;
box-sizing: border-box;
padding: 0;
}
.dialog-content {
max-height: 500px;
width: 100%;
box-sizing: border-box;
padding: 30px 20px;
overflow: auto;
margin-top: -20px;
.create-title {
display: flex;
line-height: 36px;
margin-bottom: 16px;
.title-right {
margin-left: auto;
}
}
.create-code {
font-size: 18px;
line-height: 36px;
font-weight: 800;
}
.form-item-label {
display: flex;
align-items: center;
width: 100%;
::v-deep .el-form-item__label {
width: 100%;
}
}
}
}
</style>