From 274c56dcf7aeb2705c4430826e0d0a5c90fc7aa9 Mon Sep 17 00:00:00 2001 From: goodmice Date: Thu, 11 Nov 2021 19:28:07 +0500 Subject: [PATCH] =?UTF-8?q?*=20=D0=9A=D0=BE=D0=BB=D0=BE=D0=BD=D0=BA=D0=B0?= =?UTF-8?q?=20=D1=80=D0=B0=D0=B7=D0=B4=D0=B5=D0=BB=D0=B5=D0=BD=D0=B0=20?= =?UTF-8?q?=D0=BD=D0=B0=20=D0=BE=D0=B1=D1=89=D0=B8=D0=B9=20=D0=BA=D0=BE?= =?UTF-8?q?=D0=BC=D0=BF=D0=BE=D0=BD=D0=B5=D0=BD=D1=82=20=D0=B8=20=D1=87?= =?UTF-8?q?=D0=B0=D1=81=D1=82=D0=BD=D1=8B=D0=B5=20=D0=B4=D0=BB=D1=8F=20?= =?UTF-8?q?=D0=BC=D0=BE=D0=BD=D0=B8=D1=82=D0=BE=D1=80=D0=B8=D0=BD=D0=B3?= =?UTF-8?q?=D0=B0=20=D0=B8=20=D0=B0=D1=80=D1=85=D0=B8=D0=B2=D0=B0=20*=20?= =?UTF-8?q?=D0=92=D1=8B=D0=B1=D0=BE=D1=80=20=D0=B8=D0=BD=D1=82=D0=B5=D1=80?= =?UTF-8?q?=D0=B2=D0=B0=D0=BB=D0=B0/=D0=BF=D0=B5=D1=80=D0=B8=D0=BE=D0=B4?= =?UTF-8?q?=D0=B0=20=D0=B2=D1=8B=D0=BD=D0=B5=D1=81=D0=B5=D0=BD=20=D0=B2=20?= =?UTF-8?q?=D0=BA=D0=BE=D0=BC=D0=BF=D0=BE=D0=BD=D0=B5=D0=BD=D1=82=20*=20?= =?UTF-8?q?=D0=9E=D0=BF=D1=82=D0=B8=D0=BC=D0=B8=D0=B7=D0=B8=D1=80=D0=BE?= =?UTF-8?q?=D0=B2=D0=B0=D0=BD=20ChartTimeBase=20*=20Archive=20=D1=80=D0=B0?= =?UTF-8?q?=D0=B7=D0=B4=D0=B5=D0=BB=D1=91=D0=BD=20=D0=BD=D0=B0=20=D0=BE?= =?UTF-8?q?=D1=82=D0=BE=D0=B1=D1=80=D0=B0=D0=B6=D0=B5=D0=BD=D0=B8=D0=B5=20?= =?UTF-8?q?=D0=B8=20=D0=BE=D0=B1=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D0=BA=D1=83?= =?UTF-8?q?,=20=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5=D0=BD=20=D1=81?= =?UTF-8?q?=D0=BA=D0=BE=D1=80=D0=BB=D0=BB=20=D0=B8=20=D0=BF=D0=BE=D0=B4?= =?UTF-8?q?=D0=B3=D1=80=D1=83=D0=B7=D0=BA=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/PeriodPicker.tsx | 31 ++++ src/components/charts/ChartTimeBase.tsx | 42 +++--- src/components/charts/Column.jsx | 91 ++++++++++++ src/pages/Archive.jsx | 62 -------- src/pages/Archive/ArchiveColumn.jsx | 29 ++++ src/pages/Archive/ArchiveDisplay.jsx | 30 ++++ src/pages/Archive/index.jsx | 140 ++++++++++++++++++ src/pages/TelemetryView/Column.jsx | 142 ------------------- src/pages/TelemetryView/MonitoringColumn.jsx | 80 +++++++++++ src/pages/TelemetryView/index.jsx | 29 +--- 10 files changed, 427 insertions(+), 249 deletions(-) create mode 100644 src/components/PeriodPicker.tsx create mode 100644 src/components/charts/Column.jsx delete mode 100644 src/pages/Archive.jsx create mode 100644 src/pages/Archive/ArchiveColumn.jsx create mode 100644 src/pages/Archive/ArchiveDisplay.jsx create mode 100644 src/pages/Archive/index.jsx delete mode 100644 src/pages/TelemetryView/Column.jsx create mode 100644 src/pages/TelemetryView/MonitoringColumn.jsx diff --git a/src/components/PeriodPicker.tsx b/src/components/PeriodPicker.tsx new file mode 100644 index 0000000..615caa8 --- /dev/null +++ b/src/components/PeriodPicker.tsx @@ -0,0 +1,31 @@ +import { Select } from 'antd' + +export const defaultPeriod = '600' + +const timePeriodCollection = [ + { value: '60', label: '1 минута' }, + { value: '300', label: '5 минут' }, + { value: '600', label: '10 минут' }, + { value: '1800', label: '30 минут' }, + { value: '3600', label: '1 час' }, + { value: '21600', label: '6 часов' }, + { value: '43200', label: '12 часов' }, + { value: '86400', label: '24 часа' } +] + +interface PeriodPickerProps { + defaultValue?: string + onChange?: (value: string, option: any) => void +} + +export const PeriodPicker = ({ defaultValue = defaultPeriod, onChange }: PeriodPickerProps) => ( + +) + +export default PeriodPicker diff --git a/src/components/charts/ChartTimeBase.tsx b/src/components/charts/ChartTimeBase.tsx index 5c7ff02..7bd701e 100644 --- a/src/components/charts/ChartTimeBase.tsx +++ b/src/components/charts/ChartTimeBase.tsx @@ -71,6 +71,7 @@ const defaultOptions = { position:'top' } }, + parsing: false, elements:{ point:{ radius:0, @@ -108,7 +109,6 @@ export type ChartTimeData = ChartData = ({options, dataParams const chartRef = useRef(null) const [chart, setChart] = useState() - useEffect(()=>{ - if((chartRef.current)&&(!chart)){ - let thisOptions = {} - Object.assign(thisOptions, defaultOptions, options) + useEffect(() => { + let thisOptions = {} + Object.assign(thisOptions, defaultOptions, options) - let newChart = new Chart(chartRef.current, { - type: 'line', - plugins: [ChartDataLabels], - options: thisOptions, - data: dataParams.data - }) - setChart(newChart) - - return () => chart?.destroy() - } - - if(!chart) return + let newChart = new Chart(chartRef.current, { + type: 'line', + plugins: [ChartDataLabels], + options: thisOptions, + data: { datasets: [] } + }) + setChart(newChart) + return () => newChart?.destroy() + }, [options]) + useEffect(() => { + if (!chart) return; chart.data = dataParams.data - chart.options.aspectRatio = options?.aspectRatio if(dataParams.yStart){ const start = new Date(dataParams.yStart) - const end = new Date(dataParams.yEnd ?? dataParams.yStart) - const interval = Number(dataParams.yInterval ?? (dataParams.yEnd ? end.getSeconds() - start.getSeconds() : 600)) - if (!dataParams.yEnd) - end.setSeconds(end.getSeconds() + interval) + const end = new Date(dataParams.yStart) + const interval = Number(dataParams.yInterval ?? 600) + end.setSeconds(end.getSeconds() + interval) const { unit, stepSize } = timeParamsByInterval(interval) if(chart.options.scales?.y){ @@ -235,7 +231,7 @@ export const ChartTimeBase: React.FC = ({options, dataParams } chart.update() - }, [chart, dataParams, options]) + }, [chart, dataParams]) return() } diff --git a/src/components/charts/Column.jsx b/src/components/charts/Column.jsx new file mode 100644 index 0000000..a278b7e --- /dev/null +++ b/src/components/charts/Column.jsx @@ -0,0 +1,91 @@ +import { useState, useEffect } from 'react' +import { ChartTimeBase } from './ChartTimeBase' + +const chartPluginsOptions = { + plugins: { + datalabels: { + backgroundColor: 'transparent', + borderRadius: 4, + color: '#000B', + display: context => (context.dataset.label === 'wellDepth') && 'auto', + formatter: value => `${value.y.toLocaleTimeString()} ${value.label.toPrecision(4)}`, + padding: 6, + align: 'left', + anchor: 'center', + clip: true + }, + legend: { display: false }, + tooltip: { enable: true } + } +} + +const GetRandomColor = () => '#' + Math.floor(Math.random()*(16**6-1)).toString(16) + +export const GetOrCreateDatasetByLineConfig = (data, lineConfig) => { + let dataset = data?.datasets.find(d => d.label === lineConfig.label) + if(!dataset) { + let color = lineConfig.borderColor + ?? lineConfig.backgroundColor + ?? lineConfig.color + ?? GetRandomColor() + + dataset = { + label: lineConfig.label, + data: [], + backgroundColor: lineConfig.backgroundColor ?? color, + borderColor: lineConfig.borderColor ?? color, + borderWidth: lineConfig.borderWidth ?? 1, + borderDash: lineConfig.dash ?? [], + showLine: lineConfig.showLine ?? !lineConfig.isShape, + fill: lineConfig.fill ?? (lineConfig.isShape ? 'shape' : 'none') + } + + data.datasets.push(dataset) + } + return dataset +} + +export const Column = ({ lineGroup, data, postParsing, interval, yDisplay, yStart, pointCount, savePreviousData }) => { + const [dataParams, setDataParams] = useState({data: {datasets:[]}, yStart, }) + + useEffect(()=>{ + if((lineGroup.length === 0) || (data.length === 0)) return + + setDataParams((preDataParams) => { + lineGroup.forEach(lineCfg => { + const dataset = GetOrCreateDatasetByLineConfig(preDataParams.data, lineCfg) + let points = data.map(dataItem => ({ + x: lineCfg.xConstValue ?? dataItem[lineCfg.xAccessorName], + label: dataItem[lineCfg.xAccessorName], + y: new Date(dataItem[lineCfg.yAccessorName]), + depth: dataItem.wellDepth + })).filter(point => (point.x ?? null) !== null && (point.y ?? null) !== null) + + if (savePreviousData) + points = [...dataset.data, ...points] + + if(points?.length > 2) + points.sort((a,b) => a.y > b.y ? 1 : -1) + if(points.length > pointCount) + points.splice(0, (pointCount - points.length)) + + dataset.data = points + }) + + preDataParams.yStart = yStart + preDataParams.yInterval = interval + preDataParams.displayLabels = yDisplay + + postParsing?.(preDataParams) + return {...preDataParams} + }) + + }, [data, lineGroup, interval, yDisplay, yStart, postParsing, pointCount, savePreviousData]) + + return +} + +Column.defaultProps = { + pointCount: 2048, + savePreviousData: false +} diff --git a/src/pages/Archive.jsx b/src/pages/Archive.jsx deleted file mode 100644 index 8abd9de..0000000 --- a/src/pages/Archive.jsx +++ /dev/null @@ -1,62 +0,0 @@ -import { useState, useEffect } from 'react' -import { DatePicker } from 'antd' -import { TelemetryDataSaubService } from '../services/api' -import moment from 'moment' -import { invokeWebApiWrapperAsync } from "../components/factory" -import LoaderPortal from '../components/LoaderPortal' -import { Grid, GridItem } from '../components/Grid' -import { Column } from './TelemetryView/Column' -import { paramsGroups } from './TelemetryView' - -const { RangePicker } = DatePicker - -export default function Archive({idWell}) { - const [dataSaub, setDataSaub] = useState([]) - const [rangeDate, setRangeDate] = useState([moment().subtract(3,'hours'), moment()]) - const [showLoader, setShowLoader] = useState(false) - const [chartInterval, setChartInterval] = useState(600) - - const onChangeRange = (range) => { - setRangeDate(range) - } - - useEffect(() => invokeWebApiWrapperAsync( - async () => { - const interval = (rangeDate[1] - rangeDate[0]) / 1000 - let startDate = rangeDate[0].toISOString() - - 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]) - - return ( - <> - - - console.log(e)}> - {paramsGroups.map((group, index) => ( - - - - ))} - - - - ) -} diff --git a/src/pages/Archive/ArchiveColumn.jsx b/src/pages/Archive/ArchiveColumn.jsx new file mode 100644 index 0000000..b3e7909 --- /dev/null +++ b/src/pages/Archive/ArchiveColumn.jsx @@ -0,0 +1,29 @@ +import { Grid, GridItem } from '../../components/Grid' +import { Column } from '../../components/charts/Column' + +export const ArchiveColumn = ({ lineGroup, data, interval, style, headerHeight, yStart }) => { + const dataLast = data?.[data.length - 1] + const pv = lineGroup.filter(line => line.showLabels).map(line => ({ + color: line.color, + label: line.label, + unit: line.units, + value: dataLast?.[line.xAccessorName] + })) + + return ( +
+ + {pv?.map((v, idx) => ( + {v.label} + ))} + + +
+ ) +} diff --git a/src/pages/Archive/ArchiveDisplay.jsx b/src/pages/Archive/ArchiveDisplay.jsx new file mode 100644 index 0000000..54db8b7 --- /dev/null +++ b/src/pages/Archive/ArchiveDisplay.jsx @@ -0,0 +1,30 @@ +import { useEffect, useState } from 'react' +import { Grid, GridItem } from '../../components/Grid' +import { ArchiveColumn } from './ArchiveColumn' +import { paramsGroups } from '../TelemetryView' + +export const ArchiveDisplay = ({data, startDate, interval, onWheel}) => { + const [chartData, setChartData] = useState([]) + + useEffect(() => { + const endDate = new Date(+startDate + interval) + setChartData(data.filter(elm => elm.date >= startDate && elm.date <= endDate)) + }, [data, startDate, interval]) + + return ( + + {paramsGroups.map((group, index) => ( + + + + ))} + + ) +} diff --git a/src/pages/Archive/index.jsx b/src/pages/Archive/index.jsx new file mode 100644 index 0000000..2ee941a --- /dev/null +++ b/src/pages/Archive/index.jsx @@ -0,0 +1,140 @@ +import moment from 'moment' +import { DatePicker } from 'antd' +import { useState, useEffect } from 'react' +import { TelemetryDataSaubService } from '../../services/api' +import { invokeWebApiWrapperAsync } from '../../components/factory' +import LoaderPortal from '../../components/LoaderPortal' +import { Flex } from '../../components/Grid' +import { PeriodPicker, defaultPeriod } from '../../components/PeriodPicker' +import { ArchiveDisplay } from './ArchiveDisplay' + +const DATA_COUNT = 2048 // Колличество точек на подгрузку графика +const ADDITIVE_PAGES = 2 // Дополнительные данные для графиков +const LOADING_TRIGGER = 0.5 +const MOUSE_SENSITIVITY = 1 / 530 + +const getLoadingInterval = (loaded, startDate, interval) => { + // Если данные загружены и дата не заходит за тригер дозагрузка не требуется + if ( + loaded && + +startDate - interval * LOADING_TRIGGER > loaded.start && + +startDate + interval * (LOADING_TRIGGER + 1) < loaded.end + ) + return { loadingStartDate: startDate, newLoaded: loaded, loadingInterval: 0 } + + let loadingStartDate = new Date(+startDate - interval * ADDITIVE_PAGES) + let loadingEndDate = new Date(+startDate + interval * (ADDITIVE_PAGES + 1)) + + const newLoaded = { + start: loadingStartDate, + end: loadingEndDate + } + + if (loaded) { + if (loadingStartDate >= loaded.start) + loadingStartDate = new Date(loaded.end) + if (loadingEndDate <= loaded.end) + loadingEndDate = new Date(loaded.start) + newLoaded.start = new Date(Math.min(loaded.start, loadingStartDate)) + newLoaded.end = new Date(Math.max(loaded.end, loadingEndDate)) + } + + const loadingInterval = Math.trunc((loadingEndDate - loadingStartDate) / 1000) + + return { + loadingStartDate, + newLoaded, + loadingInterval + } +} + +export default function Archive({idWell}) { + const [dataSaub, setDataSaub] = useState([]) + const [chartInterval, setChartInterval] = useState(parseInt(defaultPeriod) * 1000) + const [startDate, setStartDate] = useState(new Date(+new Date() - chartInterval)) + const [showLoader, setShowLoader] = useState(false) + const [loaded, setLoaded] = useState(null) + const [dateMinLimit, setDateMinLimit] = useState(0) + + const onGraphWheel = (e) => { + if (loaded) { + setStartDate((prevStartDate) => { + const offset = e.deltaY * chartInterval * MOUSE_SENSITIVITY + const nextStartDate = new Date(+prevStartDate + offset) + return new Date(Math.max(loaded.start, Math.min(nextStartDate, +loaded.end - chartInterval))) + }) + } + } + + useEffect(() => invokeWebApiWrapperAsync( + async () => { + const dates = await TelemetryDataSaubService.getDataDatesRange(idWell) + setDateMinLimit(new Date(dates?.from ?? 0)) + setStartDate(new Date(+new Date(dates?.to ?? new Date()) - chartInterval)) + }, + setShowLoader, + `Не удалось загрузить диапозон телеметрии для скважины "${idWell}"` + ), []) + + useEffect(() => { + setStartDate((startDate) => new Date(Math.min(+new Date() - chartInterval, startDate))) + }, [chartInterval]) + + useEffect(() => { + if (showLoader) return + const { loadingStartDate, loadingInterval, newLoaded } = getLoadingInterval(loaded, startDate, chartInterval) + if (loadingInterval <= 0) return + invokeWebApiWrapperAsync( + async () => { + const data = await TelemetryDataSaubService.getData(idWell, loadingStartDate.toISOString(), loadingInterval, DATA_COUNT) + + const loadedStartDate = new Date(Math.max(+newLoaded.start, +startDate - chartInterval * ADDITIVE_PAGES)) + const loadedEndDate = new Date(Math.min(+newLoaded.end, +startDate + chartInterval * (ADDITIVE_PAGES + 1))) + setLoaded({ start: loadedStartDate, end: loadedEndDate }) + + if (data) { + data.forEach(elm => elm.date = new Date(elm.date)) + setDataSaub((prevDataSaub) => { + const newData = [...prevDataSaub, ...data] + newData.sort((a, b) => a.date > b.date ? 1 : -1) + return newData.filter(val => { + const date = new Date(val.date) + return loadedStartDate < date && date < loadedEndDate + }) + }) + } + + }, + setShowLoader, + `Не удалось загрузить данные по скважине "${idWell}" c ${startDate.toISOString()} по ${new Date(+startDate + chartInterval).toISOString()}` + ) + }, [idWell, chartInterval, loaded, startDate]) + + return ( + <> + +
+ Начальная дата:  + setStartDate(new Date(startDate))} + value={moment(startDate)} + /> +
+
+ Период:  + setChartInterval(parseInt(val) * 1000)} /> +
+
+ + + + + ) +} diff --git a/src/pages/TelemetryView/Column.jsx b/src/pages/TelemetryView/Column.jsx deleted file mode 100644 index d67194e..0000000 --- a/src/pages/TelemetryView/Column.jsx +++ /dev/null @@ -1,142 +0,0 @@ -import { useState, useEffect } from 'react' -import { Grid, GridItem } from '../../components/Grid' -import { ChartTimeBase } from '../../components/charts/ChartTimeBase' -import { ChartTimeOnlineFooter } from './ChartTimeOnlineFooter' - -const stroke = (sz = '2px', c = 'white') => ({ textShadow: `-${sz} -${sz} 0 ${c}, ${sz} -${sz} 0 ${c}, -${sz} ${sz} 0 ${c}, ${sz} ${sz} 0 ${c}` }) - -const chartPluginsOptions = { - plugins: { - datalabels: { - backgroundColor: 'transparent', - borderRadius: 4, - color: '#000B', - display: context => (context.dataset.label === 'wellDepth') && 'auto', - formatter: value => `${value.y.toLocaleTimeString()} ${value.label.toPrecision(4)}`, - padding: 6, - align: 'left', - anchor: 'center', - clip: true - }, - legend: { display: false }, - tooltip: { enable: true } - } -} - -const GetLimitShape = (flowChartData, points, accessor) => { - const min = [], max = [] - - for (let point of points) { - const program = flowChartData.find(v => v.depthStart < point.depth && point.depth < v.depthEnd) - if (!program) continue - - min.push({ x: program[`${accessor}Min`], y: new Date(point.y), label: point.label }) - max.push({ x: program[`${accessor}Max`], y: new Date(point.y), label: point.label }) - } - - return min.concat(max.reverse()) ?? [] -} - -const GetRandomColor = () => '#' + Math.floor(Math.random()*16777215).toString(16) -const GetOrCreateDatasetByLineConfig = (data, lineConfig) => { - let dataset = data?.datasets.find(d => d.label === lineConfig.label) - if(!dataset) { - let color = lineConfig.borderColor - ?? lineConfig.backgroundColor - ?? lineConfig.color - ?? GetRandomColor() - - dataset = { - label: lineConfig.label, - data: [], - backgroundColor: lineConfig.backgroundColor ?? color, - borderColor: lineConfig.borderColor ?? color, - borderWidth: lineConfig.borderWidth ?? 1, - borderDash: lineConfig.dash ?? [], - showLine: lineConfig.showLine ?? !lineConfig.isShape, - fill: lineConfig.fill ?? (lineConfig.isShape ? 'shape' : 'none') - } - data.datasets.push(dataset); - } - return dataset -} - -export const Column = ({ lineGroup, data, flowChartData, interval, showBorder, style, headerHeight, yDisplay, yStart, showLastValues, pointCount }) => { - const [dataParams, setDataParams] = useState({data: {datasets:[]}, yStart: new Date(), }) - - let dataLast = data?.[data.length - 1] - let pv = lineGroup.filter(line => line.showLabels).map(line => ({ - color: line.color, - label: line.label, - unit: line.units, - value: dataLast?.[line.xAccessorName] - })) - - useEffect(()=>{ - if((lineGroup.length === 0) || (data.length === 0)) return - - setDataParams((preDataParams) => { - lineGroup.forEach(lineCfg => { - if (lineCfg.isShape) return - const dataset = GetOrCreateDatasetByLineConfig(preDataParams.data, lineCfg) - const points = data.map(dataItem => ({ - x: lineCfg.xConstValue ?? dataItem[lineCfg.xAccessorName], - label: dataItem[lineCfg.xAccessorName], - y: new Date(dataItem[lineCfg.yAccessorName]), - depth: dataItem.wellDepth - })).filter(point => (point.x ?? null) !== null && (point.y ?? null) !== null) - - const lineData = [ ...dataset.data, ...points,] - if(points?.length > 2) - lineData.sort((a,b) => a.y > b.y ? 1 : -1) - if(lineData.length > pointCount) - lineData.splice(0, (pointCount - lineData.length)) - - dataset.data = lineData - - //Area - if (flowChartData) { - lineGroup.filter(cfg => cfg.isShape && cfg.xAccessorName === lineCfg.xAccessorName).forEach(areaCfg => { - const dataset = GetOrCreateDatasetByLineConfig(preDataParams.data, areaCfg) - dataset.data = GetLimitShape(flowChartData, lineData, areaCfg.xAccessorName) - }) - } - }) - - preDataParams.yStart = yStart ?? new Date(Math.max(new Date(dataLast.date), preDataParams.yStart ?? new Date(0))) - if (!yStart) - preDataParams.yStart.setSeconds(preDataParams.yStart.getSeconds() - interval * 0.97) - preDataParams.yInterval = interval - preDataParams.displayLabels = yDisplay ?? false - return {...preDataParams} - }) - - }, [data, lineGroup, interval, yDisplay, yStart, flowChartData, dataLast, pointCount]) - - return ( -
- - {pv?.map((v, idx) => ( - {v.label} - ))} - -
- {showLastValues && ( - - {pv?.map((v, idx) => ( - {v.value?.toFixed(2) ?? '--'} {v.unit} - ))} - - )} - -
- {showLastValues && ( - - )} -
- ) -} - -Column.defaultProps = { - pointCount: 2048 -} diff --git a/src/pages/TelemetryView/MonitoringColumn.jsx b/src/pages/TelemetryView/MonitoringColumn.jsx new file mode 100644 index 0000000..2d7aa5d --- /dev/null +++ b/src/pages/TelemetryView/MonitoringColumn.jsx @@ -0,0 +1,80 @@ +import { Grid, GridItem } from '../../components/Grid' +import { Column, GetOrCreateDatasetByLineConfig } from '../../components/charts/Column' +import { ChartTimeOnlineFooter } from './ChartTimeOnlineFooter' +import { useEffect, useState } from 'react' + +const stroke = (sz = '2px', c = 'white') => ({ textShadow: `-${sz} -${sz} 0 ${c}, ${sz} -${sz} 0 ${c}, -${sz} ${sz} 0 ${c}, ${sz} ${sz} 0 ${c}` }) + +const GetLimitShape = (flowChartData, points, accessor) => { + const min = [], max = [] + + for (let point of points) { + const program = flowChartData.find(v => v.depthStart < point.depth && point.depth < v.depthEnd) + if (!program) continue + + min.push({ x: program[`${accessor}Min`], y: new Date(point.y), label: point.label }) + max.push({ x: program[`${accessor}Max`], y: new Date(point.y), label: point.label }) + } + + return min.concat(max.reverse()) ?? [] +} + +export const MonitoringColumn = ({ lineGroup, data, flowChartData, interval, showBorder, style, headerHeight }) => { + const [lineGroupWithoutShapes, setLineGroupWithoutShapes] = useState([]) + const dataLast = data?.[data.length - 1] + const yStart = new Date(+(dataLast?.date ? dataLast.date : new Date()) - interval) + const pv = lineGroup.filter(line => line.showLabels).map(line => ({ + color: line.color, + label: line.label, + unit: line.units, + value: dataLast?.[line.xAccessorName] + })) + + const postParsing = (data) => { + if (flowChartData) { + lineGroupWithoutShapes.forEach(lineCfg => { + const lineDataSet = GetOrCreateDatasetByLineConfig(data.data, lineCfg) + + lineGroup.filter(cfg => cfg.isShape && cfg.xAccessorName === lineCfg.xAccessorName).forEach(areaCfg => { + const dataset = GetOrCreateDatasetByLineConfig(data.data, areaCfg) + dataset.data = GetLimitShape(flowChartData, lineDataSet.data, areaCfg.xAccessorName) + }) + }) + } + } + + useEffect(() => { + setLineGroupWithoutShapes(lineGroup.filter(cfg => !cfg.isShape)) + }, [lineGroup]) + + return ( +
+ + {pv?.map((v, idx) => ( + {v.label} + ))} + +
+ + {pv?.map((v, idx) => ( + {v.value?.toFixed(2) ?? '--'} {v.unit} + ))} + + +
+ +
+ ) +} + +MonitoringColumn.defaultProps = { + pointCount: 2048 +} diff --git a/src/pages/TelemetryView/index.jsx b/src/pages/TelemetryView/index.jsx index 80cb9e7..8f81e94 100644 --- a/src/pages/TelemetryView/index.jsx +++ b/src/pages/TelemetryView/index.jsx @@ -1,7 +1,7 @@ import { useState, useEffect } from 'react' import { Select } from 'antd' -import { Column } from './Column' +import { MonitoringColumn } from './MonitoringColumn' import { CustomColumn } from './CustomColumn' import ActiveMessagesOnline from './ActiveMessagesOnline' import { ModeDisplay } from './ModeDisplay' @@ -22,6 +22,7 @@ import MomentStabPicEnabled from '../../images/DempherOn.png' import MomentStabPicDisabled from '../../images/DempherOff.png' import SpinPicEnabled from '../../images/SpinEnabled.png' import SpinPicDisabled from '../../images/SpinDisabled.png' +import { PeriodPicker, defaultPeriod } from '../../components/PeriodPicker' import '../../styles/message.css' @@ -259,19 +260,6 @@ export const paramsGroups = [ rotorTorqueGroup ] -const timePeriodCollection = [ - { value: '60', label: '1 минута' }, - { value: '300', label: '5 минут' }, - { value: '600', label: '10 минут' }, - { value: '1800', label: '30 минут' }, - { value: '3600', label: '1 час' }, - { value: '21600', label: '6 часов' }, - { value: '43200', label: '12 часов' }, - { value: '86400', label: '24 часа' } -] - -const defaultChartInterval = '600' - const getLast = (data) => Array.isArray(data) ? data.slice(-1)[0] : data @@ -305,13 +293,11 @@ const getIndexOfDrillingBy = (dataSaub) => { export default function TelemetryView({ idWell }) { const [dataSaub, setDataSaub] = useState([]) const [dataSpin, setDataSpin] = useState([]) - const [chartInterval, setChartInterval] = useState(defaultChartInterval) + const [chartInterval, setChartInterval] = useState(defaultPeriod) const [wellData, setWellData] = useState({ idState: 0 }) const [showLoader, setShowLoader] = useState(false) const [flowChartData, setFlowChartData] = useState([]) - const options = timePeriodCollection.map((line) => ) - const handleDataSaub = (data) => { if (data) { data.forEach((_, idx) => { @@ -382,9 +368,7 @@ export default function TelemetryView({ idWell }) {
Интервал:  - +
Статус:  @@ -406,7 +390,7 @@ export default function TelemetryView({ idWell }) { {paramsGroups.map((group, index) => - + showBorder={getIndexOfDrillingBy(dataSaub) === index} + /> )}