Added 'Bar' chart for 'Depth to interval' telemetry analysis

This commit is contained in:
KharchenkoVV 2021-09-09 17:39:24 +05:00
parent 8302cc8573
commit be6072e641
5 changed files with 96 additions and 313 deletions

View File

@ -1,39 +1,65 @@
import { useEffect, useState } from 'react'
import { ChartDepthToIntervalBase } from './ChartDepthToIntervalBase'
import { CreateDataset } from './ChartTimeArchive'
import { useEffect, useRef, useState } from 'react'
import { Chart, registerables } from 'chart.js';
export const ChartDepthToInterval = ({ lines, data }) => {
const [depthToIntervalDataParams, setDepthToIntervalDataParams] = useState({ data: { datasets: [] } })
Chart.register(...registerables);
useEffect(() => {
if ((!lines)
|| (!data))
return
let newDatasets = lines.map(lineCfg => {
let datasets = CreateDataset(lineCfg)
if (data.length !== 0)
datasets.data = data.map(dataItem => {
return {
x: new Date(dataItem[lineCfg.xAccessorName ?? 'intervalStartDate']),
y: dataItem[lineCfg.yAccessorName ?? 'intervalDepthProgress'],
}
})
return datasets
})
let newParams = {
displayLabels: true,
data: {
datasets: newDatasets
const defaultOptions = {
responsive: true,
aspectRatio: 2.6,
scales: {
x: {
title: {
display: true,
text: 'Дата начала интервала'
},
},
y: {
title: {
display: true,
text: 'Коэффициент скорости'
}
}
setDepthToIntervalDataParams(newParams)
}
}
}, [data, lines])
export const ChartDepthToInterval = ({ depthToIntervalData }) => {
const chartRef = useRef(null)
const [chart, setChart] = useState()
return (<>
<ChartDepthToIntervalBase dataParams={depthToIntervalDataParams} />
</>
)
useEffect(() => {
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} />)
}

View File

@ -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} />)
}

View File

@ -16,45 +16,26 @@ Chart.register(TimeScale, LinearScale, LineController, LineElement, PointElement
const defaultOptions = {
responsive: true,
aspectRatio: 2.6,
interaction: {
intersect: false,
mode: 'index',
},
aspectRatio: 2.45,
scales: {
x:{
display: true,
title: {
display: true
},
type: 'time',
time: {
unit: 'hour',
displayFormats: {
'hour': 'MM.DD'
}
},
tooltipFormat: 'DD T'
},
grid:{
drawTicks: true,
},
ticks: {
z: 1,
display : true,
textStrokeColor : "#fff",
textStrokeWidth : 2,
color:"#000",
}
},
y:{
type:'linear',
position:'top',
reverse:true,
display: true,
title: {
display: false,
text: ''
},
// ticks: {
// // forces step size to be 50 units
// stepSize: 50
// }
}
},
parsing: {
@ -68,9 +49,6 @@ const defaultOptions = {
},
},
plugins:{
legend:{
display: true,
},
datalabels: {
display: false,
},

View File

@ -22,15 +22,17 @@ export default function TelemetryAnalysisDepthToDay({idWell}) {
setBitPositionData(bitPositions)
},
setLoader,
`Не удалось получить данные для Анализа Глубина-День по скважине "${idWell}"`)
`Не удалось получить данные для анализа Глубина-День по скважине "${idWell}"`)
}, [idWell])
return (
<LoaderPortal show={loader}>
<ChartTelemetryDepthToDay
depthData={depthData}
bitPositionData={bitPositionData}
/>
<div className='mt-20px'>
<ChartTelemetryDepthToDay
depthData={depthData}
bitPositionData={bitPositionData}
/>
</div>
</LoaderPortal>
)
}

View File

@ -1,6 +1,6 @@
import { notify } from "../../components/factory"
import { useState, useEffect } from 'react'
import { TelemetryAnalyticsService } from '../../services/api'
import { invokeWebApiWrapperAsync } from '../../components/factory'
import { ChartDepthToInterval } from '../../components/charts/ChartDepthToInterval'
import { Select } from 'antd'
import LoaderPortal from '../../components/LoaderPortal'
@ -18,40 +18,41 @@ const timePeriodCollection = [
{ value: '86400', label: '24 часа' }
]
const lines = [{ label: 'График скорость проходки-интервал', yAccessorName: "intervalDepthProgress", xAccessorName: "intervalStartDate", color: '#00f' }]
export default function TelemetryAnalysisDepthToInterval({idWell}) {
const [depthToIntervalData, setDepthToIntervalData] = useState([])
const [loader, setLoader] = useState(false)
const [chartInterval, setChartInterval] = useState(600)
const children = timePeriodCollection.map((line) => <Option key={line.value}>{line.label}</Option>)
const handleReceiveDepthToIntervalData = (data) => {
setDepthToIntervalData(data)
}
const children = timePeriodCollection.map((line) =>
<Option key={line.value}>{line.label}</Option>)
useEffect(() => {
setLoader(true)
TelemetryAnalyticsService.getWellDepthToInterval(idWell, chartInterval)
.then(handleReceiveDepthToIntervalData)
.catch(error => {
notify(`Не удалось получить данные для Анализа скорость проходки-интервал "${idWell}"`,
'warning')
console.log(error)
})
.finally(setLoader(false))
invokeWebApiWrapperAsync(
async () => {
const depthToIntervalData = await
TelemetryAnalyticsService.getWellDepthToInterval(idWell, chartInterval) ?? []
setDepthToIntervalData(depthToIntervalData)
},
setLoader,
`Не удалось получить данные для анализа скорость проходки-интервал "${idWell}"`)
}, [idWell, chartInterval])
return (
<LoaderPortal show={loader}>
<Select defaultValue="600" onChange={setChartInterval}>
{children}
</Select>
<ChartDepthToInterval
data={depthToIntervalData}
lines={lines}
/>
<div className='mt-20px'>
<Select
defaultValue="600"
onChange={setChartInterval}
className='ml-30px'
>
{children}
</Select>
<ChartDepthToInterval
depthToIntervalData={depthToIntervalData}
/>
</div>
</LoaderPortal>
)
}