* Страница РТК приведена к новой модели

* РТК вырезано из мониторинга
* Удалена страница "Режимы"
* Удалено формирование новых режимов на странице "Композитная скважина"
This commit is contained in:
Александр Сироткин 2022-12-13 18:30:05 +05:00
parent f4adb528ca
commit 0ef6d67772
8 changed files with 61 additions and 280 deletions

View File

@ -59,6 +59,7 @@ export const WellSelector = memo(({ value, onChange, treeData, treeLabels, ...ot
<TreeSelect <TreeSelect
multiple multiple
treeCheckable treeCheckable
maxTagCount={'responsive'}
showCheckedStrategy={TreeSelect.SHOW_CHILD} showCheckedStrategy={TreeSelect.SHOW_CHILD}
treeDefaultExpandAll treeDefaultExpandAll
treeData={wellsTree} treeData={wellsTree}

View File

@ -1,146 +0,0 @@
import { memo, useCallback, useEffect, useState } from 'react'
import { Button, Modal, Popconfirm } from 'antd'
import { useWell } from '@asb/context'
import { makeColumn, makeGroupColumn, makeNumericRender, makeSelectColumn, Table } from '@components/Table'
import LoaderPortal from '@components/LoaderPortal'
import { invokeWebApiWrapperAsync } from '@components/factory'
import { DrillParamsService, WellOperationService } from '@api'
const getDeepValue = (data, key) => {
if (!key || key.trim() === '') return null
const keys = key.split('.')
let out = data
while (keys.length > 0) {
if (!(keys[0] in out)) return null
out = out[keys[0]]
keys.splice(0, 1)
}
return out
}
const makeNumericSorter = (keys) => (a, b) => getDeepValue(a, keys) - getDeepValue(b, keys)
const numericRender = makeNumericRender(1)
const makeNumericColumn = (title, dataIndex, render, other) => makeColumn(title, dataIndex, {
sorter: makeNumericSorter(dataIndex),
render: (_, record, index) => {
const func = render ?? ((value) => <>{value}</>)
const item = getDeepValue(record, dataIndex)
return func(item, record, index)
},
align: 'right',
...other,
})
const makeAvgRender = (dataIndex) => (avg, record) => {
const max = record[dataIndex]?.max
const fillW = (max - avg) / max * 100
return (
<div className={'avg-column'}>
<div className={'avg-fill'} style={{ width: `${fillW}%` }} />
<div className={'avg-value'}>
{numericRender(avg)}
</div>
</div>
)
}
const makeNumericAvgRange = (title, dataIndex, defaultRender = false) => makeGroupColumn(title, [
makeNumericColumn('мин', `${dataIndex}.min`),
makeNumericColumn('сред', `${dataIndex}.avg`, defaultRender ? undefined : makeAvgRender(dataIndex)),
makeNumericColumn('макс', `${dataIndex}.max`),
])
export const getColumns = async (idWell) => {
let sectionTypes = await WellOperationService.getSectionTypes(idWell)
sectionTypes = Object.entries(sectionTypes).map(([id, value]) => ({
label: value,
value: id,
}))
return [
makeSelectColumn('Конструкция секции','idWellSectionType', sectionTypes, null, {
width: 160,
sorter: makeNumericSorter('idWellSectionType'),
}),
makeNumericAvgRange('Нагрузка, т', 'axialLoad'),
makeNumericAvgRange('Давление, атм', 'pressure'),
makeNumericAvgRange('Момент на ВСП, кН·м', 'rotorTorque', true),
makeNumericAvgRange('Обороты на ВСП, об/мин', 'rotorSpeed'),
makeNumericAvgRange('Расход, л/с', 'flow'),
]
}
export const NewParamsTable = memo(({ selectedWellsKeys }) => {
const [params, setParams] = useState([])
const [paramsColumns, setParamsColumns] = useState([])
const [showParamsLoader, setShowParamsLoader] = useState(false)
const [isParamsModalVisible, setIsParamsModalVisible] = useState(false)
const [well] = useWell()
useEffect(() => {
invokeWebApiWrapperAsync(async () => setParamsColumns(await getColumns(well.id)))
}, [well])
const onParamButtonClick = useCallback(() => invokeWebApiWrapperAsync(
async () => {
setIsParamsModalVisible(true)
const params = await DrillParamsService.getCompositeAll(well.id)
setParams(params)
},
setShowParamsLoader,
`Не удалось загрузить список режимов`,
{ actionName: 'Получение списка режимов скважины', well }
), [well])
const onParamsAddClick = useCallback(() => invokeWebApiWrapperAsync(
async () => {
await DrillParamsService.save(well.id, params)
setIsParamsModalVisible(false)
},
setShowParamsLoader,
`Не удалось добавить режимы в список`,
{ actionName: 'Добавление режима скважины', well }
), [well, params])
return (
<>
<Button
size={'large'}
disabled={selectedWellsKeys.length <= 0}
onClick={onParamButtonClick}
>
Заполнить режимы текущей скважины
</Button>
<Modal
title={'Заполнить режимы текущей скважины'}
centered
open={isParamsModalVisible}
onCancel={() => setIsParamsModalVisible(false)}
width={1700}
footer={(
<Popconfirm title={'Заменить существующие режимы выбранными?'} onConfirm={onParamsAddClick}>
<Button
size={'large'}
disabled={params.length <= 0}
>Сохранить</Button>
</Popconfirm>
)}
>
<LoaderPortal show={showParamsLoader}>
<Table
bordered
size={'small'}
columns={paramsColumns}
dataSource={params}
pagination={false}
/>
</LoaderPortal>
</Modal>
</>
)
})
export default NewParamsTable

View File

@ -1,7 +1,7 @@
import { Link, useLocation } from 'react-router-dom' import { Link, useLocation } from 'react-router-dom'
import { useState, useEffect, memo, useMemo, lazy, Suspense } from 'react' import { useState, useEffect, memo, useMemo, lazy, Suspense } from 'react'
import { LineChartOutlined, ProfileOutlined, TeamOutlined } from '@ant-design/icons' import { LineChartOutlined, ProfileOutlined, TeamOutlined } from '@ant-design/icons'
import { Button, Badge, Divider, Modal, Row, Col } from 'antd' import { Button, Badge, Divider, Modal } from 'antd'
import { useWell } from '@asb/context' import { useWell } from '@asb/context'
import LoaderPortal from '@components/LoaderPortal' import LoaderPortal from '@components/LoaderPortal'
@ -17,7 +17,6 @@ import {
getOperations getOperations
} from '@utils' } from '@utils'
import NewParamsTable from './NewParamsTable'
const Tvd = lazy(() => import('@pages/Well/WellOperations/Tvd')) const Tvd = lazy(() => import('@pages/Well/WellOperations/Tvd'))
const CompaniesTable = lazy(() => import('@pages/Cluster/CompaniesTable')) const CompaniesTable = lazy(() => import('@pages/Cluster/CompaniesTable'))
@ -217,9 +216,6 @@ const WellCompositeSections = memo(({ statsWells, selectedSections }) => {
scroll={{ x: true, y: '30vh' }} scroll={{ x: true, y: '30vh' }}
pagination={false} pagination={false}
/> />
<Row justify={'end'} style={{ margin: '1rem 0' }}>
<Col><NewParamsTable selectedWellsKeys={selectedWellsKeys} /></Col>
</Row>
<Modal <Modal
title={'TVD'} title={'TVD'}

View File

@ -13,13 +13,12 @@ import { PeriodPicker, defaultPeriod } from '@components/selectors/PeriodPicker'
import { formatDate, hasPermission, isRawDate, withPermissions } from '@utils' import { formatDate, hasPermission, isRawDate, withPermissions } from '@utils'
import { Subscribe } from '@services/signalr' import { Subscribe } from '@services/signalr'
import { import {
DrillFlowChartService,
OperationStatService, OperationStatService,
TelemetryDataSaubService, TelemetryDataSaubService,
TelemetryDataSpinService TelemetryDataSpinService
} from '@api' } from '@api'
import { calcFlowData, makeChartGroups, yAxis } from './dataset' import { makeChartGroups, yAxis } from './dataset'
import { ADDITIVE_PAGES, cutData, DATA_COUNT, getLoadingInterval, makeDateTimeDisabled } from './archive_methods' 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'
@ -64,12 +63,12 @@ export const normalizeData = (data) => data?.map(item => ({
const dateSorter = makeDateSorter('date') const dateSorter = makeDateSorter('date')
const defaultDate = () => new Date(Date.now() - defaultPeriod * 1000) const defaultDate = () => new Date(Date.now() - defaultPeriod * 1000)
const makeSubjectSubsription = (subject$, handler) => { const makeSubjectSubscription = (subject$, handler) => {
const subscribtion = subject$.pipe( const subscription = subject$.pipe(
buffer(subject$.pipe(throttleTime(700))) buffer(subject$.pipe(throttleTime(700)))
).subscribe((data) => handler(data.flat().filter(Boolean))) ).subscribe((data) => handler(data.flat().filter(Boolean)))
return () => subscribtion.unsubscribe() return () => subscription.unsubscribe()
} }
const getRowDate = (row) => row && isRawDate(row.date) ? new Date(row.date) : null const getRowDate = (row) => row && isRawDate(row.date) ? new Date(row.date) : null
@ -81,7 +80,6 @@ const TelemetryView = memo(() => {
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 [rop, setRop] = useState(null) const [rop, setRop] = useState(null)
const [chartMethods, setChartMethods] = useState() const [chartMethods, setChartMethods] = useState()
@ -147,7 +145,6 @@ const TelemetryView = memo(() => {
const spinSubject$ = useMemo(() => new BehaviorSubject(), []) const spinSubject$ = useMemo(() => new BehaviorSubject(), [])
const filteredData = useMemo(() => cutData(dataSaub, domain.min, domain.max), [dataSaub, domain]) const filteredData = useMemo(() => cutData(dataSaub, domain.min, domain.max), [dataSaub, domain])
const flowData = useMemo(() => calcFlowData(dataSaub, flowChartData), [dataSaub, flowChartData])
const chartGroups = useMemo(() => makeChartGroups(), []) const chartGroups = useMemo(() => makeChartGroups(), [])
useEffect(() => { useEffect(() => {
@ -170,8 +167,8 @@ const TelemetryView = memo(() => {
setSearchParams(params) setSearchParams(params)
}, [archiveMode, endDate, chartInterval]) }, [archiveMode, endDate, chartInterval])
useEffect(() => makeSubjectSubsription(saubSubject$, handleDataSaub), [saubSubject$, handleDataSaub]) useEffect(() => makeSubjectSubscription(saubSubject$, handleDataSaub), [saubSubject$, handleDataSaub])
useEffect(() => makeSubjectSubsription(spinSubject$, handleDataSpin), [spinSubject$, handleDataSpin]) useEffect(() => makeSubjectSubscription(spinSubject$, handleDataSpin), [spinSubject$, handleDataSpin])
useEffect(() => { useEffect(() => {
if (archiveMode) return if (archiveMode) return
@ -233,8 +230,6 @@ const TelemetryView = memo(() => {
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)
@ -312,7 +307,6 @@ const TelemetryView = memo(() => {
{...chartProps} {...chartProps}
yDomain={domain} yDomain={domain}
data={filteredData} data={filteredData}
flowData={flowData}
methods={setChartMethods} methods={setChartMethods}
datasetGroups={chartGroups} datasetGroups={chartGroups}
onWheel={onWheel} onWheel={onWheel}

View File

@ -40,7 +40,6 @@ export const menuItems = [
makeItem('План', 'plan', [], <TableOutlined />), makeItem('План', 'plan', [], <TableOutlined />),
makeItem('Факт', 'fact', [], <TableOutlined />), makeItem('Факт', 'fact', [], <TableOutlined />),
makeItem('РТК', 'drillProcessFlow', [], <BarChartOutlined />), makeItem('РТК', 'drillProcessFlow', [], <BarChartOutlined />),
makeItem('Режимы', 'params', [], <ControlOutlined />),
]), ]),
makeItem('Документы', 'document', [], <FolderOutlined />, [ makeItem('Документы', 'document', [], <FolderOutlined />, [
makeItem('Растворный сервис', 'fluidService', [], <FolderOutlined />), makeItem('Растворный сервис', 'fluidService', [], <FolderOutlined />),

View File

@ -1,44 +1,83 @@
import { useState, useEffect, memo, useMemo, useCallback } from 'react' import { useState, useEffect, memo, useMemo, useCallback } from 'react'
import { useWell } from '@asb/context' import { useWell } from '@asb/context'
import {
EditableTable,
makeGroupColumn,
makeNumericColumn,
makeNumericColumnPlanFact,
makeNumericRender,
makeNumericSorter,
makeSelectColumn,
} from '@components/Table'
import LoaderPortal from '@components/LoaderPortal' import LoaderPortal from '@components/LoaderPortal'
import { invokeWebApiWrapperAsync } from '@components/factory' import { invokeWebApiWrapperAsync } from '@components/factory'
import { EditableTable, makeNumericMinMax, makeNumericStartEnd } from '@components/Table' import { ProcessMapService, WellOperationService } from '@api'
import { DrillFlowChartService } from '@api'
import { arrayOrDefault } from '@utils' import { arrayOrDefault } from '@utils'
const columns = [ const numericRender = makeNumericRender(2)
makeNumericStartEnd('Глубина, м', 'depth'),
makeNumericMinMax('Нагрузка, т', 'axialLoad'), export const getColumns = async (idWell) => {
makeNumericMinMax('Перепад давления, атм', 'pressure'), let sectionTypes = await WellOperationService.getSectionTypes(idWell)
makeNumericMinMax('Момент на ВСП, кН·м', 'rotorTorque'), sectionTypes = Object.entries(sectionTypes).map(([id, value]) => ({
makeNumericMinMax('Обороты на ВСП, об/мин', 'rotorSpeed'), label: value,
makeNumericMinMax('Расход, л/с', 'flow') value: id,
] }))
return [
makeSelectColumn('Конструкция секции','idWellSectionType', sectionTypes, null, {
width: 160,
sorter: makeNumericSorter('idWellSectionType'),
}),
makeGroupColumn('Интервал бурения, м', [
makeNumericColumn('От', 'depthStart', numericRender),
makeNumericColumn('До', 'depthEnd', numericRender),
]),
makeNumericColumnPlanFact('Перепад давления, атм', 'pressure', numericRender),
makeNumericColumnPlanFact('Нагрузка, т', 'axialLoad', numericRender),
makeNumericColumnPlanFact('Момент на ВСП, кН·м', 'topDriveTorque', numericRender),
makeNumericColumnPlanFact('Обороты на ВСП, об/мин', 'topDriveSpeed', numericRender),
makeNumericColumnPlanFact('Расход, л/с', 'flow', numericRender),
makeNumericColumn('Плановая механическая скорость, м/ч', 'ropPlan', numericRender),
]
}
export const DrillProcessFlow = memo(() => { export const DrillProcessFlow = memo(() => {
const [flows, setFlows] = useState([]) const [flows, setFlows] = useState([])
const [showLoader, setShowLoader] = useState(false) const [showLoader, setShowLoader] = useState(false)
const [columns, setColumns] = useState([])
const [well] = useWell() const [well] = useWell()
const updateFlows = useCallback(() => invokeWebApiWrapperAsync( const updateFlows = useCallback(() => invokeWebApiWrapperAsync(
async () => { async () => {
const flows = await DrillFlowChartService.getByIdWell(well.id) const flows = await ProcessMapService.getByIdWell(well.id)
setFlows(arrayOrDefault(flows)) setFlows(arrayOrDefault(flows))
}, },
setShowLoader, setShowLoader,
`Не удалось загрузить режимно-технологическую карту`, `Не удалось загрузить режимно-технологическую карту`,
{ actionName: 'Получение режимно-технологической карты', well } { actionName: 'Получение режимно-технологической карты', well },
), [well]) ), [well])
useEffect(() => {
invokeWebApiWrapperAsync(
async () => {
const columns = await getColumns(well.id)
setColumns(columns)
},
setShowLoader,
`Не удалось загрузить список конструкций секций`,
{ actionName: 'Получение списка конструкций секций', well },
)
}, [well])
useEffect(() => { useEffect(() => {
updateFlows() updateFlows()
}, [well]) }, [well])
const tableHandlers = useMemo(() => { const tableHandlers = useMemo(() => {
const handlerProps = { const handlerProps = {
service: DrillFlowChartService, service: ProcessMapService,
setLoader: setShowLoader, setLoader: setShowLoader,
onComplete: updateFlows, onComplete: updateFlows,
permission: 'DrillFlowChart.edit', permission: 'DrillFlowChart.edit',

View File

@ -1,98 +0,0 @@
import { useState, useEffect, useCallback, memo, useMemo } from 'react'
import { useWell } from '@asb/context'
import {
EditableTable,
makeSelectColumn,
makeNumericSorter,
makeNumericAvgRange,
} from '@components/Table'
import LoaderPortal from '@components/LoaderPortal'
import { invokeWebApiWrapperAsync } from '@components/factory'
import { DrillParamsService, WellOperationService } from '@api'
import { arrayOrDefault } from '@utils'
export const getColumns = async (idWell) => {
let sectionTypes = await WellOperationService.getSectionTypes(idWell)
sectionTypes = Object.entries(sectionTypes).map(([id, value]) => ({
label: value,
value: id,
}))
return [
makeSelectColumn('Конструкция секции','idWellSectionType', sectionTypes, null, {
editable: true,
width: 160,
sorter: makeNumericSorter('idWellSectionType'),
}),
makeNumericAvgRange('Нагрузка, т', 'axialLoad', 1),
makeNumericAvgRange('Перепад давления, атм', 'pressure', 1),
makeNumericAvgRange('Момент на ВСП, кН·м', 'rotorTorque', 1),
makeNumericAvgRange('Обороты на ВСП, об/мин', 'rotorSpeed', 1),
makeNumericAvgRange('Расход, л/с', 'flow', 1),
]
}
export const WellDrillParams = memo(() => {
const [params, setParams] = useState([])
const [showLoader, setShowLoader] = useState(false)
const [columns, setColumns] = useState([])
const [well] = useWell()
const updateParams = useCallback(async () => await invokeWebApiWrapperAsync(
async () => {
const params = arrayOrDefault(await DrillParamsService.getAll(well.id))
// Typescript против использования числа в качестве типа значения select
params.forEach((param) => param.idWellSectionType = `${param.idWellSectionType}`)
setParams(params)
},
setShowLoader,
`Не удалось загрузить список режимов бурения`,
{ actionName: 'Получение списка режимов бурения скважины', well }
), [well])
useEffect(() => {
(async () => {
setColumns(await getColumns(well.id))
await updateParams()
})()
}, [well.id, updateParams])
const recordParser = useCallback((record) => ({ ...record, idWell: well.id }), [well.id])
const tableHandlers = useMemo(() => {
const handlerProps = {
service: DrillParamsService,
setLoader: setShowLoader,
onComplete: updateParams,
permission: 'DrillParams.edit',
idWell: well.id,
idRecord: true,
}
return {
add: { ...handlerProps, action: 'insert', actionName: 'Добавление режима бурения', recordParser },
edit: { ...handlerProps, action: 'update', actionName: 'Редактирование режима бурения', recordParser },
delete: { ...handlerProps, action: 'delete', actionName: 'Удаление режима бурения', permission: 'DrillParams.delete' },
}
}, [well.id, updateParams, recordParser])
return (
<LoaderPortal show={showLoader}>
<EditableTable
bordered
size={'small'}
columns={columns}
dataSource={params}
tableName={'well_drill_params'}
onRowAdd={tableHandlers.add}
onRowEdit={tableHandlers.edit}
onRowDelete={tableHandlers.delete}
pagination={false}
/>
</LoaderPortal>
)
})
export default WellDrillParams

View File

@ -1,5 +1,5 @@
import { lazy, memo, useCallback, useEffect, useMemo, useState } from 'react' import { lazy, memo, useCallback, useEffect, useMemo, useState } from 'react'
import { Navigate, Route, Routes, useLocation, useParams } from 'react-router-dom' import { Navigate, Route, Routes, useParams } from 'react-router-dom'
import { WellContext, RootPathContext, useRootPath, useLayoutProps, TopRightBlockContext } from '@asb/context' import { WellContext, RootPathContext, useRootPath, useLayoutProps, TopRightBlockContext } from '@asb/context'
import { FastRunMenu } from '@components/FastRunMenu' import { FastRunMenu } from '@components/FastRunMenu'
@ -22,7 +22,6 @@ const WellOperations = lazy(() => import('./WellOperations'))
const DrillingProgram = lazy(() => import('./DrillingProgram')) const DrillingProgram = lazy(() => import('./DrillingProgram'))
const Tvd = lazy(() => import('./WellOperations/Tvd')) const Tvd = lazy(() => import('./WellOperations/Tvd'))
const WellDrillParams = lazy(() => import('./WellOperations/WellDrillParams'))
const DrillProcessFlow = lazy(() => import('./WellOperations/DrillProcessFlow')) const DrillProcessFlow = lazy(() => import('./WellOperations/DrillProcessFlow'))
const WellSectionsStat = lazy(() => import('./WellOperations/WellSectionsStat')) const WellSectionsStat = lazy(() => import('./WellOperations/WellSectionsStat'))
const WellOperationsEditorFact = lazy(() => import('./WellOperations/OperationEditor/Fact')) const WellOperationsEditorFact = lazy(() => import('./WellOperations/OperationEditor/Fact'))
@ -45,8 +44,6 @@ const breadcrumb = makeMenuBreadcrumbItemsRender(menuItems, /^\/well\/[^\/#?]+\/
const Well = memo(() => { const Well = memo(() => {
const { idWell } = useParams() const { idWell } = useParams()
const location = useLocation()
const [well, setWell] = useState({ id: idWell }) const [well, setWell] = useState({ id: idWell })
const [topRightBlock, setTopRightBlock] = useState() const [topRightBlock, setTopRightBlock] = useState()
@ -130,7 +127,6 @@ const Well = memo(() => {
<Route path={'plan'} element={<WellOperationsEditorPlan />} /> <Route path={'plan'} element={<WellOperationsEditorPlan />} />
<Route path={'fact'} element={<WellOperationsEditorFact />} /> <Route path={'fact'} element={<WellOperationsEditorFact />} />
<Route path={'drillProcessFlow'} element={<DrillProcessFlow />} /> <Route path={'drillProcessFlow'} element={<DrillProcessFlow />} />
<Route path={'params'} element={<WellDrillParams />} />
</Route> </Route>
<Route path={'document/*'} element={<Documents />} /> <Route path={'document/*'} element={<Documents />} />
<Route path={'measure/*'} element={<Measure />} /> <Route path={'measure/*'} element={<Measure />} />