asb_cloud_front/src/pages/TelemetryView/index.jsx

428 lines
12 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { useState, useEffect } from 'react'
import { Select } from 'antd'
import { MonitoringColumn } from './MonitoringColumn'
import { CustomColumn } from './CustomColumn'
import ActiveMessagesOnline from './ActiveMessagesOnline'
import { ModeDisplay } from './ModeDisplay'
import { UserOfWell } from './UserOfWells'
import { Setpoints } from './Setpoints'
import LoaderPortal from '../../components/LoaderPortal'
import { Grid, GridItem, Flex } from '../../components/Grid'
import { Subscribe } from '../../services/signalr'
import {
DrillFlowChartService,
OperationStatService,
TelemetryDataSaubService,
TelemetryDataSpinService,
WellService
} from '../../services/api'
import { makeDateSorter } from '../../components/Table'
import { invokeWebApiWrapperAsync } from '../../components/factory'
import { PeriodPicker, defaultPeriod } from '../../components/PeriodPicker'
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 '../../styles/message.css'
const { Option } = Select
const dash = [7, 3]
const blockHeightGroup = [
{
label: 'Высота блока',
units: 'м',
xAccessorName: 'blockPosition',
yAccessorName: 'date',
color: '#333',
showLabels: true
}, {
label: 'Глубина скважины',
units: 'м',
xAccessorName: 'wellDepth',
yAccessorName: 'date',
color: '#333',
showLine: false,
xConstValue: 30,
dash
}, {
label: 'Расход',
units: 'л/c',
xAccessorName: 'flow',
yAccessorName: 'date',
color: '#077',
showLabels: true,
showLine: true
}, {
label: 'Предел расхода',
units: 'л/с',
xAccessorName: 'flow',
yAccessorName: 'date',
color: 'rgba(0,119,119,.1)',
isShape: true
}
]
const blockSpeedGroup = [
{
label: 'Скорость блока',
units: 'м/ч',
xAccessorName: 'blockSpeed',
yAccessorName: 'date',
color: '#0a0',
showLabels: true,
}, {
label: 'Скорость заданная',
units: 'м/ч',
xAccessorName: 'blockSpeedSp',
yAccessorName: 'date',
color: '#0a0',
footer: 'SP',
dash
}
]
const pressureGroup = [
{
label: 'Давление',
units: 'атм',
xAccessorName: 'pressure',
yAccessorName: 'date',
color: '#c00',
showLabels: true
}, {
label: 'Давление заданное',
units: 'атм',
xAccessorName: 'pressureSp',
yAccessorName: 'date',
color: '#c00',
footer: 'SP',
dash
}, {
label: 'Давление ХХ',
units: 'атм',
xAccessorName: 'pressureIdle',
yAccessorName: 'date',
color: '#c00',
footer: 'IDLE',
dash
}, {
label: 'Перепад давления максимальный',
units: 'атм',
xAccessorName: 'pressureDeltaLimitMax',
yAccessorName: 'date',
color: '#c00',
footer: true,
dash
}, {
label: 'Предел давления заданный',
units: 'атм',
xAccessorName: 'pressure',
yAccessorName: 'date',
color: 'rgba(204,0,0,.1)',
isShape: true
}
]
const axialLoadGroup = [
{
label: 'Осевая нагрузка',
units: 'т',
xAccessorName: 'axialLoad',
yAccessorName: 'date',
color: '#00a',
showLabels: true
}, {
label: 'Осевая нагрузка заданная',
units: 'т',
xAccessorName: 'axialLoadSp',
yAccessorName: 'date',
color: '#00a',
footer: 'SP',
dash
}, {
label: 'Осевая нагрузка максимальная',
units: 'т',
xAccessorName: 'axialLoadLimitMax',
yAccessorName: 'date',
color: '#00a',
footer: true,
dash
}, {
label: 'Пределы осевой нагрузки',
units: 'т',
xAccessorName: 'axialLoad',
yAccessorName: 'date',
color: 'rgba(0,0,170,.1)',
isShape: true
}
]
const hookWeightGroup = [
{
label: 'Вес на крюке',
units: 'т',
xAccessorName: 'hookWeight',
yAccessorName: 'date',
color: '#0aa',
showLabels: true
}, {
label: 'Вес инструмента ХХ',
units: 'т',
xAccessorName: 'hookWeightIdle',
yAccessorName: 'date',
color: '#0aa',
footer: 'IDLE',
dash
}, {
label: 'Вес инструмента минимальный',
units: 'т',
xAccessorName: 'hookWeightLimitMin',
yAccessorName: 'date',
color: '#0aa',
footer: true,
dash
}, {
label: 'Вес инструмента максимальный',
units: 'т',
xAccessorName: 'hookWeightLimitMax',
yAccessorName: 'date',
color: '#0aa',
footer: true,
dash
}, {
label: 'Обороты ротора',
units: 'об/мин',
xAccessorName: 'rotorSpeed',
yAccessorName: 'date',
color: '#aa0',
showLabels: true
}, {
label: 'Скорость вращения ВСП максимальная',
units: 'об/мин',
xAccessorName: 'rotorSpeed',
yAccessorName: 'date',
color: 'rgba(170,170,0,.1)',
isShape: true
}
]
const rotorTorqueGroup = [
{
label: 'Момент на роторе',
units: 'кН·м',
xAccessorName: 'rotorTorque',
yAccessorName: 'date',
color: '#a0a',
showLabels: true
}, {
label: 'План. Момент на роторе',
units: 'кН·м',
xAccessorName: 'rotorTorqueSp',
yAccessorName: 'date',
color: '#a0a',
footer: 'SP',
dash
}, {
label: 'Момент на роторе х.х.',
units: 'кН·м',
xAccessorName: 'rotorTorqueIdle',
yAccessorName: 'date',
color: '#a0a',
footer: 'IDLE',
dash
}, {
label: 'Момент максимальный',
units: 'кН·м',
xAccessorName: 'rotorTorqueLimitMax',
yAccessorName: 'date',
color: '#a0a',
footer: true,
dash
}, {
label: 'Ограничения момента',
units: 'кН·м',
xAccessorName: 'rotorTorque',
yAccessorName: 'date',
color: 'rgba(170,0,170,.1)',
isShape: true
}
]
export const paramsGroups = [
blockHeightGroup,
blockSpeedGroup,
pressureGroup,
axialLoadGroup,
hookWeightGroup,
rotorTorqueGroup
]
const getLast = (data) =>
Array.isArray(data) ? data.slice(-1)[0] : data
const isMseEnabled = (dataSaub) => {
const lastData = getLast(dataSaub)
return (lastData?.mseState && 2) > 0
}
const isTorqueStabEnabled = (dataSpin) => {
const lastData = getLast(dataSpin)
return lastData?.state === 7
}
const isSpinEnabled = (dataSpin) => {
const lastData = getLast(dataSpin)
return lastData?.state > 0 && lastData?.state !== 6
}
const getIndexOfDrillingBy = (dataSaub) => {
const order = {
0: -1,
1: 1, // скорость
2: 2, // давление
3: 3, // нагрузка
4: 5, // момент
}
const idFeedRegulator = getLast(dataSaub)?.idFeedRegulator ?? 0
return order[idFeedRegulator] ?? -1
}
export const normalizeData = (data) => data?.map(item => ({
...item,
rotorSpeed: item.rotorSpeed < 1 ? 0 : item.rotorSpeed,
rotorTorque: item.rotorTorque < 1 ? 0 : item.rotorTorque,
blockSpeed: Math.abs(item.blockSpeed)
})) ?? []
export default function TelemetryView({ idWell }) {
const [dataSaub, setDataSaub] = useState([])
const [dataSpin, setDataSpin] = useState([])
const [chartInterval, setChartInterval] = useState(defaultPeriod)
const [wellData, setWellData] = useState({ idState: 0 })
const [showLoader, setShowLoader] = useState(false)
const [flowChartData, setFlowChartData] = useState([])
const [rop, setRop] = useState(null)
const handleDataSaub = (data) => {
if (data) {
const dataSaub = normalizeData(data)
dataSaub.sort(makeDateSorter('date'))
setDataSaub(dataSaub)
}
}
const handleDataSpin = (data) => {
if (data) {
setDataSpin(data)
}
}
useEffect(() => {
const unsubscribeSaub = Subscribe('hubs/telemetry', 'ReceiveDataSaub', `well_${idWell}`, handleDataSaub)
const unsubscribeSpin = Subscribe('hubs/telemetry', 'ReceiveDataSpin', `well_${idWell}`, handleDataSpin)
invokeWebApiWrapperAsync(
async () => {
const flowChart = await DrillFlowChartService.get(idWell)
const dataSaub = await TelemetryDataSaubService.getData(idWell, null, chartInterval)
const dataSpin = await TelemetryDataSpinService.getData(idWell, null, chartInterval)
setFlowChartData(flowChart ?? [])
handleDataSaub(dataSaub)
handleDataSpin(dataSpin)
},
null,
`Не удалось получить данные по скважине "${idWell}"`,
)
return () => {
unsubscribeSaub()
unsubscribeSpin()
}
}, [idWell, chartInterval])
useEffect(() => invokeWebApiWrapperAsync(
async () => {
const well = await WellService.get(idWell)
const rop = await OperationStatService.getClusterRopStatByIdWell(idWell)
setRop(rop)
setWellData(well ?? {})
},
setShowLoader,
`Не удалось загрузить данные по скважине "${idWell}"`
), [idWell])
const onStatusChanged = (value) => {
invokeWebApiWrapperAsync(
async () => {
const well = { ...wellData, idState: value }
await WellService.updateWell(idWell, well)
setWellData(well)
},
setShowLoader,
`Не удалось задать состояние скважины "${idWell}"`
)
}
const columnAdditionalLabels = {
1: rop && [
`ROP сред: ${rop.ropAverage.toFixed(2)} м/ч`,
`ROP макс: ${rop.ropMax.toFixed(2)} м/ч`
]
}
return (
<LoaderPortal show={showLoader}>
<Grid style={{ gridTemplateColumns: 'auto repeat(6, 1fr)' }}>
<GridItem col={'1'} row={'1'} colSpan={'8'} style={{ marginBottom: '0.5rem' }}>
<Flex>
<ModeDisplay data={dataSaub} />
<div style={{ marginLeft: '1rem' }}>
Интервал:&nbsp;
<PeriodPicker onChange={setChartInterval} />
</div>
<div style={{ marginLeft: '1rem' }}>
Статус:&nbsp;
<Select value={wellData.idState ?? 0} onChange={onStatusChanged}>
<Option value={0} disabled hidden>Неизвестно</Option>
<Option value={1}>В работе</Option>
<Option value={2}>Завершено</Option>
</Select>
</div>
<Setpoints idWell={idWell} style={{ marginLeft: '1rem' }} />
<span style={{ flexGrow: 20 }}>&nbsp;</span>
<img src={isTorqueStabEnabled(dataSpin) ? MomentStabPicEnabled : MomentStabPicDisabled} style={{ marginRight: '15px' }} alt={'TorqueMaster'} />
<img src={isSpinEnabled(dataSpin) ? SpinPicEnabled : SpinPicDisabled} style={{ marginRight: '15px' }} alt={'SpinMaster'} />
<h2 style={{ marginBottom: 0, marginRight: '15px', fontWeight: 'bold', color: isMseEnabled(dataSaub) ? 'green' : 'lightgrey' }}>MSE</h2>
<UserOfWell data={dataSaub} />
</Flex>
</GridItem>
<GridItem col={'1'} row={'2'} rowSpan={'3'} style={{ minWidth: '260px', width: '0.142fr' }}>
<CustomColumn data={dataSaub} />
</GridItem>
{paramsGroups.map((group, index) =>
<GridItem col={2 + index} row={'2'} className={'border_small'} key={`${group.label}${index}`} style={{padding:0}}>
<MonitoringColumn
showLastValues
style={{ width: '13vw' }}
data={dataSaub}
flowChartData={flowChartData}
lineGroup={group}
interval={chartInterval * 1000}
headerHeight={'50px'}
showBorder={getIndexOfDrillingBy(dataSaub) === index}
additionalLabels={columnAdditionalLabels[index]}
/>
</GridItem>
)}
<GridItem col={'2'} row={'3'} colSpan={'7'}>
<ActiveMessagesOnline idWell={idWell} />
</GridItem>
</Grid>
</LoaderPortal>
)
}