diff --git a/api/app/lib/controllers/alarm/video.js b/api/app/lib/controllers/alarm/video.js index 6c9cf56..7fd42d8 100644 --- a/api/app/lib/controllers/alarm/video.js +++ b/api/app/lib/controllers/alarm/video.js @@ -81,7 +81,7 @@ async function alarmList (ctx) { const { clickHouse } = ctx.app.fs const { utils: { judgeSuper, anxinStrucIdRange } } = ctx.app.fs const { database: anxinyun } = clickHouse.anxinyun.opts.config - const { pepProjectId, keywordTarget, keyword, state, kindId, sustainTimeStart, sustainTimeEnd, limit, page, statusId, toExport } = ctx.query + const { pepProjectId, keywordTarget, keyword, state, kindId, sustainTimeStart, sustainTimeEnd, limit, page, statusId, toExport, startTime } = ctx.query let anxinStruc = await anxinStrucIdRange({ ctx, pepProjectId, keywordTarget, keyword @@ -107,6 +107,7 @@ async function alarmList (ctx) { cameraWhereOption.push(sql) } + let statusAlarmWhereOption = [] if (sustainTimeStart && sustainTimeEnd) { let momentStart = moment(sustainTimeStart).format('YYYY-MM-DD HH:mm:ss') @@ -128,6 +129,9 @@ async function alarmList (ctx) { if (statusId) { statusAlarmWhereOption.push(`camera_status_alarm.status_id = ${statusId}`) } + if (startTime) { + statusAlarmWhereOption.push(`camera_status_alarm.create_time >= '${moment(startTime).format('YYYY-MM-DD HH:mm:ss')}'`) + } const queryStr = ` SELECT diff --git a/api/app/lib/controllers/control/analysis.js b/api/app/lib/controllers/control/analysis.js index b2de347..d2aa9f2 100644 --- a/api/app/lib/controllers/control/analysis.js +++ b/api/app/lib/controllers/control/analysis.js @@ -405,8 +405,432 @@ async function problem (ctx) { } } +async function getAlarmData (ctx) { + try { + const { models } = ctx.fs.dc; + const { clickHouse } = ctx.app.fs + const { utils: { judgeSuper, anxinStrucIdRange } } = ctx.app.fs + const { database: anxinyun } = clickHouse.anxinyun.opts.config + + const { pepProjectId, startTime } = ctx.query + + let anxinStruc = await anxinStrucIdRange({ ctx, pepProjectId }) + let whereOption = [] + if (anxinStruc.length) { + const anxinStrucIds = anxinStruc.map(a => a.strucId) + whereOption.push(`alarms.StructureId IN (${anxinStrucIds.join(",")})`) + + if (startTime) { + whereOption.push(`alarms.StartTime >= '${moment(startTime).format('YYYY-MM-DD HH:mm:ss')}'`) + } + + const alarmRes = await clickHouse.dataAlarm.query(` + SELECT + alarm.alarms.State AS State, + SourceName, StartTime, EndTime,AlarmGroup,AlarmGroupUnit, + alarms.StructureId AS StructureId, + ${anxinyun}.t_structure.name AS StructureName + FROM + alarms + LEFT JOIN ${anxinyun}.t_structure + ON ${anxinyun}.t_structure.id = alarms.StructureId + ${whereOption.length ? 'WHERE ' + whereOption.join(' AND ') : ''} + + `).toPromise(); + + alarmRes.forEach(ar => { + ar.pomsProject = ( + anxinStruc.find(as => as.strucId == ar.StructureId) || + { + pomsProject: [ + // TODO: 开发临时添加 + ] + } + ).pomsProject + }) + + ctx.status = 200; + ctx.body = alarmRes + } else { + ctx.body = [] + } + ctx.status = 200; + } catch (error) { + ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`); + ctx.status = 400; + ctx.body = { + message: typeof error == 'string' ? error : undefined + } + } +} + +async function getAlarmVideo (ctx) { + try { + const { models } = ctx.fs.dc; + const { clickHouse } = ctx.app.fs + const { utils: { judgeSuper, anxinStrucIdRange } } = ctx.app.fs + const { database: anxinyun } = clickHouse.anxinyun.opts.config + const { pepProjectId, startTime } = ctx.query + + let anxinStruc = await anxinStrucIdRange({ ctx, pepProjectId }) + const anxinStrucIds = anxinStruc.map(a => a.strucId) + let statusAlarmWhereOption = [] + if (startTime) { + statusAlarmWhereOption.push(`camera_status_alarm.create_time >= '${moment(startTime).format('YYYY-MM-DD HH:mm:ss')}'`) + } + const queryStr = ` + SELECT + cameraAlarm.cameraId AS cameraId, + cameraAlarm.venderId AS venderId, + cameraAlarm.cameraSerialNo AS cameraSerialNo, + cameraAlarm.cameraChannelNo AS cameraChannelNo, + cameraAlarm.alarmId AS alarmId, + cameraAlarm.statusId AS statusId, + cameraAlarm.createTime AS createTime, + cameraAlarm.updateTime AS updateTime, + cameraAlarm.platform AS platform, + cameraAlarm.confirmContent AS confirmContent, + cameraAlarm.confirmTime AS confirmTime, + ${'cameraAlarm.autoRestore AS autoRestore,'} + camera_status_resolve.id AS resolveId, + camera_status.describe AS statusDescribe, + camera_status_resolve.resolve AS resolve, + "gbCamera".online AS cameraOnline, + secret_yingshi.token AS yingshiToken, + anxinIpc.t_video_ipc.name AS anxinIpcPosition, + anxinStation.id AS anxinStationId, + anxinStation.name AS anxinStationName, + anxinStruc.name AS strucName, + anxinStruc.id AS strucId + FROM + ( + SELECT + camera.id AS cameraId, + camera.gb_id AS gbId, + camera.vender_id AS venderId, + camera.yingshi_secret_id AS yingshiSecretId, + camera_status_alarm.id AS alarmId, + camera_status_alarm.create_time AS createTime, + camera_status_alarm.update_time AS updateTime, + camera_status_alarm.platform AS platform, + camera_status_alarm.status_id AS statusId, + camera_status_alarm.serial_no AS cameraSerialNo, + camera_status_alarm.channel_no AS cameraChannelNo, + camera_status_alarm.confirm AS confirmContent, + ${'camera_status_alarm.auto_restore AS autoRestore,'} + camera_status_alarm.confirm_time AS confirmTime + FROM camera_status_alarm + INNER JOIN camera + ON camera.serial_no = camera_status_alarm.serial_no + AND camera.channel_no = camera_status_alarm.channel_no + LEFT JOIN vender + ON vender.id = camera.vender_id + WHERE + camera.delete = '0' + AND camera.recycle_time is null + ${statusAlarmWhereOption.length ? 'AND ' + statusAlarmWhereOption.join(' AND ') : ''} + AND alarmId IN ( + SELECT camera_status_alarm.id AS alarmId + FROM camera_status_alarm + RIGHT JOIN ${anxinyun}.t_video_ipc + ON toString(${anxinyun}.t_video_ipc.channel_no) = camera_status_alarm.channel_no + AND ${anxinyun}.t_video_ipc.serial_no = camera_status_alarm.serial_no + ${`WHERE ${anxinyun}.t_video_ipc.structure IN (${anxinStrucIds.join(',')})`} + ) + ) AS cameraAlarm + LEFT JOIN camera_status + ON cameraAlarm.platform = camera_status.platform + AND cameraAlarm.statusId = camera_status.id + LEFT JOIN camera_status_resolve + ON camera_status_resolve.status_id = camera_status.id + LEFT JOIN "gbCamera" + ON "gbCamera".id = cameraAlarm.gbId + LEFT JOIN "secret_yingshi" + ON "secret_yingshi".id = cameraAlarm.yingshiSecretId + + LEFT JOIN ${anxinyun}.t_video_ipc AS anxinIpc + ON toString(anxinIpc.channel_no) = cameraAlarm.cameraChannelNo + AND anxinIpc.serial_no = cameraAlarm.cameraSerialNo + LEFT JOIN ${anxinyun}.t_structure AS anxinStruc + ON anxinStruc.id = anxinIpc.structure + AND anxinStruc.id IN (${anxinStrucIds.join(',')}) + + LEFT JOIN ${anxinyun}.t_video_ipc_station AS anxinIpcStation + ON anxinIpcStation.ipc = anxinIpc.id + LEFT JOIN ${anxinyun}.t_sensor AS anxinStation + ON anxinStation.id = anxinIpcStation.station + ` + const alarmRes = anxinStrucIds.length ? await clickHouse.vcmp.query( + queryStr + ).toPromise() : [] + console.log(queryStr); + let returnD = [] + let positionD = {} + // 每个设备一个告警 + for (let a of alarmRes) { + if (positionD[a.cameraId]) { + } else { + let d = { + createTime: a.createTime, + confirmTime: a.confirmTime, + statusId: a.statusId, + } + returnD.push(d) + positionD[a.cameraId] = { + positionReturnD: returnD.length - 1 + } + } + } + + ctx.status = 200; + ctx.body = returnD + + } catch (error) { + ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`); + ctx.status = 400; + ctx.body = { + message: typeof error == 'string' ? error : undefined + } + } +} + +async function getAlarmUse (ctx) { + try { + const models = ctx.fs.dc.models; + const { clickHouse } = ctx.app.fs + const { utils: { anxinStrucIdRange, pomsProjectRange } } = ctx.app.fs + const { pepProjectId, startTime } = ctx.query + + let pomsProject = await pomsProjectRange({ + ctx, pepProjectId, + keywordTarget: 'pepProject', + }) + const pomsProjectIds = pomsProject.map(p => p.id) + let findOption = { + distinct: true, + where: {}, + include: [{ + model: models.App, + where: {}, + attributes: { + exclude: ['projectId'] + }, + include: [{ + model: models.ProjectCorrelation, + where: {}, + attributes: { + exclude: ['createTime', 'createUser', 'anxinProjectId',] + }, + }] + }] + } + if (startTime) { + findOption.where.createTime = { $gt: moment(startTime).format('YYYY-MM-DD HH:mm:ss') } + } + findOption.include[0].include[0].where.id = { $in: pomsProjectIds } + const listRes = await models.AppAlarm.findAll(findOption) + for (let lr of listRes) { + if (lr.app && lr.app.projectCorrelations) { + for (let p of lr.app.projectCorrelations) { + let corProjectCorrelations = pomsProject.find(pr => pr.id == p.id) + if (corProjectCorrelations) { + p.dataValues = corProjectCorrelations + } + } + } + } + ctx.status = 200; + ctx.body = listRes + + } catch (error) { + ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`); + ctx.status = 400; + ctx.body = { + message: typeof error == 'string' ? error : undefined + } + } +} + +async function getStatisticOnline (ctx) { + try { + const { models } = ctx.fs.dc; + const { clickHouse } = ctx.app.fs + const { utils: { judgeSuper, anxinStrucIdRange } } = ctx.app.fs + const { database: anxinyun } = clickHouse.anxinyun.opts.config + const { pepProjectId } = ctx.query + + let anxinStruc = await anxinStrucIdRange({ ctx, pepProjectId }) + if (anxinStruc.length) { + const anxinStrucIds = anxinStruc.map(a => a.strucId) + // 查在线率 + const strucOnlineClient = ctx.app.fs.esclient.strucOnline + + const onlineRes = anxinStrucIds.length ? + await strucOnlineClient.search({ + index: strucOnlineClient.config.index, + type: strucOnlineClient.config.type, + body: { + "query": { + "bool": { + "filter": [ + { + "range": { + "collect_time": { + "gte": `${moment().subtract(32, 'hours').format('YYYY-MM-DDTHH:mm:ss.SSS')}Z`, + // "gte": "2023-08-24T08:00:00.000Z", + } + } + }, + { + "terms": { + "structure": anxinStrucIds, + // "structure": [1, 2, 3] + } + } + ] + } + }, + "sort": [ + { + "collect_time": { + "order": "asc" + } + } + ], + "size": 10000 + } + }) + : { + hits: { + hits: [] + } + } + + for (let struc of anxinStruc) { + let curOnline = + onlineRes.hits.hits + .filter((h) => h._source.structure == struc.strucId) + // .sort((a, b) => { + // return a._source.collect_time - b._source.collect_time + // }) + .map(s => { + return { + ...s._source, + rate: s._source.online / s._source.total * 100 + } + }) + struc.online = curOnline + } + + ctx.status = 200; + ctx.body = anxinStruc; + } else { + ctx.status = 200; + ctx.body = []; + } + + } catch (error) { + ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`); + ctx.status = 400; + ctx.body = { + message: typeof error == 'string' ? error : undefined + } + } +} + +async function getStrucSeries (ctx) { + try { + const { models } = ctx.fs.dc; + const { clickHouse } = ctx.app.fs + const { utils: { judgeSuper, anxinStrucIdRange } } = ctx.app.fs + const { database: anxinyun } = clickHouse.anxinyun.opts.config + const { pepProjectId } = ctx.query + + let anxinStruc = await anxinStrucIdRange({ ctx, pepProjectId }) + if (anxinStruc.length) { + const anxinStrucIds = anxinStruc.map(a => a.strucId) + // 查在线率 + const strucSeriesClient = ctx.app.fs.esclient.strucSeries + + const seriesRes = anxinStrucIds.length ? + await strucSeriesClient.search({ + index: strucSeriesClient.config.index, + type: strucSeriesClient.config.type, + body: { + // "query": { + // "bool": { + // "filter": [ + // { + // "range": { + // "collect_time": { + // "gte": `${moment().subtract(32, 'hours').format('YYYY-MM-DDTHH:mm:ss.SSS')}Z`, + // // "gte": "2023-08-24T08:00:00.000Z", + // } + // } + // }, + // { + // "terms": { + // "structure": anxinStrucIds, + // // "structure": [1, 2, 3] + // } + // } + // ] + // } + // }, + "sort": [ + { + "collect_time": { + "order": "asc" + } + } + ], + "size": 10000 + } + }) + : { + hits: { + hits: [] + } + } + + for (let struc of anxinStruc) { + let curSeries = + seriesRes.hits.hits + .filter((h) => h._source.structure == struc.strucId) + // .sort((a, b) => { + // return a._source.collect_time - b._source.collect_time + // }) + .map(s => { + return { + ...s._source, + } + }) + struc.series = curSeries + } + + ctx.status = 200; + ctx.body = anxinStruc; + } else { + ctx.status = 200; + ctx.body = []; + } + + } catch (error) { + ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`); + ctx.status = 400; + ctx.body = { + message: typeof error == 'string' ? error : undefined + } + } +} module.exports = { personnelApp, - problem + problem, + getAlarmData, + getAlarmVideo, + getAlarmUse, + getStatisticOnline, + getStrucSeries, + } \ No newline at end of file diff --git a/api/app/lib/controllers/control/data.js b/api/app/lib/controllers/control/data.js index 0b7d5e4..c254c25 100644 --- a/api/app/lib/controllers/control/data.js +++ b/api/app/lib/controllers/control/data.js @@ -418,6 +418,6 @@ module.exports = { getAppAlarmsAggDay, getVideoAlarmsAggDay, getAlarmsHandleStatistics, - getLatestDynamic + }; \ No newline at end of file diff --git a/api/app/lib/routes/control/index.js b/api/app/lib/routes/control/index.js index be4e73c..0087589 100644 --- a/api/app/lib/routes/control/index.js +++ b/api/app/lib/routes/control/index.js @@ -40,4 +40,18 @@ module.exports = function (app, router, opts) { //最新动态 app.fs.api.logAttr['GET/latest/dynamic'] = { content: '查询最新动态', visible: false }; router.get('/latest/dynamic', csData.getLatestDynamic); + + app.fs.api.logAttr['GET/alarm/data'] = { content: '查询数据告警', visible: false }; + router.get('/alarm/data', analysis.getAlarmData); + + app.fs.api.logAttr['GET/alarm/use'] = { content: '查询应用告警', visible: false }; + router.get('/alarm/use', analysis.getAlarmUse); + + app.fs.api.logAttr['GET/statisticOnline'] = { content: '查询数据在线率', visible: false }; + router.get('/StatisticOnline', analysis.getStatisticOnline); + + app.fs.api.logAttr['GET/strucSeries'] = { content: '查询数据在线率', visible: false }; + router.get('/strucSeries', analysis.getStrucSeries); + + }; \ No newline at end of file diff --git a/api/config.js b/api/config.js index 15fde3c..f097faa 100644 --- a/api/config.js +++ b/api/config.js @@ -141,6 +141,10 @@ const CAIYUN_KEY = process.env.CAIYUN_KEY || flags.caiyunKey || '1l0eNveMANMXEIJ const ES_PLATFORM_NAME = process.env.ES_PLATFORM_NAME || flags.esPlatformName || 'anxincloud' || 'anxinyun'; + +const ES_CONTINUITY_NAME = process.env.ES_CONTINUITY_NAME || flags.esContinuityName + || 'anxincloud' + || 'anxinyun'; const ANXINCLOUD_ES_NODES_REST = process.env.ANXINCLOUD_ES_NODES_REST || flags.es; // //business_key // const BUSINESS_KEY=process.env.BUSINESS_KEY||['160','161'] @@ -340,6 +344,12 @@ const product = { index: `${ES_PLATFORM_NAME}_online`, type: flags.esType ? flags.esType : '_doc' }, + strucSeries: { + //告警记录 + rootURL: ANXINCLOUD_ES_NODES_REST.split(','), + index: `${ES_CONTINUITY_NAME}_continue`, + type: flags.esType ? flags.esType : '_doc' + }, } } } diff --git a/web/client/src/components/setup.jsx b/web/client/src/components/setup.jsx index 36254c9..669d859 100644 --- a/web/client/src/components/setup.jsx +++ b/web/client/src/components/setup.jsx @@ -11,7 +11,7 @@ function Setup (props) { tableType, tableList, layout, //格式 - data=8 + data = 8 } = props; const [check, setCheck] = useState([]); @@ -107,7 +107,7 @@ function Setup (props) { key={itm.value} value={itm.value} style={layout == 'long' ? {} : checkboxcss} - disabled={ischeck(itm.value) || ['workbench', 'dynamic', 'handle', 'DeviceAccess','discovery'].includes(itm.value)} + disabled={ischeck(itm.value) || ['workbench', 'handle', 'DeviceAccess', 'discovery'].includes(itm.value)} > {itm.name} diff --git a/web/client/src/index.less b/web/client/src/index.less index b12da1d..cbf5f9a 100644 --- a/web/client/src/index.less +++ b/web/client/src/index.less @@ -1,79 +1,87 @@ // webpack (vite 用 alias 兼容了) // @import '~@douyinfe/semi-ui/dist/css/semi.min.css'; -@import '~perfect-scrollbar/css/perfect-scrollbar.css'; -@import '~nprogress/nprogress.css'; -@import '~simplebar-react/dist/simplebar.min.css'; - +@import "~perfect-scrollbar/css/perfect-scrollbar.css"; +@import "~nprogress/nprogress.css"; +@import "~simplebar-react/dist/simplebar.min.css"; *, *::before, *::after { - box-sizing: border-box; + box-sizing: border-box; } -html, -body { - margin: 0; - height: 100%; - width: 100%; - overflow: hidden; +* { + :focus-visible { + outline: none; + } +} - #App { - height: 100%; - } +.ps__rail-y, +.ps__rail-x { + background: transparent !important; +} - .semi-timepicker-panel { +html, +body { + margin: 0; + height: 100%; + width: 100%; + overflow: hidden; - //时间选择器不显示滚动栏 - ::-webkit-scrollbar { - display: none; - /* Chrome Safari */ + #App { + height: 100%; + } - } + .semi-timepicker-panel { + //时间选择器不显示滚动栏 + ::-webkit-scrollbar { + display: none; + /* Chrome Safari */ + } - scrollbar-width: none; - /* firefox */ - -ms-overflow-style: none; - /* IE 10+ */ - overflow-x: hidden; - overflow-y: auto; - } + scrollbar-width: none; + /* firefox */ + -ms-overflow-style: none; + /* IE 10+ */ + overflow-x: hidden; + overflow-y: auto; + } - a:link { - text-decoration: none; - color: unset - } + a:link { + text-decoration: none; + color: unset; + } - a:visited { - text-decoration: none; - color: unset - } + a:visited { + text-decoration: none; + color: unset; + } - a:hover { - text-decoration: none; - color: unset - } + a:hover { + text-decoration: none; + color: unset; + } - a:active { - text-decoration: none; - color: unset - } + a:active { + text-decoration: none; + color: unset; + } } // SEMI 全局样式 .semi-popover-content { - min-width: 300px + min-width: 300px; } .semi-portal-inner { - position: fixed; + position: fixed; } @font-face { - font-family: 'YouSheBiaoTiHei'; //这个可以任意取,但是应与后面相对应eg:yxingguang - src: url('/assets/fonts/YouSheBiaoTiHei-2.ttf'); + font-family: "YouSheBiaoTiHei"; //这个可以任意取,但是应与后面相对应eg:yxingguang + src: url("/assets/fonts/YouSheBiaoTiHei-2.ttf"); } @font-face { - font-family: 'DINExp'; //这个可以任意取,但是应与后面相对应eg:yxingguang - src: url('/assets/fonts/DINExp.ttf'); - } \ No newline at end of file + font-family: "DINExp"; //这个可以任意取,但是应与后面相对应eg:yxingguang + src: url("/assets/fonts/DINExp.ttf"); +} diff --git a/web/client/src/sections/control/actions/control.js b/web/client/src/sections/control/actions/control.js index d4f6c51..9c6c203 100644 --- a/web/client/src/sections/control/actions/control.js +++ b/web/client/src/sections/control/actions/control.js @@ -129,4 +129,52 @@ export function getLatestDynamic (query) { //查询最新动态 msg: { option: '查询最新动态' }, reducer: { name: '' } }); +} + +export function getAlarmData (query = {}) { //查询数据告警 + return dispatch => basicAction({ + type: 'get', + dispatch: dispatch, + query, + actionType: 'GET_ALARM_DATA', + url: `${ApiTable.getAlarmData}`, + msg: { error: '查询数据告警失败' }, + reducer: { name: '' } + }); +} + +export function getAlarmUse (query = {}) { //查询视频告警 + return dispatch => basicAction({ + type: 'get', + dispatch: dispatch, + query, + actionType: 'GET_ALARM_USE', + url: `${ApiTable.getAlarmUse}`, + msg: { error: '查询应用告警失败' }, + reducer: { name: '' } + }); +} + +export function getStatisticOnline (query = {}) { //查询数据在线率 + return dispatch => basicAction({ + type: 'get', + dispatch: dispatch, + query, + actionType: 'GET_STATISTICS_ONLINE', + url: `${ApiTable.getStatisticOnline}`, + msg: { error: '查询数据在线率失败' }, + reducer: { name: 'statisticOnline' } + }); +} + +export function getStrucSeries (query = {}) { //查询数据连续率 + return dispatch => basicAction({ + type: 'get', + dispatch: dispatch, + query, + actionType: 'GET_STRUC_SERIES', + url: `${ApiTable.getStrucSeries}`, + msg: { error: '查询数据连续率失败' }, + reducer: { name: 'strucSeries' } + }); } \ No newline at end of file diff --git a/web/client/src/sections/control/components/alarm-chart.js b/web/client/src/sections/control/components/alarm-chart.js new file mode 100644 index 0000000..6153b42 --- /dev/null +++ b/web/client/src/sections/control/components/alarm-chart.js @@ -0,0 +1,774 @@ +import React, { useEffect, useState, useRef } from 'react'; +import { connect } from 'react-redux'; +import { Timeline, Card, Button, Modal, Form, Tooltip, Select, Radio, RadioGroup } from '@douyinfe/semi-ui'; +import { push } from 'react-router-redux'; +import { Setup, OutHidden } from "$components"; +import ReactECharts from 'echarts-for-react'; +import moment from "moment"; +import SimpleBar from 'simplebar-react'; + + + +const AlarmChart = ({ dispatch, actions, user, history, projectPoms, loading, socket, pepProjectId, statisticOnline, strucSeries }) => { + + const { control, install, problem } = actions + const [setup, setSetup] = useState(false); //设置是否显现 + const [tableType, setTableType] = useState(''); //localStorage存储名 + const [tableSetup, setTableSetup] = useState([]); //单一表格设置信息 + const [long, setLong] = useState(''); //最新动态设置 + const [pomsList, setPomsList] = useState([]); //项目 + const [faultId, setFaultId] = useState(''); //项目id 故障数统计 + const [onlineId, setOnlineId] = useState(''); //项目id 数据在线率 + const [successionId, setSuccessionId] = useState(''); //项目id 数据连续率 + const [setData, setSetData] = useState(); //设置总数 + const [radioStatistics, setRadioStatistics] = useState('day'); //故障数统计(日,周,月) + const [radioRank, setRadioRank] = useState('day'); //故障数排名(日,周,月) + const [dataAlarm, setDataAlarm] = useState({}); //故障数统计--数据类 + const [videoAlarm, setVideoAlarm] = useState({}); //故障故障数统计--视频 + const [useAlarm, setUseAlarm] = useState({}); //故障故障数统计--应用 + const [behind, setBehind] = useState(true); //故障数统计下钻 + const [behindShow, setBehindShow] = useState([]); //故障数统计展示 + const [videoData, setVideoData] = useState([]); //视频故障统计 + const [datumData, setDatumData] = useState([]); //数据故障统计 + const [useData, setUseData] = useState([]); //应用故障统计 + const [rankData, setRankData] = useState([]); //故障排名 + + const [onlineStruc, setOnlineStruc] = useState([]) + const [online, setOnline] = useState([]) + const [value, setValue] = useState([]) + + const [seriesStruc, setSeriesStruc] = useState([]) + const [series, setSeries] = useState([]) + const [seriesValue, setSeriesValue] = useState([]) + + + + useEffect(() => { + if (projectPoms?.length > 0) { + let data = projectPoms?.filter(v => v.pepProjectIsDelete != 1)?.map(v => ({ value: v.id, label: v.pepProjectName || v.name })) + setPomsList(data) + } + }, [projectPoms]) + + useEffect(async () => { + if (projectPoms?.length > 0) { + let data = await getData('day', pepProjectId, true, true) + await getRank(data) + setRadioStatistics('day') + setRadioRank('day') + setFaultId('') + if (pepProjectId) { + setPomsList(projectPoms?.filter(v => v.pepProjectIsDelete != 1 && ((pepProjectId?.length > 0 ? pepProjectId.split(",")?.map(p => Number(p)) : [pepProjectId]).includes(v.id)))?.map(v => ({ value: v.id, label: v.pepProjectName || v.name }))) + } else { + setPomsList(projectPoms?.filter(v => v.pepProjectIsDelete != 1)?.map(v => ({ value: v.id, label: v.pepProjectName || v.name }))) + } + } + }, [pepProjectId, projectPoms]) + + + useEffect(() => { + setOnlineId('') + dispatch(control.getStatisticOnline({ pepProjectId: onlineId || pepProjectId })).then(res => { + if (res.success) { + let data = res.payload.data?.filter(s => !onlineId || s.pomsProject?.map(p => p.id)?.includes(onlineId)) + setOnlineStruc(data?.map(v => ({ value: v.strucId, label: v.strucName })) || []) + setOnline(data?.slice(0, 10) || []) + setValue(data?.map(v => v.strucId)?.slice(0, 10) || []) + } + }) + }, [onlineId, pepProjectId]) + + useEffect(() => { + setSuccessionId('') + dispatch(control.getStrucSeries({ pepProjectId: successionId || pepProjectId })).then(res => { + if (res.success) { + let data = res.payload.data?.filter(s => !successionId || s.pomsProject?.map(p => p.id)?.includes(successionId)) + setSeriesStruc(data?.map(v => ({ value: v.strucId, label: v.strucName })) || []) + setSeries(data?.slice(0, 10) || []) + setSeriesValue(data?.map(v => v.strucId)?.slice(0, 10) || []) + } + }) + }, [successionId, pepProjectId]) + + const getRank = async (data) => { + let dataList = [] + if (pepProjectId && (!pepProjectId?.length || pepProjectId.split(",")?.length == 1) || projectPoms?.legend == 1) { + + data?.AlarmData?.filter(p => p.State < 3)?.forEach(s => { + let find = dataList?.find(a => a.StructureId == s.StructureId) + if (find) { + find.sum += 1 + } else { + dataList.push({ + StructureId: s.StructureId, + name: s.StructureName, + sum: 1 + }) + } + }) + + data?.AlarmVideo?.filter(p => !p.confirmTime)?.forEach(s => { + s?.struc?.forEach(f => { + let find = dataList?.find(a => a.id == s.StructureId) + if (find) { + find.sum += 1 + } else { + dataList.push({ + StructureId: f.id, + name: f.name, + sum: 1 + }) + } + }) + }) + + } else { + projectPoms?.filter(v => v.pepProjectIsDelete != 1)?.forEach(d => { + let data1 = data?.AlarmData?.filter(v => v.pomsProject?.map(p => p.id)?.includes(d.id) && v.State < 3)?.length || 0 + let data2 = data?.AlarmVideo?.filter(v => v.pomsProject?.map(p => p.id)?.includes(d.id) && !v.confirmTime)?.length || 0 + dataList.push({ + name: d.name || d.pepProjectName, + sum: data1 + data2 + }) + }) + + } + setRankData(dataList.sort((a, b) => b.sum - a.sum)?.slice(0, 5) || []) + } + + const getData = async (radio, pepProjectId, diff1, diff2) => { + let data = {} + await dispatch(control.getAlarmData({ + pepProjectId: pepProjectId, + startTime: moment().startOf(radio).format('YYYY-MM-DD HH:mm:ss') + })).then((res) => { + if (res.success) { + if (diff1) { + data.AlarmData = res.payload.data || [] + } + if (diff2) { + setDatumData(res.payload.data || []) + let group = { + group1: { sum: 0, untreated: 0 }, group2: { sum: 0, untreated: 0 }, group3: { sum: 0, untreated: 0 }, + group4: { sum: 0, untreated: 0 }, group5: { sum: 0, untreated: 0 } + } + res.payload.data?.forEach(v => { + group['group' + v.AlarmGroup].sum += 1 + if (v.State < 3) { + group['group' + v.AlarmGroup].untreated += 1 + } + }) + setDataAlarm(group) + } + } + }) + + + + await dispatch(problem.getAlarmVideoList({ + pepProjectId: pepProjectId, + startTime: moment().startOf(radio).format('YYYY-MM-DD HH:mm:ss') + })).then((res) => { + if (res.success) { + if (diff1) { + data.AlarmVideo = res.payload.data || [] + } + if (diff2) { + setVideoData(res.payload.data || []) + let alarmData = { sum: 0, untreated: 0 } + res.payload.data?.forEach(v => { + alarmData.sum += 1 + if (!v.confirmTime) { + alarmData.untreated += 1 + } + }) + setVideoAlarm(alarmData) + } + + } + }) + + if (diff2) { + await dispatch(control.getAlarmUse({ + pepProjectId: pepProjectId, + startTime: moment().startOf(radio).format('YYYY-MM-DD HH:mm:ss') + })).then((res) => { + if (res.success) { + setUseData(res.payload.data || []) + let alarmData = { sum: 0, untreated: 0 } + res.payload.data?.forEach(v => { + alarmData.sum += 1 + if (!v.confirmTime) { + alarmData.untreated += 1 + } + }) + setUseAlarm(alarmData) + } + }) + } + + return data + } + + + const behindHandle = (key) => { + let show = [] + switch (key) { + case '数据中段': + dispatch(problem.getAlarmDataGroup()).then((res) => { + if (res.success) { + let data = res.payload.data?.find(v => v.desc == '数据中断')?.unit || [] + let sumData = datumData?.filter(s => s.AlarmGroup == 1) || [] + data?.forEach(v => { + let dataList = sumData?.filter(s => s.AlarmGroupUnit == v.id) || [] + let untreated = dataList?.filter(s => s.State < 3)?.length || 0 + show.push({ + name: v.name, + untreated: untreated, + processed: dataList?.length - untreated, + num: dataList?.length + }) + }) + setBehindShow(show.sort((a, b) => b.num - a.num)?.slice(0, 5) || []) + } + }) + break; + case '数据异常': + dispatch(problem.getAlarmDataGroup()).then((res) => { + if (res.success) { + let data = res.payload.data?.find(v => v.desc == '数据异常')?.unit || [] + let sumData = datumData?.filter(s => s.AlarmGroup == 2) || [] + data?.forEach(v => { + let dataList = sumData?.filter(s => s.AlarmGroupUnit == v.id) || [] + let untreated = dataList?.filter(s => s.State < 3)?.length || 0 + show.push({ + name: v.name, + untreated: untreated, + processed: dataList?.length - untreated, + num: dataList?.length + }) + }) + setBehindShow(show.sort((a, b) => b.num - a.num)?.slice(0, 5) || []) + } + }) + break; + case '策略命中': + dispatch(problem.getAlarmDataGroup()).then((res) => { + if (res.success) { + let data = res.payload.data?.find(v => v.desc == '策略命中')?.unit || [] + let sumData = datumData?.filter(s => s.AlarmGroup == 3) || [] + data?.forEach(v => { + let dataList = sumData?.filter(s => s.AlarmGroupUnit == v.id) || [] + let untreated = dataList?.filter(s => s.State < 3)?.length || 0 + show.push({ + name: v.name, + untreated: untreated, + processed: dataList?.length - untreated, + num: dataList?.length + }) + }) + setBehindShow(show.sort((a, b) => b.num - a.num)?.slice(0, 5) || []) + } + }) + break; + case '视频异常': + dispatch(problem.getAlarmVideoExceptionType()).then((res) => { + if (res.success) { + res.payload.data?.forEach(v => { + let dataList = videoData?.filter(s => s.statusId == v.statusId) || [] + let untreated = dataList?.filter(s => !s.confirmTime)?.length || 0 + show.push({ + name: v.describe, + untreated: untreated, + processed: dataList?.length - untreated, + num: dataList?.length + }) + }) + setBehindShow(show.sort((a, b) => b.num - a.num)?.slice(0, 5) || []) + } + }) + + setBehind(false) + + break; + case '应用异常': + [{ name: '接口报错', value: 'apiError ' }, + { name: '加载超时', value: 'timeout' }, + { name: '元素异常', value: 'element' }].forEach(v => { + let dataList = useData?.filter(s => s.type == v.value) || [] + let untreated = dataList?.filter(s => !s.confirmTime)?.length || 0 + show.push({ + name: v.name, + untreated: untreated, + processed: dataList?.length - untreated, + num: dataList?.length + }) + }) + setBehindShow(show.sort((a, b) => b.num - a.num)?.slice(0, 5) || []) + break; + case '设备异常': + dispatch(problem.getAlarmDataGroup()).then((res) => { + if (res.success) { + let data = res.payload.data?.find(v => v.desc == '掉线' || v.desc == '不活跃')?.unit || [] + let sumData = datumData?.filter(s => s.AlarmGroup == 4 || s.AlarmGroup == 5) || [] + data?.forEach(v => { + let dataList = sumData?.filter(s => s.AlarmGroupUnit == v.id) || [] + let untreated = dataList?.filter(s => s.State < 3)?.length || 0 + show.push({ + name: v.name, + untreated: untreated, + processed: dataList?.length - untreated, + num: dataList?.length + }) + }) + setBehindShow(show.sort((a, b) => b.num - a.num)?.slice(0, 5) || []) + } + }) + break; + } + setBehind(false) + } + + + return (<> + {/* 数据分析 */} + { +