iot-ui-vue/src/views/DataCollect/Collector/Point/Save/SaveModBus.vue

464 lines
15 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 lang="">
<j-modal
:title="data.id ? '编辑' : '新增'"
:visible="true"
width="700px"
@cancel="handleCancel"
>
<j-form
class="form"
layout="vertical"
:model="formData"
name="basic"
autocomplete="off"
:rules="ModBusRules"
ref="formRef"
>
<j-form-item label="点位名称" name="name">
<j-input
placeholder="请输入点位名称"
v-model:value="formData.name"
/>
</j-form-item>
<j-form-item
label="功能码"
:name="['configuration', 'function']"
:rules="ModBusRules.function"
>
<j-select
style="width: 100%"
v-model:value="formData.configuration.function"
:options="[
{ label: '01线圈寄存器', value: 'Coils' },
{ label: '02离散输入寄存器', value: 'DiscreteInputs' },
{ label: '03保存寄存器', value: 'HoldingRegisters' },
{ label: '04输入寄存器', value: 'InputRegisters' },
]"
placeholder="请选择所功能码"
allowClear
show-search
:filter-option="filterOption"
@change="changeFunction"
/>
</j-form-item>
<j-form-item
label="地址"
:name="['pointKey']"
:rules="[
...ModBusRules.pointKey,
{
validator: checkPointKey,
trigger: 'blur',
},
]"
>
<j-input-number
style="width: 100%"
placeholder="请输入地址"
v-model:value="formData.pointKey"
:min="0"
:max="999999999"
:precision="0"
/>
</j-form-item>
<p style="color: #616161" v-if="formData.configuration.function">
PLC地址: {{
InitAddress[formData.configuration.function] +
Number(formData.pointKey) || 0
}}
</p>
<j-form-item
label="寄存器数量"
:name="['configuration', 'parameter', 'quantity']"
:rules="ModBusRules.quantity"
>
<j-input-number
style="width: 100%"
placeholder="请输入寄存器数量"
v-model:value="formData.configuration.parameter.quantity"
:min="1"
:max="255"
:precision="0"
@blur="changeQuantity"
/>
</j-form-item>
<j-form-item
v-if="['HoldingRegisters', 'InputRegisters'].includes(formData.configuration.function)"
label="数据类型"
:name="['configuration', 'codec', 'provider']"
:rules="[
...ModBusRules.provider,
{
validator: checkProvider,
trigger: 'change',
},
]"
>
<j-select
style="width: 100%"
v-model:value="formData.configuration.codec.provider"
:options="providerList"
placeholder="请选择数据类型"
allowClear
show-search
:filter-option="filterOption"
/>
</j-form-item>
<j-form-item
label="缩放因子"
:name="[
'configuration',
'codec',
'configuration',
'scaleFactor',
]"
:rules="ModBusRules.scaleFactor"
>
<j-input-number
style="width: 100%"
placeholder="请输入缩放因子"
v-model:value="
formData.configuration.codec.configuration.scaleFactor
"
/>
</j-form-item>
<j-form-item
label="小数保留位数"
:name="['configuration', 'codec', 'configuration', 'scale']"
>
<j-input-number
style="width: 100%"
placeholder="请输入小数保留位数"
:min="0"
:max="255"
:precision="0"
v-model:value="
formData.configuration.codec.configuration.scale
"
/>
</j-form-item>
<j-form-item
v-if="formData.configuration.function"
label="访问类型"
name="accessModes"
>
<j-card-select
multiple
:showImage="false"
v-model:value="formData.accessModes"
:options="
formData.configuration.function === 'InputRegisters' ||
formData.configuration.function === 'DiscreteInputs'
? [{ label: '读', value: 'read' }]
: [
{ label: '读', value: 'read' },
{ label: '写', value: 'write' },
]
"
:column="2"
/>
</j-form-item>
<j-form-item
:name="['nspwc']"
v-if="
formData.accessModes?.includes('write') &&
formData.configuration.function === 'HoldingRegisters'
"
>
<span style="margin-right: 10px">非标准协议写入配置</span>
<j-switch
@change="changeNspwc"
v-model:checked="formData.nspwc"
/>
</j-form-item>
<j-form-item
v-if="
!!formData.nspwc &&
formData.accessModes?.includes('write') &&
formData.configuration.function === 'HoldingRegisters'
"
label="是否写入数据区长度"
:name="['configuration', 'parameter', 'writeByteCount']"
:rules="ModBusRules.writeByteCount"
>
<j-card-select
:showImage="false"
v-model:value="
formData.configuration.parameter.writeByteCount
"
:options="[
{ label: '是', value: true },
{ label: '否', value: false },
]"
@change="changeWriteByteCount"
:column="2"
/>
</j-form-item>
<j-form-item
v-if="
!!formData.nspwc &&
formData.accessModes?.includes('write') &&
formData.configuration.function === 'HoldingRegisters'
"
label="自定义数据区长度byte"
:name="['configuration', 'parameter', 'byteCount']"
:rules="ModBusRules.byteCount"
>
<j-input
placeholder="请输入自定义数据区长度byte"
v-model:value="formData.configuration.parameter.byteCount"
/>
</j-form-item>
<j-form-item
label="采集频率"
:name="['configuration', 'interval']"
:rules="[...ModBusRules.interval]"
>
<j-input-number
style="width: 100%"
placeholder="请输入采集频率"
v-model:value="formData.configuration.interval"
addon-after="ms"
:max="9999999999999998"
/>
</j-form-item>
<j-form-item label="" :name="['features']">
<j-checkbox-group v-model:value="formData.features">
<j-checkbox value="changedOnly" name="type"
>只推送变化的数据</j-checkbox
>
</j-checkbox-group>
</j-form-item>
<j-form-item label="说明" :name="['description']">
<j-textarea
placeholder="请输入说明"
v-model:value="formData.description"
:maxlength="200"
:rows="3"
showCount
/>
</j-form-item>
</j-form>
<template #footer>
<j-button key="back" @click="handleCancel">取消</j-button>
<PermissionButton
key="submit"
type="primary"
:loading="loading"
@click="handleOk"
style="margin-left: 8px"
:hasPermission="`DataCollect/Collector:${
id ? 'update' : 'add'
}`"
>
确认
</PermissionButton>
</template>
</j-modal>
</template>
<script lang="ts" setup>
import {
savePointBatch,
updatePoint,
_validateField,
queryCodecProvider,
} from '@/api/data-collect/collector';
import { ModBusRules, checkProviderData } from '../../data.ts';
import type { FormInstance } from 'ant-design-vue';
import type { Rule } from 'ant-design-vue/lib/form';
import { cloneDeep } from 'lodash-es';
const props = defineProps({
data: {
type: Object,
default: () => {},
},
});
const emit = defineEmits(['change']);
const loading = ref(false);
const providerListAll: any = ref([]);
const providerList: any = ref([]);
const formRef = ref<FormInstance>();
const id = props.data.id;
const collectorId = props.data.collectorId;
const provider = props.data.provider;
const oldPointKey = props.data.pointKey;
const InitAddress = {
Coils: 1,
DiscreteInputs: 10001,
HoldingRegisters: 40001,
InputRegisters: 30001,
};
const formData = ref({
name: '',
configuration: {
function: undefined,
interval: 3000,
parameter: {
quantity: 1,
writeByteCount: '',
byteCount: 2,
address: '',
},
codec: {
provider: undefined,
configuration: {
scaleFactor: 1,
scale: undefined,
},
},
},
pointKey: undefined,
accessModes: [],
nspwc: false,
features: [],
description: '',
});
const handleOk = async () => {
const data = await formRef.value?.validate();
delete data?.nspwc;
const { codec } = data?.configuration;
if (!['HoldingRegisters', 'InputRegisters'].includes(data?.configuration.function)) {
codec.provider = 'int8';
}
const { interval } = formData.value.configuration;
const params = {
...props.data,
...data,
provider,
collectorId,
interval,
};
// address是多余字段但是react版本上使用到了这个字段
params.configuration.parameter = {
...params.configuration.parameter,
address: data?.pointKey,
};
loading.value = true;
const response = !id
? await savePointBatch(params).catch(() => {})
: await updatePoint(id, { ...props.data, ...params }).catch(() => {});
emit('change', response?.status === 200);
loading.value = false;
};
const handleCancel = () => {
emit('change', false);
};
const changeQuantity = () => {
const { configuration, nspwc } = formData.value;
if (configuration.function === 'HoldingRegisters') {
formRef.value?.validate();
}
if (nspwc) {
configuration.parameter.byteCount =
Number(configuration.parameter.quantity) * 2;
}
};
const changeNspwc = (value: boolean) => {
const { configuration } = formData.value;
if (value) {
configuration.parameter.byteCount =
Number(configuration.parameter.quantity || 0) * 2;
}
};
const changeWriteByteCount = (value: Array<string>) => {
formData.value.configuration.parameter.writeByteCount = value[0];
};
const changeFunction = (value: string) => {
formData.value.accessModes =
value === 'InputRegisters' ? ['read'] : ['read', 'write'];
};
const checkProvider = (_rule: Rule, value: string): Promise<any> =>
new Promise(async (resolve, reject) => {
if (value) {
const { quantity } = formData.value.configuration.parameter;
return checkProviderData[value] > Number(quantity) * 2
? reject('数据类型长度需 <= 寄存器数量 * 2')
: resolve('');
} else {
return reject('');
}
});
const checkPointKey = (_rule: Rule, value: string): Promise<any> =>
new Promise(async (resolve, reject) => {
if (value || Number(value) === 0) {
if (Number(oldPointKey) === Number(value)) return resolve('');
if (typeof value === 'object') return resolve('');
const res: any = await _validateField(collectorId, {
pointKey: value,
});
return res.result?.passed ? resolve('') : reject(res.result.reason);
} else {
return reject('请输入地址');
}
});
const filterOption = (input: string, option: any) => {
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0;
};
const getProviderList = async () => {
const res: any = await queryCodecProvider();
providerListAll.value = res.result
.filter((i: any) => i.id !== 'property' && i.id !== 'bool')
.map((item: any) => ({
value: item.id,
label: item.name,
}));
setProviderList(formData.value.configuration.function);
};
getProviderList();
const setProviderList = (value: string | undefined) => {
providerList.value = providerListAll.value;
};
watch(
() => formData.value.configuration.function,
(value) => {
setProviderList(value);
},
{ immediate: true, deep: true },
);
watch(
() => props.data,
(value) => {
if (value.id && value.provider === 'MODBUS_TCP') {
const _value: any = cloneDeep(value);
const { writeByteCount, byteCount } =
_value.configuration.parameter;
formData.value = _value;
if (!!_value.accessModes[0]?.value) {
formData.value.accessModes = value.accessModes.map(
(i: any) => i.value,
);
}
if (!!_value.features[0]?.value) {
formData.value.features = value.features.map(
(i: any) => i.value,
);
}
formData.value.nspwc = !!writeByteCount || !!byteCount;
}
},
{ immediate: true, deep: true },
);
</script>
<style lang="less" scoped></style>