forked from ddrilling/asb_cloud_front
Мониторинг, сообщения и архив перенесены в группу телеметрия. Улучшена мемоизация Setpoints, так же они вынесены в общую директорию в TelemetryView
This commit is contained in:
parent
db870d8586
commit
82183532b8
31
src/pages/Telemetry/Archive/ArchiveColumn.jsx
Normal file
31
src/pages/Telemetry/Archive/ArchiveColumn.jsx
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
import { memo, useMemo } from 'react'
|
||||||
|
|
||||||
|
import { Grid, GridItem } from '@components/Grid'
|
||||||
|
import { Column } from '@components/charts/Column'
|
||||||
|
|
||||||
|
export const ArchiveColumn = memo(({ lineGroup, data, interval, style, headerHeight, yStart }) => {
|
||||||
|
const lgws = useMemo(() => lineGroup.filter(cfg => !cfg.isShape), [lineGroup])
|
||||||
|
const pv = useMemo(() => lgws.filter(line => line.showLabels).map(line => ({
|
||||||
|
color: line.color,
|
||||||
|
label: line.label
|
||||||
|
})), [lgws])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={style}>
|
||||||
|
<Grid style={{ height: headerHeight, margin: 0 }}>
|
||||||
|
{pv?.map((v, idx) => (
|
||||||
|
<GridItem className={'display_chart_label'} key={idx} row={idx} col={1} style={{ color: v.color, padding: '0 8px' }}>{v.label}</GridItem>
|
||||||
|
))}
|
||||||
|
</Grid>
|
||||||
|
<Column
|
||||||
|
data={data}
|
||||||
|
lineGroup={lgws}
|
||||||
|
interval={interval}
|
||||||
|
yDisplay={false}
|
||||||
|
yStart={yStart}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
export default ArchiveColumn
|
66
src/pages/Telemetry/Archive/ArchiveDisplay.jsx
Normal file
66
src/pages/Telemetry/Archive/ArchiveDisplay.jsx
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
import { memo, useMemo } from 'react'
|
||||||
|
|
||||||
|
import { Grid, GridItem } from '@components/Grid'
|
||||||
|
|
||||||
|
import { paramsGroups } from '../TelemetryView'
|
||||||
|
import { ArchiveColumn } from './ArchiveColumn'
|
||||||
|
|
||||||
|
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, 5)
|
||||||
|
let x1 = findIdx(end, x0, 3)
|
||||||
|
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 ArchiveDisplay = memo(({data, startDate, interval, onWheel}) => {
|
||||||
|
const endDate = useMemo(() => new Date(+startDate + interval), [startDate, interval])
|
||||||
|
const chartData = useMemo(() => cutData(data, startDate, endDate), [data, startDate, endDate])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Grid onWheel={onWheel}>
|
||||||
|
{paramsGroups.map((group, index) => (
|
||||||
|
<GridItem col={index + 1} row={'1'} className={'border_small'} key={`${group.label}${index}`} style={{ padding: 0 }}>
|
||||||
|
<ArchiveColumn
|
||||||
|
style={{ width: '15vw' }}
|
||||||
|
data={chartData}
|
||||||
|
lineGroup={group}
|
||||||
|
interval={interval}
|
||||||
|
headerHeight={'50px'}
|
||||||
|
yStart={startDate}
|
||||||
|
/>
|
||||||
|
</GridItem>
|
||||||
|
))}
|
||||||
|
</Grid>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
export default ArchiveDisplay
|
185
src/pages/Telemetry/Archive/index.jsx
Normal file
185
src/pages/Telemetry/Archive/index.jsx
Normal file
@ -0,0 +1,185 @@
|
|||||||
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
|
import { useState, useEffect, memo, useCallback } from 'react'
|
||||||
|
|
||||||
|
import { Flex } from '@components/Grid'
|
||||||
|
import LoaderPortal from '@components/LoaderPortal'
|
||||||
|
import { invokeWebApiWrapperAsync } from '@components/factory'
|
||||||
|
import { DatePickerWrapper, makeDateSorter } from '@components/Table'
|
||||||
|
import { PeriodPicker, defaultPeriod } from '@components/selectors/PeriodPicker'
|
||||||
|
import { TelemetryDataSaubService } from '@api'
|
||||||
|
|
||||||
|
import { normalizeData } from '../TelemetryView'
|
||||||
|
import { ArchiveDisplay, cutData } from './ArchiveDisplay'
|
||||||
|
|
||||||
|
const DATA_COUNT = 2048 // Колличество точек на подгрузку графика
|
||||||
|
const ADDITIVE_PAGES = 2 // Дополнительные данные для графиков
|
||||||
|
const LOADING_TRIGGER = 0.5
|
||||||
|
const WHEEL_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 = +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 range = (start, end) => {
|
||||||
|
const result = []
|
||||||
|
for (let i = start; i < end; i++)
|
||||||
|
result.push(i)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Archive = memo(({ idWell }) => {
|
||||||
|
const [dataSaub, setDataSaub] = useState([])
|
||||||
|
const [dateLimit, setDateLimit] = useState({ from: 0, to: new Date() })
|
||||||
|
const [chartInterval, setChartInterval] = useState(parseInt(defaultPeriod) * 1000)
|
||||||
|
const [startDate, setStartDate] = useState(new Date(Date.now() - chartInterval))
|
||||||
|
const [showLoader, setShowLoader] = useState(false)
|
||||||
|
const [loaded, setLoaded] = useState(null)
|
||||||
|
|
||||||
|
const onGraphWheel = useCallback((e) => {
|
||||||
|
if (loaded && dateLimit.from && dateLimit.to) {
|
||||||
|
setStartDate((prevStartDate) => {
|
||||||
|
const offset = e.deltaY * chartInterval * WHEEL_SENSITIVITY
|
||||||
|
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])
|
||||||
|
|
||||||
|
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])
|
||||||
|
|
||||||
|
const isDateTimeDisabled = useCallback((date) => ({
|
||||||
|
disabledHours: () => range(0, 24).filter(h => {
|
||||||
|
if (!date) return false
|
||||||
|
const dt = +new Date(date).setHours(h)
|
||||||
|
return dt < dateLimit.from || dt > +dateLimit.to - chartInterval
|
||||||
|
}),
|
||||||
|
disabledMinutes: () => range(0, 60).filter(m => {
|
||||||
|
if (!date) return false
|
||||||
|
const dt = +new Date(date).setMinutes(m)
|
||||||
|
return dt < dateLimit.from || dt > +dateLimit.to - chartInterval
|
||||||
|
}),
|
||||||
|
disabledSeconds: () => range(0, 60).filter(s => {
|
||||||
|
if (!date) return false
|
||||||
|
const dt = +new Date(date).setSeconds(s)
|
||||||
|
return dt < dateLimit.from || dt > +dateLimit.to - chartInterval
|
||||||
|
})
|
||||||
|
}), [dateLimit])
|
||||||
|
|
||||||
|
useEffect(() => invokeWebApiWrapperAsync(
|
||||||
|
async () => {
|
||||||
|
let dates = await TelemetryDataSaubService.getDataDatesRange(idWell)
|
||||||
|
dates = {
|
||||||
|
from: new Date(dates?.from ?? 0),
|
||||||
|
to: new Date(dates?.to ?? 0)
|
||||||
|
}
|
||||||
|
setDateLimit(dates)
|
||||||
|
setStartDate(new Date(Math.max(dates.from, +dates.to - chartInterval)))
|
||||||
|
},
|
||||||
|
setShowLoader,
|
||||||
|
`Не удалось загрузить диапозон телеметрии для скважины "${idWell}"`,
|
||||||
|
'Загрузка диапозона телеметрии'
|
||||||
|
), [])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setStartDate((startDate) => new Date(Math.min(Date.now() - chartInterval, startDate)))
|
||||||
|
setDataSaub([])
|
||||||
|
}, [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, ...normalizeData(data)]
|
||||||
|
newData.sort(makeDateSorter('date'))
|
||||||
|
return cutData(newData, loadedStartDate, loadedEndDate)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
setShowLoader,
|
||||||
|
`Не удалось загрузить данные по скважине "${idWell}" c ${startDate.toISOString()} по ${new Date(+startDate + chartInterval).toISOString()}`,
|
||||||
|
'Загрузка телеметрий в диапозоне'
|
||||||
|
)
|
||||||
|
}, [idWell, chartInterval, loaded, startDate])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Flex style={{margin: '8px 8px 0'}}>
|
||||||
|
<div>
|
||||||
|
Начальная дата:
|
||||||
|
<DatePickerWrapper
|
||||||
|
value={startDate}
|
||||||
|
disabledDate={isDateDisabled}
|
||||||
|
disabledTime={isDateTimeDisabled}
|
||||||
|
onChange={(startDate) => setStartDate(new Date(startDate))}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div style={{ marginLeft: '1rem' }}>
|
||||||
|
Период:
|
||||||
|
<PeriodPicker onChange={(val) => setChartInterval(val * 1000)} />
|
||||||
|
</div>
|
||||||
|
</Flex>
|
||||||
|
<LoaderPortal show={showLoader}>
|
||||||
|
<ArchiveDisplay
|
||||||
|
data={dataSaub}
|
||||||
|
startDate={startDate}
|
||||||
|
interval={chartInterval}
|
||||||
|
onWheel={onGraphWheel}
|
||||||
|
/>
|
||||||
|
</LoaderPortal>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
export default Archive
|
@ -6,7 +6,7 @@ import { invokeWebApiWrapperAsync } from '@components/factory'
|
|||||||
import { Subscribe } from '@services/signalr'
|
import { Subscribe } from '@services/signalr'
|
||||||
import { MessageService } from '@api'
|
import { MessageService } from '@api'
|
||||||
|
|
||||||
import { columns } from '@pages/Messages'
|
import { columns } from '../Messages'
|
||||||
|
|
||||||
import '@styles/message.css'
|
import '@styles/message.css'
|
||||||
|
|
@ -1,4 +1,4 @@
|
|||||||
import { useState } from 'react'
|
import { memo, useCallback, useMemo, useState } from 'react'
|
||||||
import { Select, Modal, Input, InputNumber } from 'antd'
|
import { Select, Modal, Input, InputNumber } from 'antd'
|
||||||
|
|
||||||
import { SetpointsService } from '@api'
|
import { SetpointsService } from '@api'
|
||||||
@ -8,13 +8,13 @@ import { invokeWebApiWrapperAsync } from '@components/factory'
|
|||||||
import { makeNumericRender, EditableTable } from '@components/Table'
|
import { makeNumericRender, EditableTable } from '@components/Table'
|
||||||
import { PeriodPicker, defaultPeriod } from '@components/selectors/PeriodPicker'
|
import { PeriodPicker, defaultPeriod } from '@components/selectors/PeriodPicker'
|
||||||
|
|
||||||
export const SetpointSender = ({ idWell, onClose, visible, setpointNames }) => {
|
export const SetpointSender = memo(({ idWell, onClose, visible, setpointNames }) => {
|
||||||
const [expirePeriod, setExpirePeriod] = useState(defaultPeriod)
|
const [expirePeriod, setExpirePeriod] = useState(defaultPeriod)
|
||||||
const [comment, setComment] = useState('')
|
const [comment, setComment] = useState('')
|
||||||
const [setpoints, setSetpoints] = useState([])
|
const [setpoints, setSetpoints] = useState([])
|
||||||
const [isLoading, setIsLoading] = useState(false)
|
const [isLoading, setIsLoading] = useState(false)
|
||||||
|
|
||||||
const addingColumns = [
|
const addingColumns = useMemo(() => [
|
||||||
{
|
{
|
||||||
title: 'Наименование уставки',
|
title: 'Наименование уставки',
|
||||||
dataIndex: 'name',
|
dataIndex: 'name',
|
||||||
@ -33,27 +33,27 @@ export const SetpointSender = ({ idWell, onClose, visible, setpointNames }) => {
|
|||||||
render: makeNumericRender(1),
|
render: makeNumericRender(1),
|
||||||
align: 'right'
|
align: 'right'
|
||||||
}
|
}
|
||||||
]
|
], [setpointNames])
|
||||||
|
|
||||||
const onAdd = async (sp) => setSetpoints((prevSp) => {
|
const onAdd = useCallback(async (sp) => setSetpoints((prevSp) => {
|
||||||
sp.key = Date.now()
|
sp.key = Date.now()
|
||||||
prevSp.push(sp)
|
prevSp.push(sp)
|
||||||
return prevSp
|
return prevSp
|
||||||
})
|
}), [])
|
||||||
|
|
||||||
const onEdit = async (sp) => setSetpoints((prevSp) => {
|
const onEdit = useCallback(async (sp) => setSetpoints((prevSp) => {
|
||||||
const idx = prevSp.findIndex((val) => val.key === sp.key)
|
const idx = prevSp.findIndex((val) => val.key === sp.key)
|
||||||
prevSp[idx] = sp
|
prevSp[idx] = sp
|
||||||
return prevSp
|
return prevSp
|
||||||
})
|
}), [])
|
||||||
|
|
||||||
const onDelete = async (sp) => setSetpoints((prevSp) => {
|
const onDelete = useCallback(async (sp) => setSetpoints((prevSp) => {
|
||||||
const idx = prevSp.findIndex((val) => val.key === sp.key)
|
const idx = prevSp.findIndex((val) => val.key === sp.key)
|
||||||
prevSp.splice(idx, 1)
|
prevSp.splice(idx, 1)
|
||||||
return prevSp
|
return prevSp
|
||||||
})
|
}), [])
|
||||||
|
|
||||||
const onModalOk = () => invokeWebApiWrapperAsync(
|
const onModalOk = useCallback(() => invokeWebApiWrapperAsync(
|
||||||
async () => {
|
async () => {
|
||||||
const setpointsObject = setpoints.reduce((obj, sp) => {
|
const setpointsObject = setpoints.reduce((obj, sp) => {
|
||||||
obj[sp.name] = sp.value
|
obj[sp.name] = sp.value
|
||||||
@ -66,12 +66,12 @@ export const SetpointSender = ({ idWell, onClose, visible, setpointNames }) => {
|
|||||||
comment: comment
|
comment: comment
|
||||||
}
|
}
|
||||||
await SetpointsService.insert(idWell, request)
|
await SetpointsService.insert(idWell, request)
|
||||||
onClose(true)
|
await onClose(true)
|
||||||
},
|
},
|
||||||
setIsLoading,
|
setIsLoading,
|
||||||
`Не удалось отправить уставки по скважине "${idWell}"`,
|
`Не удалось отправить уставки по скважине "${idWell}"`,
|
||||||
`Рекомендация новыой уставки`
|
`Рекомендация новыой уставки`
|
||||||
)
|
), [idWell, setpoints, comment, expirePeriod, onClose])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
@ -115,4 +115,6 @@ export const SetpointSender = ({ idWell, onClose, visible, setpointNames }) => {
|
|||||||
</LoaderPortal>
|
</LoaderPortal>
|
||||||
</Modal>
|
</Modal>
|
||||||
)
|
)
|
||||||
}
|
})
|
||||||
|
|
||||||
|
export default SetpointSender
|
@ -72,3 +72,5 @@ export const SetpointViewer = memo(({ setpoint, visible, onClose, setpointNames
|
|||||||
</Modal>
|
</Modal>
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
export default SetpointViewer
|
@ -1,19 +1,19 @@
|
|||||||
import moment from 'moment'
|
|
||||||
import { Button, Modal } from 'antd'
|
import { Button, Modal } from 'antd'
|
||||||
import { useState, useEffect } from 'react'
|
import { useState, useEffect, memo, useCallback, useMemo } from 'react'
|
||||||
|
|
||||||
import { Table } from '@components/Table'
|
import { Table } from '@components/Table'
|
||||||
import { UserView } from '@components/views'
|
import { UserView } from '@components/views'
|
||||||
import LoaderPortal from '@components/LoaderPortal'
|
import LoaderPortal from '@components/LoaderPortal'
|
||||||
import { invokeWebApiWrapperAsync } from '@components/factory'
|
import { invokeWebApiWrapperAsync } from '@components/factory'
|
||||||
|
import { hasPermission } from '@utils/permissions'
|
||||||
import { makeStringCutter } from '@utils/string'
|
import { makeStringCutter } from '@utils/string'
|
||||||
|
import { formatDate } from '@utils'
|
||||||
import { SetpointsService } from '@api'
|
import { SetpointsService } from '@api'
|
||||||
|
|
||||||
import { SetpointSender } from './SetpointSender'
|
import SetpointSender from './SetpointSender'
|
||||||
import { SetpointViewer, getSetpointStatus } from './SetpointViewer'
|
import { SetpointViewer, getSetpointStatus } from './SetpointViewer'
|
||||||
import { hasPermission } from '@asb/utils/permissions'
|
|
||||||
|
|
||||||
export const Setpoints = ({ idWell, ...other }) => {
|
export const Setpoints = memo(({ idWell, ...other }) => {
|
||||||
const [isModalVisible, setIsModalVisible] = useState(false)
|
const [isModalVisible, setIsModalVisible] = useState(false)
|
||||||
const [isSenderVisible, setIsSenderVisible] = useState(false)
|
const [isSenderVisible, setIsSenderVisible] = useState(false)
|
||||||
const [isViewerVisible, setIsViewerVisible] = useState(false)
|
const [isViewerVisible, setIsViewerVisible] = useState(false)
|
||||||
@ -37,21 +37,21 @@ export const Setpoints = ({ idWell, ...other }) => {
|
|||||||
'Получение списка имён уставок'
|
'Получение списка имён уставок'
|
||||||
), [idWell])
|
), [idWell])
|
||||||
|
|
||||||
const showMore = (id) => {
|
const showMore = useCallback((id) => {
|
||||||
const selected = setpoints.find((sp) => sp.id === id)
|
const selected = setpoints.find((sp) => sp.id === id)
|
||||||
setSelected(selected ?? {})
|
setSelected(selected ?? {})
|
||||||
setIsViewerVisible(true)
|
setIsViewerVisible(true)
|
||||||
}
|
}, [setpoints])
|
||||||
|
|
||||||
const historyColumns = [
|
const historyColumns = useMemo(() => [
|
||||||
{ title: 'Дата', dataIndex: 'uploadDate', render: item => moment(item).format('DD MMM YYYY, HH:mm:ss') },
|
{ title: 'Дата', dataIndex: 'uploadDate', render: item => formatDate(item) },
|
||||||
{ title: 'Автор', dataIndex: 'author', render: (user) => <UserView user={user} /> },
|
{ title: 'Автор', dataIndex: 'author', render: (user) => <UserView user={user} /> },
|
||||||
{ title: 'Комментарий', dataIndex: 'comment', render: makeStringCutter() },
|
{ title: 'Комментарий', dataIndex: 'comment', render: makeStringCutter() },
|
||||||
{ title: 'Статус', dataIndex: 'idState', render: (id) => getSetpointStatus(parseInt(id)) },
|
{ title: 'Статус', dataIndex: 'idState', render: (id) => getSetpointStatus(parseInt(id)) },
|
||||||
{ dataIndex: 'id', render: (id) => <Button onClick={() => showMore(id)}>Подробнее</Button> },
|
{ dataIndex: 'id', render: (id) => <Button onClick={() => showMore(id)}>Подробнее</Button> },
|
||||||
]
|
], [showMore])
|
||||||
|
|
||||||
const updateTable = async () => await invokeWebApiWrapperAsync(
|
const updateTable = useCallback(async () => await invokeWebApiWrapperAsync(
|
||||||
async () => {
|
async () => {
|
||||||
const setpoints = await SetpointsService.getByIdWell(idWell)
|
const setpoints = await SetpointsService.getByIdWell(idWell)
|
||||||
setSetpoints(setpoints)
|
setSetpoints(setpoints)
|
||||||
@ -59,19 +59,19 @@ export const Setpoints = ({ idWell, ...other }) => {
|
|||||||
setIsLoading,
|
setIsLoading,
|
||||||
`Не удалось загрузить список для скважины "${idWell}"`,
|
`Не удалось загрузить список для скважины "${idWell}"`,
|
||||||
'Получение списка скважин'
|
'Получение списка скважин'
|
||||||
)
|
), [idWell])
|
||||||
|
|
||||||
const onOpenClick = async () => {
|
const onOpenClick = useCallback(async () => {
|
||||||
await updateTable()
|
await updateTable()
|
||||||
setIsModalVisible(true)
|
setIsModalVisible(true)
|
||||||
}
|
}, [updateTable])
|
||||||
|
|
||||||
const onSenderClose = (pushed) => {
|
const onSenderClose = useCallback(async (pushed) => {
|
||||||
if (pushed) updateTable()
|
if (pushed) await updateTable()
|
||||||
setIsSenderVisible(false)
|
setIsSenderVisible(false)
|
||||||
}
|
}, [updateTable])
|
||||||
|
|
||||||
return hasPermission('Setpoints.get') && (
|
return !hasPermission('Setpoints.get') ? null : (
|
||||||
<div {...other}>
|
<div {...other}>
|
||||||
<Button onClick={onOpenClick} loading={isLoading}>
|
<Button onClick={onOpenClick} loading={isLoading}>
|
||||||
Рекомендованные уставки
|
Рекомендованные уставки
|
||||||
@ -115,4 +115,6 @@ export const Setpoints = ({ idWell, ...other }) => {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
})
|
||||||
|
|
||||||
|
export default Setpoints
|
52
src/pages/Telemetry/index.jsx
Normal file
52
src/pages/Telemetry/index.jsx
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
import { Switch, useParams } from 'react-router-dom'
|
||||||
|
import { memo, useMemo } from 'react'
|
||||||
|
import { Layout, Menu } from 'antd'
|
||||||
|
import { AlertOutlined, FundViewOutlined, DatabaseOutlined } from '@ant-design/icons'
|
||||||
|
|
||||||
|
import { PrivateRoute, PrivateDefaultRoute, PrivateMenuItem } from '@components/Private'
|
||||||
|
|
||||||
|
import Archive from './Archive'
|
||||||
|
import Messages from './Messages'
|
||||||
|
import TelemetryView from './TelemetryView'
|
||||||
|
|
||||||
|
import '@styles/index.css'
|
||||||
|
|
||||||
|
const { Content } = Layout
|
||||||
|
|
||||||
|
export const Telemetry = memo(({ idWell }) => {
|
||||||
|
const { tab } = useParams()
|
||||||
|
const rootPath = useMemo(() => `/well/${idWell}/telemetry`, [idWell])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Layout>
|
||||||
|
<Menu mode={'horizontal'} selectable={true} selectedKeys={[tab]} className={'well_menu'}>
|
||||||
|
<PrivateMenuItem.Link root={rootPath} key={'monitoring'} path={'monitoring'} icon={<FundViewOutlined />} title={'Мониторинг'}/>
|
||||||
|
<PrivateMenuItem.Link root={rootPath} key={'messages'} path={'messages'} icon={<AlertOutlined/>} title={'Сообщения'} />
|
||||||
|
<PrivateMenuItem.Link root={rootPath} key={'archive'} path={'archive'} icon={<DatabaseOutlined />} title={'Архив'} />
|
||||||
|
</Menu>
|
||||||
|
|
||||||
|
<Layout>
|
||||||
|
<Content className={'site-layout-background'}>
|
||||||
|
<Switch>
|
||||||
|
<PrivateRoute path={`${rootPath}/monitoring`}>
|
||||||
|
<TelemetryView idWell={idWell} />
|
||||||
|
</PrivateRoute>
|
||||||
|
<PrivateRoute path={`${rootPath}/messages`}>
|
||||||
|
<Messages idWell={idWell} />
|
||||||
|
</PrivateRoute>
|
||||||
|
<PrivateRoute path={`${rootPath}/archive`}>
|
||||||
|
<Archive idWell={idWell} />
|
||||||
|
</PrivateRoute>
|
||||||
|
<PrivateDefaultRoute urls={[
|
||||||
|
`${rootPath}/monitoring`,
|
||||||
|
`${rootPath}/messages`,
|
||||||
|
`${rootPath}/archive`,
|
||||||
|
]}/>
|
||||||
|
</Switch>
|
||||||
|
</Content>
|
||||||
|
</Layout>
|
||||||
|
</Layout>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
export default Telemetry
|
@ -2,9 +2,7 @@ import { memo, useMemo } from 'react'
|
|||||||
import {
|
import {
|
||||||
FolderOutlined,
|
FolderOutlined,
|
||||||
FundViewOutlined,
|
FundViewOutlined,
|
||||||
AlertOutlined,
|
|
||||||
FilePdfOutlined,
|
FilePdfOutlined,
|
||||||
DatabaseOutlined,
|
|
||||||
ExperimentOutlined,
|
ExperimentOutlined,
|
||||||
DeploymentUnitOutlined,
|
DeploymentUnitOutlined,
|
||||||
} from '@ant-design/icons'
|
} from '@ant-design/icons'
|
||||||
@ -14,12 +12,10 @@ import { Switch, useParams } from 'react-router-dom'
|
|||||||
import { PrivateRoute, PrivateDefaultRoute, PrivateMenuItem } from '@components/Private'
|
import { PrivateRoute, PrivateDefaultRoute, PrivateMenuItem } from '@components/Private'
|
||||||
|
|
||||||
import Report from './Report'
|
import Report from './Report'
|
||||||
import Archive from './Archive'
|
|
||||||
import Measure from './Measure'
|
import Measure from './Measure'
|
||||||
import Messages from './Messages'
|
|
||||||
import Analytics from './Analytics'
|
import Analytics from './Analytics'
|
||||||
import Documents from './Documents'
|
import Documents from './Documents'
|
||||||
import TelemetryView from './TelemetryView'
|
import Telemetry from './Telemetry'
|
||||||
import WellOperations from './WellOperations'
|
import WellOperations from './WellOperations'
|
||||||
import DrillingProgram from './DrillingProgram'
|
import DrillingProgram from './DrillingProgram'
|
||||||
import TelemetryAnalysis from './TelemetryAnalysis'
|
import TelemetryAnalysis from './TelemetryAnalysis'
|
||||||
@ -35,12 +31,10 @@ export const Well = memo(() => {
|
|||||||
return (
|
return (
|
||||||
<Layout>
|
<Layout>
|
||||||
<Menu mode={'horizontal'} selectable={true} selectedKeys={[tab]} className={'well_menu'}>
|
<Menu mode={'horizontal'} selectable={true} selectedKeys={[tab]} className={'well_menu'}>
|
||||||
<PrivateMenuItem.Link root={rootPath} key={'telemetry'} path={'telemetry'} icon={<FundViewOutlined />} title={'Мониторинг'}/>
|
<PrivateMenuItem.Link root={rootPath} key={'telemetry'} path={'telemetry'} icon={<FundViewOutlined />} title={'Телеметрия'}/>
|
||||||
<PrivateMenuItem.Link root={rootPath} key={'message'} path={'message'} icon={<AlertOutlined/>} title={'Сообщения'} />
|
|
||||||
<PrivateMenuItem.Link root={rootPath} key={'report'} path={'report'} icon={<FilePdfOutlined />} title={'Рапорт'} />
|
<PrivateMenuItem.Link root={rootPath} key={'report'} path={'report'} icon={<FilePdfOutlined />} title={'Рапорт'} />
|
||||||
<PrivateMenuItem.Link root={rootPath} key={'analytics'} path={'analytics'} icon={<DeploymentUnitOutlined />} title={'Аналитика'} />
|
<PrivateMenuItem.Link root={rootPath} key={'analytics'} path={'analytics'} icon={<DeploymentUnitOutlined />} title={'Аналитика'} />
|
||||||
<PrivateMenuItem.Link root={rootPath} key={'operations'} path={'operations'} icon={<FolderOutlined />} title={'Операции по скважине'} />
|
<PrivateMenuItem.Link root={rootPath} key={'operations'} path={'operations'} icon={<FolderOutlined />} title={'Операции по скважине'} />
|
||||||
<PrivateMenuItem.Link root={rootPath} key={'archive'} path={'archive'} icon={<DatabaseOutlined />} title={'Архив'} />
|
|
||||||
{/* <PrivateMenuItem.Link root={rootPath} key={'telemetryAnalysis'} path={'telemetryAnalysis'} icon={<FundProjectionScreenOutlined />} title={'Операции по телеметрии'} /> */}
|
{/* <PrivateMenuItem.Link root={rootPath} key={'telemetryAnalysis'} path={'telemetryAnalysis'} icon={<FundProjectionScreenOutlined />} title={'Операции по телеметрии'} /> */}
|
||||||
<PrivateMenuItem.Link root={rootPath} key={'document'} path={'document'} icon={<FolderOutlined />} title={'Документы'} />
|
<PrivateMenuItem.Link root={rootPath} key={'document'} path={'document'} icon={<FolderOutlined />} title={'Документы'} />
|
||||||
<PrivateMenuItem.Link root={rootPath} key={'measure'} path={'measure'} icon={<ExperimentOutlined />} title={'Измерения'} />
|
<PrivateMenuItem.Link root={rootPath} key={'measure'} path={'measure'} icon={<ExperimentOutlined />} title={'Измерения'} />
|
||||||
@ -50,11 +44,8 @@ export const Well = memo(() => {
|
|||||||
<Layout>
|
<Layout>
|
||||||
<Content className={'site-layout-background'}>
|
<Content className={'site-layout-background'}>
|
||||||
<Switch>
|
<Switch>
|
||||||
<PrivateRoute path={`${rootPath}/telemetry`}>
|
<PrivateRoute path={`${rootPath}/telemetry/:tab?`}>
|
||||||
<TelemetryView idWell={idWell} />
|
<Telemetry idWell={idWell} />
|
||||||
</PrivateRoute>
|
|
||||||
<PrivateRoute path={`${rootPath}/message`}>
|
|
||||||
<Messages idWell={idWell} />
|
|
||||||
</PrivateRoute>
|
</PrivateRoute>
|
||||||
<PrivateRoute path={`${rootPath}/report`}>
|
<PrivateRoute path={`${rootPath}/report`}>
|
||||||
<Report idWell={idWell} />
|
<Report idWell={idWell} />
|
||||||
@ -65,9 +56,6 @@ export const Well = memo(() => {
|
|||||||
<PrivateRoute path={`${rootPath}/operations/:tab?`}>
|
<PrivateRoute path={`${rootPath}/operations/:tab?`}>
|
||||||
<WellOperations idWell={idWell} />
|
<WellOperations idWell={idWell} />
|
||||||
</PrivateRoute>
|
</PrivateRoute>
|
||||||
<PrivateRoute path={`${rootPath}/archive`}>
|
|
||||||
<Archive idWell={idWell} />
|
|
||||||
</PrivateRoute>
|
|
||||||
<PrivateRoute path={`${rootPath}/telemetryAnalysis/:tab?`}>
|
<PrivateRoute path={`${rootPath}/telemetryAnalysis/:tab?`}>
|
||||||
<TelemetryAnalysis idWell={idWell} />
|
<TelemetryAnalysis idWell={idWell} />
|
||||||
</PrivateRoute>
|
</PrivateRoute>
|
||||||
@ -82,11 +70,9 @@ export const Well = memo(() => {
|
|||||||
</PrivateRoute>
|
</PrivateRoute>
|
||||||
<PrivateDefaultRoute urls={[
|
<PrivateDefaultRoute urls={[
|
||||||
`${rootPath}/telemetry`,
|
`${rootPath}/telemetry`,
|
||||||
`${rootPath}/message`,
|
|
||||||
`${rootPath}/report`,
|
`${rootPath}/report`,
|
||||||
`${rootPath}/analytics`,
|
`${rootPath}/analytics`,
|
||||||
`${rootPath}/operations`,
|
`${rootPath}/operations`,
|
||||||
`${rootPath}/archive`,
|
|
||||||
`${rootPath}/telemetryAnalysis`,
|
`${rootPath}/telemetryAnalysis`,
|
||||||
`${rootPath}/document`,
|
`${rootPath}/document`,
|
||||||
`${rootPath}/measure`,
|
`${rootPath}/measure`,
|
||||||
|
@ -92,9 +92,11 @@ export const requirements: PermissionRecord = {
|
|||||||
document: {
|
document: {
|
||||||
'*': ['Deposit.get', 'File.get'],
|
'*': ['Deposit.get', 'File.get'],
|
||||||
},
|
},
|
||||||
|
telemetry: {
|
||||||
archive: ['Deposit.get', 'TelemetryDataSaub.get'],
|
archive: ['Deposit.get', 'TelemetryDataSaub.get'],
|
||||||
telemetry: ['Deposit.get', 'DrillFlowChart.get', 'TelemetryDataSaub.get', 'TelemetryDataSpin.get'],
|
monitoring: ['Deposit.get', 'DrillFlowChart.get', 'TelemetryDataSaub.get', 'TelemetryDataSpin.get'],
|
||||||
message: ['Deposit.get', 'TelemetryDataSaub.get'],
|
messages: ['Deposit.get', 'TelemetryDataSaub.get'],
|
||||||
|
},
|
||||||
report: ['Deposit.get', 'Report.get'],
|
report: ['Deposit.get', 'Report.get'],
|
||||||
analytics: {
|
analytics: {
|
||||||
composite: {
|
composite: {
|
||||||
|
Loading…
Reference in New Issue
Block a user