diff --git a/src/pages/Well/NavigationMenu.jsx b/src/pages/Well/NavigationMenu.jsx index e6b9181..1b53e9b 100644 --- a/src/pages/Well/NavigationMenu.jsx +++ b/src/pages/Well/NavigationMenu.jsx @@ -4,7 +4,6 @@ import { BarChartOutlined, BuildOutlined, ControlOutlined, - DatabaseOutlined, DeploymentUnitOutlined, ExperimentOutlined, FilePdfOutlined, @@ -20,7 +19,6 @@ export const menuItems = [ makeItem('Телеметрия', 'telemetry', [], , [ makeItem('Мониторинг', 'telemetry', [], ), makeItem('Сообщения', 'messages', [], ), - makeItem('Архив', 'archive', [], ), makeItem('ННБ', 'dashboard_nnb', [], ), makeItem('Операции', 'operations', [], ), makeItem('Наработка', 'operation_time', [], ), diff --git a/src/pages/Well/Telemetry/Archive/index.jsx b/src/pages/Well/Telemetry/Archive/index.jsx deleted file mode 100644 index 8c8c493..0000000 --- a/src/pages/Well/Telemetry/Archive/index.jsx +++ /dev/null @@ -1,270 +0,0 @@ -/* eslint-disable react-hooks/exhaustive-deps */ -import { useState, useEffect, memo, useCallback, useMemo } from 'react' -import { useSearchParams } from 'react-router-dom' -import { Select } from 'antd' - -import { useWell } from '@asb/context' -import { Flex } from '@components/Grid' -import { D3MonitoringCharts } from '@components/d3/monitoring' -import { CopyUrlButton } from '@components/CopyUrl' -import LoaderPortal from '@components/LoaderPortal' -import { invokeWebApiWrapperAsync } from '@components/factory' -import { DatePickerWrapper, makeDateSorter } from '@components/Table' -import { PeriodPicker, defaultPeriod } from '@components/selectors/PeriodPicker' -import { formatDate, range, withPermissions } from '@utils' -import { TelemetryDataSaubService } from '@api' - -import { normalizeData } from '../TelemetryView' -import { cursorRender } from '../TelemetryView/cursorRender' -import { makeChartGroups, yAxis } from '../TelemetryView/dataset' - -const DATA_COUNT = 2048 // Колличество точек на подгрузку графика -const ADDITIVE_PAGES = 2 // Дополнительные данные для графиков -const LOADING_TRIGGER = 0.5 - -const scrollOptions = [ - { label: '10%', value: 0.1 }, - { label: '15%', value: 0.15 }, - { label: '25%', value: 0.25 }, -] - -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 = +startDate - interval * ADDITIVE_PAGES - let loadingEndDate = +startDate + interval * (ADDITIVE_PAGES + 1) - - const newLoaded = { - start: loadingStartDate, - end: loadingEndDate - } - - if (loaded) { - if (loadingStartDate >= loaded.start) - loadingStartDate = loaded.end - if (loadingEndDate <= loaded.end) - loadingEndDate = loaded.start - newLoaded.start = Math.min(loaded.start, loadingStartDate) - newLoaded.end = Math.max(loaded.end, loadingEndDate) - } - - const loadingInterval = Math.trunc((loadingEndDate - loadingStartDate) / 1000) - - return { - loadingStartDate: new Date(loadingStartDate), - newLoaded: { - start: new Date(newLoaded.start), - end: new Date(newLoaded.end) - }, - loadingInterval - } -} - -const interpolationSearch = (data, begin, end, accessor) => { - const fy = (i) => new Date(data[i]?.[accessor] ?? 0) - const fx = (y, b, e) => Math.round(b + (y - fy(b)) * (e - b) / (fy(e) - fy(b))) - const findIdx = (val, startIdx, c) => { - let x = startIdx - let endIdx = data.length - 1 - if(val < fy(startIdx)) - return startIdx - if(val > fy(endIdx)) - return endIdx - for(let i = 0; i < c; i++){ - x = fx(val, startIdx, endIdx) - if(fy(x) < val) - startIdx = x - else - endIdx = x - if ((startIdx === endIdx)||(fy(startIdx) === fy(endIdx))) - return x - } - return x - } - let x0 = findIdx(begin, 0, 100) - let x1 = findIdx(end, x0, 100) - return { start: x0, end: x1, count: x1 - x0 } -} - -export const cutData = (data, beginDate, endDate) => { - if (data?.length > 0) { - let { start, end } = interpolationSearch(data, beginDate, endDate, 'date') - if (start > 0) start-- - if (end + 1 < end.length) end++ - return data.slice(start, end) - } - return data -} - -const chartGroups = makeChartGroups([]) - -const Archive = memo(() => { - const [dataSaub, setDataSaub] = useState([]) - const [dateLimit, setDateLimit] = useState({ from: 0, to: new Date() }) - const [showLoader, setShowLoader] = useState(false) - const [loaded, setLoaded] = useState(null) - - const [well] = useWell() - const [search, setSearchParams] = useSearchParams() - - const getInitialRange = useCallback(() => parseInt(search.get('range') ?? defaultPeriod) * 1000, [search]) - - const [scrollPercent, setScrollPercent] = useState(0.15) - const [chartInterval, setChartInterval] = useState(getInitialRange) - const getInitialDate = useCallback(() => new Date(search.get('start') ?? (Date.now() - chartInterval)), [search, chartInterval]) - const [startDate, setStartDate] = useState(getInitialDate) - - const onGraphWheel = useCallback((e) => { - if (loaded && dateLimit.from && dateLimit.to) { - setStartDate((prevStartDate) => { - const offset = e.deltaY / 100 * chartInterval * scrollPercent - const nextStartDate = +prevStartDate + offset - const firstPossibleDate = Math.max(loaded.start, dateLimit.from) - const lastPossibleDate = Math.min(dateLimit.to, (loaded.end ?? Date.now())) - chartInterval - return new Date(Math.max(firstPossibleDate, Math.min(nextStartDate, lastPossibleDate))) - }) - } - }, [loaded, dateLimit, chartInterval, scrollPercent]) - - const isDateDisabled = useCallback((date) => { - if (!date) return false - const dt = new Date(date).setHours(0, 0, 0, 0) - return dt < dateLimit.from || dt > +dateLimit.to - chartInterval - }, [dateLimit, chartInterval]) - - const isDateTimeDisabled = useCallback((date) => ({ - disabledHours: () => range(24).filter(h => { - if (!date) return false - const dt = +new Date(date).setHours(h) - return dt < dateLimit.from || dt > +dateLimit.to - chartInterval - }), - disabledMinutes: () => range(60).filter(m => { - if (!date) return false - const dt = +new Date(date).setMinutes(m) - return dt < dateLimit.from || dt > +dateLimit.to - chartInterval - }), - disabledSeconds: () => range(60).filter(s => { - if (!date) return false - const dt = +new Date(date).setSeconds(s) - return dt < dateLimit.from || dt > +dateLimit.to - chartInterval - }) - }), [dateLimit, chartInterval]) - - useEffect(() => { - const params = {} - if (startDate) - params.start = startDate.toISOString() - if (chartInterval) - params.range = chartInterval / 1000 - setSearchParams(params) - }, [startDate, chartInterval]) - - useEffect(() => { - invokeWebApiWrapperAsync( - async () => { - let dates = await TelemetryDataSaubService.getDataDatesRange(well.id) - dates = { - from: new Date(dates?.from ?? 0), - to: new Date(dates?.to ?? 0) - } - setDateLimit(dates) - }, - setShowLoader, - `Не удалось загрузить диапозон телеметрии`, - { actionName: 'Загрузка диапозона телеметрии', well } - ) - }, [well]) - - useEffect(() => { - setStartDate((prev) => new Date(Math.max(dateLimit.from, Math.min(+prev, +dateLimit.to - chartInterval)))) - }, [chartInterval, dateLimit]) - - useEffect(() => { - if (showLoader) return - const { loadingStartDate, loadingInterval, newLoaded } = getLoadingInterval(loaded, startDate, chartInterval) - if (loadingInterval <= 0) return - invokeWebApiWrapperAsync( - async () => { - const data = await TelemetryDataSaubService.getData(well.id, 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, ...normalizeData(data)] - newData.sort(makeDateSorter('date')) - return cutData(newData, loadedStartDate, loadedEndDate) - }) - } - - }, - setShowLoader, - `Не удалось загрузить данные c ${formatDate(startDate)} по ${formatDate(+startDate + chartInterval)}`, - { actionName: 'Загрузка телеметрий в диапозоне', well } - ) - }, [well, chartInterval, loaded, startDate]) - - const onRangeChange = useCallback((value) => { - setChartInterval(value * 1000) - setDataSaub([]) - setLoaded(null) - }, []) - - const domain = useMemo(() => ({ min: startDate, max: new Date(+startDate + chartInterval)}), [startDate, chartInterval]) - const chartData = useMemo(() => cutData(dataSaub, domain.min, domain.max), [dataSaub, domain]) - - return ( - - - - Начальная дата: - setStartDate(new Date(startDate))} - /> - - - Период: - - - - Прокрутка: - - - - - formatDate(d) - }} - plugins={{ - menu: { enabled: false }, - cursor: { - render: cursorRender, - } - }} - style={{ flexGrow: 1 }} - height={'76vh'} - onWheel={onGraphWheel} - /> - - ) -}) - -export default withPermissions(Archive, ['TelemetryDataSaub.get']) diff --git a/src/pages/Well/index.jsx b/src/pages/Well/index.jsx index 398a0b8..c54347c 100644 --- a/src/pages/Well/index.jsx +++ b/src/pages/Well/index.jsx @@ -28,7 +28,6 @@ const WellSectionsStat = lazy(() => import('./WellOperations/WellSectionsStat')) const WellOperationsEditorFact = lazy(() => import('./WellOperations/OperationEditor/Fact')) const WellOperationsEditorPlan = lazy(() => import('./WellOperations/OperationEditor/Plan')) -const Archive = lazy(() => import('./Telemetry/Archive')) const Messages = lazy(() => import('./Telemetry/Messages')) const Operations = lazy(() => import('./Telemetry/Operations')) const DashboardNNB = lazy(() => import('./Telemetry/DashboardNNB')) @@ -99,7 +98,6 @@ const Well = memo(() => { } /> } /> - } /> } /> } /> } />