map/ui/dasadmin/src/views/backend/WindBlower/index.vue

1735 lines
59 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>
<el-dialog v-model="visible" :title="subSystemName" width="1020" :before-close="handleClose" :show-close="false">
<div class="radioItem">
<el-radio-group v-model="dialogradioactiveName">
<el-radio :value="138">模拟量</el-radio>
<el-radio :value="140">状态量</el-radio>
</el-radio-group>
</div>
<div class="dialogContent">
<div class="PitchPart">
<el-row :gutter="20">
<div class="Pitchitem" v-for="item in subSystemDataList" :key="item.name">
<span class="PitchitemLeft">{{ item.name }}</span>
<span class="PitchitemRight">{{ item.value }}</span>
</div>
</el-row>
</div>
</div>
<template #footer>
<div class="dialog-footer">
<el-button type="primary">导出</el-button>
<el-button @click="visible = false">取消</el-button>
</div>
</template>
</el-dialog>
<div class="windBlower" ref="windBlower">
<el-row :gutter="10">
<el-col :md="24" :lg="6" :span="6">
<div class="cardContentLeft">
<!--实时预览-->
<div class="overview">
<div class="cardLabel">实时预览</div>
<div class="overviewDataSection" ref="listContainer">
<div class="overviewDataSectionItem">
<span class="realLeft">网侧A相电压</span>
<span class="reafRight">{{ overviewData.iul1_690v }}</span>
</div>
<div class="overviewDataSectionItem">
<span class="realLeft">网侧B相电压</span>
<span class="reafRight">{{ overviewData.iul2_690v }}</span>
</div>
<div class="overviewDataSectionItem">
<span class="realLeft">网侧C相电压</span>
<span class="reafRight">{{ overviewData.iul3_690v }}</span>
</div>
<div class="overviewDataSectionItem">
<span class="realLeft">有功功率:</span>
<span class="reafRight">{{ overviewData.igenpower }}</span>
</div>
<div class="overviewDataSectionItem">
<span class="realLeft">无功功率:</span>
<span class="reafRight">{{ overviewData.ireactivepower }}</span>
</div>
<div class="overviewDataSectionItem">
<span class="realLeft">功率因素:</span>
<span class="reafRight">{{ overviewData.icosphi }}</span>
</div>
<div class="overviewDataSectionItem">
<span class="realLeft">主轴承温度A</span>
<span class="reafRight">{{ overviewData.itemprotorbeara_1sec }}</span>
</div>
<div class="overviewDataSectionItem">
<span class="realLeft">主轴承温度B</span>
<span class="reafRight">{{ overviewData.itemprotorbeara_1sec }}</span>
</div>
<div class="overviewDataSectionItem">
<span class="realLeft">总扭缆角度:</span>
<span class="reafRight">{{ overviewData.icabletwisttotal }}</span>
</div>
<div class="overviewDataSectionItem">
<span class="realLeft">发动机驱动侧轴承温度:</span>
<span class="reafRight">{{ overviewData.itempgenbearde_1sec }}</span>
</div>
<div class="overviewDataSectionItem">
<span class="realLeft">发动机非驱动侧轴承温度:</span>
<span class="reafRight">{{ overviewData.itempgenbearnde_1sec }}</span>
</div>
</div>
<div class="overviewDataBtn">
<el-icon color="#0064AA" @click="() => (overviewSlotData.visible = true)"><DArrowRight /></el-icon>
</div>
</div>
<!--功率趋势-->
<div class="power">
<div class="cardLabel">功率趋势</div>
<div class="chartBox">
<div class="power-chart" ref="powerChartRef"></div>
</div>
</div>
</div>
</el-col>
<el-col :md="24" :lg="12" :span="12">
<div class="cardContentCenter">
<!--风机控制-->
<div class="controlBackgroundImg">
<div class="control-type">
<el-tag v-if="realTimeData.locked === 1" class="control-tag control-tag-left" type="primary">已锁定</el-tag>
<el-tag class="control-tag" type="primary">{{ realTimeDataState }}</el-tag>
</div>
<div class="btnLeft">
<el-button
@click="sendCommand('setTurbineFastStart')"
v-if="realTimeData.iturbineoperationmode !== 16"
class="control-btn"
type="primary"
>启动</el-button
>
<el-button @click="sendCommand('setTurbineStop')" v-else class="control-btn" type="primary">停机</el-button>
<el-button @click="sendCommand('setTurbineResetStatusCode')" class="control-btn" type="primary">复位</el-button>
<el-button @click="sendManualCommand(1)" v-if="realTimeData.locked !== 1" class="control-btn" type="primary"
>锁定</el-button
>
<el-button @click="sendManualCommand(0)" v-else class="control-btn" type="primary">解锁</el-button>
</div>
<el-tooltip content="变桨/轮毂系统">
<div @click="openSubSystem(1)" class="dot index-1"></div>
</el-tooltip>
<el-tooltip content="轴承/传动链/齿轮箱系统">
<div @click="openSubSystem(2)" class="dot index-2"></div>
</el-tooltip>
<el-tooltip content="发电机系统">
<div @click="openSubSystem(3)" class="dot index-3"></div>
</el-tooltip>
<el-tooltip content="机舱系统">
<div @click="openSubSystem(4)" class="dot index-4"></div>
</el-tooltip>
<el-tooltip content="控制系统">
<div @click="openSubSystem(5)" class="dot index-5"></div>
</el-tooltip>
<el-tooltip content="环境/气象系统">
<div @click="openSubSystem(6)" class="dot index-6"></div>
</el-tooltip>
</div>
<div class="Parameters">
<div class="Parameters-item">
<img src="~assets/WindBlower/power.png" />
<p class="Parameters-font">{{ realTimeDataForSingle.igenpower }}</p>
<p>有功功率(MW)</p>
</div>
<div class="Parameters-item">
<img src="~assets/WindBlower/generator.png" />
<p class="Parameters-font">{{ realTimeDataForSingle.igenspeed }}</p>
<p>发电机转速(RPM)</p>
</div>
<div class="Parameters-item">
<img src="~assets/WindBlower/pitch.png" />
<p class="Parameters-font">{{ realTimeDataForSingle.ipitchangle }}</p>
<p>变桨角度(°)</p>
</div>
<div class="Parameters-item">
<img src="~assets/WindBlower/speed.png" />
<p class="Parameters-font">{{ realTimeDataForSingle.iwindspeed }}</p>
<p>风速(m/s)</p>
</div>
<div class="Parameters-item">
<img src="~assets/WindBlower/direction.png" />
<p class="Parameters-font">{{ realTimeDataForSingle.iwinddirection }}</p>
<p>风向(°)</p>
</div>
</div>
<div class="chartPart">
<div class="chartPart-item">
<div class="chartParm" ref="temperatureChartRef1"></div>
</div>
<div class="chartPart-item">
<div class="chartParm" ref="temperatureChartRef2"></div>
</div>
<div class="chartPart-item">
<div class="chartParm" ref="temperatureChartRef3"></div>
</div>
<div class="chartPart-item item_bar">
<div class="frequencyChart" ref="frequencyChartRef"></div>
</div>
</div>
</div>
</el-col>
<el-col :md="24" :lg="6" :span="6" style="background: #f5f5f5">
<div class="cardContentRight">
<!--发电量概况-->
<div class="summarize">
<div class="cardLabel">发电量概况</div>
<div class="summarize-panel-list">
<div class="summarize-panel">
<div class="summarize-panel-pic">
<img src="~assets/dashboard/fdl1.png" alt="" />
</div>
<div class="summarize-panel-base">
<div>
<span class="content-number">{{ realTimeDataForSingle.ikwhthisday }}</span>
</div>
<div>kWh</div>
<div>日发电量</div>
</div>
</div>
<div class="summarize-panel">
<div class="summarize-panel-pic">
<img src="~assets/dashboard/fdl2.png" alt="" />
</div>
<div class="summarize-panel-base">
<div>
<span class="content-number">{{ realTimeDataForSingle.monthprodenergy }}</span>
</div>
<div>万kWh</div>
<div>月发电量</div>
</div>
</div>
<div class="summarize-panel">
<div class="summarize-panel-pic">
<img src="~assets/dashboard/fdl3.png" alt="" />
</div>
<div class="summarize-panel-base">
<div>
<span class="content-number">{{ realTimeDataForSingle.yearprodenergy }}</span>
</div>
<div>万kWh</div>
<div>年发电量</div>
</div>
</div>
<div class="summarize-panel">
<div class="summarize-panel-pic">
<img src="~assets/dashboard/fdl4.png" alt="" />
</div>
<div class="summarize-panel-base">
<div>
<span class="content-number">{{ realTimeDataForSingle.ikwhoverall }}</span>
</div>
<div>万kWh</div>
<div>总发电量</div>
</div>
</div>
</div>
</div>
<!--发电量趋势-->
<div class="trend">
<div class="cardLabel">发电量趋势</div>
<el-radio-group class="trendTabs" v-model="trendChartType" @change="tabhandleClick">
<el-radio-button label="日" value="day" />
<el-radio-button label="月" value="month" />
</el-radio-group>
<div class="trend-chart" ref="trendChartRef"></div>
</div>
<!--实时告警-->
<div class="alarm" style="margin-bottom: 0">
<div class="cardLabel">实时告警</div>
<el-table
:data="tableData"
class="tablePart"
height="calc(100% - 38px)"
ref="myTable"
@mouseover.native="clearScroll"
@mouseleave.native="createScroll"
>
<el-table-column fixed prop="time" label="时间" />
<el-table-column prop="alertcontent" label="告警信息" />
</el-table>
</div>
</div>
</el-col>
</el-row>
</div>
<el-dialog v-model="overviewSlotData.visible" title="实时预览">
<template #header>
<div class="overviewSlot">
<span style="font-size: 20px">实时预览</span>
<div class="radioForOverviewType">
<el-radio-group v-model="overviewSlotData.type">
<el-radio value="138">模拟量</el-radio>
<el-radio value="140">状态量</el-radio>
</el-radio-group>
</div>
</div>
</template>
<Overview v-bind="overviewSlotData"></Overview>
</el-dialog>
</template>
<script setup lang="ts">
import { nextTick, onActivated, onMounted, reactive, ref, computed, onBeforeMount, onUnmounted, VNode, VNodeRef } from 'vue'
import * as echarts from 'echarts'
import { useEventListener } from '@vueuse/core'
import { useI18n } from 'vue-i18n'
import { DArrowRight } from '@element-plus/icons-vue'
import { getRealValueListReq, getRealValueRangeReq } from '/@/api/backend/deviceModel/request'
import { getModelAttributeListReq } from '/@/api/backend/deviceModel/request'
import { useRoute } from 'vue-router'
import Overview from './overview.vue'
import { TableInstance } from 'element-plus'
import { dayjs, ElMessage, ElMessageBox } from 'element-plus'
import { getRealTimeState, getCutDecimalsValue } from '/@/views/backend/equipment/airBlower/utils'
import { sendCommandReq, sendManualCommandReq } from '/@/api/backend/control/request'
import { getAlarmListReq } from '/@/api/backend/alarms/request'
import { useEnumStore } from '/@/stores/enums'
const enumStore = useEnumStore()
const route = useRoute()
const { t } = useI18n()
const windBlower = ref()
const computedHeight = reactive({
powerHeight: '305px',
centerHeight: '1100px',
alarmHeight: '360px',
})
const sizeChange = () => {
const rect = windBlower.value?.getBoundingClientRect()
if (!rect) return
computedHeight.powerHeight = rect.height - 626 + 'px'
computedHeight.alarmHeight = rect.height - 570 + 'px'
computedHeight.centerHeight = rect.height - 20 + 'px'
}
let timer: any = null
let myTable = ref<TableInstance>()
const overviewData = reactive({
iul1_690v: '-',
iul2_690v: '-',
iul3_690v: '-',
igenpower: '-',
ireactivepower: '-',
icosphi: '-',
itemprotorbeara_1sec: '-',
itemprotorbearb_1sec: '-',
icabletwisttotal: '-',
itempgenbearde_1sec: '-',
itempgenbearnde_1sec: '-',
})
const realTimeDataForSingle = ref<any>({
ikwhthisday: '-',
ikwhoverall: '-',
igenpower: '-',
igenspeed: '-',
ipitchangle: '',
iwindspeed: '-',
iwinddirection: '-',
monthprodenergy: '-',
yearprodenergy: '-',
})
const state: {
charts: { powerChart: any; temperatureChart1: any; temperatureChart2: any; temperatureChart3: any; frequencyChart: any; trendChart: any }
remark: string
workingTimeFormat: string
pauseWork: boolean
} = reactive({
charts: { powerChart: null, temperatureChart1: null, temperatureChart2: null, temperatureChart3: null, frequencyChart: null, trendChart: null },
remark: 'dashboard.Loading',
workingTimeFormat: '',
pauseWork: false,
})
const powerChartRef = ref<VNodeRef>()
const powerChartData: { time: any; values: any } = {
time: {},
values: {},
}
const initpowerChart = () => {
const powerChart = state.charts.powerChart ?? echarts.init(powerChartRef.value as unknown as HTMLElement)
const option = {
grid: {
top: 50,
right: 23,
bottom: 10,
left: 18,
containLabel: true,
},
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow',
},
},
xAxis: {
type: 'category',
axisLine: {
show: true,
lineStyle: {
color: '#dadada',
width: 1,
type: 'solid',
},
},
axisLabel: {
//x轴文字的配置
show: true,
color: '#4E5969',
interval: 'auto',
//rotate: 45
},
splitLine: {
//分割线配置
show: false,
lineStyle: {
color: '#999999',
},
},
data: powerChartData.time.iTheoreticalPower,
},
yAxis: [
{
type: 'value',
name: '功率MW',
nameTextStyle: {
color: '#4E5969',
},
axisLine: {
show: false,
lineStyle: {
color: '#dadada',
width: 0,
type: 'solid',
},
},
axisLabel: {
//x轴文字的配置
show: true,
color: '#4E5969',
},
axisTick: { show: false },
splitLine: {
interval: 50,
lineStyle: {
type: 'dashed',
color: '#dadada',
},
},
},
{
type: 'value',
name: '风速m/s',
nameTextStyle: {
color: '#4E5969',
},
axisLine: {
show: false,
lineStyle: {
color: '#dadada',
width: 0,
type: 'solid',
},
},
axisLabel: {
//x轴文字的配置
show: true,
color: '#4E5969',
},
axisTick: { show: false },
splitLine: {
interval: 50,
lineStyle: {
type: 'dashed',
color: '#dadada',
},
},
},
],
legend: {
data: ['有功功率', '理论有功功率', '风速'],
textStyle: {
color: '#73767a',
},
},
series: [
{
name: '有功功率',
type: 'line',
barWidth: 20,
itemStyle: {
color: '#00A7EB',
barBorderRadius: 2,
},
smooth: 0.6,
symbol: 'none',
data: powerChartData.values?.iGenPower ?? [],
},
{
name: '理论有功功率',
type: 'line',
barWidth: 20,
itemStyle: {
color: '#62bd25',
barBorderRadius: 2,
},
smooth: 0.6,
symbol: 'none',
data: powerChartData.values?.iTheoreticalPower ?? [],
},
{
name: '风速',
type: 'line',
yAxisIndex: 1,
itemStyle: {
color: '#FF7E00',
barBorderRadius: 2,
},
smooth: 0.6,
symbol: 'none',
/*areaStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{
offset: 0,
color: 'rgba(255,126,0,0.8)'
}
])
},*/
data: powerChartData.values?.iWindSpeed ?? [],
},
],
}
powerChart.setOption(option)
state.charts.powerChart = powerChart
}
const TrendDataForDay: {
currentPeriod: {
time: string[]
value: number[]
}
samePeriod: {
time: string[]
value: number[]
}
} = {
currentPeriod: {
time: [],
value: [],
},
samePeriod: {
time: [],
value: [],
},
}
const TrendDataForMonth = [
{
currentPeriod: 26.3,
samePeriod: 53.5,
generationTime: '2024-10-01',
},
{
currentPeriod: 36.3,
samePeriod: 53.5,
generationTime: '2024-10-02',
},
{
currentPeriod: 46.3,
samePeriod: 53.5,
generationTime: '2024-10-03',
},
]
const trendChartRef = ref<VNodeRef>()
const trendChartType = ref<'day' | 'month'>('day')
const tabhandleClick = () => {
state.charts.trendChart.clear()
nextTick(() => {
initTrendChart(trendChartType.value)
})
}
const initTrendChart = (type: 'day' | 'month') => {
const currentPeriod: number[] = type === 'day' ? TrendDataForDay.currentPeriod.value : TrendDataForMonth.map((item) => item.currentPeriod)
const samePeriod: number[] = type === 'day' ? TrendDataForDay.samePeriod.value : TrendDataForMonth.map((item) => item.samePeriod)
const xAxisdata: string[] = type === 'day' ? TrendDataForDay.currentPeriod.time : TrendDataForMonth.map((item) => item.generationTime)
const trendChart = state.charts?.trendChart ?? echarts.init(trendChartRef.value as unknown as HTMLElement)
const option = {
grid: {
top: 30,
right: 10,
bottom: 20,
left: 25,
borderColor: '#dadada',
},
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow',
},
},
xAxis: {
type: 'category',
axisLine: {
show: true,
lineStyle: {
color: '#dadada',
width: 1,
type: 'solid',
},
},
axisLabel: {
//x轴文字的配置
show: true,
color: '#4E5969',
},
splitLine: {
//分割线配置
show: false,
lineStyle: {
color: '#999999',
},
},
data: xAxisdata,
},
yAxis: [
{
type: 'value',
name: 'kWh',
nameTextStyle: {
color: '#4E5969',
},
axisLine: {
show: false,
lineStyle: {
color: '#dadada',
width: 0,
type: 'solid',
},
},
axisLabel: {
//x轴文字的配置
show: true,
color: '#4E5969',
},
axisTick: { show: false },
splitLine: {
interval: 50,
lineStyle: {
type: 'dashed',
color: '#dadada',
},
},
},
],
legend: {
data: ['本期', '同期'],
textStyle: {
color: '#73767a',
},
},
series: [
{
name: '本期',
data: currentPeriod,
type: 'bar',
smooth: true,
itemStyle: {
color: '#0277B3',
},
},
{
name: '同期',
data: samePeriod,
type: 'bar',
smooth: true,
itemStyle: {
color: '#00A096',
},
},
],
}
trendChart.setOption(option)
state.charts.trendChart = trendChart
}
const temperatureChartsData: { name: string; value: string | number }[] = [
{
name: '环境温度(°C)',
value: 56,
},
{
name: '舱内温度(°C)',
value: 56,
},
{
name: '环境温度(°C)',
value: 56,
},
]
const temperatureChartRef1 = ref<VNodeRef>()
const temperatureChartRef2 = ref<VNodeRef>()
const temperatureChartRef3 = ref<VNodeRef>()
const initTemperatureChart = () => {
const temperatureChart1 = state.charts.temperatureChart1 ?? echarts.init(temperatureChartRef1.value as unknown as HTMLElement)
const temperatureChart2 = state.charts.temperatureChart2 ?? echarts.init(temperatureChartRef2.value as unknown as HTMLElement)
const temperatureChart3 = state.charts.temperatureChart3 ?? echarts.init(temperatureChartRef3.value as unknown as HTMLElement)
const options = []
for (let i = 0; i < temperatureChartsData.length; i++) {
const option = {
grid: {
top: 30,
//right: 100,
bottom: 0,
left: -100,
containLabel: true,
},
xAxis: {
type: 'category',
show: false,
},
yAxis: {
type: 'value',
show: false,
},
series: [
{
name: temperatureChartsData[i].name,
data: [temperatureChartsData[i].value],
type: 'bar',
label: {
show: true,
align: 'center',
formatter: `{a|{c}}\n{b|{a}}`,
rich: {
a: {
color: '#333333',
fontSize: 22,
},
b: {
color: '#4E5969',
fontSize: 14,
},
},
position: 'insideBottom',
offset: [62, 0],
},
itemStyle: {
color: '#048bd2',
borderRadius: [0, 0, 4, 4],
},
barWidth: 20,
},
{
show: true,
type: 'bar',
barGap: '-100%',
barWidth: 20, // 统计条宽度
itemStyle: {
normal: {
color: '#048bd2',
opacity: 0.2,
borderRadius: 4,
},
},
z: 1,
data: [100],
},
],
aria: {
enabled: true,
decal: {
show: true,
decals: {
symbol: 'rect',
symbolSize: 3,
dashArrayX: 3,
rotation: 45,
color: 'rgba(0, 0, 0, 0.1)',
},
},
},
}
options.push(option)
}
temperatureChart1.setOption(options[0])
state.charts.temperatureChart1 = temperatureChart1
temperatureChart2.setOption(options[1])
state.charts.temperatureChart2 = temperatureChart2
temperatureChart3.setOption(options[2])
state.charts.temperatureChart3 = temperatureChart3
}
var frequencyData: {
name: string[]
value: number[]
} = {
name: [
'0~22.5',
'22.5~45',
'45~67.5',
'67.5~90',
'90~112.5',
'112.5~135',
'135~157.5',
'157.5~180',
'180~202.5',
'202.5~225',
'225~247.5',
'247.5~270',
'270~292.5',
'292.5~315',
'315~337.5',
'337.5~360',
],
value: [],
}
const frequencyChartRef = ref()
const initFrequencyChart = () => {
const frequencyChart = state.charts.frequencyChart ?? echarts.init(frequencyChartRef.value as unknown as HTMLElement)
// const seriesdata: any = frequencyData.map((item) => item.value)
const option = {
title: [
{
text: '风频图',
textStyle: {
color: '#4E5969',
fontSize: 14,
fontFamily: 'PingFangSC-Semibold',
},
padding: 15,
},
],
polar: {
radius: [0, '70%'],
},
radiusAxis: {
axisLine: {
show: true,
lineStyle: {
color: '#dadada',
},
},
axisLabel: {
show: true,
color: 'rgba(0,0,0,0.45)',
fontSize: 10,
},
axisTick: {
show: true,
lineStyle: {
color: '#dadada',
},
},
max: 100,
},
angleAxis: {
min: 0,
interval: 22.5,
startAngle: 90,
type: 'category',
axisLine: {
show: true,
lineStyle: {
color: '#dadada',
},
},
axisLabel: {
show: true,
color: 'rgba(0,0,0,0.45)',
fontSize: 10,
boundaryGap: false,
formatter: function (value: string) {
const reg = /.*(?=\~)/
return value.match(reg)![0]
},
},
axisTick: {
show: false,
lineStyle: {
color: '#dadada',
},
},
data: frequencyData.name,
//startAngle: 75
},
tooltip: {},
series: {
type: 'bar',
data: frequencyData.value,
coordinateSystem: 'polar',
itemStyle: {
color: '#0277B3',
},
// label: {
// show: true,
// position: 'middle',
// formatter: '{b}: {c}',
// },
},
animation: false,
}
frequencyChart.setOption(option)
state.charts.frequencyChart = frequencyChart
}
const echartsResize = () => {
nextTick(() => {
const chartKeys = Object.keys(state.charts) as Array<keyof typeof state.charts>
chartKeys.forEach((key) => {
state.charts[key].resize()
})
})
}
onActivated(() => {
echartsResize()
})
const tableData = ref<{ time: string; alertcontent: string }[]>([])
const clearScroll = () => {
clearInterval(timer)
timer = null
}
const createScroll = () => {
clearScroll()
const table = myTable.value!.layout.table.refs
const tableWrapper = table.bodyWrapper.firstElementChild!.firstElementChild
timer = setInterval(() => {
tableWrapper!.scrollTop += 1
if (tableWrapper!.clientHeight + tableWrapper!.scrollTop == tableWrapper!.scrollHeight) {
tableWrapper!.scrollTop = 0
}
}, 30)
}
const realTimeData = ref<any>({
iturbineoperationmode: 1111,
locked: 0,
})
const realTimeDataState = computed(() => {
switch (realTimeData.value.iturbineoperationmode) {
case 20:
return '并网'
case 10:
return '维护'
case 8:
return '限功率运行'
case 0:
return '离线'
case 16:
return '启动'
case 6:
return '正常停机'
case 1:
return '紧急停机'
case 2:
return '停机'
case 11:
return '待机'
case 1110:
return '解缆状态'
case 1111:
return '电网故障停机'
case 1112:
return '安全链停机'
}
})
const getModelList = () => {
return new Promise((resolve) => {
getModelAttributeListReq({ iotModelId: route.query.iotModelId as string }).then((res) => {
resolve(res.rows)
})
})
}
const getRealTimeData = () => {
return new Promise((resolve) => {
getRealValueListReq([{ deviceId: route.query.irn as string }]).then((res) => {
resolve(res.data?.[route.query.irn as string])
})
})
}
const createRealTimeData = async () => {
try {
const modelList: any = await getModelList()
const realData: any = await getRealTimeData()
realTimeData.value.iturbineoperationmode = getRealTimeState(realData)
realTimeData.value.locked = realData.locked
temperatureChartsData[0].value = getCutDecimalsValue(realData.itempoutdoor_1sec)
temperatureChartsData[1].value = getCutDecimalsValue(realData.itempnacelle_1sec)
temperatureChartsData[2].value = getCutDecimalsValue(realData.itempoutdoor_1sec)
const ipitchangle = Math.min(realData.ipitchangle1, realData.ipitchangle2, realData.ipitchangle3)
realTimeDataForSingle.value.ipitchangle = ipitchangle / 1 === 0 ? ipitchangle : Math.floor(ipitchangle * 1000) / 1000
const overviewDatakeys: any = Object.keys(overviewData)
const sigleDataKeys: any = Object.keys(realTimeDataForSingle.value)
const dataFor138And139: { name: string; value: string }[] = []
const dataFor140: { name: string; value: string }[] = []
const realDataForSub: any = [
{ type138: [], type140: [] },
{ type138: [], type140: [] },
{ type138: [], type140: [] },
{ type138: [], type140: [] },
{ type138: [], type140: [] },
{ type138: [], type140: [] },
]
modelList.forEach((item: any) => {
const realVal = realData[item.attributeCode.toLowerCase()]
let val = getCutDecimalsValue(realVal)
if (enumStore.keys.includes(item.attributeCode)) {
val = enumStore.data[item.attributeCode][val]
}
if (sigleDataKeys.includes(item.attributeCode.toLowerCase())) {
realTimeDataForSingle.value[item.attributeCode.toLowerCase()] = val === '-' ? val : val
}
if (overviewDatakeys.includes(item.attributeCode.toLowerCase())) {
overviewData[item.attributeCode.toLowerCase() as keyof typeof overviewData] = val === '-' ? val : val + item.unit
}
const showData = {
name: item.attributeName,
value: val === '-' ? val : val + item.unit,
}
if (item.attributeType === 138 || item.attributeType === 139) {
dataFor138And139.push({
name: item.attributeName,
value: val === '-' ? val : val + item.unit,
})
} else if (item.attributeType === 140) {
dataFor140.push(showData)
}
if (item.subSystem === '变桨系统' || item.subSystem === '轮毂') {
if (item.attributeType === 138 || item.attributeType === 139) {
realDataForSub[0].type138.push(showData)
} else if (item.attributeType === 140) {
realDataForSub[0].type140.push(showData)
}
} else if (item.subSystem === '轴承' || item.subSystem === '传动链' || item.subSystem === '齿轮箱') {
if (item.attributeType === 138 || item.attributeType === 139) {
realDataForSub[1].type138.push(showData)
} else if (item.attributeType === 140) {
realDataForSub[1].type140.push(showData)
}
} else if (item.subSystem === '发电机') {
if (item.attributeType === 138 || item.attributeType === 139) {
realDataForSub[2].type138.push(showData)
} else if (item.attributeType === 140) {
realDataForSub[2].type140.push(showData)
}
} else if (item.subSystem === '机舱') {
if (item.attributeType === 138 || item.attributeType === 139) {
realDataForSub[3].type138.push(showData)
} else if (item.attributeType === 140) {
realDataForSub[3].type140.push(showData)
}
} else if (item.subSystem === '控制系统') {
if (item.attributeType === 138 || item.attributeType === 139) {
realDataForSub[4].type138.push(showData)
} else if (item.attributeType === 140) {
realDataForSub[4].type140.push(showData)
}
} else if (item.subSystem === '环境' || item.subSystem === '气象') {
if (item.attributeType === 138 || item.attributeType === 139) {
realDataForSub[5].type138.push(showData)
} else if (item.attributeType === 140) {
realDataForSub[5].type140.push(showData)
}
}
})
overviewSlotData.type138 = dataFor138And139
overviewSlotData.type140 = dataFor140
realTimeForSubSystem.type1 = realDataForSub[0]
realTimeForSubSystem.type2 = realDataForSub[1]
realTimeForSubSystem.type3 = realDataForSub[2]
realTimeForSubSystem.type4 = realDataForSub[3]
realTimeForSubSystem.type5 = realDataForSub[4]
realTimeForSubSystem.type6 = realDataForSub[5]
} catch (err) {
console.log(err)
}
}
const subSystemName = ref('')
const visible = ref(false)
const handleClose = (done: () => void) => {
visible.value = false
}
const dialogradioactiveName = ref(138)
const realTimeForSubSystem = reactive<any>({
type1: null,
type2: null,
type3: null,
type4: null,
type5: null,
type6: null,
})
const curSubSystem = ref('type1')
const subSystemDataList = computed(() => {
const type = dialogradioactiveName.value === 138 ? 'type138' : 'type140'
return realTimeForSubSystem[curSubSystem.value][type]
})
const openSubSystem = (type: number) => {
switch (type) {
case 1:
subSystemName.value = '变桨/轮毂系统'
curSubSystem.value = 'type1'
break
case 2:
subSystemName.value = '轴承/传动链/齿轮箱系统'
curSubSystem.value = 'type2'
break
case 3:
subSystemName.value = '发电机系统'
curSubSystem.value = 'type3'
break
case 4:
subSystemName.value = '机舱系统'
curSubSystem.value = 'type4'
break
case 5:
subSystemName.value = '控制系统'
curSubSystem.value = 'type5'
break
case 6:
subSystemName.value = '环境/气象系统'
curSubSystem.value = 'type6'
break
}
visible.value = true
}
const overviewSlotData = reactive<{ visible: boolean; type: '138' | '140'; type138: any[]; type140: any[] }>({
visible: false,
type: '138',
type138: [],
type140: [],
})
let autoUpdateForSecondTimer: any = null
let autoUpdateTimerForMinuteTimer: any = null
let autoUpdateTimerForHourTimer: any = null
const autoUpdate = () => {
createRealTimeData()
initTemperatureChart()
if (!autoUpdateForSecondTimer) {
autoUpdateForSecondTimer = setInterval(() => {
createRealTimeData()
initTemperatureChart()
}, 2000)
}
if (!autoUpdateTimerForMinuteTimer) {
autoUpdateTimerForMinuteTimer = setInterval(() => {
getAllChartData(['power', 'frequency'])
}, 60000)
}
if (!autoUpdateTimerForHourTimer) {
autoUpdateTimerForHourTimer = setInterval(() => {
getAllChartData(['trend'])
}, 60000 * 30)
}
}
const getChartData = <T extends string = any>(params: {
startTime: number
endTime: number
attr: T[]
interval: string
}): Promise<{ times: { [K in T]: string[] }; val: { [K in T]: number[] } }> => {
return new Promise((resolve) => {
const data = {
startTime: params.startTime,
endTime: params.endTime,
devices: [
{
deviceId: route.query.irn as string,
attributes: params.attr,
},
],
interval: params.interval,
}
getRealValueRangeReq(data).then((res) => {
if (res.success) {
const data = res.data[route.query.irn as string]
const rangeKeys = Object.keys(data)
const times: any = {}
const val: any = {}
rangeKeys.forEach((key) => {
times[key] = []
val[key] = []
data[key].times.forEach((item: number) => {
times[key].push(dayjs(item).format('HH:mm'))
})
data[key].values.forEach((item: number) => {
val[key].push(item)
})
})
resolve({ times, val })
}
})
})
}
const getThisDayChartData = () => {
return new Promise((resolve) => {
getChartData<'iGenPower' | 'iTheoreticalPower' | 'iWindSpeed'>({
startTime: new Date(new Date().toLocaleDateString()).getTime(),
endTime: Date.now(),
attr: ['iGenPower', 'iTheoreticalPower', 'iWindSpeed'],
interval: '5m',
}).then(({ times, val }) => {
powerChartData.time = { iGenPower: times.iGenPower, iTheoreticalPower: times.iTheoreticalPower, iWindSpeed: times.iWindSpeed }
powerChartData.values = { iGenPower: val.iGenPower, iTheoreticalPower: val.iTheoreticalPower, iWindSpeed: val.iWindSpeed }
resolve(true)
})
})
}
const getLastYearTodayTimestamp = () => {
const now = new Date()
const lastYear = now.getFullYear() - 1
const lastYearToday = new Date(lastYear, now.getMonth(), now.getDate())
return lastYearToday.getTime()
}
const getLastYearChartData = () => {
return new Promise((resolve) => {
const start = getLastYearTodayTimestamp()
const end = new Date()
end.setFullYear(end.getFullYear() - 1)
getChartData<'iKWhThisDay'>({
startTime: start,
endTime: end.getTime(),
attr: ['iKWhThisDay'],
interval: '1h',
}).then(({ times, val }) => {
TrendDataForDay.samePeriod.time = times.iKWhThisDay
TrendDataForDay.samePeriod.value = val.iKWhThisDay
resolve(true)
})
})
}
const getThisDayChartForHourData = () => {
return new Promise((resolve) => {
getChartData<'iKWhThisDay'>({
startTime: new Date(new Date().toLocaleDateString()).getTime(),
endTime: Date.now(),
attr: ['iKWhThisDay'],
interval: '1h',
}).then(({ times, val }) => {
TrendDataForDay.currentPeriod.time = times.iKWhThisDay
TrendDataForDay.currentPeriod.value = val.iKWhThisDay
resolve(true)
})
})
}
const getThisDayChartDataForMinute = () => {
return new Promise((resolve) => {
getChartData<'iWindDirection'>({
startTime: new Date(new Date().toLocaleDateString()).getTime(),
endTime: Date.now(),
attr: ['iWindDirection'],
interval: '1m',
}).then(({ val }) => {
const len = val.iWindDirection.length
const result: number[] = new Array(16).fill(0)
val.iWindDirection.forEach((item: number) => {
item = item < 0 ? 360 + item : item > 360 ? 360 : item
const divisor = Math.ceil(item / 22.5) - 1
result[divisor] += 1
})
const percent = result.map((item) => Math.floor((item / len) * 100000) / 1000)
frequencyData.value = percent
resolve(true)
})
})
}
const getAllChartData = (type: ('power' | 'trend' | 'frequency')[] = ['power', 'trend', 'frequency']) => {
if (type.includes('power')) {
getThisDayChartData().then(() => {
initpowerChart()
initTemperatureChart()
})
}
if (type.includes('trend')) {
const trendDataForLastYear = getLastYearChartData()
const trendDataForThisDay = getThisDayChartForHourData()
Promise.all([trendDataForLastYear, trendDataForThisDay]).then(() => {
initTrendChart(trendChartType.value)
})
}
if (type.includes('frequency')) {
getThisDayChartDataForMinute().then((res) => {
initFrequencyChart()
})
}
}
const sendCommand = (type: 'setTurbineFastStart' | 'setTurbineStop' | 'setTurbineResetStatusCode') => {
const sendTypeEnum = {
setTurbineFastStart: '风机快速启动指令',
setTurbineStop: '风机停机指令',
setTurbineResetStatusCode: '风机复位故障代码指令',
}
ElMessageBox.confirm('确认发送' + sendTypeEnum[type] + '吗?', '', {
confirmButtonText: '确认',
cancelButtonText: '取消',
type: 'warning',
}).then(() => {
const serviceName = sendTypeEnum[type]
const optDesc = serviceName + ',设定值为1'
sendCommandReq({
deviceId: route.query.irn as string,
serviceCode: type,
serviceName,
optDesc,
opValue: 1,
}).then((res) => {
if (res.code == 200) {
ElMessage.success('指令发送成功')
} else {
ElMessage.error('指令发送失败')
}
})
})
}
const sendManualCommand = (type: 1 | 0) => {
const serviceName = type === 0 ? '风机解锁' : '风机锁定'
ElMessageBox.confirm('确认' + serviceName + '吗?', '', {
confirmButtonText: '确认',
cancelButtonText: '取消',
type: 'warning',
}).then(() => {
sendManualCommandReq({
deviceId: route.query.irn as string,
serviceCode: 'Locked',
serviceName,
optDesc: serviceName + ',设定值为:' + type,
opValue: type,
}).then((res) => {
if (res.code == 200) {
ElMessage.success('指令发送成功')
} else {
ElMessage.error('指令发送失败')
}
})
})
}
const getAlarmList = () => {
const start = dayjs().subtract(3, 'day').startOf('day').toDate().getTime()
const end = dayjs().endOf('day').toDate().getTime()
console.log(route.query.name)
getAlarmListReq({
startTime: start,
endTime: end,
deviceCode: [route.query.name],
}).then((res) => {
if (res.rows) {
tableData.value = res.rows.map((item: any) => {
return {
time: dayjs(item.eventTime).format('YYYY-MM-DD HH:mm:ss'),
alertcontent: item.eventText,
}
})
}
})
}
getAlarmList()
onMounted(() => {
window.addEventListener('resize', sizeChange)
sizeChange()
getAllChartData()
createScroll()
useEventListener(window, 'resize', echartsResize)
autoUpdate()
})
onUnmounted(() => {
window.removeEventListener('resize', sizeChange)
autoUpdateForSecondTimer && clearInterval(autoUpdateForSecondTimer)
autoUpdateTimerForHourTimer && clearInterval(autoUpdateTimerForHourTimer)
autoUpdateTimerForMinuteTimer && clearInterval(autoUpdateTimerForMinuteTimer)
const chartKeys = Object.keys(state.charts) as Array<keyof typeof state.charts>
chartKeys.forEach((key) => {
state.charts[key] && state.charts[key].dispose()
})
})
</script>
<style scoped lang="scss">
$marginNum: 10px;
$labelHeight: 38px;
@mixin cardDefaultStyle {
margin-top: $marginNum;
margin-bottom: $marginNum;
padding: 10px;
border-radius: 10px;
background-color: #fff;
}
@mixin cardlabel {
.cardLabel {
width: 100%;
height: $labelHeight;
font-size: 18px;
line-height: 18px;
font-weight: 600;
color: #4e5969;
}
}
.radioItem {
margin-top: -43px;
float: right;
}
.dialogContent {
max-height: 500px;
overflow-y: auto;
overflow-x: hidden;
.PitchPart {
.Pitchitem {
border: 1px solid #e1edf6;
display: flex;
justify-content: space-between;
margin-left: 15px;
width: 480px;
.PitchitemLeft {
display: flex;
align-items: center;
width: 325px;
min-height: 50px;
background: #f7f9fc;
padding-left: 20px;
font-size: 14px;
color: #4e5969;
letter-spacing: 0;
font-weight: 500;
word-break: break-all;
}
.PitchitemRight {
/*width: 180px;*/
height: 25px;
line-height: 50px;
padding-right: 20px;
font-size: 14px;
color: #4e5969;
letter-spacing: 0;
font-weight: 500;
}
}
}
}
.dialog-footer {
text-align: center;
.el-button {
width: 120px;
height: 40px;
}
}
.overviewSlot {
display: flex;
.radioForOverviewType {
margin-left: auto;
}
}
.windBlower {
width: 100%;
height: 100%;
min-height: 920px;
background-color: #f2f3f5;
// overflow: hidden;
.el-row{
width: calc(100% - 10px);
}
.cardContentLeft {
width: 100%;
.overview {
width: 100%;
@include cardDefaultStyle;
@include cardlabel;
.overviewDataSection {
width: 100%;
min-height: 495px;
.overviewDataSectionItem {
font-size: 14px;
color: #333333;
display: flex;
justify-content: space-between;
background: #f0f6ff;
border-radius: 4px;
width: 100%;
min-height: 40px;
line-height: 40px;
margin-bottom: 5px;
padding: 0 10px;
}
}
.overviewDataBtn {
display: flex;
justify-content: center;
align-items: end;
width: 100%;
height: 40px;
font-size: 22px;
.el-icon {
transform: rotate(90deg);
}
:hover {
cursor: pointer;
}
}
}
.power {
@include cardDefaultStyle;
@include cardlabel;
width: 100%;
min-height: 285px;
height: v-bind('computedHeight.powerHeight');
.chartBox {
width: 100%;
height: calc(100% - $labelHeight);
.power-chart {
width: 100%;
height: 100%;
}
}
}
}
.cardContentCenter {
@include cardDefaultStyle;
display: flex;
padding-bottom: 20px;
flex-direction: column;
justify-content: space-around;
width: 100%;
min-height: 900px;
height: v-bind('computedHeight.centerHeight');
.controlBackgroundImg {
position: relative;
width: 100%;
// height: 450px;
aspect-ratio: 43 / 24;
background: url('/@/assets/WindBlower/bg.png') no-repeat;
background-size: contain;
.control-type {
width: 100%;
display: flex;
.control-tag {
background: #0064aa;
border-radius: 0 8px 0 0;
min-width: 80px;
height: 40px;
color: #ffffff;
font-size: 20px;
border: none;
margin-left: auto;
}
.control-tag-left {
margin-left: 0;
border-radius: 8px 0 0 0;
background-color: #ff4949;
}
}
.btnLeft {
display: flex;
flex-direction: column;
align-items: flex-end;
position: absolute;
bottom: 0;
.el-button {
width: 80px;
height: 40px;
// background: rgb(0,100,170);
border-radius: 6px;
color: #ffffff;
border: none;
margin-bottom: 10px;
}
}
.dot {
position: absolute;
width: 15px;
height: 15px;
border-radius: 10px;
background-color: #ff7e00;
&:hover {
cursor: pointer;
}
&:nth-of-type(1) {
top: 25%;
left: 50%;
}
}
.index-1 {
left: 25%;
top: 60%;
}
.index-2 {
left: 52%;
top: 53%;
}
.index-3 {
left: 65%;
top: 60%;
}
.index-4 {
left: 83%;
top: 54%;
}
.index-5 {
left: 75%;
top: 28%;
}
.index-6 {
left: 73%;
top: 4%;
}
}
.Parameters {
margin-top: 10px;
width: 100%;
display: flex;
.Parameters-item {
margin: 5px;
width: 20%;
height: 160px;
background: #f0f6ff;
border-radius: 8px;
text-align: center;
color: #4e5969;
.Parameters-font {
font-size: 20px;
color: #333333;
}
}
}
.chartPart {
margin-top: auto;
width: 100%;
min-height: 220px;
flex-shrink: 1;
display: flex;
justify-content: space-between;
.chartPart-item {
margin: 5px;
width: 23%;
height: 100%;
background: #f0f6ff;
border-radius: 8px;
text-align: center;
color: #4e5969;
.chartParm {
width: 100%;
height: 100%;
min-height: 220px;
}
.frequencyChart {
width: 100%;
height: 100%;
}
}
.item_bar {
width: 31%;
}
}
}
.cardContentRight {
width: 100%;
height: 100%;
// min-height: 920px;
.summarize {
width: 100%;
@include cardDefaultStyle;
@include cardlabel;
.summarize-panel-list {
width: 100%;
display: flex;
justify-content: space-between;
}
.summarize-panel {
margin: 5px;
width: 25%;
display: flex;
flex-direction: column;
align-items: center;
background-color: #f0f6ff;
border-radius: 10px;
.summarize-panel-base {
width: 100%;
flex-direction: column;
align-items: center;
div {
display: flex;
justify-content: center;
font-size: 14px;
color: rgb(78, 89, 105);
.content-number {
color: #333333;
font-size: 20px;
}
}
}
}
}
.trend {
position: relative;
width: 100%;
height: 360px;
@include cardDefaultStyle;
@include cardlabel;
.trendTabs {
position: absolute;
right: 10px;
top: 5px;
}
.trend-chart {
width: 100%;
height: calc(100% - $labelHeight);
}
}
.alarm {
width: 100%;
min-height: 350px;
height: v-bind('computedHeight.alarmHeight');
@include cardDefaultStyle;
@include cardlabel;
}
}
}
</style>