forked from ddrilling/asb_cloud_front
Удалены старые страницы, использующие TelemetryAnalytics
This commit is contained in:
parent
f0659f234c
commit
9e8648d33e
@ -1,163 +0,0 @@
|
||||
import { Table, Select, DatePicker } from "antd";
|
||||
import { TelemetryAnalyticsService } from "../src/services/api";
|
||||
import { useState, useEffect } from "react";
|
||||
import { useParams } from "react-router-dom";
|
||||
import notify from "../src/components/notify";
|
||||
import LoaderPortal from "../src/components/LoaderPortal";
|
||||
import moment from "moment";
|
||||
import "../styles/message.css";
|
||||
|
||||
const { Option } = Select;
|
||||
const pageSize = 26;
|
||||
const { RangePicker } = DatePicker;
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: "Название операции",
|
||||
key: "name",
|
||||
dataIndex: "name",
|
||||
},
|
||||
{
|
||||
title: "Дата начала операции",
|
||||
key: "beginDate",
|
||||
dataIndex: "beginDate",
|
||||
render: (item) => moment.utc(item).local().format("DD MMM YYYY, HH:mm:ss"),
|
||||
},
|
||||
{
|
||||
title: "Дата окончания операции",
|
||||
key: "endDate",
|
||||
dataIndex: "endDate",
|
||||
render: (item) => moment.utc(item).local().format("DD MMM YYYY, HH:mm:ss"),
|
||||
},
|
||||
{
|
||||
title: "Глубина скважины в начале операции",
|
||||
key: "beginWellDepth",
|
||||
dataIndex: "startWellDepth",
|
||||
},
|
||||
{
|
||||
title: "Глубина скважины в конце операции",
|
||||
key: "endWellDepth",
|
||||
dataIndex: "endWellDepth",
|
||||
},
|
||||
];
|
||||
|
||||
const filterOptions = [
|
||||
{ label: "Невозможно определить операцию", value: 1 },
|
||||
{ label: "Роторное бурение", value: 2 },
|
||||
{ label: "Слайдирование", value: 3 },
|
||||
{ label: "Подъем с проработкой", value: 4 },
|
||||
{ label: "Спуск с проработкой", value: 5 },
|
||||
{ label: "Подъем с промывкой", value: 6 },
|
||||
{ label: "Спуск с промывкой", value: 7 },
|
||||
{ label: "Спуск в скважину", value: 8 },
|
||||
{ label: "Спуск с вращением", value: 9 },
|
||||
{ label: "Подъем из скважины", value: 10 },
|
||||
{ label: "Подъем с вращением", value: 11 },
|
||||
{ label: "Промывка в покое", value: 12 },
|
||||
{ label: "Промывка с вращением", value: 13 },
|
||||
{ label: "Удержание в клиньях", value: 14 },
|
||||
{ label: "Неподвижное состояние", value: 15 },
|
||||
{ label: "Вращение без циркуляции", value: 16 },
|
||||
{ label: "На поверхности", value: 17 },
|
||||
];
|
||||
|
||||
export default function WellTelemetryAnalysis() {
|
||||
let { id } = useParams();
|
||||
|
||||
const [page, setPage] = useState(1);
|
||||
const [range, setRange] = useState([]);
|
||||
const [categories, setCategories] = useState([]);
|
||||
const [pagination, setPagination] = useState(null);
|
||||
const [operations, setOperations] = useState([]);
|
||||
|
||||
const [loader, setLoader] = useState(false);
|
||||
|
||||
const children = filterOptions.map((line) => (
|
||||
<Option key={line.value}>{line.label}</Option>
|
||||
));
|
||||
|
||||
const onChangeRange = (range) => {
|
||||
setRange(range);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const GetOperations = async () => {
|
||||
setLoader(true);
|
||||
try {
|
||||
let begin = null;
|
||||
let end = null;
|
||||
if (range?.length > 1) {
|
||||
begin = range[0].toISOString();
|
||||
end = range[1].toISOString();
|
||||
}
|
||||
|
||||
await TelemetryAnalyticsService.getOperationsByWell(
|
||||
`${id}`,
|
||||
(page - 1) * pageSize,
|
||||
pageSize,
|
||||
categories,
|
||||
begin,
|
||||
end
|
||||
).then((paginatedOperations) => {
|
||||
setOperations(
|
||||
paginatedOperations?.items.map((o) => {
|
||||
return {
|
||||
key: o.id,
|
||||
begin: o.date,
|
||||
...o,
|
||||
};
|
||||
})
|
||||
);
|
||||
|
||||
setPagination({
|
||||
total: paginatedOperations?.count,
|
||||
current: Math.floor(paginatedOperations?.skip / pageSize),
|
||||
});
|
||||
});
|
||||
} catch (ex) {
|
||||
notify(`Не удалось загрузить операции по скважине "${id}"`, "error");
|
||||
console.log(ex);
|
||||
}
|
||||
setLoader(false);
|
||||
};
|
||||
GetOperations();
|
||||
}, [id, categories, range, page]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="filter-group">
|
||||
<h3 className="filter-group-heading">Фильтр операций</h3>
|
||||
<Select
|
||||
mode="multiple"
|
||||
allowClear
|
||||
placeholder="Фильтр операций"
|
||||
className="filter-selector"
|
||||
value={categories}
|
||||
onChange={setCategories}
|
||||
>
|
||||
{children}
|
||||
</Select>
|
||||
<RangePicker
|
||||
showTime
|
||||
placeholder={["Дата начала операции", "Дата окончания операции"]}
|
||||
onChange={onChangeRange}
|
||||
/>
|
||||
</div>
|
||||
<LoaderPortal show={loader}>
|
||||
<Table
|
||||
columns={columns}
|
||||
dataSource={operations}
|
||||
size={"small"}
|
||||
pagination={{
|
||||
pageSize: pageSize,
|
||||
showSizeChanger: false,
|
||||
total: pagination?.total,
|
||||
current: page,
|
||||
onChange: (page) => setPage(page),
|
||||
}}
|
||||
rowKey={(record) => record.id}
|
||||
/>
|
||||
</LoaderPortal>
|
||||
</>
|
||||
);
|
||||
}
|
@ -1,41 +0,0 @@
|
||||
import moment from 'moment'
|
||||
import { DatePicker } from 'antd'
|
||||
import { useState, useEffect } from 'react'
|
||||
import { useParams } from 'react-router-dom'
|
||||
|
||||
import LoaderPortal from '@components/LoaderPortal'
|
||||
import { invokeWebApiWrapperAsync } from '@components/factory'
|
||||
import { TelemetryAnalyticsService } from '@api'
|
||||
import { ChartOperationTime } from './charts/ChartOperationTime'
|
||||
|
||||
const { RangePicker } = DatePicker
|
||||
|
||||
const lines = [{ labelAccessorName: 'processName', pieceAccessorName: 'duration' }]
|
||||
|
||||
export const AnalysisOperationTime = () => {
|
||||
const { id } = useParams()
|
||||
const [operationTimeData, setOperationTimeData] = useState([])
|
||||
const [loader, setLoader] = useState(false)
|
||||
const [range, setRange] = useState([moment().subtract(1,'days'), moment()])
|
||||
|
||||
useEffect(() => invokeWebApiWrapperAsync(
|
||||
async () => {
|
||||
const begin = range?.length > 1 ? range[0].toISOString() : null
|
||||
const end = range?.length > 1 ? range[1].toISOString() : null
|
||||
const summary = await TelemetryAnalyticsService.getOperationsSummary(id, begin, end)
|
||||
setOperationTimeData(summary)
|
||||
},
|
||||
setLoader,
|
||||
`Не удалось получить данные для анализа Операция-Время по скважине '${id}' за период с ${begin} по ${end}`,
|
||||
'Получение данных для анализа Операция-Время по скважине'
|
||||
), [id, range])
|
||||
|
||||
return (
|
||||
<LoaderPortal show={loader}>
|
||||
<RangePicker showTime onChange={(range) => setRange(range)} />
|
||||
<ChartOperationTime data={operationTimeData} lines={lines} />
|
||||
</LoaderPortal>
|
||||
)
|
||||
}
|
||||
|
||||
export default AnalysisOperationTime
|
@ -1,68 +0,0 @@
|
||||
import moment from 'moment'
|
||||
import { useEffect, useState } from 'react'
|
||||
|
||||
import { ChartOpertationTimeBase } from './ChartOperationTimeBase'
|
||||
|
||||
export const CreateLabels = () => {
|
||||
let labels = []
|
||||
return labels
|
||||
}
|
||||
|
||||
export const CreateData = (lineConfig) => {
|
||||
let datasets = {
|
||||
label: lineConfig.label,
|
||||
data: [],
|
||||
backgroundColor: [
|
||||
'#f00', '#ff0', '#f0f', '#0ff', '#00f', '#0f0'
|
||||
],
|
||||
}
|
||||
return datasets
|
||||
}
|
||||
|
||||
export const ChartOperationTime = ({ lines, data, rangeDate }) => {
|
||||
const [opertationTimeDataParams, setOpertationTimeDataParams] = useState({ data: { labels: [], datasets: [] } })
|
||||
|
||||
useEffect(() => {
|
||||
if ((!lines)
|
||||
|| (!data))
|
||||
return
|
||||
|
||||
let newLabels = lines.map(lineCfg => {
|
||||
let labels = CreateLabels(lineCfg)
|
||||
if (data.length !== 0)
|
||||
labels = data.map(dataItem => {
|
||||
return dataItem[lineCfg.labelAccessorName]
|
||||
})
|
||||
return labels
|
||||
})
|
||||
|
||||
let newDatasets = lines.map(lineCfg => {
|
||||
let datasets = CreateData(lineCfg)
|
||||
if (data.length !== 0)
|
||||
datasets.data = data.map(dataItem => {
|
||||
return dataItem[lineCfg.pieceAccessorName]
|
||||
})
|
||||
return datasets
|
||||
})
|
||||
|
||||
let interval = rangeDate ? (rangeDate[1] - rangeDate[0]) / 1000 : null
|
||||
let startDate = rangeDate ? rangeDate[0] : moment()
|
||||
let newParams = {
|
||||
xInterval: interval,
|
||||
xStart: startDate,
|
||||
data: {
|
||||
labels: newLabels,
|
||||
datasets: newDatasets
|
||||
}
|
||||
}
|
||||
setOpertationTimeDataParams(newParams)
|
||||
|
||||
console.log(newParams)
|
||||
|
||||
}, [data, lines, rangeDate])
|
||||
|
||||
return (<>
|
||||
<ChartOpertationTimeBase dataParams={opertationTimeDataParams} />
|
||||
</>
|
||||
)
|
||||
}
|
@ -1,177 +0,0 @@
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
import {
|
||||
Chart,
|
||||
ArcElement,
|
||||
TimeScale,
|
||||
Legend,
|
||||
PointElement,
|
||||
ChartData,
|
||||
ChartTypeRegistry,
|
||||
ChartOptions,
|
||||
DoughnutController,
|
||||
} from 'chart.js'
|
||||
import 'chartjs-adapter-moment'
|
||||
import ChartDataLabels from 'chartjs-plugin-datalabels'
|
||||
|
||||
Chart.register(TimeScale, DoughnutController, PointElement, ArcElement, Legend, ChartDataLabels)
|
||||
|
||||
const defaultOptions = {
|
||||
responsive: true,
|
||||
title: {
|
||||
display: true,
|
||||
position: 'top',
|
||||
text: 'Doughnut Chart',
|
||||
fontSize: 18,
|
||||
fontColor: '#111'
|
||||
},
|
||||
legend: {
|
||||
display: true,
|
||||
position: 'bottom',
|
||||
labels: {
|
||||
fontColor: '#333',
|
||||
fontSize: 16
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export type ChartTimeData = ChartData<keyof ChartTypeRegistry, {
|
||||
x: String
|
||||
label: number
|
||||
y: number
|
||||
}[], unknown>
|
||||
|
||||
export type ChartTimeDataParams = {
|
||||
data: ChartTimeData,
|
||||
xStart?: Date,
|
||||
xInterval?: number,
|
||||
displayLabels?: Boolean,
|
||||
}
|
||||
|
||||
export type ChartTimeBaseProps = {
|
||||
dataParams: ChartTimeDataParams,
|
||||
// TODO: Create good type for options
|
||||
options?: ChartOptions<keyof ChartTypeRegistry> | any,
|
||||
}
|
||||
|
||||
export type TimeParams = {
|
||||
unit: String
|
||||
stepSize: number
|
||||
}
|
||||
|
||||
const linesPerInterval = 32
|
||||
|
||||
export const timeUnitByInterval = (intervalSec: number): String => {
|
||||
if (intervalSec <= 60)
|
||||
return 'millisecond'
|
||||
|
||||
if (intervalSec <= 32 * 60)
|
||||
return 'second'
|
||||
|
||||
if (intervalSec <= 32 * 60 * 60)
|
||||
return 'minute'
|
||||
|
||||
if (intervalSec <= 32 * 12 * 60 * 60)
|
||||
return 'hour'
|
||||
|
||||
if (intervalSec <= 32 * 24 * 60 * 60)
|
||||
return 'day'
|
||||
|
||||
if (intervalSec <= 32 * 7 * 24 * 60 * 60)
|
||||
return 'week'
|
||||
|
||||
if (intervalSec <= 32 * 30.4375 * 24 * 60 * 60)
|
||||
return 'month'
|
||||
|
||||
if (intervalSec <= 32 * 121.75 * 24 * 60 * 60)
|
||||
return 'quarter'
|
||||
else
|
||||
return 'year'
|
||||
}
|
||||
|
||||
export const timeParamsByInterval = (intervalSec: number): TimeParams => {
|
||||
let stepSize = intervalSec
|
||||
let unit = timeUnitByInterval(intervalSec)
|
||||
|
||||
switch (unit) {
|
||||
case 'millisecond':
|
||||
stepSize *= 1000
|
||||
break
|
||||
case 'second':
|
||||
//stepSize *= 1
|
||||
break
|
||||
case 'minute':
|
||||
stepSize /= 60
|
||||
break
|
||||
case 'hour':
|
||||
stepSize /= 60 * 60
|
||||
break
|
||||
case 'day':
|
||||
stepSize /= 24 * 60 * 60
|
||||
break
|
||||
case 'week':
|
||||
stepSize /= 7 * 24 * 60 * 60
|
||||
break
|
||||
case 'month':
|
||||
stepSize /= 30 * 24 * 60 * 60
|
||||
break
|
||||
case 'quarter':
|
||||
stepSize /= 91 * 24 * 60 * 60
|
||||
break
|
||||
case 'year':
|
||||
stepSize /= 365.25 * 24 * 60 * 60
|
||||
break
|
||||
}
|
||||
|
||||
stepSize = Math.round(stepSize / linesPerInterval)
|
||||
stepSize = stepSize > 0 ? stepSize : 1
|
||||
return { unit, stepSize }
|
||||
}
|
||||
|
||||
export const ChartOpertationTimeBase: React.FC<ChartTimeBaseProps> = ({ options, dataParams }) => {
|
||||
const chartRef = useRef<HTMLCanvasElement>(null)
|
||||
const [chart, setChart] = useState<any>()
|
||||
|
||||
useEffect(() => {
|
||||
if ((chartRef.current) && (!chart)) {
|
||||
let thisOptions = {}
|
||||
Object.assign(thisOptions, defaultOptions, options)
|
||||
|
||||
let newChart = new Chart(chartRef.current, {
|
||||
type: 'doughnut',
|
||||
plugins: [ChartDataLabels],
|
||||
options: thisOptions,
|
||||
data: dataParams.data
|
||||
})
|
||||
setChart(newChart)
|
||||
|
||||
return () => chart?.destroy()
|
||||
}
|
||||
}, [chart, options, dataParams])
|
||||
|
||||
useEffect(() => {
|
||||
if (!chart)
|
||||
return
|
||||
|
||||
chart.data = dataParams.data
|
||||
chart.options.aspectRatio = options?.aspectRatio
|
||||
if (dataParams.xStart) {
|
||||
let interval = Number(dataParams.xInterval ?? 600)
|
||||
let start = new Date(dataParams.xStart)
|
||||
let end = new Date(dataParams.xStart)
|
||||
end.setSeconds(end.getSeconds() + interval)
|
||||
let { unit, stepSize } = timeParamsByInterval(interval)
|
||||
|
||||
if (chart.options.scales?.x) {
|
||||
chart.options.scales.x.max = end.getTime()
|
||||
chart.options.scales.x.min = start.getTime()
|
||||
chart.options.scales.x.ticks.display = dataParams.displayLabels ?? true
|
||||
chart.options.scales.x.time.unit = unit
|
||||
chart.options.scales.x.time.stepSize = stepSize
|
||||
}
|
||||
}
|
||||
|
||||
chart.update()
|
||||
}, [chart, dataParams, options])
|
||||
|
||||
return (<canvas ref={chartRef} />)
|
||||
}
|
@ -1,99 +0,0 @@
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
import {
|
||||
Chart,
|
||||
TimeScale,
|
||||
LinearScale,
|
||||
Legend,
|
||||
LineController,
|
||||
PointElement,
|
||||
LineElement
|
||||
} from 'chart.js'
|
||||
import 'chartjs-adapter-moment'
|
||||
import ChartDataLabels from 'chartjs-plugin-datalabels'
|
||||
import zoomPlugin from 'chartjs-plugin-zoom'
|
||||
|
||||
Chart.register(TimeScale, LinearScale, LineController, LineElement, PointElement, Legend, ChartDataLabels, zoomPlugin)
|
||||
|
||||
const defaultOptions = {
|
||||
responsive: true,
|
||||
aspectRatio: 2.45,
|
||||
scales: {
|
||||
x: {
|
||||
type: 'time',
|
||||
time: {
|
||||
unit: 'hour',
|
||||
displayFormats: {
|
||||
hour: 'MM.DD'
|
||||
},
|
||||
tooltipFormat: 'DD T'
|
||||
},
|
||||
},
|
||||
y: {
|
||||
type: 'linear',
|
||||
position: 'top',
|
||||
reverse: true,
|
||||
// ticks: {
|
||||
// // forces step size to be 50 units
|
||||
// stepSize: 50
|
||||
// }
|
||||
}
|
||||
},
|
||||
parsing: {
|
||||
xAxisKey: 'date',
|
||||
yAxisKey: 'depth'
|
||||
},
|
||||
elements: {
|
||||
point: {
|
||||
radius: 1.7,
|
||||
hoverRadius: 5,
|
||||
},
|
||||
},
|
||||
plugins: {
|
||||
datalabels: {
|
||||
display: false,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
const makeDataset = (data, label, color, width = 1.5, dash) => ({
|
||||
label: label,
|
||||
data: data,
|
||||
backgroundColor: color,
|
||||
borderColor: color,
|
||||
borderWidth: width,
|
||||
borderDash: dash,
|
||||
})
|
||||
|
||||
export const ChartTelemetryDepthToDay = ({ depthData, bitPositionData }) => {
|
||||
const chartRef = useRef(null)
|
||||
const [chart, setChart] = useState()
|
||||
|
||||
useEffect(() => {
|
||||
const data = {
|
||||
datasets: [
|
||||
makeDataset(depthData, 'Глубина', '#0A0'),
|
||||
makeDataset(bitPositionData, 'Положение долота', 'blue'),
|
||||
]
|
||||
}
|
||||
|
||||
if(chartRef.current && !chart) {
|
||||
const thisOptions = {}
|
||||
Object.assign(thisOptions, defaultOptions)
|
||||
|
||||
const newChart = new Chart(chartRef.current, {
|
||||
type: 'line',
|
||||
plugins: [ChartDataLabels],
|
||||
options: thisOptions,
|
||||
data: data
|
||||
})
|
||||
setChart(newChart)
|
||||
|
||||
return () => chart?.destroy()
|
||||
} else {
|
||||
chart.data = data
|
||||
chart.update()
|
||||
}
|
||||
}, [chart, depthData, bitPositionData])
|
||||
|
||||
return(<canvas ref={chartRef} />)
|
||||
}
|
@ -1,74 +0,0 @@
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
import { Chart, registerables } from 'chart.js'
|
||||
|
||||
Chart.register(...registerables)
|
||||
|
||||
const defaultOptions = {
|
||||
responsive: true,
|
||||
aspectRatio: 2.6,
|
||||
scales: {
|
||||
x: {
|
||||
title: {
|
||||
display: true,
|
||||
text: 'Дата начала интервала'
|
||||
},
|
||||
},
|
||||
y: {
|
||||
title: {
|
||||
display: true,
|
||||
text: 'Коэффициент скорости'
|
||||
}
|
||||
}
|
||||
},
|
||||
plugins: {
|
||||
datalabels: {
|
||||
display: false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const ChartTelemetryDepthToInterval = ({ depthToIntervalData }) => {
|
||||
const chartRef = useRef(null)
|
||||
const [chart, setChart] = useState()
|
||||
|
||||
const calculateBarWidth = (dataLength) => {
|
||||
if (dataLength < 3) return 150
|
||||
if (dataLength < 16) return 70
|
||||
return 10
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
const xData = depthToIntervalData.map(el => new Date(el.intervalStartDate).toLocaleString())
|
||||
const yData = depthToIntervalData.map(el => el.intervalDepthProgress.toFixed(3))
|
||||
|
||||
const data = {
|
||||
labels: xData,
|
||||
datasets: [{
|
||||
label: 'Скорость проходки за интервал',
|
||||
data: yData,
|
||||
borderColor: '#00b300',
|
||||
borderWidth: 2,
|
||||
backgroundColor: '#0A0',
|
||||
barThickness: calculateBarWidth(xData.length)
|
||||
}]
|
||||
}
|
||||
|
||||
const thisOptions = {}
|
||||
Object.assign(thisOptions, defaultOptions)
|
||||
|
||||
if (chartRef.current && !chart) {
|
||||
const newChart = new Chart(chartRef.current, {
|
||||
type: 'bar',
|
||||
options: thisOptions,
|
||||
data: data
|
||||
})
|
||||
setChart(newChart)
|
||||
} else {
|
||||
chart.data = data
|
||||
chart.options = thisOptions
|
||||
chart.update()
|
||||
}
|
||||
}, [chart, depthToIntervalData])
|
||||
|
||||
return (<canvas ref={chartRef} />)
|
||||
}
|
@ -1,89 +0,0 @@
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
import { Chart, registerables } from 'chart.js'
|
||||
|
||||
Chart.register(...registerables)
|
||||
|
||||
const transformSecondsToHoursString = (seconds) => {
|
||||
const hours = Math.floor(seconds / 3600)
|
||||
const minutes = Math.floor((seconds % 3600) / 60)
|
||||
const s = seconds - (hours * 3600) - (minutes * 60)
|
||||
|
||||
return `${hours} ч.${minutes} мин.${s} сек.`
|
||||
}
|
||||
|
||||
const transformSecondsToTimeString = (seconds) => {
|
||||
if (seconds === 1) // 1 is default state if null returned (1 is to show chart anyway with 0 sec)
|
||||
return '0 сек.'
|
||||
else if(seconds < 60)
|
||||
return seconds + ' сек.'
|
||||
else if (seconds < 3600)
|
||||
return Math.floor(seconds / 60) + ' мин. ' + (0.6 * (seconds % 60)).toFixed() + ' сек.'
|
||||
else
|
||||
return transformSecondsToHoursString(seconds)
|
||||
}
|
||||
|
||||
const defaultOptions = {
|
||||
responsive: true,
|
||||
aspectRatio: 2.8,
|
||||
plugins: {
|
||||
datalabels: {
|
||||
color: '#ffffff',
|
||||
formatter: transformSecondsToTimeString,
|
||||
font: {
|
||||
weight: 'bold'
|
||||
}
|
||||
},
|
||||
tooltip: {
|
||||
callbacks: {
|
||||
label: (tooltipItem) => transformSecondsToTimeString(tooltipItem.parsed),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const chartPartsColors = [
|
||||
'#54a60c', '#0ca68a', '#0c8aa6', '#0c57a6', '#0c33a6',
|
||||
'#6f10d5', '#d510a1', '#f1bc41', '#c5f141', '#41f196',
|
||||
'#41cbf1', '#4196f1', '#bf41f1', '#41f1c5', '#cbf141',
|
||||
'#f1ce41', '#f17f41', '#f14141', '#34b40e', '#420eb4'
|
||||
]
|
||||
|
||||
export const ChartTelemetryOperationsSummary = ({ operationsData }) => {
|
||||
const chartRef = useRef(null)
|
||||
const [chart, setChart] = useState()
|
||||
|
||||
useEffect(() => {
|
||||
const namesData = operationsData?.map(el => el.operationName) ?? ['Нет операций']
|
||||
const durationsData = operationsData?.map(el => el.duration) ?? [1]
|
||||
|
||||
const data = {
|
||||
labels: namesData,
|
||||
datasets: [
|
||||
{
|
||||
label: 'Скорость проходки за интервал',
|
||||
data: durationsData,
|
||||
borderColor: chartPartsColors,
|
||||
backgroundColor: chartPartsColors,
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
const thisOptions = {}
|
||||
Object.assign(thisOptions, defaultOptions)
|
||||
|
||||
if (chartRef.current && !chart) {
|
||||
const newChart = new Chart(chartRef.current, {
|
||||
type: 'doughnut',
|
||||
options: thisOptions,
|
||||
data: data
|
||||
})
|
||||
setChart(newChart)
|
||||
} else {
|
||||
chart.data = data
|
||||
chart.options = thisOptions
|
||||
chart.update()
|
||||
}
|
||||
}, [chart, operationsData])
|
||||
|
||||
return (<canvas ref={chartRef} />)
|
||||
}
|
@ -1,46 +0,0 @@
|
||||
import { useState, useEffect, memo, useContext } from 'react'
|
||||
|
||||
import { IdWellContext } from '@asb/context'
|
||||
import LoaderPortal from '@components/LoaderPortal'
|
||||
import { invokeWebApiWrapperAsync } from '@components/factory'
|
||||
import { ChartTelemetryDepthToDay } from '@components/charts/ChartTelemetryDepthToDay'
|
||||
import { TelemetryAnalyticsService } from '@api'
|
||||
|
||||
|
||||
export const TelemetryAnalysisDepthToDay = memo(() => {
|
||||
const [depthData, setDepthData] = useState([])
|
||||
const [bitPositionData, setBitPositionData] = useState([])
|
||||
const [loader, setLoader] = useState(false)
|
||||
|
||||
const idWell = useContext(IdWellContext)
|
||||
|
||||
useEffect(() => invokeWebApiWrapperAsync(
|
||||
async () => {
|
||||
const depthToDayData = await TelemetryAnalyticsService.getWellDepthToDay(idWell)
|
||||
|
||||
const depths = depthToDayData.map(el => ({depth: el.wellDepth, date: el.date}))
|
||||
.sort((a, b) => new Date(a.date) - new Date(b.date))
|
||||
setDepthData(depths)
|
||||
|
||||
const bitPositions = depthToDayData.map(el => ({depth: el.bitDepth, date: el.date}))
|
||||
.sort((a, b) => new Date(a.date) - new Date(b.date))
|
||||
setBitPositionData(bitPositions)
|
||||
},
|
||||
setLoader,
|
||||
`Не удалось получить данные для анализа Глубина-День по скважине "${idWell}"`,
|
||||
'Получение данных для анализа Глубина-День'
|
||||
), [idWell])
|
||||
|
||||
return (
|
||||
<LoaderPortal show={loader}>
|
||||
<div className={'mt-20px'}>
|
||||
<ChartTelemetryDepthToDay
|
||||
depthData={depthData}
|
||||
bitPositionData={bitPositionData}
|
||||
/>
|
||||
</div>
|
||||
</LoaderPortal>
|
||||
)
|
||||
})
|
||||
|
||||
export default TelemetryAnalysisDepthToDay
|
@ -1,53 +0,0 @@
|
||||
import { Select } from 'antd'
|
||||
import { useState, useEffect, memo, useContext } from 'react'
|
||||
|
||||
import { IdWellContext } from '@asb/context'
|
||||
import LoaderPortal from '@components/LoaderPortal'
|
||||
import { invokeWebApiWrapperAsync } from '@components/factory'
|
||||
import { ChartTelemetryDepthToInterval } from '@components/charts/ChartTelemetryDepthToInterval'
|
||||
import { TelemetryAnalyticsService } from '@api'
|
||||
import { arrayOrDefault } from '@utils'
|
||||
|
||||
|
||||
const timePeriodCollection = [
|
||||
{ value: '3600', label: '1 час' },
|
||||
{ value: '21600', label: '6 часов' },
|
||||
{ value: '43200', label: '12 часов' },
|
||||
{ value: '86400', label: '24 часа' }
|
||||
]
|
||||
|
||||
export const TelemetryAnalysisDepthToInterval = memo(() => {
|
||||
const [depthToIntervalData, setDepthToIntervalData] = useState([])
|
||||
const [chartInterval, setChartInterval] = useState(86400)
|
||||
const [loader, setLoader] = useState(false)
|
||||
|
||||
const idWell = useContext(IdWellContext)
|
||||
|
||||
useEffect(() => invokeWebApiWrapperAsync(
|
||||
async () => {
|
||||
const depthToIntervalData = await TelemetryAnalyticsService.getWellDepthToInterval(idWell, chartInterval)
|
||||
setDepthToIntervalData(arrayOrDefault(depthToIntervalData))
|
||||
},
|
||||
setLoader,
|
||||
`Не удалось получить данные для анализа скорость проходки-интервал "${idWell}"`,
|
||||
'Получение данных для анализа скорость проходки-интервал'
|
||||
), [idWell, chartInterval])
|
||||
|
||||
return (
|
||||
<LoaderPortal show={loader}>
|
||||
<div className={'mt-20px'}>
|
||||
<Select
|
||||
options={timePeriodCollection}
|
||||
onChange={setChartInterval}
|
||||
defaultValue={'86400'}
|
||||
className={'ml-30px'}
|
||||
/>
|
||||
<ChartTelemetryDepthToInterval
|
||||
depthToIntervalData={depthToIntervalData}
|
||||
/>
|
||||
</div>
|
||||
</LoaderPortal>
|
||||
)
|
||||
})
|
||||
|
||||
export default TelemetryAnalysisDepthToInterval
|
@ -1,72 +0,0 @@
|
||||
import moment from 'moment'
|
||||
import { Form, DatePicker } from 'antd'
|
||||
import { useState, useEffect, memo, useContext } from 'react'
|
||||
|
||||
import { IdWellContext } from '@asb/context'
|
||||
import LoaderPortal from '@components/LoaderPortal'
|
||||
import { invokeWebApiWrapperAsync } from '@components/factory'
|
||||
import { ChartTelemetryOperationsSummary } from '@components/charts/ChartTelemetryOperationsSummary'
|
||||
import { TelemetryAnalyticsService } from '@api'
|
||||
|
||||
|
||||
const { RangePicker } = DatePicker
|
||||
|
||||
export const TelemetryAnalysisOperationsSummary = memo(() => {
|
||||
const [avilableDatesRange, setAviableDatesRange] = useState([moment(),moment()])
|
||||
const [filterDateRange, setFilterDateRange] = useState([moment().subtract(1, 'days'),moment()])
|
||||
const [operationsData, setOperationsData] = useState([])
|
||||
const [loader, setLoader] = useState(false)
|
||||
|
||||
const idWell = useContext(IdWellContext)
|
||||
|
||||
const disabledDate = (current) => current < avilableDatesRange[0] || current > avilableDatesRange[1]
|
||||
|
||||
useEffect(() => invokeWebApiWrapperAsync(
|
||||
async() => {
|
||||
const datesRange = await TelemetryAnalyticsService.getOperationsDateRange(idWell)
|
||||
setAviableDatesRange(datesRange)
|
||||
},
|
||||
setLoader,
|
||||
`Не удалось загрузить диапозон дат для скважины "${idWell}"`,
|
||||
'Получение диапозона дат телеметрии скважины'
|
||||
), [idWell])
|
||||
|
||||
useEffect(() => invokeWebApiWrapperAsync(
|
||||
async () => {
|
||||
const operationsSummaryData = await TelemetryAnalyticsService.getOperationsSummary(idWell,
|
||||
filterDateRange[0].toISOString(), filterDateRange[1].toISOString())
|
||||
|
||||
setOperationsData(operationsSummaryData)
|
||||
},
|
||||
setLoader,
|
||||
`Не удалось получить данные для анализа операций по скважине "${idWell}"`,
|
||||
'Получение данных для анализа операций по скважине'
|
||||
), [idWell, filterDateRange])
|
||||
|
||||
return (
|
||||
<LoaderPortal show={loader}>
|
||||
<div className={'w-100 mt-20px'}>
|
||||
<Form layout={'vertical'} name={'operationsForm'} initialValues={{ remember: true }}>
|
||||
<div className={'d-flex'}>
|
||||
<Form.Item
|
||||
name={'period'}
|
||||
className={'ml-30px'}
|
||||
initialValue={filterDateRange}
|
||||
label={'Диапазон дат графика:'}
|
||||
>
|
||||
<RangePicker
|
||||
showTime
|
||||
allowClear={false}
|
||||
disabledDate={disabledDate}
|
||||
onCalendarChange={(dates) => setFilterDateRange(dates)}
|
||||
/>
|
||||
</Form.Item>
|
||||
</div>
|
||||
</Form>
|
||||
</div>
|
||||
<ChartTelemetryOperationsSummary operationsData={operationsData}/>
|
||||
</LoaderPortal>
|
||||
)
|
||||
})
|
||||
|
||||
export default TelemetryAnalysisOperationsSummary
|
@ -1,5 +0,0 @@
|
||||
import { memo } from 'react'
|
||||
|
||||
export const TelemetryAnalysisOperationsToInterval = memo(() => (<>123</>))
|
||||
|
||||
export default TelemetryAnalysisOperationsToInterval
|
@ -1,44 +0,0 @@
|
||||
import { useParams } from 'react-router-dom'
|
||||
import { memo, useContext, useMemo } from 'react'
|
||||
import { FolderOutlined } from '@ant-design/icons'
|
||||
import { Layout } from 'antd'
|
||||
|
||||
import { RootPathContext } from '@asb/context'
|
||||
import { PrivateMenu, PrivateSwitch } from '@components/Private'
|
||||
|
||||
import TelemetryAnalysisDepthToDay from './TelemetryAnalysisDepthToDay'
|
||||
import TelemetryAnalysisDepthToInterval from './TelemetryAnalysisDepthToInterval'
|
||||
import TelemetryAnalysisOperationsSummary from './TelemetryAnalysisOperationsSummary'
|
||||
import TelemetryAnalysisOperationsToInterval from './TelemetryAnalysisOperationsToInterval'
|
||||
|
||||
const { Content } = Layout
|
||||
|
||||
export const TelemetryAnalysis = memo(() => {
|
||||
const { tab } = useParams()
|
||||
const root = useContext(RootPathContext)
|
||||
const rootPath = useMemo(() => `${root}/telemetryAnalysis`, [root])
|
||||
|
||||
return (
|
||||
<RootPathContext.Provider value={rootPath}>
|
||||
<PrivateMenu mode={'horizontal'} selectable={true} className={'well_menu'} selectedKeys={[tab]}>
|
||||
<PrivateMenu.Link icon={<FolderOutlined />} key={'depthToDay'} title={'Глубина-день'} />
|
||||
<PrivateMenu.Link icon={<FolderOutlined />} key={'depthToInterval'} title={'Глубина-интервал'} />
|
||||
<PrivateMenu.Link icon={<FolderOutlined />} key={'operationsSummary'} title={'Все операции'} />
|
||||
<PrivateMenu.Link icon={<FolderOutlined />} key={'operationsToInterval'} title={'Операции-интервал'} />
|
||||
</PrivateMenu>
|
||||
|
||||
<Layout>
|
||||
<Content className={'site-layout-background'}>
|
||||
<PrivateSwitch elseRedirect={['depthToDay', 'depthToInterval', 'operationsSummary', 'operationsToInterval']}>
|
||||
<TelemetryAnalysisDepthToDay key={'depthToDay'} />
|
||||
<TelemetryAnalysisDepthToInterval key={'depthToInterval'} />
|
||||
<TelemetryAnalysisOperationsSummary key={'operationsSummary'} />
|
||||
<TelemetryAnalysisOperationsToInterval key={'operationsToInterval'} />
|
||||
</PrivateSwitch>
|
||||
</Content>
|
||||
</Layout>
|
||||
</RootPathContext.Provider>
|
||||
)
|
||||
})
|
||||
|
||||
export default TelemetryAnalysis
|
Loading…
Reference in New Issue
Block a user