import React, { useContext, useEffect, useRef, useState } from 'react'; import { ConfigProvider, Modal, Radio, Slider, InputNumber, Input, Button } from 'antd'; import classNames from 'classnames'; import moment from 'moment'; import { BasicChart } from '@wisdom-components/basicchart'; import { getHistoryInfo } from '../../apis'; import { std } from 'mathjs'; import skmeans from 'skmeans'; import { outlierArr, timeArr, chartArr, average, markArr } from '../utils'; import './index.less'; const IntellectDraw = (props) => { const { getPrefixCls } = useContext(ConfigProvider.ConfigContext); const prefixCls = getPrefixCls('intellect-draw'); const { deviceCode, sensors, deviceType, changeSpin } = props; const [open, setOpen] = useState(false); const [outlier, setOutlier] = useState(3); // 过滤异常 const [sensorData, setSensorData] = useState([]); // 所有数据 const [chartData, setChartData] = useState([]); // 图表数据 const [sensitive, setSensitive] = useState(10); // 敏感度 const [timeCycle, setTimeCycle] = useState(60); const [timeDan, setTimeDan] = useState(1); const [options, setOptions] = useState({}); const chartRef = useRef(null); // 确定 const onOk = () => { props.onOk && props.onOk(123); }; // 取消 const onCancel = () => { setOpen(false); props.onCancel && props.onCancel(); }; // 获取历史数据 const getSensorsData = async () => { changeSpin(true); const params = { isDilute: true, zoom: '', unit: '', dateFrom: moment().subtract(8, 'day').format('YYYY-MM-DD 00:00:00'), dateTo: moment().subtract(1, 'day').format('YYYY-MM-DD 23:59:59'), acrossTables: [{ deviceCode: deviceCode, sensors: sensors, deviceType: deviceType }], isBoxPlots: true, }; const results = await getHistoryInfo(params); changeSpin(false); const historyData = results?.data?.[0] || {}; setSensorData(() => { return historyData; }); }; // 图表数据处理 const chartDataHandle = (data) => { const times = moment().subtract(1, 'day').format('YYYY-MM-DD'); const chart = data.map((item) => { return { ...item, time: moment(item.pt).format(times + ' HH:mm:ss'), }; }); return chart; }; // 聚集方法 const clusteredMothod = () => {}; // 渲染图表 const renderChart = (_chartData) => { const chartDatas = _chartData.map((item) => { return [new Date(item.time).getTime(), item.pv]; }); const clustered = skmeans(chartDatas, 24); const { centroids = [] } = clustered; const _centroids = centroids.sort((a, b) => { return a[0] - b[0]; }); console.log(_centroids); const option = { xAxis: { type: 'time', axisTick: { alignWithLabel: true, }, boundaryGap: false, splitLine: { show: true, lineStyle: { type: 'dashed', }, }, }, yAxis: { type: 'value', name: 'm', position: 'left', alignTicks: true, axisLine: { show: true, }, axisLabel: { formatter: '{value}', }, }, series: [ { type: 'scatter', name: sensors, sampling: 'average', large: true, symbolSize: 5, data: _chartData.map((item) => { return [new Date(item.time).getTime(), item.pv]; }), }, { type: 'line', name: sensors, sampling: 'average', large: true, data: _centroids.map((item) => { return [Math.floor(item[0]), item[1]]; }), }, ], }; setOptions(option); }; const proposeRender = () => { return ( <> <div className={classNames(`${prefixCls}-propose-list`)}> <div className={classNames(`${prefixCls}-propose-value`)}> <div className={classNames(`${prefixCls}-value-list`)}> <Input style={{ width: '150px', }} addonBefore="低低限" disabled /> </div> <div className={classNames(`${prefixCls}-value-list`)}> <Input style={{ width: '150px', }} addonBefore="低限" disabled /> </div> <div className={classNames(`${prefixCls}-value-list`)}> <Input style={{ width: '150px', }} addonBefore="高限" disabled /> </div> <div className={classNames(`${prefixCls}-value-list`)}> <Input style={{ width: '150px', }} addonBefore="高高限" disabled /> </div> </div> <div className={classNames(`${prefixCls}-propose-range`)}> <span className={classNames(`${prefixCls}-label`)}>允许浮动范围:</span> <Slider min={0} max={100} style={{ width: '100px' }} onChange={(value) => { setSensitive(value); }} value={typeof sensitive === 'number' ? sensitive : 0} /> <InputNumber min={1} max={100} style={{ margin: '0 16px', width: '100px', }} formatter={(value) => `${value}%`} value={sensitive} onChange={(value) => { setSensitive(value); }} /> </div> </div> </> ); }; useEffect(() => { open && getSensorsData(); // eslint-disable-next-line react-hooks/exhaustive-deps }, [open]); useEffect(() => { const { dataModel = [] } = sensorData; if (!dataModel.length) return setChartData([]); const count = Math.floor((24 * 60) / timeCycle); const times = moment().subtract(1, 'day').format('YYYY-MM-DD'); const _dataModel = dataModel.map((item) => { return { ...item, time: moment(item.pt).format(times + ' HH:mm:ss'), }; }); const _chartData = []; const _clustered = []; for (let i = 0; i < count; i++) { const data = _dataModel.filter((item) => { const time = new Date(item.time).getTime(); const min = new Date(moment(times + ' 00:00:00').add(i * timeCycle, 'minute')).getTime(); const max = new Date( moment(times + ' 00:00:00').add((i + 1) * timeCycle, 'minute'), ).getTime(); return time && time >= min && max >= time; }); const clusteredArr = []; const dataArr = []; const pvArr = data.map((item) => { return item.pv; }); const stdVal = pvArr.length ? std(pvArr) : 0; const medianVal = pvArr.length ? average(pvArr) : 0; const range = { min: medianVal - outlier * stdVal, max: medianVal + outlier * stdVal, }; data.forEach((item) => { if (item.pv >= range.min && item.pv <= range.max) dataArr.push(item); clusteredArr.push([new Date(item.time).getTime(), item.pv]); }); const clustered = clusteredArr.length ? skmeans(clusteredArr, 1) : {}; const { centroids = [] } = clustered; _chartData.push(...dataArr); _clustered.push(...centroids); } renderChart(_chartData, _clustered); // eslint-disable-next-line react-hooks/exhaustive-deps }, [sensorData, outlier]); useEffect(() => { setOpen(props.open); }, [props.open]); return ( <> <div className={classNames(`${prefixCls}`)}> <div className={classNames(`${prefixCls}-header`)}> <div className={classNames(`${prefixCls}-list`)}> <span className={classNames(`${prefixCls}-item`)}> <span className={classNames(`${prefixCls}-label`)}>异常值剔除:</span> <Slider marks={markArr} step={null} style={{ width: '200px', }} defaultValue={1} onChange={(value) => { console.log(outlierArr); setOutlier(outlierArr[value]?.value || 0); }} min={0} max={3} tooltip={{ formatter: (value) => { return markArr[value]; }, }} /> </span> <span className={classNames(`${prefixCls}-item`)}> <span className={classNames(`${prefixCls}-label`)}>限值时段个数:</span> <InputNumber min={1} max={10} style={{ width: '100px', }} value={timeDan} onChange={(value) => { setTimeDan(value); }} /> </span> </div> <div className={classNames(`${prefixCls}-propose`)}> <span className={classNames(`${prefixCls}-label`)}>建议限值:</span> {proposeRender()} </div> </div> <div className={classNames(`${prefixCls}-chart`)}> <BasicChart ref={chartRef} option={options} notMerge style={{ width: '100%', height: '100%' }} /> </div> </div> </> ); }; export default IntellectDraw;