Commit f94a56bb authored by 李纪文's avatar 李纪文

feat: 组态增加图层设置

parent 451437c1
......@@ -12,6 +12,10 @@ const HistoryTrend = (props) => {
const prefixCls = getPrefixCls('history-trend');
const { deviceCode, sensors, deviceType, changeSpin } = props;
const chartRef = useRef(null);
const infoRef = useRef({
decimalPoint: 2,
unit: '',
})
const [sensitive, setSensitive] = useState(10); // 敏感度
const [timeType, setTimeType] = useState('近7日'); // 时间
......@@ -76,6 +80,11 @@ const HistoryTrend = (props) => {
let historyData = [];
results.forEach((result) => {
const _historyData = result?.data?.[0]?.dataModel || [];
const info = result?.data?.[0] || {};
infoRef.current = {
decimalPoint: info?.decimalPoint || 2,
unit: info?.unit || ''
}
historyData = historyData.concat([..._historyData]);
});
console.log(historyData);
......@@ -161,11 +170,12 @@ const HistoryTrend = (props) => {
let max = Math.max(...pvArr);
let min = Math.min(...pvArr);
console.log(max, min);
const decimalPoint = infoRef.current.decimalPoint || 2;
const data = [
(min * (1 - sensitive / 100)).toFixed(2) * 1,
(min * (1 + sensitive / 100)).toFixed(2) * 1,
(max * (1 - sensitive / 100)).toFixed(2) * 1,
(max * (1 + sensitive / 100)).toFixed(2) * 1,
(min * (1 - sensitive / 100)).toFixed(decimalPoint) * 1,
(min * (1 + sensitive / 100)).toFixed(decimalPoint) * 1,
(max * (1 - sensitive / 100)).toFixed(decimalPoint) * 1,
(max * (1 + sensitive / 100)).toFixed(decimalPoint) * 1,
];
const color = ['#CB2D2D', '#0087F7'];
const name = ['低低限', '低限', '高限', '高高限'];
......
......@@ -80,7 +80,7 @@ const LimitCurve = (props) => {
<Modal
closable={false}
centered
width={width || '1200px'}
width={width || '1300px'}
footer={null}
open={open}
visible={open}
......
import React, { useContext, useEffect, useRef, useState } from 'react';
import { ConfigProvider, Modal, Radio, Slider, InputNumber, Input, Button } from 'antd';
import { ConfigProvider, Modal, Radio, Slider, InputNumber, Input, Button, Checkbox } 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 { outlierArr, timeArr, chartArr, average, markArr, median } from '../utils';
import './index.less';
const IntellectDraw = (props) => {
......@@ -21,21 +21,11 @@ const IntellectDraw = (props) => {
const [sensitive, setSensitive] = useState(10); // 敏感度
const [timeCycle, setTimeCycle] = useState(60);
const [timeDan, setTimeDan] = useState(1);
const [foldData, setFoldData] = useState([]);
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);
......@@ -47,6 +37,7 @@ const IntellectDraw = (props) => {
dateTo: moment().subtract(1, 'day').format('YYYY-MM-DD 23:59:59'),
acrossTables: [{ deviceCode: deviceCode, sensors: sensors, deviceType: deviceType }],
isBoxPlots: true,
ignoreOutliers: true,
};
const results = await getHistoryInfo(params);
changeSpin(false);
......@@ -70,10 +61,76 @@ const IntellectDraw = (props) => {
};
// 聚集方法
const clusteredMothod = () => {};
const clusteredMothod = (clustered, num) => {
console.log(clustered);
let foldLine = [];
const arr = [];
const times = moment().subtract(1, 'day').format('YYYY-MM-DD');
const data = clustered.map((item) => {
return item[1];
});
const medianVal = median([...data]);
console.log(data, medianVal);
if (timeDan === 1) {
return [
[new Date(moment(times + ' 00:00:00')).getTime(), medianVal],
[new Date(moment(times + ' 23:59:59')).getTime(), medianVal],
];
}
data.forEach((item, index) => {
if (
index === 0 ||
(medianVal < item && medianVal > data[index - 1] && index > 0) ||
(medianVal > item && medianVal < data[index - 1] && index > 0)
) {
arr.push([
{
x: clustered[index][0],
y: clustered[index][1],
l: item,
},
]);
} else {
arr[arr.length - 1].push({
x: clustered[index][0],
y: clustered[index][1],
l: item,
});
}
});
console.log(arr);
arr.forEach((list, index) => {
const _list = list.map((item) => {
return item['y'];
});
const _medianVal = median([..._list]);
if (index === 0) {
foldLine = foldLine.concat([
[new Date(moment(times + ' 00:00:00')).getTime(), _medianVal],
[list[list.length - 1].x, _medianVal],
]);
} else if (index === arr.length - 1) {
foldLine = foldLine.concat([
[arr[index - 1].at(-1).x, _medianVal],
[new Date(moment(times + ' 23:59:59')).getTime(), _medianVal],
]);
} else {
foldLine = foldLine.concat([
[arr[index - 1].at(-1).x, _medianVal],
[list[list.length - 1].x, _medianVal],
]);
}
});
console.log(foldLine);
if (arr.length === timeDan || arr.length === num) {
return foldLine;
} else {
return clusteredMothod(foldLine, arr.length);
}
};
// 渲染图表
const renderChart = (_chartData) => {
const renderChart = (_chartData, _clustered) => {
const chartDatas = _chartData.map((item) => {
return [new Date(item.time).getTime(), item.pv];
});
......@@ -83,6 +140,8 @@ const IntellectDraw = (props) => {
return a[0] - b[0];
});
console.log(_centroids);
const foldLine = clusteredMothod(_centroids, 0);
console.log(foldLine);
const option = {
xAxis: {
type: 'time',
......@@ -122,87 +181,132 @@ const IntellectDraw = (props) => {
},
{
type: 'line',
name: sensors,
name: '趋势',
sampling: 'average',
large: true,
data: _centroids.map((item) => {
return [Math.floor(item[0]), item[1]];
}),
},
{
type: 'line',
name: '限值',
data: foldLine.map((item) => {
return [Math.floor(item[0]), item[1]];
}),
},
],
};
setOptions(option);
setFoldData(foldDataMethod(foldLine));
};
// 限值数据处理
const foldDataMethod = (data) => {
let _data = [];
data.forEach((item, index) => {
if (index % 2 === 0) {
_data = _data.concat([[item]]);
} else {
_data[Math.floor(index / 2)].push([...item]);
}
});
console.log(_data);
return _data.map((list) => {
return {
start: moment(list[0][0]).format('HH:mm'),
end: moment(list[1][0]).format('HH:mm'),
value: list[0][1],
wave: 10,
};
});
};
const onCheckChange = (checkedValues) => {
console.log(checkedValues);
};
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>
</>
<div className={classNames(`${prefixCls}-propose-box`)}>
<Checkbox.Group onChange={onCheckChange}>
{foldData.map((list, index) => {
return (
<div className={classNames(`${prefixCls}-propose-list`)} key={index}>
<div className={classNames(`${prefixCls}-propose-select`)}>
<Checkbox value={index}>
{list.start}-{list.end}
</Checkbox>
</div>
<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>
);
})}
</Checkbox.Group>
</div>
);
};
......@@ -233,8 +337,7 @@ const IntellectDraw = (props) => {
).getTime();
return time && time >= min && max >= time;
});
const clusteredArr = [];
const dataArr = [];
let dataArr = [];
const pvArr = data.map((item) => {
return item.pv;
});
......@@ -246,16 +349,13 @@ const IntellectDraw = (props) => {
};
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;
if (!outlier) dataArr = [].concat([...data]);
_chartData.push(...dataArr);
_clustered.push(...centroids);
}
renderChart(_chartData, _clustered);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [sensorData, outlier]);
}, [sensorData, outlier, timeDan]);
useEffect(() => {
setOpen(props.open);
......
......@@ -37,9 +37,25 @@
display: flex;
}
&-propose-box {
display: flex;
flex-direction: column;
}
&-propose-list {
display: flex;
align-items: center;
margin-bottom: 5px;
&:last-of-type {
margin-bottom: 0px;
}
}
&-propose-select {
margin: 10px;
display: flex;
align-items: center;
}
&-value-list {
......
......@@ -54,3 +54,18 @@ export const chartArr = [
export const average = (arr) => {
return arr.reduce((acc, cur) => acc + cur, 0) / arr.length;
};
// 中位数方法
export const median = (data) => {
if (data.length === 0) return 0;
data.sort((a, b) => {
return a - b;
});
const half = Math.floor(data.length / 2);
if (data.length % 2) return data[half];
return (data[half - 1] + data[half]) / 2;
};
......@@ -2373,6 +2373,7 @@ const ConfigurationView = (props) => {
relinkableTo: true,
zOrder: 1,
},
new go.Binding('layerName', 'layerName').makeTwoWay(),
new go.Binding('fromSpot', 'fromPort', (d) => {
return spotConverter(d);
}),
......@@ -2422,6 +2423,7 @@ const ConfigurationView = (props) => {
relinkableTo: true,
zOrder: 1,
},
new go.Binding('layerName', 'layerName').makeTwoWay(),
new go.Binding('fromSpot', 'fromPort', function (d) {
return spotConverter(d);
}),
......
......@@ -3017,6 +3017,7 @@ const ConfigurationView = (props) => {
relinkableTo: true,
zOrder: 1,
},
new go.Binding('layerName', 'layerName').makeTwoWay(),
new go.Binding('fromSpot', 'fromPort', (d) => {
return spotConverter(d);
}),
......@@ -3066,6 +3067,7 @@ const ConfigurationView = (props) => {
relinkableTo: true,
zOrder: 1,
},
new go.Binding('layerName', 'layerName').makeTwoWay(),
new go.Binding('fromSpot', 'fromPort', function (d) {
return spotConverter(d);
}),
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment