Удалены лишние компоненты, графики частично переработаны

This commit is contained in:
goodmice 2021-11-01 14:47:12 +05:00
parent 1a832694b4
commit 7c81352638
6 changed files with 111 additions and 311 deletions

View File

@ -1,123 +0,0 @@
import { useState, useEffect } from 'react';
import { Button, Select, Tag, Popover, Row, Tooltip } from 'antd';
import { ChartTimeArchive } from './charts/ChartTimeArchive';
import { DeleteOutlined } from '@ant-design/icons';
const { Option } = Select;
const linesCollection = [
{ label: "Глубина забоя", xAccessorName: "wellDepth", color: '#f00' },
{ label: "Положение инструмента", xAccessorName: "bitDepth", color: '#ff0' },
{ label: "Положение талевого блока", xAccessorName: "blockPosition", color: '#f0f' },
{ label: "Талевый блок. Мин положение", xAccessorName: "blockPositionMin", color: '#0ff' },
{ label: "Талевый блок. Макс положение", xAccessorName: "blockPositionMax", color: '#0f0' },
{ label: "Скорость талевого блока", xAccessorName: "blockSpeed", color: '#00f' },
{ label: "Скорости талевого блока. Задание", xAccessorName: "blockSpeedSp", color: '#c00' },
{ label: "Талевый блок. Задание скорости для роторного бурения", xAccessorName: "blockSpeedSpRotor", color: '#cc0' },
{ label: "Талевый блок. Задание скорости для режима слайда", xAccessorName: "blockSpeedSpSlide", color: '#c0c' },
{ label: "Талевый блок. Задание скорости для проработки", xAccessorName: "blockSpeedSpDevelop", color: '#0cc' },
{ label: "Давление", xAccessorName: "pressure", color: '#0c0' },
{ label: "Давление. Холостой ход", xAccessorName: "pressureIdle", color: '#00c' },
{ label: "Давление. Задание", xAccessorName: "pressureSp", color: '#900' },
{ label: "Давление. Задание для роторного бурения", xAccessorName: "pressureSpRotor", color: '#990' },
{ label: "Давление. Задание для режима слайда", xAccessorName: "pressureSpSlide", color: '#909' },
{ label: "Давление. Задание для проработки", xAccessorName: "pressureSpDevelop", color: '#099' },
{ label: "Давление дифф. Аварийное макс.", xAccessorName: "pressureDeltaLimitMax", color: '#090' },
{ label: "Осевая нагрузка", xAccessorName: "axialLoad", color: '#009' },
{ label: "Осевая нагрузка. Задание", xAccessorName: "axialLoadSp", color: '#600' },
{ label: "Осевая нагрузка. Аварийная макс.", xAccessorName: "axialLoadLimitMax", color: '#660' },
{ label: "Вес на крюке", xAccessorName: "hookWeight", color: '#606' },
{ label: "Вес на крюке. Холостой ход", xAccessorName: "hookWeightIdle", color: '#066' },
{ label: "Вес на крюке. Посадка", xAccessorName: "hookWeightLimitMin", color: '#060' },
{ label: "Вес на крюке. Затяжка", xAccessorName: "hookWeightLimitMax", color: '#006' },
{ label: "Момент на роторе", xAccessorName: "rotorTorque", color: '#300' },
{ label: "Момент на роторе. Холостой ход", xAccessorName: "rotorTorqueIdle", color: '#330' },
{ label: "Момент на роторе. Задание", xAccessorName: "rotorTorqueSp", color: '#303' },
{ label: "Момент на роторе. Аварийный макс.", xAccessorName: "rotorTorqueLimitMax", color: '#033' },
{ label: "Обороты ротора", xAccessorName: "rotorSpeed", color: '#030' },
{ label: "Расход", xAccessorName: "flow", color: '#003' },
{ label: "Расход. Холостой ход", xAccessorName: "flowIdle", color: '#666' },
{ label: "Расход. Аварийный макс.", xAccessorName: "flowDeltaLimitMax", color: '#ccc' },
]
const tagRender = ({ label, value, closable, onClose }) =>{
const onPreventMouseDown = event => {
event.preventDefault();
event.stopPropagation();
};
let color = linesCollection.find(l=>l.xAccessorName === value)?.color
return (
<Tag
onMouseDown={onPreventMouseDown}
closable={closable}
onClose={onClose}
style={{ marginRight: 3 }}>
<span style={{backgroundColor:color}}>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>
<span>&nbsp;{label}</span>
</Tag>
);
}
export function ArchiveColumn({ data, config, rangeDate, chartRatio, onRemoveChart, onSaveConfig }) {
const [lines, setLines] = useState([]);
useEffect(() => {
setLines(config.lines);
},[config]);
const handleLinesSetChange = (linesKeys) => {
let newLines = linesCollection.filter(line => linesKeys.includes(line.xAccessorName));
config.lines = newLines;
if(onSaveConfig)
onSaveConfig()
setLines(newLines);
};
let selectedValues = lines?.map(line=>line.xAccessorName)??[]
const select = <Select
mode="multiple"
placeholder="Выберите линии"
value={selectedValues}
allowClear={false}
showArrow
bordered={false}
tagRender={tagRender}
onChange={handleLinesSetChange}
style={{
minWidth: "300px",
maxWidth: "400px"
}}
>
{linesCollection.map((line) => (<Option key={line.xAccessorName} value={line.xAccessorName} color={line.color}>{line.label}</Option>))}
</Select>;
const popBar = <Row>
{select}
<Tooltip title="Удалить этот график">
<Button onClick={() => onRemoveChart(config.id)}><DeleteOutlined /></Button>
</Tooltip>
</Row>
return (
<>
<Popover content={popBar}>
<div>
<ChartTimeArchive
data={data}
yDisplay={config.yDisplay}
lines={lines}
rangeDate={rangeDate}
chartRatio={chartRatio}
/>
</div>
</Popover>
</>);
}

View File

@ -1,16 +0,0 @@
import ChartDataLabels from 'chartjs-plugin-datalabels'
const defaultOptionsDepthLabels = {
borderWidth: 1,
borderColor: black,
borderRadius: 4,
clamp: true,
display: true,
data: {
datasets: [{
datalabels: {
color: '#0f4000'
}
}]
}
}

View File

@ -108,6 +108,7 @@ export type ChartTimeData = ChartData<keyof ChartTypeRegistry, {
export type ChartTimeDataParams = { export type ChartTimeDataParams = {
data: ChartTimeData, data: ChartTimeData,
yStart?: Date, yStart?: Date,
yEnd?: Date,
yInterval?: number, yInterval?: number,
displayLabels?: Boolean, displayLabels?: Boolean,
} }
@ -217,10 +218,11 @@ export const ChartTimeBase: React.FC<ChartTimeBaseProps> = ({options, dataParams
chart.data = dataParams.data chart.data = dataParams.data
chart.options.aspectRatio = options?.aspectRatio chart.options.aspectRatio = options?.aspectRatio
if(dataParams.yStart){ if(dataParams.yStart){
const interval = Number(dataParams.yInterval ?? 600)
const start = new Date(dataParams.yStart) const start = new Date(dataParams.yStart)
const end = new Date(dataParams.yStart) const end = new Date(dataParams.yEnd ?? dataParams.yStart)
end.setSeconds(end.getSeconds() + interval) const interval = Number(dataParams.yInterval ?? (dataParams.yEnd ? end.getSeconds() - start.getSeconds() : 600))
if (!dataParams.yEnd)
end.setSeconds(end.getSeconds() + interval)
const { unit, stepSize } = timeParamsByInterval(interval) const { unit, stepSize } = timeParamsByInterval(interval)
if(chart.options.scales?.y){ if(chart.options.scales?.y){

View File

@ -1,144 +1,62 @@
import { useRef, useLayoutEffect, useState, useEffect } from 'react' import { useState, useEffect } from 'react'
import { import { DatePicker } from 'antd'
Button,
DatePicker,
Row,
Col,
Tooltip} from 'antd'
import { TelemetryDataSaubService } from '../services/api' import { TelemetryDataSaubService } from '../services/api'
import {generateUUID} from '../services/UidGenerator'
import { ArchiveColumn } from '../components/ArchiveColumn'
import moment from 'moment' import moment from 'moment'
import { notify } from "../components/factory" import { invokeWebApiWrapperAsync } from "../components/factory"
import LoaderPortal from '../components/LoaderPortal' import LoaderPortal from '../components/LoaderPortal'
import { Grid, GridItem } from '../components/Grid'
import { Column } from './TelemetryView/Column'
import { paramsGroups } from './TelemetryView'
const { RangePicker } = DatePicker const { RangePicker } = DatePicker
const SaveObject = (key, obj) => {
let json = JSON.stringify(obj)
localStorage.setItem(key, json)
}
const LoadObject = (key) => {
let json = localStorage.getItem(key)
return json ? JSON.parse(json) : null
}
export default function Archive({idWell}) { export default function Archive({idWell}) {
const [saubData, setSaubData] = useState([]) const [dataSaub, setDataSaub] = useState([])
const [chartsCfgs, setChartsCfgs] = useState([])
const [rangeDate, setRangeDate] = useState([moment().subtract(3,'hours'), moment()]) const [rangeDate, setRangeDate] = useState([moment().subtract(3,'hours'), moment()])
const [geometry, setGeometry] = useState({ratioRest:1, ratio1st:1, wRest:.5, w1st:.5}) const [showLoader, setShowLoader] = useState(false)
const [loader, setLoader] = useState(false) const [chartInterval, setChartInterval] = useState(600)
const chartsCfgsKey = 'chartsCfgs'
const chartsContainerRef = useRef();
const handleReceiveDataSaub = (data) => {
if (data)
setSaubData(data)
}
const onAddChart = () => {
let newChartCfgs = [...chartsCfgs, {id: generateUUID(), yDisplay: false, aspectRatio:1}]
setChartsCfgs(newChartCfgs)
}
const onRemoveChart = (id) => {
let newChartCfgs = chartsCfgs.filter(cfg => cfg.id !== id )
setChartsCfgs(newChartCfgs)
}
const onSaveConfig = ()=>{
SaveObject(chartsCfgsKey, chartsCfgs)
}
const onChangeRange = (range) => { const onChangeRange = (range) => {
setRangeDate(range) setRangeDate(range)
} }
useLayoutEffect(()=>{ useEffect(() => invokeWebApiWrapperAsync(
if(chartsContainerRef.current && chartsCfgs?.length){ async () => {
let w = chartsContainerRef.current.offsetWidth //1792 const interval = (rangeDate[1] - rangeDate[0]) / 1000
w = w > 0 ? w : 1792 let startDate = rangeDate[0].toISOString()
let ot = chartsContainerRef.current.offsetTop
let ph = chartsContainerRef.current.offsetParent.offsetHeight
let h = ph - ot - 32 //761
h = h > 0 ? h : 761
let chartsCount = chartsCfgs.length const data = await TelemetryDataSaubService.getData(idWell, startDate, interval, 2048)
data?.sort((a, b) => a.date > b.date ? 1 : -1)
setDataSaub(data ?? [])
setChartInterval(interval)
},
setShowLoader,
`Не удалось загрузить данные по скважине "${idWell}" c ${rangeDate[0]} по ${rangeDate[1]}`
), [idWell, rangeDate])
let labelLenght = 8 return (
let borderWidth = 8 <>
let wRest = Math.floor((w - labelLenght)/chartsCount) - borderWidth <RangePicker
let w1st = wRest + labelLenght showTime
allowClear={false}
let ratio1st = w1st/h onChange = {onChangeRange}
let ratioRest = wRest/h value = {rangeDate}
/>
setGeometry({ratio1st, ratioRest, w1st, wRest}) <LoaderPortal show={showLoader}>
} <Grid onWheel={(e) => console.log(e)}>
},[chartsContainerRef, chartsCfgs]) {paramsGroups.map((group, index) => (
<GridItem col={index+1} row={'1'} className={'border_small'} key={`${group.label}${index}`} style={{padding:0}}>
useEffect(() => { <Column
let cfgs = LoadObject(chartsCfgsKey) style={{ width: '15vw' }}
if(cfgs) data={dataSaub}
setChartsCfgs(cfgs) lineGroup={group}
},[]) interval={chartInterval}
headerHeight={'50px'}
useEffect(()=>{ />
SaveObject(chartsCfgsKey, chartsCfgs) </GridItem>
},[chartsCfgs]) ))}
</Grid>
useEffect(() => { </LoaderPortal>
let interval = (rangeDate[1] - rangeDate[0]) / 1000 </>
let startDate = rangeDate[0].toISOString() )
setLoader(true)
TelemetryDataSaubService.getData(idWell, startDate, interval, 2048)
.then(handleReceiveDataSaub)
.catch(error => {
notify(`Не удалось загрузить данные по скважине (${idWell}) c ${rangeDate[0]} по ${rangeDate[1]}`, 'error')
console.error(error)
})
.finally(()=>setLoader(false))
}, [idWell, rangeDate]);
let charts = null
if(chartsCfgs.length > 0){
chartsCfgs[0].yDisplay = true
charts = chartsCfgs.map((cfg, i) =>
<Col flex={`${i===0 ? geometry.w1st : geometry.wRest}px`}
key={cfg.id}>
<ArchiveColumn
data={saubData}
rangeDate={rangeDate}
chartRatio={i===0 ? geometry.ratio1st : geometry.ratioRest}
onRemoveChart={onRemoveChart}
onSaveConfig={onSaveConfig}
config={cfg}/>
</Col>)
}
return (<>
<Tooltip title="Добавить график">
<Button
type="primary"
onClick={onAddChart}
disabled={chartsCfgs.length >= 6}>
+
</Button>
</Tooltip>
<RangePicker
showTime
allowClear={false}
onChange = {onChangeRange}
value = {rangeDate}
/>
<LoaderPortal show={loader}>
<Row ref={chartsContainerRef}>
{charts}
</Row>
</LoaderPortal>
</>)
} }

View File

@ -18,7 +18,7 @@ const chartPluginsOptions = {
anchor: 'center', anchor: 'center',
clip: true clip: true
}, },
legend:{ display: false }, legend: { display: false },
tooltip: { enable: true } tooltip: { enable: true }
} }
} }
@ -61,7 +61,7 @@ const GetOrCreateDatasetByLineConfig = (data, lineConfig) => {
return dataset return dataset
} }
export const Column = ({ lineGroup, data, flowChartData, interval, showBorder, style, headerHeight, yDisplay }) => { export const Column = ({ lineGroup, data, flowChartData, interval, showBorder, style, headerHeight, yDisplay, yStart, showLastValues, pointCount }) => {
const [dataParams, setDataParams] = useState({data: {datasets:[]}, yStart: new Date(), }) const [dataParams, setDataParams] = useState({data: {datasets:[]}, yStart: new Date(), })
let dataLast = data?.[data.length - 1] let dataLast = data?.[data.length - 1]
@ -89,26 +89,29 @@ export const Column = ({ lineGroup, data, flowChartData, interval, showBorder, s
const lineData = [ ...dataset.data, ...points,] const lineData = [ ...dataset.data, ...points,]
if(points?.length > 2) if(points?.length > 2)
lineData.sort((a,b) => a.y > b.y ? 1 : -1) lineData.sort((a,b) => a.y > b.y ? 1 : -1)
if(lineData.length > 1024) if(lineData.length > pointCount)
lineData.splice(0, (1024 - lineData.length)) lineData.splice(0, (pointCount - lineData.length))
dataset.data = lineData dataset.data = lineData
//Area //Area
lineGroup.filter(cfg => cfg.isShape && cfg.xAccessorName === lineCfg.xAccessorName).forEach(areaCfg => { if (flowChartData) {
const dataset = GetOrCreateDatasetByLineConfig(preDataParams.data, areaCfg) lineGroup.filter(cfg => cfg.isShape && cfg.xAccessorName === lineCfg.xAccessorName).forEach(areaCfg => {
dataset.data = GetLimitShape(flowChartData, lineData, areaCfg.xAccessorName) const dataset = GetOrCreateDatasetByLineConfig(preDataParams.data, areaCfg)
}) dataset.data = GetLimitShape(flowChartData, lineData, areaCfg.xAccessorName)
})
}
}) })
preDataParams.yStart = new Date(Math.max(new Date(dataLast.date), preDataParams.yStart ?? new Date(0))) preDataParams.yStart = yStart ?? new Date(Math.max(new Date(dataLast.date), preDataParams.yStart ?? new Date(0)))
preDataParams.yStart.setSeconds(preDataParams.yStart.getSeconds() - interval * 0.97) if (!yStart)
preDataParams.yStart.setSeconds(preDataParams.yStart.getSeconds() - interval * 0.97)
preDataParams.yInterval = interval preDataParams.yInterval = interval
preDataParams.displayLabels = yDisplay ?? false preDataParams.displayLabels = yDisplay ?? false
return {...preDataParams} return {...preDataParams}
}) })
}, [data, lineGroup, interval, yDisplay, flowChartData, dataLast]) }, [data, lineGroup, interval, yDisplay, yStart, flowChartData, dataLast, pointCount])
return ( return (
<div style={style}> <div style={style}>
@ -118,14 +121,22 @@ export const Column = ({ lineGroup, data, flowChartData, interval, showBorder, s
))} ))}
</Grid> </Grid>
<div style={{ position: 'relative' }}> <div style={{ position: 'relative' }}>
<Grid className={'display_chart_values'}> {showLastValues && (
{pv?.map((v, idx) => ( <Grid className={'display_chart_values'}>
<GridItem key={idx} col={1} row={idx} style={{ ...stroke(), color: v.color, padding: '0 4px' }}>{v.value?.toFixed(2) ?? '--'} {v.unit}</GridItem> {pv?.map((v, idx) => (
))} <GridItem key={idx} col={1} row={idx} style={{ ...stroke(), color: v.color, padding: '0 4px' }}>{v.value?.toFixed(2) ?? '--'} {v.unit}</GridItem>
</Grid> ))}
</Grid>
)}
<ChartTimeBase dataParams = {dataParams} options = { chartPluginsOptions } /> <ChartTimeBase dataParams = {dataParams} options = { chartPluginsOptions } />
</div> </div>
<ChartTimeOnlineFooter data={dataLast} lineGroup={lineGroup} /> {showLastValues && (
<ChartTimeOnlineFooter data={dataLast} lineGroup={lineGroup} />
)}
</div> </div>
) )
} }
Column.defaultProps = {
pointCount: 2048
}

View File

@ -250,7 +250,14 @@ const rotorTorqueGroup = [
} }
] ]
const paramsGroups = [blockHeightGroup, blockSpeedGroup, pressureGroup, axialLoadGroup, hookWeightGroup, rotorTorqueGroup] export const paramsGroups = [
blockHeightGroup,
blockSpeedGroup,
pressureGroup,
axialLoadGroup,
hookWeightGroup,
rotorTorqueGroup
]
const timePeriodCollection = [ const timePeriodCollection = [
{ value: '60', label: '1 минута' }, { value: '60', label: '1 минута' },
@ -400,6 +407,7 @@ export default function TelemetryView({ idWell }) {
{paramsGroups.map((group, index) => {paramsGroups.map((group, index) =>
<GridItem col={2 + index} row={'2'} className={'border_small'} key={`${group.label}${index}`} style={{padding:0}}> <GridItem col={2 + index} row={'2'} className={'border_small'} key={`${group.label}${index}`} style={{padding:0}}>
<Column <Column
showLastValues
style={{ width: '13vw' }} style={{ width: '13vw' }}
data={dataSaub} data={dataSaub}
flowChartData={flowChartData} flowChartData={flowChartData}