报警故障阈值配置调整

This commit is contained in:
dong 2023-08-24 13:11:59 +08:00
parent 057554d224
commit 264d1a696c
5 changed files with 387 additions and 436 deletions

View File

@ -1,47 +1,11 @@
import { api } from 'src/boot/axios';
import { PageDto, PageQueryDto } from './ApiCommon';
const BaseUrl = '/api/config/device';
export interface Item {
id: number;
lineId: number;
configName?: string;
configType: number;
deviceType: string;
deviceOperator: string;
deviceUnit: string;
deviceConfigType: string;
val?: number;
}
export class PagingQueryParams extends PageQueryDto {
name?: string;
}
/**
*
* @param params
* @returns
*/
export async function pageQuery(
params: PagingQueryParams
): Promise<PageDto<Item>> {
const response = await api.get(`${BaseUrl}/page`, {
params: params,
});
return response.data;
}
interface createParams {
export interface ParamsItem {
id?: number;
lineId: number;
name: string;
deviceType: string;
guardUnit: string;
operator: string;
configDeviceType: string;
val: number;
configData: string;
}
/**
@ -49,7 +13,7 @@ interface createParams {
* @param params
* @returns
*/
export function saveThreshold(data: createParams) {
export function saveThreshold(data: ParamsItem) {
return api.post(`${BaseUrl}/save`, data);
}
@ -57,15 +21,7 @@ export function saveThreshold(data: createParams) {
*
* @param lineId 线id
*/
export async function getBaseDataByLineId(lineId: number): Promise<Item[]> {
export async function getBaseDataByLineId(lineId: number): Promise<ParamsItem> {
const response = await api.get(`${BaseUrl}/initData/${lineId}`);
return response.data;
}
/**
*
* @param id id
*/
export function deleteThresholdConfig(id: number) {
return api.delete(`${BaseUrl}/${id}`);
}

View File

@ -63,3 +63,28 @@ export enum saveAlertTypeData {
= 'AXLE_LED_ORANGE',
= 'AXLE_LED_ORANGE_MOST',
}
export const GuardConfigTypeData = {
switchLostTimes: { label: '道岔失表', unit: '秒', deviceType: '道岔' },
switchLostMostNums: {
label: '道岔大面积失表',
unit: '个',
deviceType: '道岔',
},
redLedMostNums: { label: '大面积红灯带', unit: '个', deviceType: '计轴区段' },
orangeLedMostNums: {
label: '大面积橙灯带',
unit: '个',
deviceType: '计轴区段',
},
canNotOpenTimes: {
label: '站台门无法打开',
unit: '秒',
deviceType: '屏蔽门',
},
canNotCloseTimes: {
label: '站台门无法关闭',
unit: '秒',
deviceType: '屏蔽门',
},
};

View File

@ -1,165 +1,101 @@
<template>
<div class="q-pa-md">
<q-table
<q-markup-table
class="threshold-sticky-header-table"
ref="tableRef"
title="报警故障阈值配置"
:style="{ height: tableHeight + 'px' }"
:rows="rows"
:columns="columnDefs"
row-key="id"
v-model:pagination="pagination"
:rows-per-page-options="[10, 20, 50, 100]"
:loading="loading"
:filter="filter"
binary-state-sort
@request="onRequest"
>
<template v-slot:top-right>
<q-input
dense
debounce="1000"
v-model="filter.name"
label="名称"
></q-input>
<q-btn flat round color="primary" icon="search" />
<q-btn color="primary" label="新建" @click="createConfig" />
</template>
<template v-slot:body-cell-operations="props">
<q-td :props="props">
<div class="q-gutter-sm row justify-center">
<q-btn
color="primary"
:disable="operateDisabled"
label="编辑"
@click="editThresholdValue(props.row)"
/>
<q-btn
color="red"
:disable="operateDisabled"
label="删除"
@click="deleteData(props.row)"
/>
</div>
</q-td>
</template>
</q-table>
<q-dialog
v-model="thresholdFormShow"
persistent
transition-show="scale"
transition-hide="scale"
>
<q-card style="width: 400px">
<q-card-section>
<q-form ref="myForm" @submit="onSubmit" class="q-gutter-md">
<div class="text-h6">
{{ thresholdForm.id ? '编辑' : '新建' }}
<thead>
<tr>
<th colspan="4">
<div class="row no-wrap items-center">
<div class="text-h6 q-py-md">报警故障阈值配置</div>
<q-space />
<q-btn
color="primary"
label="保存"
:disable="operateDisabled"
@click="onSubmit"
/>
</div>
<q-input
outlined
label="名称 * "
v-model="thresholdForm.name"
lazy-rules
:rules="[(val) => val.length > 0 || '请输入名称!']"
/>
<q-select
outlined
v-model="thresholdForm.lineId"
:options="lineOptions"
@update:model-value="changeLineId"
emit-value
map-options
label="线路 * "
lazy-rules
:rules="[(val) => val.length > 0 || '请选择线路!']"
/>
<q-select
outlined
v-model="thresholdForm.deviceType"
:options="typeOptions"
@update:model-value="changeDeviceType"
emit-value
map-options
label="设备类型 * "
lazy-rules
:rules="[(val) => val.length > 0 || '请选择设备类型!']"
/>
<q-select
outlined
v-model="thresholdForm.configDeviceType"
:options="operatorOptions"
@update:model-value="changeConfigDeviceType"
emit-value
map-options
:option-label="
(row) =>
showAlertTypeData[row.deviceConfigType] ||
row.deviceConfigType
"
option-value="deviceConfigType"
label="配置类型 * "
lazy-rules
:rules="[(val) => val.length > 0 || '请选择配置类型!']"
/>
<q-card flat bordered>
<q-card-section style="height: 113px">
<div class="text-grey-7">配置数据 *</div>
<div
class="q-gutter-sm row justify-center items-baseline"
v-show="thresholdForm.configDeviceType"
>
<div>
{{
unitObj[thresholdForm.operator as keyof typeof unitObj] ||
''
}}
</div>
<q-input
v-model.number="thresholdForm.val"
type="number"
outlined
dense
lazy-rules
:rules="[(val) => val > 0 || '请输入大于0的值']"
></q-input>
<div>
{{
unitObj[
thresholdForm.guardUnit as keyof typeof unitObj
] || ''
}}
</div>
</div>
</q-card-section>
</q-card>
<q-card-actions align="right">
<q-btn color="primary" label="保存" type="submit" />
<q-btn label="取消" v-close-popup />
</q-card-actions>
</q-form>
</q-card-section>
</q-card>
</q-dialog>
</th>
</tr>
<tr>
<th v-for="header in columnDefs" :key="header.name">
{{ header.label }}
</th>
</tr>
</thead>
<tbody>
<tr v-for="row in resData.list" :key="row.dataKey">
<td align="center">{{ row.deviceType }}</td>
<td align="center">{{ row.label }}</td>
<td align="center">
<q-badge color="primary">
{{ row.value }}
</q-badge>
<q-popup-edit
title="编辑值"
v-model.number="row.value"
buttons
:cover="false"
:validate="caloriesRangeValidation"
@hide="caloriesRangeValidation"
v-slot="scope"
>
<q-input
type="number"
v-model.number="scope.value"
hint="大于0的数字"
:error="errorCalories"
:error-message="errorMessageCalories"
dense
autofocus
@keyup.enter="scope.set"
/>
</q-popup-edit>
</td>
<td align="center">{{ row.unit }}</td>
</tr>
</tbody>
</q-markup-table>
</div>
</template>
<script setup lang="ts">
import { ref, reactive, onMounted, computed } from 'vue';
import { useQuasar, type QTableColumn, QForm } from 'quasar';
import { useQuasar, type QTableColumn } from 'quasar';
import {
pageQuery,
saveThreshold,
getBaseDataByLineId,
deleteThresholdConfig,
Item,
ParamsItem,
} from '../api/AlarmThresholdApi';
import { getLineList } from '../api/LineInfoApi';
import { ApiError } from 'src/boot/axios';
import { showAlertTypeData } from 'src/components/alarm/alarmInfoEnum';
import { GuardConfigTypeData } from 'src/components/alarm/alarmInfoEnum';
import { alert } from 'src/protos/guardConfig';
import { fromUint8Array, toUint8Array } from 'js-base64';
interface Item {
deviceType: string;
label: string;
value: number;
unit: string;
dataKey: keyof ProtoKey;
}
interface ProtoKey {
switchLostTimes: number;
switchLostMostNums: number;
redLedMostNums: number;
orangeLedMostNums: number;
canNotOpenTimes: number;
canNotCloseTimes: number;
}
interface ResData {
id?: string;
lineId: string;
list: Item[];
}
const $q = useQuasar();
@ -174,51 +110,6 @@ const tableHeight = computed(() => {
return props.sizeHeight - 32;
});
const typeOptions = [
{ label: '道岔', value: 'DEVICE_TYPE_SWITCH' },
{ label: '计轴区段', value: 'DEVICE_TYPE_TRACK' },
{ label: '屏蔽门', value: 'DEVICE_TYPE_PLATFORM' },
];
const unitObj = {
TIMES: '秒',
NUMS: '个',
LT: '小于等于',
GE: '大于等于',
};
const operatorOptions = computed(() => {
return operatorList.value.filter((item) => {
return (
item.deviceType == thresholdForm.deviceType &&
item.lineId == +thresholdForm.lineId
);
});
});
const operatorList = computed(() => {
let arr: Item[] = [];
const data = configDataMap[+thresholdForm.lineId];
if (data) {
arr = data;
}
return arr;
});
type DataConfig = {
[key: number]: Item[];
};
const configDataMap = reactive<DataConfig>({});
function getConfigDataByLineId(val: number) {
getBaseDataByLineId(val)
.then((res) => {
configDataMap[val] = res;
})
.catch((err) => {
console.log(err, '---err---');
});
}
let lineOptions: Array<{ label: string; value: string }> = [];
function getAllLineList() {
lineOptions = [];
@ -236,117 +127,84 @@ function getAllLineList() {
console.log(err, '---err--');
});
}
const lineOptionsMap = computed(() => {
const obj: { [k: string]: string } = {};
lineOptions.forEach((item: { value: string; label: string }) => {
obj[item.value] = item.label;
});
return obj;
});
const typeOptionsMap = computed(() => {
const obj: { [k: string]: string } = {};
typeOptions.forEach((item: { value: string; label: string }) => {
obj[item.value] = item.label;
});
return obj;
});
onMounted(() => {
tableRef.value.requestServerInteraction();
getAllLineList();
onRequest();
});
const columnDefs: QTableColumn[] = [
{
name: 'configName',
label: '名称',
field: 'configName',
required: true,
align: 'center',
},
{
name: 'lineId',
label: '线路',
field: (row) => {
return lineOptionsMap.value[row.lineId] || '';
},
align: 'center',
},
{
name: 'deviceType',
label: '设备类型',
field: (row) => {
return typeOptionsMap.value[row.deviceType] || '';
},
field: 'deviceType',
align: 'center',
},
{
name: 'deviceConfigType',
name: 'label',
label: '配置类型',
field: (row) => {
return showAlertTypeData[row.deviceConfigType] || row.deviceConfigType;
},
field: 'label',
align: 'center',
},
{
name: 'deviceOperator',
label: '符号',
field: (row) => {
return unitObj[row.deviceOperator as keyof typeof unitObj] || '';
},
align: 'center',
},
{
name: 'val',
name: 'value',
label: '值',
field: 'val',
field: 'value',
align: 'center',
},
{
name: 'deviceUnit',
name: 'unit',
label: '单位',
field: (row) => {
return unitObj[row.deviceUnit as keyof typeof unitObj] || '';
},
field: 'unit',
align: 'center',
},
{ name: 'operations', label: '操作', field: 'operations', align: 'center' },
];
const errorCalories = ref(false);
const errorMessageCalories = ref('');
function caloriesRangeValidation(val?: number) {
if (val !== undefined) {
if (val <= 0) {
errorCalories.value = true;
errorMessageCalories.value = '请输入大于0的数字!';
return false;
}
}
errorCalories.value = false;
errorMessageCalories.value = '';
return true;
}
const operateDisabled = ref(false);
const tableRef = ref();
const rows = reactive([]);
const filter = reactive({
name: '',
});
const loading = ref(false);
const pagination = ref({
sortBy: 'desc',
descending: false,
page: 1,
rowsPerPage: 10,
rowsNumber: 10,
const resData = reactive<ResData>({
id: '',
lineId: '',
list: [],
});
async function onRequest(props: any) {
const { page, rowsPerPage, sortBy, descending } = props.pagination;
const filter = props.filter;
async function onRequest() {
loading.value = true;
try {
let response = await pageQuery({
current: page,
size: rowsPerPage,
name: filter.name,
const lineId = 3;
const res = await getBaseDataByLineId(lineId);
resData.id = res ? res.id + '' : '';
resData.lineId = res ? res.lineId + '' : lineId + '';
resData.list = [];
const base64 = res ? res.configData : '';
const storage = alert.GuardConfig.deserialize(toUint8Array(base64));
console.log('加载数据', storage.toObject());
const data = storage.toObject();
Object.keys(data).forEach((key) => {
const obj: Item = {
dataKey: key as keyof ProtoKey,
value: data[key as keyof ProtoKey] || 0,
...GuardConfigTypeData[key as keyof typeof GuardConfigTypeData],
};
resData.list.push(obj);
});
const pageData = response;
pagination.value.rowsNumber = pageData.total;
pagination.value.page = page;
pagination.value.rowsPerPage = rowsPerPage;
pagination.value.sortBy = sortBy;
pagination.value.descending = descending;
rows.splice(0, rows.length, ...(pageData.records as []));
} catch (err) {
const error = err as ApiError;
$q.notify({
@ -358,126 +216,46 @@ async function onRequest(props: any) {
}
}
const thresholdFormShow = ref(false);
function createConfig() {
initFormData();
thresholdFormShow.value = true;
}
const myForm = ref<QForm | null>(null);
function onSubmit() {
myForm.value?.validate().then(async (res) => {
if (res) {
operateDisabled.value = true;
try {
const params = {
lineId: +thresholdForm.lineId,
name: thresholdForm.name,
deviceType: thresholdForm.deviceType,
configDeviceType: thresholdForm.configDeviceType,
guardUnit: thresholdForm.guardUnit,
operator: thresholdForm.operator,
val: thresholdForm.val,
};
if (thresholdForm.id) {
Object.assign(params, { id: +thresholdForm.id });
}
await saveThreshold(params);
thresholdFormShow.value = false;
tableRef.value.requestServerInteraction(); //
} catch (err) {
const error = err as ApiError;
$q.notify({
type: 'negative',
message: error.title,
});
} finally {
operateDisabled.value = false;
}
}
});
}
const thresholdForm = reactive({
id: 0,
name: '',
lineId: '',
deviceType: '',
configDeviceType: '',
guardUnit: '',
operator: '',
val: 0,
});
function initFormData() {
thresholdForm.id = 0;
thresholdForm.name = '';
thresholdForm.lineId = '';
thresholdForm.deviceType = '';
thresholdForm.configDeviceType = '';
thresholdForm.guardUnit = '';
thresholdForm.operator = '';
thresholdForm.val = 0;
}
function changeLineId(val: string) {
thresholdForm.configDeviceType = '';
if (val && !configDataMap[+val]) {
getConfigDataByLineId(+val);
}
}
function changeDeviceType() {
thresholdForm.configDeviceType = '';
}
function changeConfigDeviceType(val: string) {
const find = operatorOptions.value.find((item) => {
return item.deviceConfigType == val;
});
if (find) {
thresholdForm.guardUnit = find.deviceUnit;
thresholdForm.operator = find.deviceOperator;
} else {
thresholdForm.guardUnit = '';
thresholdForm.operator = '';
thresholdForm.val = 0;
}
}
function editThresholdValue(row: Item) {
thresholdFormShow.value = true;
thresholdForm.id = row.id;
thresholdForm.name = row.configName || '';
thresholdForm.lineId = row.lineId + '';
thresholdForm.deviceType = row.deviceType;
thresholdForm.guardUnit = row.deviceUnit;
thresholdForm.operator = row.deviceOperator;
thresholdForm.configDeviceType = row.deviceConfigType;
thresholdForm.val = row.val || 0;
if (!configDataMap[row.lineId]) {
getConfigDataByLineId(row.lineId);
}
}
async function deleteData(row: Item) {
async function onSubmit() {
operateDisabled.value = true;
$q.dialog({
title: '确认',
message: `确认删除配置 "${row.configName}" 吗?`,
cancel: true,
})
.onOk(async () => {
try {
await deleteThresholdConfig(row.id);
tableRef.value.requestServerInteraction(); //
} catch (err) {
const error = err as ApiError;
$q.notify({
type: 'negative',
message: error.title,
});
}
})
.onDismiss(() => {
operateDisabled.value = false;
try {
const storage = new alert.GuardConfig();
resData.list.forEach((item) => {
storage[item.dataKey] = item.value;
});
const base64 = fromUint8Array(storage.serialize());
const obj: ParamsItem = {
lineId: +resData.lineId,
configData: base64,
};
if (resData.id) {
Object.assign(obj, { id: +resData.id });
}
await saveThreshold(obj);
onRequest();
} catch (err) {
const error = err as ApiError;
$q.notify({
type: 'negative',
message: error.title,
});
} finally {
operateDisabled.value = false;
}
}
</script>
<style lang="scss">
.threshold-sticky-header-table {
thead tr th {
background-color: #ffffff;
position: sticky;
z-index: 1;
}
thead tr:first-child th {
top: 0;
}
thead tr:last-child th {
top: 79px;
}
}
</style>

View File

@ -28,6 +28,8 @@ export namespace alert {
AXLE_LED_RED = 13,
AXLE_LED_RED_MOST = 14,
AXLE_LED_ORANGE = 15,
AXLE_LED_ORANGE_MOST = 16
AXLE_LED_ORANGE_MOST = 16,
SWITCH_LOST_MOST = 17,
TRAIN_SIGNAL = 18
}
}

190
src/protos/guardConfig.ts Normal file
View File

@ -0,0 +1,190 @@
/**
* Generated by the protoc-gen-ts. DO NOT EDIT!
* compiler version: 4.23.1
* source: guardConfig.proto
* git: https://github.com/thesayyn/protoc-gen-ts */
import * as pb_1 from "google-protobuf";
export namespace alert {
export class GuardConfig extends pb_1.Message {
#one_of_decls: number[][] = [];
constructor(data?: any[] | {
switchLostTimes?: number;
switchLostMostNums?: number;
redLedMostNums?: number;
orangeLedMostNums?: number;
canNotOpenTimes?: number;
canNotCloseTimes?: number;
}) {
super();
pb_1.Message.initialize(this, Array.isArray(data) ? data : [], 0, -1, [], this.#one_of_decls);
if (!Array.isArray(data) && typeof data == "object") {
if ("switchLostTimes" in data && data.switchLostTimes != undefined) {
this.switchLostTimes = data.switchLostTimes;
}
if ("switchLostMostNums" in data && data.switchLostMostNums != undefined) {
this.switchLostMostNums = data.switchLostMostNums;
}
if ("redLedMostNums" in data && data.redLedMostNums != undefined) {
this.redLedMostNums = data.redLedMostNums;
}
if ("orangeLedMostNums" in data && data.orangeLedMostNums != undefined) {
this.orangeLedMostNums = data.orangeLedMostNums;
}
if ("canNotOpenTimes" in data && data.canNotOpenTimes != undefined) {
this.canNotOpenTimes = data.canNotOpenTimes;
}
if ("canNotCloseTimes" in data && data.canNotCloseTimes != undefined) {
this.canNotCloseTimes = data.canNotCloseTimes;
}
}
}
get switchLostTimes() {
return pb_1.Message.getFieldWithDefault(this, 1, 0) as number;
}
set switchLostTimes(value: number) {
pb_1.Message.setField(this, 1, value);
}
get switchLostMostNums() {
return pb_1.Message.getFieldWithDefault(this, 2, 0) as number;
}
set switchLostMostNums(value: number) {
pb_1.Message.setField(this, 2, value);
}
get redLedMostNums() {
return pb_1.Message.getFieldWithDefault(this, 3, 0) as number;
}
set redLedMostNums(value: number) {
pb_1.Message.setField(this, 3, value);
}
get orangeLedMostNums() {
return pb_1.Message.getFieldWithDefault(this, 4, 0) as number;
}
set orangeLedMostNums(value: number) {
pb_1.Message.setField(this, 4, value);
}
get canNotOpenTimes() {
return pb_1.Message.getFieldWithDefault(this, 5, 0) as number;
}
set canNotOpenTimes(value: number) {
pb_1.Message.setField(this, 5, value);
}
get canNotCloseTimes() {
return pb_1.Message.getFieldWithDefault(this, 6, 0) as number;
}
set canNotCloseTimes(value: number) {
pb_1.Message.setField(this, 6, value);
}
static fromObject(data: {
switchLostTimes?: number;
switchLostMostNums?: number;
redLedMostNums?: number;
orangeLedMostNums?: number;
canNotOpenTimes?: number;
canNotCloseTimes?: number;
}): GuardConfig {
const message = new GuardConfig({});
if (data.switchLostTimes != null) {
message.switchLostTimes = data.switchLostTimes;
}
if (data.switchLostMostNums != null) {
message.switchLostMostNums = data.switchLostMostNums;
}
if (data.redLedMostNums != null) {
message.redLedMostNums = data.redLedMostNums;
}
if (data.orangeLedMostNums != null) {
message.orangeLedMostNums = data.orangeLedMostNums;
}
if (data.canNotOpenTimes != null) {
message.canNotOpenTimes = data.canNotOpenTimes;
}
if (data.canNotCloseTimes != null) {
message.canNotCloseTimes = data.canNotCloseTimes;
}
return message;
}
toObject() {
const data: {
switchLostTimes?: number;
switchLostMostNums?: number;
redLedMostNums?: number;
orangeLedMostNums?: number;
canNotOpenTimes?: number;
canNotCloseTimes?: number;
} = {};
if (this.switchLostTimes != null) {
data.switchLostTimes = this.switchLostTimes;
}
if (this.switchLostMostNums != null) {
data.switchLostMostNums = this.switchLostMostNums;
}
if (this.redLedMostNums != null) {
data.redLedMostNums = this.redLedMostNums;
}
if (this.orangeLedMostNums != null) {
data.orangeLedMostNums = this.orangeLedMostNums;
}
if (this.canNotOpenTimes != null) {
data.canNotOpenTimes = this.canNotOpenTimes;
}
if (this.canNotCloseTimes != null) {
data.canNotCloseTimes = this.canNotCloseTimes;
}
return data;
}
serialize(): Uint8Array;
serialize(w: pb_1.BinaryWriter): void;
serialize(w?: pb_1.BinaryWriter): Uint8Array | void {
const writer = w || new pb_1.BinaryWriter();
if (this.switchLostTimes != 0)
writer.writeInt32(1, this.switchLostTimes);
if (this.switchLostMostNums != 0)
writer.writeInt32(2, this.switchLostMostNums);
if (this.redLedMostNums != 0)
writer.writeInt32(3, this.redLedMostNums);
if (this.orangeLedMostNums != 0)
writer.writeInt32(4, this.orangeLedMostNums);
if (this.canNotOpenTimes != 0)
writer.writeInt32(5, this.canNotOpenTimes);
if (this.canNotCloseTimes != 0)
writer.writeInt32(6, this.canNotCloseTimes);
if (!w)
return writer.getResultBuffer();
}
static deserialize(bytes: Uint8Array | pb_1.BinaryReader): GuardConfig {
const reader = bytes instanceof pb_1.BinaryReader ? bytes : new pb_1.BinaryReader(bytes), message = new GuardConfig();
while (reader.nextField()) {
if (reader.isEndGroup())
break;
switch (reader.getFieldNumber()) {
case 1:
message.switchLostTimes = reader.readInt32();
break;
case 2:
message.switchLostMostNums = reader.readInt32();
break;
case 3:
message.redLedMostNums = reader.readInt32();
break;
case 4:
message.orangeLedMostNums = reader.readInt32();
break;
case 5:
message.canNotOpenTimes = reader.readInt32();
break;
case 6:
message.canNotCloseTimes = reader.readInt32();
break;
default: reader.skipField();
}
}
return message;
}
serializeBinary(): Uint8Array {
return this.serialize();
}
static deserializeBinary(bytes: Uint8Array): GuardConfig {
return GuardConfig.deserialize(bytes);
}
}
}