forked from ddrilling/asb_cloud_front
Добавлена подгрузка данных для архива
This commit is contained in:
parent
871e71e777
commit
59b1d49286
102
src/pages/Well/Telemetry/TelemetryView/archive_methods.js
Normal file
102
src/pages/Well/Telemetry/TelemetryView/archive_methods.js
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
import { range } from 'd3'
|
||||||
|
|
||||||
|
export const DATA_COUNT = 2048 // Колличество точек на подгрузку графика
|
||||||
|
export const ADDITIVE_PAGES = 2 // Дополнительные данные для графиков
|
||||||
|
export const LOADING_TRIGGER = 0.5 // Кол-во экранов до подгрузки
|
||||||
|
|
||||||
|
export const getLoadingInterval = (loaded, endDate, interval) => {
|
||||||
|
// Если данные загружены и дата не заходит за тригер дозагрузка не требуется
|
||||||
|
if (
|
||||||
|
(loaded && (loaded.start ?? null) !== null && (loaded.end ?? null) !== null) &&
|
||||||
|
+endDate - interval * (LOADING_TRIGGER + 1) > loaded.start &&
|
||||||
|
+endDate + interval * LOADING_TRIGGER < loaded.end
|
||||||
|
)
|
||||||
|
return { loadingStartDate: endDate, newLoaded: loaded, loadingInterval: 0 }
|
||||||
|
|
||||||
|
let loadingStartDate = +endDate - interval * (ADDITIVE_PAGES + 1)
|
||||||
|
let loadingEndDate = +endDate + interval * ADDITIVE_PAGES
|
||||||
|
|
||||||
|
const newLoaded = {
|
||||||
|
start: loadingStartDate,
|
||||||
|
end: loadingEndDate
|
||||||
|
}
|
||||||
|
|
||||||
|
if (loaded && (loaded.start ?? null) !== null && (loaded.end ?? null) !== null) {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
export const makeDateTimeDisabled = (dateLimit, chartInterval) => ({
|
||||||
|
disabledDate: (date) => {
|
||||||
|
if (!date) return false
|
||||||
|
const dt = new Date(date).setHours(0, 0, 0, 0)
|
||||||
|
return dt < dateLimit.from || dt > +dateLimit.to - chartInterval
|
||||||
|
},
|
||||||
|
disabledTime: (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
|
||||||
|
})
|
||||||
|
}),
|
||||||
|
})
|
@ -1,15 +1,16 @@
|
|||||||
import { useState, useEffect, useCallback, memo, useMemo } from 'react'
|
import { useState, useEffect, useCallback, memo, useMemo } from 'react'
|
||||||
import { BehaviorSubject, buffer, throttleTime } from 'rxjs'
|
import { BehaviorSubject, buffer, throttleTime } from 'rxjs'
|
||||||
import { useSearchParams } from 'react-router-dom'
|
import { useSearchParams } from 'react-router-dom'
|
||||||
import { Button, Select } from 'antd'
|
import { Alert, Button, Select } from 'antd'
|
||||||
|
|
||||||
import { useWell } from '@asb/context'
|
import { useWell } from '@asb/context'
|
||||||
import { DatePickerWrapper, makeDateSorter } from '@components/Table'
|
|
||||||
import { D3MonitoringCharts } from '@components/d3/monitoring'
|
|
||||||
import LoaderPortal from '@components/LoaderPortal'
|
import LoaderPortal from '@components/LoaderPortal'
|
||||||
|
import { CopyUrlButton } from '@components/CopyUrl'
|
||||||
|
import { D3MonitoringCharts } from '@components/d3/monitoring'
|
||||||
import { invokeWebApiWrapperAsync } from '@components/factory'
|
import { invokeWebApiWrapperAsync } from '@components/factory'
|
||||||
|
import { DatePickerWrapper, makeDateSorter } from '@components/Table'
|
||||||
import { PeriodPicker, defaultPeriod } from '@components/selectors/PeriodPicker'
|
import { PeriodPicker, defaultPeriod } from '@components/selectors/PeriodPicker'
|
||||||
import { formatDate, hasPermission, isRawDate, range, withPermissions } from '@utils'
|
import { formatDate, hasPermission, isRawDate, withPermissions } from '@utils'
|
||||||
import { Subscribe } from '@services/signalr'
|
import { Subscribe } from '@services/signalr'
|
||||||
import {
|
import {
|
||||||
DrillFlowChartService,
|
DrillFlowChartService,
|
||||||
@ -19,6 +20,7 @@ import {
|
|||||||
} from '@api'
|
} from '@api'
|
||||||
|
|
||||||
import { makeChartGroups, yAxis } from './dataset'
|
import { makeChartGroups, yAxis } from './dataset'
|
||||||
|
import { ADDITIVE_PAGES, cutData, DATA_COUNT, getLoadingInterval, makeDateTimeDisabled } from './archive_methods'
|
||||||
import ActiveMessagesOnline from './ActiveMessagesOnline'
|
import ActiveMessagesOnline from './ActiveMessagesOnline'
|
||||||
import TelemetrySummary from './TelemetrySummary'
|
import TelemetrySummary from './TelemetrySummary'
|
||||||
import WirelineRunOut from './WirelineRunOut'
|
import WirelineRunOut from './WirelineRunOut'
|
||||||
@ -60,7 +62,7 @@ export const normalizeData = (data) => data?.map(item => ({
|
|||||||
})) ?? []
|
})) ?? []
|
||||||
|
|
||||||
const dateSorter = makeDateSorter('date')
|
const dateSorter = makeDateSorter('date')
|
||||||
const defaultDate = () => Date.now() - defaultPeriod * 1000
|
const defaultDate = () => new Date(Date.now() - defaultPeriod * 1000)
|
||||||
|
|
||||||
const makeSubjectSubsription = (subject$, handler) => {
|
const makeSubjectSubsription = (subject$, handler) => {
|
||||||
const subscribtion = subject$.pipe(
|
const subscribtion = subject$.pipe(
|
||||||
@ -70,54 +72,33 @@ const makeSubjectSubsription = (subject$, handler) => {
|
|||||||
return () => subscribtion.unsubscribe()
|
return () => subscribtion.unsubscribe()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getRowDate = (row) => (row && isRawDate(row.date)) ? +new Date(row.date) : null
|
||||||
|
|
||||||
const TelemetryView = memo(() => {
|
const TelemetryView = memo(() => {
|
||||||
const [well, updateWell] = useWell()
|
const [well, updateWell] = useWell()
|
||||||
const [searchParams, setSearchParams] = useSearchParams()
|
const [searchParams, setSearchParams] = useSearchParams()
|
||||||
|
|
||||||
const [currentWellId, setCurrentWellId] = useState(null)
|
|
||||||
const [dataSaub, setDataSaub] = useState([])
|
const [dataSaub, setDataSaub] = useState([])
|
||||||
const [dataSpin, setDataSpin] = useState([])
|
const [dataSpin, setDataSpin] = useState([])
|
||||||
const [showLoader, setShowLoader] = useState(false)
|
const [showLoader, setShowLoader] = useState(false)
|
||||||
const [flowChartData, setFlowChartData] = useState([])
|
const [flowChartData, setFlowChartData] = useState([])
|
||||||
const [rop, setRop] = useState(null)
|
const [rop, setRop] = useState(null)
|
||||||
const [domain, setDomain] = useState({})
|
|
||||||
const [chartMethods, setChartMethods] = useState()
|
const [chartMethods, setChartMethods] = useState()
|
||||||
|
|
||||||
const [chartInterval, setChartInterval] = useState(defaultPeriod)
|
const [loadedDataRange, setLoadedDataRange] = useState({})
|
||||||
const [startDate, setStartDate] = useState(defaultDate)
|
const [chartInterval, setChartInterval] = useState(defaultPeriod * 1000)
|
||||||
const [dateLimit, setDateLimit] = useState({ from: 0, to: new Date() })
|
const [endDate, setEndDate] = useState(defaultDate)
|
||||||
|
const [dateLimit, setDateLimit] = useState({ from: 0, to: Date.now() })
|
||||||
|
|
||||||
const [archiveMode, setArchiveMode] = useState(false)
|
const [archiveMode, setArchiveMode] = useState(false)
|
||||||
|
|
||||||
const onStatusChanged = useCallback((value) => updateWell({ idState: value }), [well])
|
const onStatusChanged = useCallback((value) => updateWell({ idState: value }), [well])
|
||||||
|
|
||||||
const isDateDisabled = useCallback((date) => {
|
const dateTimeDisabled = useMemo(() => makeDateTimeDisabled(dateLimit, chartInterval), [dateLimit, chartInterval])
|
||||||
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])
|
|
||||||
|
|
||||||
const handleDataSaub = useCallback((data, replace = false) => {
|
const handleDataSaub = useCallback((data, replace = false) => {
|
||||||
setDataSaub((prev) => {
|
setDataSaub((prev) => {
|
||||||
if (!data)
|
if (!data || !Array.isArray(data))
|
||||||
return replace ? [] : prev
|
return replace ? [] : prev
|
||||||
const dataSaub = normalizeData(data)
|
const dataSaub = normalizeData(data)
|
||||||
const out = replace ? [...dataSaub] : [...prev, ...dataSaub]
|
const out = replace ? [...dataSaub] : [...prev, ...dataSaub]
|
||||||
@ -126,90 +107,71 @@ const TelemetryView = memo(() => {
|
|||||||
})
|
})
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
const handleDataSpin = useCallback((data) => data && setDataSpin((prev) => [...prev, ...data]), [])
|
const handleDataSpin = useCallback((data, replace) => {
|
||||||
|
setDataSpin((prev) => {
|
||||||
|
if (!data || !Array.isArray(data))
|
||||||
|
return replace ? [] : prev
|
||||||
|
return replace ? data : [...prev, ...data]
|
||||||
|
})
|
||||||
|
}, [])
|
||||||
|
|
||||||
const onWheel = useCallback((value) => {
|
const onWheel = useCallback((e) => {
|
||||||
if (!archiveMode && value.deltaY < 0) {
|
if (!archiveMode && e.deltaY < 0) {
|
||||||
setArchiveMode(true)
|
setArchiveMode(true)
|
||||||
// load data
|
setDataSaub([])
|
||||||
} else {
|
setDataSpin([])
|
||||||
// move
|
} else if (archiveMode) {
|
||||||
|
setEndDate((prevEndDate) => {
|
||||||
|
const offset = e.deltaY / 100 * chartInterval * 0.15 // сдвиг в 15% интервала
|
||||||
|
const nextEndDate = +prevEndDate + offset
|
||||||
|
const firstPossibleDate = Math.max(loadedDataRange?.start || 0, dateLimit.from) + chartInterval
|
||||||
|
const lastPossibleDate = Math.min(dateLimit.to, (loadedDataRange?.end ?? Date.now()))
|
||||||
|
const out = new Date(Math.max(firstPossibleDate, Math.min(nextEndDate, lastPossibleDate)))
|
||||||
|
if (e.deltaY > 0 && +out >= lastPossibleDate) { // Автопереход к актуальным данным при прокручивании в самый низ
|
||||||
|
setArchiveMode(false)
|
||||||
|
setLoadedDataRange(null)
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}, [archiveMode])
|
}, [archiveMode, loadedDataRange, chartInterval, dateLimit])
|
||||||
|
|
||||||
const spinLast = useMemo(() => dataSpin.at(-1), [dataSpin])
|
const domain = useMemo(() => ({ min: new Date(+endDate - chartInterval), max: endDate }), [endDate, chartInterval])
|
||||||
const saubLast = useMemo(() => dataSaub.at(-1), [dataSaub])
|
|
||||||
|
const spinLast = useMemo(() => getLast(dataSpin), [dataSpin])
|
||||||
|
const saubLast = useMemo(() => getLast(dataSaub), [dataSaub])
|
||||||
const summaryData = useMemo(() => ({ ...saubLast, ...rop }), [saubLast, rop])
|
const summaryData = useMemo(() => ({ ...saubLast, ...rop }), [saubLast, rop])
|
||||||
|
|
||||||
const saubSubject$ = useMemo(() => new BehaviorSubject(), [])
|
const saubSubject$ = useMemo(() => new BehaviorSubject(), [])
|
||||||
const spinSubject$ = useMemo(() => new BehaviorSubject(), [])
|
const spinSubject$ = useMemo(() => new BehaviorSubject(), [])
|
||||||
|
|
||||||
const filteredData = useMemo(() => {
|
|
||||||
let i, j
|
|
||||||
for (i = 0; i < dataSaub.length; i++) {
|
|
||||||
const date = +new Date(dataSaub[i]?.date)
|
|
||||||
if (date >= +domain.min) break
|
|
||||||
}
|
|
||||||
|
|
||||||
for (j = dataSaub.length - 1; j >= i; j--) {
|
|
||||||
const date = +new Date(dataSaub[i]?.date)
|
|
||||||
if (date <= +domain.max) break
|
|
||||||
}
|
|
||||||
|
|
||||||
if (i >= j) return []
|
|
||||||
return dataSaub.slice(i, j)
|
|
||||||
}, [dataSaub, domain])
|
|
||||||
|
|
||||||
const chartGroups = useMemo(() => makeChartGroups(flowChartData), [flowChartData])
|
const chartGroups = useMemo(() => makeChartGroups(flowChartData), [flowChartData])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setArchiveMode(isRawDate(searchParams.get('start')))
|
if (!searchParams.has('range') || !searchParams.has('end')) return
|
||||||
const interval = parseInt(searchParams.get('range') || defaultPeriod)
|
setArchiveMode(isRawDate(searchParams.get('end')))
|
||||||
const date = new Date(searchParams.get('start') || (Date.now() - interval))
|
const interval = parseInt(searchParams.get('range') || defaultPeriod) * 1000
|
||||||
|
const date = new Date(searchParams.get('end') || Date.now())
|
||||||
setChartInterval(interval)
|
setChartInterval(interval)
|
||||||
setStartDate(date)
|
setEndDate(date)
|
||||||
}, [searchParams])
|
}, []) // Получение параметров должно работать только при открытии страницы
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (archiveMode) return
|
const params = {}
|
||||||
const subscribtion = saubSubject$.pipe(
|
if (archiveMode) {
|
||||||
buffer(saubSubject$.pipe(throttleTime(700)))
|
if (endDate)
|
||||||
).subscribe((data) => handleDataSaub(data.flat().filter(Boolean)))
|
params.end = endDate.toISOString()
|
||||||
|
if (chartInterval)
|
||||||
return () => subscribtion.unsubscribe()
|
params.range = parseInt(chartInterval / 1000)
|
||||||
}, [saubSubject$, archiveMode])
|
}
|
||||||
|
setSearchParams(params)
|
||||||
useEffect(() => {
|
}, [archiveMode, endDate, chartInterval])
|
||||||
if (archiveMode) return
|
|
||||||
const subscribtion = spinSubject$.pipe(
|
|
||||||
buffer(spinSubject$.pipe(throttleTime(700)))
|
|
||||||
).subscribe((data) => handleDataSpin(data.flat().filter(Boolean)))
|
|
||||||
|
|
||||||
return () => subscribtion.unsubscribe()
|
|
||||||
}, [spinSubject$, archiveMode])
|
|
||||||
|
|
||||||
useEffect(() => makeSubjectSubsription(saubSubject$, handleDataSaub), [saubSubject$, handleDataSaub])
|
useEffect(() => makeSubjectSubsription(saubSubject$, handleDataSaub), [saubSubject$, handleDataSaub])
|
||||||
useEffect(() => makeSubjectSubsription(spinSubject$, handleDataSpin), [spinSubject$, handleDataSpin])
|
useEffect(() => makeSubjectSubsription(spinSubject$, handleDataSpin), [spinSubject$, handleDataSpin])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (currentWellId == well.id) return
|
if (archiveMode) return
|
||||||
setCurrentWellId(well.id)
|
|
||||||
invokeWebApiWrapperAsync(
|
|
||||||
async () => {
|
|
||||||
const flowChart = await DrillFlowChartService.getByIdWell(well.id)
|
|
||||||
const dataSaub = await TelemetryDataSaubService.getData(well.id, null, chartInterval)
|
|
||||||
const dataSpin = await TelemetryDataSpinService.getData(well.id, null, chartInterval)
|
|
||||||
setFlowChartData(flowChart ?? [])
|
|
||||||
handleDataSaub(dataSaub, true)
|
|
||||||
handleDataSpin(dataSpin)
|
|
||||||
},
|
|
||||||
null,
|
|
||||||
`Не удалось получить данные`,
|
|
||||||
{ actionName: 'Получение данных по скважине', well }
|
|
||||||
)
|
|
||||||
}, [well, chartInterval, currentWellId, handleDataSaub])
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const unsubscribe = Subscribe(
|
const unsubscribe = Subscribe(
|
||||||
'hubs/telemetry', `well_${well.id}`,
|
'hubs/telemetry', `well_${well.id}`,
|
||||||
{ methodName: 'ReceiveDataSaub', handler: (data) => saubSubject$.next(data) },
|
{ methodName: 'ReceiveDataSaub', handler: (data) => saubSubject$.next(data) },
|
||||||
@ -217,11 +179,56 @@ const TelemetryView = memo(() => {
|
|||||||
)
|
)
|
||||||
|
|
||||||
return () => unsubscribe()
|
return () => unsubscribe()
|
||||||
}, [well.id, saubSubject$, spinSubject$])
|
}, [archiveMode, well.id, saubSubject$, spinSubject$])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (archiveMode) return
|
||||||
|
invokeWebApiWrapperAsync(
|
||||||
|
async () => {
|
||||||
|
const dataSaub = await TelemetryDataSaubService.getData(well.id, null, chartInterval / 1000)
|
||||||
|
const dataSpin = await TelemetryDataSpinService.getData(well.id, null, chartInterval / 1000)
|
||||||
|
handleDataSaub(dataSaub, true)
|
||||||
|
handleDataSpin(dataSpin, true)
|
||||||
|
},
|
||||||
|
setShowLoader,
|
||||||
|
`Не удалось получить данные`,
|
||||||
|
{ actionName: 'Получение данных по скважине', well }
|
||||||
|
)
|
||||||
|
}, [archiveMode, chartInterval, well])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!archiveMode || showLoader) return
|
||||||
|
const { loadingStartDate, loadingInterval, newLoaded } = getLoadingInterval(loadedDataRange, endDate, 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, +endDate - chartInterval * (ADDITIVE_PAGES + 1)))
|
||||||
|
const loadedEndDate = new Date(Math.min(+newLoaded.end, +endDate + chartInterval * ADDITIVE_PAGES))
|
||||||
|
setLoadedDataRange({ start: loadedStartDate, end: loadedEndDate })
|
||||||
|
|
||||||
|
if (data) {
|
||||||
|
data.forEach(elm => elm.date = new Date(elm.date))
|
||||||
|
setDataSaub((prevDataSaub) => {
|
||||||
|
const newData = [...prevDataSaub, ...normalizeData(data)]
|
||||||
|
newData.sort(dateSorter)
|
||||||
|
return cutData(newData, loadedStartDate, loadedEndDate)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
setShowLoader,
|
||||||
|
`Не удалось загрузить данные c ${formatDate(newLoaded.start)} по ${formatDate(newLoaded.end)}`,
|
||||||
|
{ actionName: 'Загрузка телеметрий в диапозоне', well }
|
||||||
|
)
|
||||||
|
}, [archiveMode, showLoader, well, chartInterval, loadedDataRange, endDate, handleDataSaub])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
invokeWebApiWrapperAsync(
|
invokeWebApiWrapperAsync(
|
||||||
async () => {
|
async () => {
|
||||||
|
const flowChart = await DrillFlowChartService.getByIdWell(well.id)
|
||||||
|
setFlowChartData(flowChart ?? [])
|
||||||
const rop = await OperationStatService.getClusterRopStatByIdWell(well.id)
|
const rop = await OperationStatService.getClusterRopStatByIdWell(well.id)
|
||||||
setRop(rop)
|
setRop(rop)
|
||||||
let dates = await TelemetryDataSaubService.getDataDatesRange(well.id)
|
let dates = await TelemetryDataSaubService.getDataDatesRange(well.id)
|
||||||
@ -232,72 +239,81 @@ const TelemetryView = memo(() => {
|
|||||||
setDateLimit(dates)
|
setDateLimit(dates)
|
||||||
},
|
},
|
||||||
setShowLoader,
|
setShowLoader,
|
||||||
`Не удалось загрузить данные`,
|
e => `Не удалось загрузить данные: ${String(e)}`,
|
||||||
{ actionName: 'Получение данных по скважине', well }
|
{ actionName: 'Получение данных по скважине', well }
|
||||||
)
|
)
|
||||||
}, [well])
|
}, [well])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!saubLast || archiveMode) return
|
if (archiveMode || !saubLast || !isRawDate(saubLast.date)) return
|
||||||
const last = new Date(saubLast.date)
|
setEndDate(new Date(saubLast.date))
|
||||||
const startDate = new Date(+last - chartInterval * 1000)
|
}, [archiveMode, saubLast])
|
||||||
setStartDate(startDate)
|
|
||||||
setDomain({ min: startDate, max: last })
|
|
||||||
}, [archiveMode, saubLast, chartInterval])
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<LoaderPortal show={showLoader} style={{ flexGrow: 1 }}>
|
<LoaderPortal show={showLoader} style={{ flexGrow: 1 }}>
|
||||||
<div className={'telemetry-view-page'}>
|
<div className={'telemetry-view-page'}>
|
||||||
<TelemetrySummary data={summaryData} />
|
<div className={'archive-alert-wrapper'}>
|
||||||
<div className={'page-top'}>
|
<TelemetrySummary data={summaryData} />
|
||||||
<div>
|
<div className={'page-top'}>
|
||||||
Статус:
|
<div>
|
||||||
<Select value={well.idState ?? 0} onChange={onStatusChanged} disabled={!hasPermission('Well.edit')}>
|
Статус:
|
||||||
<Option value={0} disabled hidden>Неизвестно</Option>
|
<Select value={well.idState ?? 0} onChange={onStatusChanged} disabled={!hasPermission('Well.edit')}>
|
||||||
<Option value={1}>В работе</Option>
|
<Option value={0} disabled hidden>Неизвестно</Option>
|
||||||
<Option value={2}>Завершено</Option>
|
<Option value={1}>В работе</Option>
|
||||||
</Select>
|
<Option value={2}>Завершено</Option>
|
||||||
</div>
|
</Select>
|
||||||
<Setpoints />
|
</div>
|
||||||
<WirelineRunOut />
|
<Setpoints />
|
||||||
<div className={'icons'}>
|
<WirelineRunOut />
|
||||||
<img src={isTorqueStabEnabled(spinLast) ? MomentStabPicEnabled : MomentStabPicDisabled} alt={'TorqueMaster'} />
|
<div className={'icons'}>
|
||||||
<img src={isSpinEnabled(spinLast) ? SpinPicEnabled : SpinPicDisabled} alt={'SpinMaster'} />
|
<img src={isTorqueStabEnabled(spinLast) ? MomentStabPicEnabled : MomentStabPicDisabled} alt={'TorqueMaster'} />
|
||||||
<h2 style={{ marginBottom: 0, fontWeight: 'bold', color: isMseEnabled(saubLast) ? 'green' : 'lightgrey' }}>MSE</h2>
|
<img src={isSpinEnabled(spinLast) ? SpinPicEnabled : SpinPicDisabled} alt={'SpinMaster'} />
|
||||||
</div>
|
<h2 style={{ marginBottom: 0, fontWeight: 'bold', color: isMseEnabled(saubLast) ? 'green' : 'lightgrey' }}>MSE</h2>
|
||||||
</div>
|
</div>
|
||||||
<div className={'page-top'}>
|
</div>
|
||||||
<div>
|
{archiveMode && (
|
||||||
Начальная дата:
|
<div className={'archive-alert'}>
|
||||||
<DatePickerWrapper
|
<Alert
|
||||||
disabled={!archiveMode}
|
showIcon
|
||||||
value={startDate}
|
type={'warning'}
|
||||||
disabledDate={isDateDisabled}
|
message={'В режиме архива данные не обновляются!'}
|
||||||
disabledTime={isDateTimeDisabled}
|
description={'Для возобновления обновления пролистайте вниз до коцна или нажмите кнопку "Выйти из архива"'}
|
||||||
onChange={(startDate) => setStartDate(new Date(startDate))}
|
/>
|
||||||
/>
|
</div>
|
||||||
</div>
|
)}
|
||||||
<div>
|
</div>
|
||||||
Интервал:
|
<div className={'page-top'}>
|
||||||
<PeriodPicker onChange={setChartInterval} />
|
<div>
|
||||||
</div>
|
Последняя дата:
|
||||||
<Button onClick={() => chartMethods?.setSettingsVisible(true)}>Настроить графики</Button>
|
<DatePickerWrapper
|
||||||
<Button onClick={() => setArchiveMode((prev) => !prev)}>
|
{...dateTimeDisabled}
|
||||||
{archiveMode ? 'Выйти из архива' : 'Войти в архив'}
|
value={endDate}
|
||||||
</Button>
|
disabled={!archiveMode}
|
||||||
</div>
|
onChange={(endDate) => setEndDate(new Date(endDate))}
|
||||||
<D3MonitoringCharts
|
/>
|
||||||
{...chartProps}
|
</div>
|
||||||
yDomain={domain}
|
<div>
|
||||||
data={filteredData}
|
Интервал:
|
||||||
methods={setChartMethods}
|
<PeriodPicker value={chartInterval / 1000} onChange={(value) => setChartInterval(value * 1000)} />
|
||||||
datasetGroups={chartGroups}
|
</div>
|
||||||
onWheel={onWheel}
|
<Button onClick={() => chartMethods?.setSettingsVisible(true)}>Настроить графики</Button>
|
||||||
/>
|
<Button onClick={() => setArchiveMode((prev) => !prev)} danger={archiveMode}>
|
||||||
<ActiveMessagesOnline well={well} />
|
{archiveMode ? 'Выйти из архива' : 'Войти в архив'}
|
||||||
</div>
|
</Button>
|
||||||
</LoaderPortal>
|
{archiveMode && <CopyUrlButton />}
|
||||||
)
|
</div>
|
||||||
|
<D3MonitoringCharts
|
||||||
|
{...chartProps}
|
||||||
|
yDomain={domain}
|
||||||
|
data={dataSaub}
|
||||||
|
methods={setChartMethods}
|
||||||
|
datasetGroups={chartGroups}
|
||||||
|
onWheel={onWheel}
|
||||||
|
/>
|
||||||
|
<ActiveMessagesOnline well={well} />
|
||||||
|
</div>
|
||||||
|
</LoaderPortal>
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
export default withPermissions(TelemetryView, [
|
export default withPermissions(TelemetryView, [
|
||||||
|
@ -53,6 +53,26 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.archive-alert-wrapper {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 10px;
|
||||||
|
position: relative;
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.archive-alert {
|
||||||
|
display: flex;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
background-color: #0001;
|
||||||
|
}
|
||||||
|
|
||||||
@media only screen and (max-width: 1280px) {
|
@media only screen and (max-width: 1280px) {
|
||||||
.telemetry-view-page {
|
.telemetry-view-page {
|
||||||
--page-gap: 7.5px;
|
--page-gap: 7.5px;
|
||||||
|
Loading…
Reference in New Issue
Block a user