This commit is contained in:
高云鹏 2024-11-27 17:30:11 +08:00
commit b9ded9def6
9 changed files with 218 additions and 199 deletions

View File

@ -5,6 +5,7 @@ import com.das.modules.cache.service.CacheService;
import com.das.modules.calc.domain.entity.CalcModule;
import com.googlecode.aviator.AviatorEvaluatorInstance;
import com.googlecode.aviator.Expression;
import com.googlecode.aviator.runtime.type.AviatorRuntimeJavaType;
import lombok.extern.slf4j.Slf4j;
import org.quartz.Job;
import org.quartz.JobDataMap;
@ -13,6 +14,7 @@ import org.quartz.JobExecutionException;
import org.springframework.util.StopWatch;
import java.util.Map;
import java.util.stream.Collectors;
@Slf4j
public class CalcJob implements Job {
@ -34,7 +36,12 @@ public class CalcJob implements Job {
log.error("expression is null, calcModule={}", calcModule.getName());
throw new JobExecutionException("expression is null");
}
Map<String,Object> envs = expression.newEnv("G_DEVICES", cacheService.getEquipmentCache().getDevicesCache());
//准备全局变量
Map<String,Object> envs = expression.newEnv();
//风场信息
envs.put("G_WF", cacheService.getEquipmentCache().getDevicesCache().stream().filter(c->c.getObjectType().equals(10001)).findFirst().get());
//风机信息
envs.put("G_GENS", cacheService.getEquipmentCache().getDevicesCache().stream().filter(c->c.getObjectType().equals(10002)).collect(Collectors.toList()));
Object result = expression.execute(envs);
sw.stop();
log.debug("任务[{}]已执行,结果:{}, 耗时:{}秒", calcModule.getName(), result, sw.getTotalTimeMillis()/1000.0);

View File

@ -73,6 +73,7 @@ public class CalcService {
if (sh.checkExists(jobKey)){
throw new SchedulerException("计算模块已启动,请先停止该模块");
}
aviator.compile(scriptModule.getName(), scriptModule.getScript(), true);
JobDetail jobDetail = JobBuilder
.newJob(CalcJob.class)
.setJobData(dataMap)
@ -95,6 +96,7 @@ public class CalcService {
JobKey jobKey = JobKey.jobKey(name, "CalcEngine");
if (sh.checkExists(jobKey)){
sh.deleteJob(jobKey);
aviator.invalidateCacheByKey(name);
}
}

View File

@ -31,19 +31,15 @@
<el-option v-for="v in alarmTypes" :key="v.value" :label="v.label" :value="v.value"></el-option>
</el-select>
<div style="width: 20px"></div>
<el-button
:icon="Search"
type="primary"
@click="paginationOptions.current == 1 ? getalarmsList() : (paginationOptions.current = 1)"
>查询</el-button
>
<el-button :loading="isLoading" :icon="Search" type="primary" @click="searchOperate()">查询</el-button>
</el-space>
</el-header>
<el-main class="mainMain">
<div class="tabsPart">
<el-table :data="alarmsTableData" class="tablePart" highlight-current-row>
<el-table-column prop="eventTimeFormate" :label="AlarmsFieldsEnums['alarmTime']" align="center"> </el-table-column>
<el-table-column prop="devicecodeName" :label="AlarmsFieldsEnums['airBlowerNumber']" align="center"> </el-table-column>
<el-table-column prop="deviceName" :label="AlarmsFieldsEnums['airBlowerName']" align="center"> </el-table-column>
<el-table-column prop="deviceCode" :label="AlarmsFieldsEnums['airBlowerNumber']" align="center"> </el-table-column>
<el-table-column prop="eventText" :label="AlarmsFieldsEnums['faultDescription']" align="center">
<template #default="{ row }">
<span v-html="formatText(row.eventText)"></span>
@ -157,6 +153,11 @@ const alarmTypes = ref([
{ label: '告警', value: 1 },
{ label: '提示', value: 0 },
])
const isLoading = ref(false)
const searchOperate = () => {
isLoading.value = true
paginationOptions.current == 1 ? getalarmsList() : (paginationOptions.current = 1)
}
const searchalarms = (): GetAlarmsTableParam => {
const start = timeRange.value[0]
@ -197,13 +198,13 @@ const getalarmsList = () => {
const transparams = searchalarms()
getAlarmListReq(transparams)
.then((res: any) => {
isLoading.value = false
if (res.code == 200) {
paginationOptions.total = res.total
alarmsTableData.value = res.rows.map((item: any) => {
return {
...item,
eventTimeFormate: timestampToTime(item.eventTime),
devicecodeName: item.deviceName + item.deviceCode,
}
})
} else {
@ -211,6 +212,7 @@ const getalarmsList = () => {
}
})
.catch((err) => {
isLoading.value = false
ElMessage.error(err?.response?.data?.msg ?? '查询失败')
})
}

View File

@ -32,6 +32,7 @@ export enum AlarmsFieldsEnums {
'id' = 'id',
'alarmTime' = '报警时间',
'airBlowerNumber' = '风机编号',
'airBlowerName' = '风机名称',
'faultDescription' = '故障描述',
'alarmGrade' = '报警等级',
'alarmType' = '报警类别',

View File

@ -347,7 +347,7 @@ const getTableData = (deviceCode) => {
startTime: new Date(new Date().toLocaleDateString()).getTime(),
endTime: Date.now(),
deviceCode: deviceCode,
// eventLevel:2
eventLevel:2 || 1
}
// console.log(JSON.stringify(data))
getAlarmListReq(data).then((res) => {

View File

@ -1,7 +1,7 @@
<template>
<div class="statAnalysis">
<el-menu :default-active="activeIndex" class="headerList" mode="horizontal" @select="handleSelect">
<el-menu-item index="0" key="0"> {{ headerList[0] }} </el-menu-item>
<!-- <el-menu-item index="0" key="0"> {{ headerList[0] }} </el-menu-item> -->
<el-tooltip class="box-item" effect="dark" content="同一测点,不同时间段对比" placement="right-start">
<el-menu-item index="1" key="1"> {{ headerList[1] }} </el-menu-item>
</el-tooltip>
@ -9,15 +9,11 @@
<el-menu-item index="2" key="2"> {{ headerList[2] }} </el-menu-item>
</el-tooltip>
</el-menu>
<keep-alive>
<PowerCurveAnalysis v-if="activeIndex == 0"></PowerCurveAnalysis>
<TrendAnalysis v-if="activeIndex == '1'"></TrendAnalysis>
</keep-alive>
<keep-alive>
<TrendAnalysis v-if="activeIndex == 1"></TrendAnalysis>
</keep-alive>
<keep-alive>
<TrendComparison v-if="activeIndex == 2"></TrendComparison>
<TrendComparison v-if="activeIndex == '2'"></TrendComparison>
</keep-alive>
</div>
</template>
@ -29,10 +25,10 @@ import TrendAnalysis from './trendAnalysis.vue'
import TrendComparison from './trendComparison.vue'
import PowerCurveAnalysis from './powerCurveAnalysis.vue'
const config = useConfig()
const activeIndex = ref(0)
const activeIndex = ref('1')
const { t } = useI18n()
const headerList = [t('statAnalysis.PowerCurveAnalysis'), t('statAnalysis.trendAnalysis'), t('statAnalysis.trendComparison')]
const handleSelect = (index: number) => {
const handleSelect = (index: string) => {
activeIndex.value = index
}
</script>

View File

@ -395,11 +395,8 @@ const statAnalysisExport = () => {
}
</script>
<style scoped lang="scss">
.statAnalysis {
.contain {
height: 100%;
.contain {
height: calc(100% - 60px);
}
.headerPart {
padding: 20px;
display: flex;
@ -476,7 +473,7 @@ const statAnalysisExport = () => {
max-height: 120px;
overflow: hidden;
.headerPart {
padding: 10px 20px;
padding: 0px 10px 20px 20px;
}
&.expand {
max-height: max-content;

View File

@ -60,20 +60,6 @@
@change="timechange(index)"
/>
</div>
<!-- <div class="topLeft">
<div class="selectPart" v-if="calculate[index] && calculate[index]['max'] !== ''">
<span>{{ t('statAnalysis.max') }}</span>
<span class="max">{{ calculate[index]['max'] }}</span>
</div>
<div class="selectPart" v-if="calculate[index] && calculate[index]['min'] !== ''">
<span>{{ t('statAnalysis.min') }}</span>
<span class="min">{{ calculate[index]['min'] }}</span>
</div>
<div class="selectPart" v-if="calculate[index] && calculate[index]['average'] !== ''">
<span>{{ t('statAnalysis.average') }}</span>
<span class="average">{{ calculate[index]['average'] }}</span>
</div>
</div> -->
</div>
</div>
</div>
@ -104,7 +90,7 @@
</div>
<template #footer>
<span class="dialog-footer">
<el-button type="primary" @click="selectstatAnalysisAttributes"> 保存 </el-button>
<el-button type="primary" @click="selectstatAnalysisAttributes"> 确认 </el-button>
<el-button @click="showMeasure = false">取消</el-button>
</span>
</template>
@ -126,10 +112,18 @@ const statAnalysisSelect = reactive({
interval: '',
time: '',
})
const times: any = reactive([{ time: '' }])
const getFormattedDate = (offset: number) => {
const date = new Date()
date.setDate(date.getDate() + offset)
const year = date.getFullYear()
const month = String(date.getMonth() + 1).padStart(2, '0')
const day = String(date.getDate()).padStart(2, '0')
return `${year}-${month}-${day}`
}
const times: any = reactive([[getFormattedDate(0) + ' 00:00:00', getFormattedDate(0) + ' 23:59:59']])
const addTime = () => {
if (times.length < 4) {
times.push({ time: '' })
times.push([getFormattedDate(times.length) + ' 00:00:00', getFormattedDate(times.length) + ' 23:59:59'])
customName.push(statAnalysisSelect.attributes + String(times.length))
} else {
ElMessage.info('最多可添加四条查询曲线!')
@ -298,15 +292,7 @@ const shortcuts = [
},
},
]
const getFormattedDate = (offset: number) => {
const date = new Date()
date.setDate(date.getDate() + offset)
const year = date.getFullYear()
const month = String(date.getMonth() + 1).padStart(2, '0')
const day = String(date.getDate()).padStart(2, '0')
return `${year}-${month}-${day}`
}
const getDateRange = (type: 'week' | 'month') => {
const today = new Date()
if (type === 'week') {
@ -590,7 +576,7 @@ const timestampToTime = (timestamp: any) => {
padding: 0 20px;
height: 60px;
.addline {
margin: 4px 20px;
height: 40px;
}
.topLeft {
display: flex;
@ -672,30 +658,6 @@ const timestampToTime = (timestamp: any) => {
height: 38px;
}
}
.max,
.min,
.average {
border-radius: 6px;
height: 40px;
width: 72px;
text-align: center;
line-height: 40px;
}
.max {
background: rgba(254, 55, 49, 0.2);
border: 1px solid #fe3731;
color: #fe3731;
}
.min {
background: rgba(0, 160, 150, 0.2);
border: 1px solid #00a096;
color: #00a096;
}
.average {
background: rgba(0, 100, 170, 0.2);
border: 1px solid #0064aa;
color: #0064aa;
}
.customName {
width: 360px;
}

View File

@ -5,7 +5,7 @@
<div class="selectPart">
<span>{{ t('statAnalysis.time') }}</span>
<el-date-picker
class="datetime-picker"
class="statAnalysisSelect"
v-model="statAnalysisTime"
:type="statAnalysisInterval == '1d' ? 'daterange' : 'datetimerange'"
:value-format="statAnalysisInterval == '1d' ? 'YYYY-MM-DD' : 'YYYY-MM-DD HH:mm:ss'"
@ -20,68 +20,69 @@
<el-option v-for="v in statAnalysisSelectOptions.interval" :key="v.value" :label="v.label" :value="v.value"></el-option>
</el-select>
</div>
<el-button class="addline" type="primary" :icon="Plus" @click="addDevice()"> 增加</el-button>
</div>
<div class="topRight">
<el-button type="primary" :loading="isLoading" @click="statAnalysisOperate()">{{ t('statAnalysis.search') }}</el-button>
<el-button style="color: #0064aa" @click="statAnalysisExport()">{{ t('statAnalysis.export') }}</el-button>
</div>
</el-header>
<div class="timeColumns">
<div class="headerPart" v-for="(deviceId, index) in statAnalysisDeviceId" :key="index">
<div class="topLeft">
<div class="selectPart">
<el-input v-model="customName[index]" class="customName"></el-input>
<span>{{ t('statAnalysis.deviceId') }}</span>
<el-select
v-model="statAnalysisDeviceId[index]"
@change="deviceIdChange(index)"
:placeholder="'请选择' + t('statAnalysis.deviceId')"
class="statAnalysisSelect"
>
<el-option v-for="v in statAnalysisSelectOptions.deviceId" :key="v.value" :label="v.label" :value="v.value"></el-option>
</el-select>
</div>
<div class="selectPart">
<span>{{ t('statAnalysis.attributes') }}</span>
<el-input
class="statAnalysisSelect"
v-model="statAnalysisAttributes[index]"
@click="selectAtteibutes(index)"
:placeholder="'请选择' + t('statAnalysis.attributes')"
></el-input>
</div>
<div class="topLeft">
<div class="icon">
<el-button
v-show="index === statAnalysisDeviceId.length - 1"
type="primary"
size="small"
:icon="Plus"
circle
@click="addDevice(index)"
>
</el-button>
</div>
<div class="icon">
<el-button v-show="index !== 0" type="danger" size="small" :icon="Delete" @click="switchDevice(index)" circle></el-button>
</div>
<div class="selectPart" v-if="calculate[index] && calculate[index]['max'] !== ''">
<span>{{ t('statAnalysis.max') }}</span>
<span class="max">{{ calculate[index]['max'] }}</span>
</div>
<div class="selectPart" v-if="calculate[index] && calculate[index]['min'] !== ''">
<span>{{ t('statAnalysis.min') }}</span>
<span class="min">{{ calculate[index]['min'] }}</span>
</div>
<div class="selectPart" v-if="calculate[index] && calculate[index]['average'] !== ''">
<span>{{ t('statAnalysis.average') }}</span>
<span class="average">{{ calculate[index]['average'] }}</span>
<div class="main">
<div class="left">
<div class="timeColumns">
<div class="deviceColums">
<div class="moduleRow" v-for="(deviceId, index) in statAnalysisDeviceId" :key="index">
<div class="item">
<el-icon v-show="index !== 0" class="removeModule" @click="switchDevice(index)">
<Close />
</el-icon>
<div class="selectPart">
<span>名称</span>
<el-input v-model="customName[index]" class="customName"></el-input>
<span>{{ t('statAnalysis.deviceId') }}</span>
<el-select
v-model="statAnalysisDeviceId[index]"
@change="deviceIdChange(index)"
:placeholder="'请选择' + t('statAnalysis.deviceId')"
class="statAnalysisSelect"
>
<el-option
v-for="v in statAnalysisSelectOptions.deviceId"
:key="v.value"
:label="v.label"
:value="v.value"
></el-option>
</el-select>
</div>
<div class="selectPart">
<span>{{ t('statAnalysis.attributes') }}</span>
<el-input
class="statAnalysisSelectattributes"
v-model="statAnalysisAttributes[index]"
@click="selectAtteibutes(index)"
:placeholder="'请选择' + t('statAnalysis.attributes')"
></el-input>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="right">
<div
ref="chartContainer"
style="
position: absolute;
top: 127px;
width: calc(100% - 430px);
height: calc(100% - 187px);
border: 1px solid rgb(217, 217, 217);
margin: 30px 0;
"
></div>
</div>
</div>
<div ref="chartContainer" style="position: absolute; bottom: 0px; width: 100%; height: 400px; border: 1px solid rgb(217, 217, 217)"></div>
<el-dialog v-model="showMeasure" title="测点名称" :width="800">
<template #header>
<div class="measureSlotHeader">
@ -93,7 +94,7 @@
</div>
<template #footer>
<span class="dialog-footer">
<el-button type="primary" @click="selectstatAnalysisAttributes"> 保存 </el-button>
<el-button type="primary" @click="selectstatAnalysisAttributes"> 确认 </el-button>
<el-button @click="showMeasure = false">取消</el-button>
</span>
</template>
@ -105,11 +106,11 @@ import { markRaw, reactive, ref, watch, nextTick, onMounted, computed } from 'vu
import { useI18n } from 'vue-i18n'
import { queryWindTurbinesPages, historyReq, trendContrastExport } from '/@/api/backend/statAnalysis/request'
import { ElMessage, ElMenu } from 'element-plus'
import { DArrowRight, Plus, Delete } from '@element-plus/icons-vue'
import { DArrowRight, Plus, Delete, Close } from '@element-plus/icons-vue'
import MeasurementPage from './analysisAttributes.vue'
import * as echarts from 'echarts'
const { t } = useI18n()
const statAnalysisTime = ref('')
const statAnalysisInterval = ref('')
const statAnalysisDeviceId = reactive([''])
const statAnalysisAttributes = reactive([''])
@ -124,11 +125,22 @@ const statAnalysisSelectOptions: any = reactive({
],
deviceId: [],
})
const getFormattedDate = (offset: number) => {
const date = new Date()
date.setDate(date.getDate() + offset)
const year = date.getFullYear()
const month = String(date.getMonth() + 1).padStart(2, '0')
const day = String(date.getDate()).padStart(2, '0')
return `${year}-${month}-${day}`
}
const statAnalysisTime = ref([getFormattedDate(0) + ' 00:00:00', getFormattedDate(0) + ' 23:59:59'])
const openModelIndex = ref(0)
const addDevice = (index: number) => {
const addDevice = () => {
statAnalysisDeviceId.push('')
statAnalysisAttributes.push('')
customName.push(String(index + 2))
customName.push(String(statAnalysisDeviceId.length))
}
const switchDevice = (index: number) => {
statAnalysisDeviceId.splice(index, 1)
@ -155,8 +167,9 @@ const selectAtteibutes = (index: number) => {
}
const deviceIdChange = (index: number) => {
statAnalysisAttributeCode[index] = ''
statAnalysisAttributes[index] = ''
statAnalysisAttributeCode[index] = statAnalysisAttributeCode[index - 1] || ''
statAnalysisAttributes[index] = statAnalysisAttributes[index - 1] || ''
customName[index] = statAnalysisAttributes[index] + String(index + 1)
}
const showMeasure = ref(false)
@ -280,15 +293,7 @@ const shortcuts = [
},
},
]
const getFormattedDate = (offset: number) => {
const date = new Date()
date.setDate(date.getDate() + offset)
const year = date.getFullYear()
const month = String(date.getMonth() + 1).padStart(2, '0')
const day = String(date.getDate()).padStart(2, '0')
return `${year}-${month}-${day}`
}
const getDateRange = (type: 'week' | 'month') => {
const today = new Date()
if (type === 'week') {
@ -313,6 +318,19 @@ const getDateRange = (type: 'week' | 'month') => {
const isLoading = ref(false)
const statAnalysisOperate = () => {
const findCode = statAnalysisAttributeCode.filter((item: any) => {
return item.length
})
if (!statAnalysisTime.value) {
ElMessage.info('请选择查询时间!')
return
} else if (!statAnalysisDeviceId.length) {
ElMessage.info('请选择风机!')
return
} else if (!findCode.length) {
ElMessage.info('请选择测点名称!')
return
}
isLoading.value = true
option.series = []
option.legend.data = []
@ -348,7 +366,7 @@ const historyDataReq = (data: any) => {
data: yData,
animation: false,
}
calculate.value[dataIndex] = calculateStats(yData)
// calculate.value[dataIndex] = calculateStats(yData)
option.tooltip = {
show: true,
trigger: 'axis',
@ -410,6 +428,7 @@ const getRequestData = () => {
}
return deviceId
}, [])
console.log(devices)
const requestData = {
devices: devices,
interval: statAnalysisInterval.value || '5m',
@ -446,12 +465,25 @@ const timestampToTime = (timestamp: any) => {
height: 100%;
.contain {
height: calc(100% - 60px);
.main {
display: flex;
height: calc(100% - 60px);
.right {
width: calc(100% - 418px);
height: 100%;
}
}
}
.headerPart {
padding: 20px;
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 20px;
height: 60px;
.addline {
height: 40px;
}
.topLeft {
display: flex;
.icon {
@ -459,78 +491,98 @@ const timestampToTime = (timestamp: any) => {
height: 40px;
padding: 10px 0;
}
.selectPart {
display: flex;
justify-content: space-between;
align-items: center;
height: 40px;
margin-right: 20px;
span {
margin-right: 10px;
}
.customName {
width: 120px;
margin-right: 10px;
}
.statAnalysisSelect {
width: 200px;
:deep(.el-select__wrapper) {
height: 40px;
}
:deep(.el-input__inner) {
height: 38px;
}
}
.calc {
.max,
.min,
.average {
border-radius: 6px;
height: 40px;
width: 72px;
text-align: center;
line-height: 40px;
}
.max {
background: rgba(254, 55, 49, 0.2);
border: 1px solid #fe3731;
color: #fe3731;
}
.min {
background: rgba(0, 160, 150, 0.2);
border: 1px solid #00a096;
color: #00a096;
}
.average {
background: rgba(0, 100, 170, 0.2);
border: 1px solid #0064aa;
color: #0064aa;
}
}
}
.dialog-footer button:first-child {
margin-right: 10px;
}
}
.topRight {
// width: 436px;
display: flex;
justify-content: space-between;
margin: 12px 0;
.el-button {
width: 100px;
height: 40px;
}
}
}
.selectPart {
display: flex;
justify-content: space-between;
align-items: center;
height: 40px;
margin-right: 20px;
span {
margin-right: 10px;
}
.customName {
width: 120px;
margin-right: 10px;
}
.statAnalysisSelect {
width: 200px;
:deep(.el-select__wrapper) {
height: 40px;
}
:deep(.el-input__inner) {
height: 38px;
}
}
}
.timeColumns {
height: calc(100% - 480px);
overflow-y: auto;
.moduleRow {
.item {
position: relative;
margin: 0px 12px 12px;
padding: 12px 12px 8px;
border-radius: 20px;
box-shadow: 0px 0px 12px rgba(0, 0, 0, 0.12);
.topLeft {
display: flex;
}
.selectPart {
width: 388px;
justify-content: left;
margin: 4px;
span {
white-space: nowrap;
}
.statAnalysisSelectattributes {
width: 320px;
height: 30px;
}
.statAnalysisSelect {
height: 30px;
:deep(.el-select__wrapper) {
height: 30px;
}
:deep(.el-input__inner) {
height: 28px;
}
}
.customName {
height: 30px;
}
}
}
}
.removeModule {
position: absolute;
top: 6px;
right: 8px;
color: rgba(0, 0, 0, 0);
cursor: pointer;
transition: color 0.5s;
}
&:hover {
.removeModule {
color: rgba(0, 0, 0, 0.55);
border: 1px solid rgba(0, 0, 0, 0.55);
border-radius: 50px;
}
}
.headerPart {
padding: 2px 20px;
}
.topLeft {
display: flex !important;
}
}
#myEChart {
width: 100%;