forked from ddrilling/asb_cloud_front
Added 'Bar' chart for 'Depth to interval' telemetry analysis
This commit is contained in:
parent
8302cc8573
commit
be6072e641
@ -1,39 +1,65 @@
|
|||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useRef, useState } from 'react'
|
||||||
import { ChartDepthToIntervalBase } from './ChartDepthToIntervalBase'
|
import { Chart, registerables } from 'chart.js';
|
||||||
import { CreateDataset } from './ChartTimeArchive'
|
|
||||||
|
|
||||||
export const ChartDepthToInterval = ({ lines, data }) => {
|
Chart.register(...registerables);
|
||||||
const [depthToIntervalDataParams, setDepthToIntervalDataParams] = useState({ data: { datasets: [] } })
|
|
||||||
|
|
||||||
useEffect(() => {
|
const defaultOptions = {
|
||||||
if ((!lines)
|
responsive: true,
|
||||||
|| (!data))
|
aspectRatio: 2.6,
|
||||||
return
|
scales: {
|
||||||
|
x: {
|
||||||
let newDatasets = lines.map(lineCfg => {
|
title: {
|
||||||
let datasets = CreateDataset(lineCfg)
|
display: true,
|
||||||
if (data.length !== 0)
|
text: 'Дата начала интервала'
|
||||||
datasets.data = data.map(dataItem => {
|
},
|
||||||
return {
|
},
|
||||||
x: new Date(dataItem[lineCfg.xAccessorName ?? 'intervalStartDate']),
|
y: {
|
||||||
y: dataItem[lineCfg.yAccessorName ?? 'intervalDepthProgress'],
|
title: {
|
||||||
}
|
display: true,
|
||||||
})
|
text: 'Коэффициент скорости'
|
||||||
return datasets
|
|
||||||
})
|
|
||||||
|
|
||||||
let newParams = {
|
|
||||||
displayLabels: true,
|
|
||||||
data: {
|
|
||||||
datasets: newDatasets
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
setDepthToIntervalDataParams(newParams)
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}, [data, lines])
|
export const ChartDepthToInterval = ({ depthToIntervalData }) => {
|
||||||
|
const chartRef = useRef(null)
|
||||||
|
const [chart, setChart] = useState()
|
||||||
|
|
||||||
return (<>
|
useEffect(() => {
|
||||||
<ChartDepthToIntervalBase dataParams={depthToIntervalDataParams} />
|
|
||||||
</>
|
const xData = depthToIntervalData.map(el => new Date(el.intervalStartDate).toLocaleString())
|
||||||
)
|
const yData = depthToIntervalData.map(el => el.intervalDepthProgress.toFixed(3))
|
||||||
|
|
||||||
|
let data = {
|
||||||
|
labels: xData,
|
||||||
|
datasets: [
|
||||||
|
{
|
||||||
|
label: 'Скорость проходки за интервал',
|
||||||
|
data: yData,
|
||||||
|
borderColor: '#0A0',
|
||||||
|
backgroundColor: '#0A0',
|
||||||
|
barThickness: xData.length < 3 ? 150 : 70
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
let thisOptions = {}
|
||||||
|
Object.assign(thisOptions, defaultOptions)
|
||||||
|
|
||||||
|
if ((chartRef.current) && (!chart)) {
|
||||||
|
let 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,224 +0,0 @@
|
|||||||
import { useEffect, useRef, useState } from 'react'
|
|
||||||
import {
|
|
||||||
Chart,
|
|
||||||
TimeScale,
|
|
||||||
Legend,
|
|
||||||
PointElement,
|
|
||||||
ChartData,
|
|
||||||
ChartTypeRegistry,
|
|
||||||
ChartOptions,
|
|
||||||
BarController,
|
|
||||||
BarElement,
|
|
||||||
TimeSeriesScale,
|
|
||||||
LinearScale,
|
|
||||||
LineController,
|
|
||||||
} from 'chart.js'
|
|
||||||
import 'chartjs-adapter-moment'
|
|
||||||
import ChartDataLabels from 'chartjs-plugin-datalabels'
|
|
||||||
|
|
||||||
Chart.register(TimeScale, BarController, BarElement, PointElement, TimeSeriesScale, LineController, LinearScale, Legend, ChartDataLabels)
|
|
||||||
|
|
||||||
const defaultOptions = {
|
|
||||||
responsive: true,
|
|
||||||
aspectRatio: 4,
|
|
||||||
animation: false,
|
|
||||||
tooltips: {
|
|
||||||
enabled: true,
|
|
||||||
callbacks: {
|
|
||||||
label(tooltipItem: any) {
|
|
||||||
return tooltipItem.yLabel;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
events: ['mousemove', 'mouseout', 'click', 'touchstart', 'touchmove'],
|
|
||||||
scales: {
|
|
||||||
x: {
|
|
||||||
position: 'bottom',
|
|
||||||
type: 'time',
|
|
||||||
reverse: false,
|
|
||||||
time: {
|
|
||||||
stepSize: 20,
|
|
||||||
displayFormats: {
|
|
||||||
millisecond: 'HH:mm:ss.SSS',
|
|
||||||
second: 'HH:mm:ss',
|
|
||||||
minute: 'HH:mm:ss',
|
|
||||||
hour: 'DD HH:mm:ss',
|
|
||||||
day: 'MM.DD HH:mm',
|
|
||||||
week: 'yy.MM.DD HH:mm',
|
|
||||||
month: 'yyyy.MM.DD',
|
|
||||||
quarter: 'yyyy.MM.DD',
|
|
||||||
year: 'yyyy.MM',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
grid: {
|
|
||||||
drawTicks: false,
|
|
||||||
},
|
|
||||||
ticks: {
|
|
||||||
z: 1,
|
|
||||||
display: true,
|
|
||||||
textStrokeColor: "#ffff",
|
|
||||||
textStrokeWidth: 2,
|
|
||||||
color: "#000",
|
|
||||||
}
|
|
||||||
},
|
|
||||||
y: {
|
|
||||||
beginAtZero: true,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
elements: {
|
|
||||||
point: {
|
|
||||||
radius: 0,
|
|
||||||
hoverRadius: 5,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
plugins: {
|
|
||||||
legend: {
|
|
||||||
display: true,
|
|
||||||
},
|
|
||||||
datalabels: {
|
|
||||||
display: false,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export type ChartTimeData = ChartData<keyof ChartTypeRegistry, {
|
|
||||||
x: Date;
|
|
||||||
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 ChartDepthToIntervalBase: 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: 'bar',
|
|
||||||
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 height='100%' width='100%' ref={chartRef} />)
|
|
||||||
}
|
|
@ -16,45 +16,26 @@ Chart.register(TimeScale, LinearScale, LineController, LineElement, PointElement
|
|||||||
|
|
||||||
const defaultOptions = {
|
const defaultOptions = {
|
||||||
responsive: true,
|
responsive: true,
|
||||||
aspectRatio: 2.6,
|
aspectRatio: 2.45,
|
||||||
interaction: {
|
|
||||||
intersect: false,
|
|
||||||
mode: 'index',
|
|
||||||
},
|
|
||||||
scales: {
|
scales: {
|
||||||
x:{
|
x:{
|
||||||
display: true,
|
|
||||||
title: {
|
|
||||||
display: true
|
|
||||||
},
|
|
||||||
type: 'time',
|
type: 'time',
|
||||||
time: {
|
time: {
|
||||||
unit: 'hour',
|
unit: 'hour',
|
||||||
displayFormats: {
|
displayFormats: {
|
||||||
'hour': 'MM.DD'
|
'hour': 'MM.DD'
|
||||||
}
|
},
|
||||||
|
tooltipFormat: 'DD T'
|
||||||
},
|
},
|
||||||
grid:{
|
|
||||||
drawTicks: true,
|
|
||||||
},
|
|
||||||
ticks: {
|
|
||||||
z: 1,
|
|
||||||
display : true,
|
|
||||||
textStrokeColor : "#fff",
|
|
||||||
textStrokeWidth : 2,
|
|
||||||
color:"#000",
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
y:{
|
y:{
|
||||||
type:'linear',
|
type:'linear',
|
||||||
position:'top',
|
position:'top',
|
||||||
reverse:true,
|
reverse:true,
|
||||||
display: true,
|
// ticks: {
|
||||||
title: {
|
// // forces step size to be 50 units
|
||||||
display: false,
|
// stepSize: 50
|
||||||
text: ''
|
// }
|
||||||
},
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
parsing: {
|
parsing: {
|
||||||
@ -68,9 +49,6 @@ const defaultOptions = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
plugins:{
|
plugins:{
|
||||||
legend:{
|
|
||||||
display: true,
|
|
||||||
},
|
|
||||||
datalabels: {
|
datalabels: {
|
||||||
display: false,
|
display: false,
|
||||||
},
|
},
|
||||||
|
@ -22,15 +22,17 @@ export default function TelemetryAnalysisDepthToDay({idWell}) {
|
|||||||
setBitPositionData(bitPositions)
|
setBitPositionData(bitPositions)
|
||||||
},
|
},
|
||||||
setLoader,
|
setLoader,
|
||||||
`Не удалось получить данные для Анализа Глубина-День по скважине "${idWell}"`)
|
`Не удалось получить данные для анализа Глубина-День по скважине "${idWell}"`)
|
||||||
}, [idWell])
|
}, [idWell])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<LoaderPortal show={loader}>
|
<LoaderPortal show={loader}>
|
||||||
<ChartTelemetryDepthToDay
|
<div className='mt-20px'>
|
||||||
depthData={depthData}
|
<ChartTelemetryDepthToDay
|
||||||
bitPositionData={bitPositionData}
|
depthData={depthData}
|
||||||
/>
|
bitPositionData={bitPositionData}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</LoaderPortal>
|
</LoaderPortal>
|
||||||
)
|
)
|
||||||
}
|
}
|
@ -1,6 +1,6 @@
|
|||||||
import { notify } from "../../components/factory"
|
|
||||||
import { useState, useEffect } from 'react'
|
import { useState, useEffect } from 'react'
|
||||||
import { TelemetryAnalyticsService } from '../../services/api'
|
import { TelemetryAnalyticsService } from '../../services/api'
|
||||||
|
import { invokeWebApiWrapperAsync } from '../../components/factory'
|
||||||
import { ChartDepthToInterval } from '../../components/charts/ChartDepthToInterval'
|
import { ChartDepthToInterval } from '../../components/charts/ChartDepthToInterval'
|
||||||
import { Select } from 'antd'
|
import { Select } from 'antd'
|
||||||
import LoaderPortal from '../../components/LoaderPortal'
|
import LoaderPortal from '../../components/LoaderPortal'
|
||||||
@ -18,40 +18,41 @@ const timePeriodCollection = [
|
|||||||
{ value: '86400', label: '24 часа' }
|
{ value: '86400', label: '24 часа' }
|
||||||
]
|
]
|
||||||
|
|
||||||
const lines = [{ label: 'График скорость проходки-интервал', yAccessorName: "intervalDepthProgress", xAccessorName: "intervalStartDate", color: '#00f' }]
|
|
||||||
|
|
||||||
export default function TelemetryAnalysisDepthToInterval({idWell}) {
|
export default function TelemetryAnalysisDepthToInterval({idWell}) {
|
||||||
const [depthToIntervalData, setDepthToIntervalData] = useState([])
|
const [depthToIntervalData, setDepthToIntervalData] = useState([])
|
||||||
const [loader, setLoader] = useState(false)
|
const [loader, setLoader] = useState(false)
|
||||||
const [chartInterval, setChartInterval] = useState(600)
|
const [chartInterval, setChartInterval] = useState(600)
|
||||||
|
|
||||||
const children = timePeriodCollection.map((line) => <Option key={line.value}>{line.label}</Option>)
|
const children = timePeriodCollection.map((line) =>
|
||||||
|
<Option key={line.value}>{line.label}</Option>)
|
||||||
const handleReceiveDepthToIntervalData = (data) => {
|
|
||||||
setDepthToIntervalData(data)
|
|
||||||
}
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setLoader(true)
|
setLoader(true)
|
||||||
TelemetryAnalyticsService.getWellDepthToInterval(idWell, chartInterval)
|
invokeWebApiWrapperAsync(
|
||||||
.then(handleReceiveDepthToIntervalData)
|
async () => {
|
||||||
.catch(error => {
|
const depthToIntervalData = await
|
||||||
notify(`Не удалось получить данные для Анализа скорость проходки-интервал "${idWell}"`,
|
TelemetryAnalyticsService.getWellDepthToInterval(idWell, chartInterval) ?? []
|
||||||
'warning')
|
|
||||||
console.log(error)
|
setDepthToIntervalData(depthToIntervalData)
|
||||||
})
|
},
|
||||||
.finally(setLoader(false))
|
setLoader,
|
||||||
|
`Не удалось получить данные для анализа скорость проходки-интервал "${idWell}"`)
|
||||||
}, [idWell, chartInterval])
|
}, [idWell, chartInterval])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<LoaderPortal show={loader}>
|
<LoaderPortal show={loader}>
|
||||||
<Select defaultValue="600" onChange={setChartInterval}>
|
<div className='mt-20px'>
|
||||||
{children}
|
<Select
|
||||||
</Select>
|
defaultValue="600"
|
||||||
<ChartDepthToInterval
|
onChange={setChartInterval}
|
||||||
data={depthToIntervalData}
|
className='ml-30px'
|
||||||
lines={lines}
|
>
|
||||||
/>
|
{children}
|
||||||
|
</Select>
|
||||||
|
<ChartDepthToInterval
|
||||||
|
depthToIntervalData={depthToIntervalData}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</LoaderPortal>
|
</LoaderPortal>
|
||||||
)
|
)
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user