forked from ddrilling/asb_cloud_front
Add archive page
This commit is contained in:
parent
f221ae5290
commit
5131259b3f
123
src/components/ArchiveColumn.jsx
Normal file
123
src/components/ArchiveColumn.jsx
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
import { useState, useEffect } from 'react';
|
||||||
|
import { Button, Select, Tag, Popover, Row, Tooltip } from 'antd';
|
||||||
|
import { ChartTimeArchive } from './charts/ChartTimeArchive';
|
||||||
|
//import { SlidersOutlined } from '@ant-design/icons';
|
||||||
|
|
||||||
|
const { Option } = Select;
|
||||||
|
|
||||||
|
const linesCollection = [
|
||||||
|
{ label: "Глубина забоя", xAccessorName: "wellDepth", color: '#f00' },
|
||||||
|
{ label: "Положение инструмента", xAccessorName: "bitDepth", color: '#ff0' },
|
||||||
|
{ label: "Высота талевого блока", xAccessorName: "blockPosition", color: '#f0f' },
|
||||||
|
{ label: "Талевый блок. Мин положение", xAccessorName: "blockPositionMin", color: '#0ff' },
|
||||||
|
{ label: "Талевый блок. Макс положение", xAccessorName: "blockPositionMax", color: '#0f0' },
|
||||||
|
{ label: "Скорость талевого блока", xAccessorName: "blockSpeed", color: '#00f' },
|
||||||
|
|
||||||
|
{ label: "Скорости талевого блока. Задание", xAccessorName: "blockSpeedSp", color: '#c00' },
|
||||||
|
{ label: "Талевый блок. Задание скорости для роторного бурения", xAccessorName: "blockSpeedSpRotor", color: '#cc0' },
|
||||||
|
{ label: "Талевый блок. Задание скорости для режима слайда", xAccessorName: "blockSpeedSpSlide", color: '#c0c' },
|
||||||
|
{ label: "Талевый блок. Задание скорости для проработки", xAccessorName: "blockSpeedSpDevelop", color: '#0cc' },
|
||||||
|
{ label: "Давление", xAccessorName: "pressure", color: '#0c0' },
|
||||||
|
{ label: "Давление. Холостой ход", xAccessorName: "pressureIdle", color: '#00c' },
|
||||||
|
|
||||||
|
{ label: "Давление. Задание", xAccessorName: "pressureSp", color: '#900' },
|
||||||
|
{ label: "Давление. Задание для роторного бурения", xAccessorName: "pressureSpRotor", color: '#990' },
|
||||||
|
{ label: "Давление. Задание для режима слайда", xAccessorName: "pressureSpSlide", color: '#909' },
|
||||||
|
{ label: "Давление. Задание для проработки", xAccessorName: "pressureSpDevelop", color: '#099' },
|
||||||
|
{ label: "Давление дифф. Аварийное макс.", xAccessorName: "pressureDeltaLimitMax", color: '#090' },
|
||||||
|
{ label: "Осевая нагрузка", xAccessorName: "axialLoad", color: '#009' },
|
||||||
|
|
||||||
|
{ label: "Осевая нагрузка. Задание", xAccessorName: "axialLoadSp", color: '#600' },
|
||||||
|
{ label: "Осевая нагрузка. Аварийная макс.", xAccessorName: "axialLoadLimitMax", color: '#660' },
|
||||||
|
{ label: "Вес на крюке", xAccessorName: "hookWeight", color: '#606' },
|
||||||
|
{ label: "Вес на крюке. Холостой ход", xAccessorName: "hookWeightIdle", color: '#066' },
|
||||||
|
{ label: "Вес на крюке. Посадка", xAccessorName: "hookWeightLimitMin", color: '#060' },
|
||||||
|
{ label: "Вес на крюке. Затяжка", xAccessorName: "hookWeightLimitMax", color: '#006' },
|
||||||
|
|
||||||
|
{ label: "Момент на роторе", xAccessorName: "rotorTorque", color: '#300' },
|
||||||
|
{ label: "Момент на роторе. Холостой ход", xAccessorName: "rotorTorqueIdle", color: '#330' },
|
||||||
|
{ label: "Момент на роторе. Задание", xAccessorName: "rotorTorqueSp", color: '#303' },
|
||||||
|
{ label: "Момент на роторе. Аварийный макс.", xAccessorName: "rotorTorqueLimitMax", color: '#033' },
|
||||||
|
{ label: "Обороты ротора", xAccessorName: "rotorSpeed", color: '#030' },
|
||||||
|
{ label: "Расход", xAccessorName: "flow", color: '#003' },
|
||||||
|
|
||||||
|
{ label: "Расход. Холостой ход", xAccessorName: "flowIdle", color: '#666' },
|
||||||
|
{ label: "Расход. Аварийный макс.", xAccessorName: "flowDeltaLimitMax", color: '#ccc' },
|
||||||
|
]
|
||||||
|
|
||||||
|
const tagRender = ({ label, value, closable, onClose }) =>{
|
||||||
|
const onPreventMouseDown = event => {
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
};
|
||||||
|
|
||||||
|
let color = linesCollection.find(l=>l.xAccessorName === value)?.color
|
||||||
|
return (
|
||||||
|
<Tag
|
||||||
|
//color={color}
|
||||||
|
onMouseDown={onPreventMouseDown}
|
||||||
|
closable={closable}
|
||||||
|
onClose={onClose}
|
||||||
|
style={{ marginRight: 3 }}>
|
||||||
|
<span style={{backgroundColor:color}}> </span>
|
||||||
|
<span> {label}</span>
|
||||||
|
</Tag>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ArchiveColumn({ data, config, rangeDate, chartRatio, onRemoveChart, onSaveConfig }) {
|
||||||
|
const [lines, setLines] = useState([]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setLines(config.lines);
|
||||||
|
},[config]);
|
||||||
|
|
||||||
|
const handleLinesSetChange = (linesKeys) => {
|
||||||
|
let newLines = linesCollection.filter(line => linesKeys.includes(line.xAccessorName));
|
||||||
|
config.lines = newLines;
|
||||||
|
if(onSaveConfig)
|
||||||
|
onSaveConfig()
|
||||||
|
setLines(newLines);
|
||||||
|
};
|
||||||
|
|
||||||
|
let selectedValues = lines?.map(line=>line.xAccessorName)??[]
|
||||||
|
|
||||||
|
const select = <Select
|
||||||
|
mode="multiple"
|
||||||
|
placeholder="Выберите линии"
|
||||||
|
value={selectedValues}
|
||||||
|
//showArrow
|
||||||
|
bordered={false}
|
||||||
|
tagRender={tagRender}
|
||||||
|
onChange={handleLinesSetChange}
|
||||||
|
style={{
|
||||||
|
minWidth: "300px",
|
||||||
|
maxWidth: "400px"
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{linesCollection.map((line) => (<Option key={line.xAccessorName} value={line.xAccessorName} color={line.color}>{line.label}</Option>))}
|
||||||
|
</Select>;
|
||||||
|
|
||||||
|
const popBar = <Row>
|
||||||
|
{select}
|
||||||
|
<Tooltip title="Удалить этот график">
|
||||||
|
<Button onClick={() => onRemoveChart(config.id)}>X</Button>
|
||||||
|
</Tooltip>
|
||||||
|
</Row>
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Popover content={popBar}>
|
||||||
|
<div>
|
||||||
|
<ChartTimeArchive
|
||||||
|
data={data}
|
||||||
|
yDisplay={config.yDisplay}
|
||||||
|
lines={lines}
|
||||||
|
rangeDate={rangeDate}
|
||||||
|
chartRatio={chartRatio}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</Popover>
|
||||||
|
|
||||||
|
</>);
|
||||||
|
}
|
@ -1,4 +1,5 @@
|
|||||||
import { useState, useEffect } from 'react'
|
import { useState, useEffect } from 'react'
|
||||||
|
import { useParams } from 'react-router-dom'
|
||||||
import { WellService } from '../services/api'
|
import { WellService } from '../services/api'
|
||||||
import Loader from '../components/Loader'
|
import Loader from '../components/Loader'
|
||||||
import { TreeSelect } from 'antd'
|
import { TreeSelect } from 'antd'
|
||||||
@ -38,6 +39,7 @@ export default function WellTreeSelector(props) {
|
|||||||
const [wellsTree, setWellsTree] = useState([])
|
const [wellsTree, setWellsTree] = useState([])
|
||||||
const [loader, setLoader] = useState(false)
|
const [loader, setLoader] = useState(false)
|
||||||
const history = useHistory()
|
const history = useHistory()
|
||||||
|
let { id } = useParams();
|
||||||
|
|
||||||
let updateWellsList = async () => {
|
let updateWellsList = async () => {
|
||||||
setLoader(true)
|
setLoader(true)
|
||||||
@ -72,6 +74,7 @@ export default function WellTreeSelector(props) {
|
|||||||
treeData={wellsTree}
|
treeData={wellsTree}
|
||||||
treeDefaultExpandAll
|
treeDefaultExpandAll
|
||||||
onSelect={onSelect}
|
onSelect={onSelect}
|
||||||
|
value = {id}
|
||||||
/>
|
/>
|
||||||
{loader && <Loader />}
|
{loader && <Loader />}
|
||||||
</>
|
</>
|
||||||
|
73
src/components/charts/ChartTimeArchive.jsx
Normal file
73
src/components/charts/ChartTimeArchive.jsx
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
import moment from 'moment';
|
||||||
|
import { useEffect, useState} from 'react';
|
||||||
|
import {ChartTimeBase} from './ChartTimeBase'
|
||||||
|
|
||||||
|
const GetRandomColor = () => "#" + Math.floor(Math.random()*16777215).toString(16)
|
||||||
|
|
||||||
|
const CreateDataset = (lineConfig) => {
|
||||||
|
let color = lineConfig.borderColor
|
||||||
|
?? lineConfig.backgroundColor
|
||||||
|
?? lineConfig.color
|
||||||
|
?? GetRandomColor()
|
||||||
|
|
||||||
|
let dataset = {
|
||||||
|
label: lineConfig.label,
|
||||||
|
data: [],
|
||||||
|
backgroundColor: lineConfig.backgroundColor ?? color,
|
||||||
|
borderColor: lineConfig.borderColor ?? color,
|
||||||
|
borderWidth: lineConfig.borderWidth ?? 1,
|
||||||
|
borderDash: lineConfig.dash ?? [],
|
||||||
|
}
|
||||||
|
return dataset
|
||||||
|
}
|
||||||
|
|
||||||
|
const ChartOptions = {
|
||||||
|
responsive: true,
|
||||||
|
// plugins:{
|
||||||
|
// legend:{
|
||||||
|
// maxHeight: 64,
|
||||||
|
// fullSize: true,
|
||||||
|
// display: true,
|
||||||
|
// posision:'chartArea',
|
||||||
|
// align: 'start',
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ChartTimeArchive = ({lines, data, yDisplay, rangeDate, chartRatio}) => {
|
||||||
|
const [dataParams, setDataParams] = useState({data:{datasets:[]}})
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if ((!lines)
|
||||||
|
|| (!data))
|
||||||
|
return
|
||||||
|
|
||||||
|
let newDatasets = lines.map(lineCfg => {
|
||||||
|
let dataset = CreateDataset(lineCfg)
|
||||||
|
dataset.data = data.map(dataItem => {
|
||||||
|
return {
|
||||||
|
x: dataItem[lineCfg.xAccessorName],
|
||||||
|
y: new Date(dataItem[lineCfg.yAccessorName??'date'])
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return dataset
|
||||||
|
})
|
||||||
|
|
||||||
|
let interval = rangeDate ? (rangeDate[1] - rangeDate[0]) / 1000 : null
|
||||||
|
let startDate = rangeDate ? rangeDate[0] : moment()
|
||||||
|
let newParams = {
|
||||||
|
yInterval: interval,
|
||||||
|
yStart: startDate,
|
||||||
|
displayLabels: yDisplay??false,
|
||||||
|
data: {
|
||||||
|
datasets: newDatasets
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setDataParams(newParams)
|
||||||
|
}, [data, lines, yDisplay, rangeDate, chartRatio])
|
||||||
|
|
||||||
|
const opt = ChartOptions
|
||||||
|
opt.aspectRatio = chartRatio
|
||||||
|
|
||||||
|
return (<ChartTimeBase dataParams={dataParams} options={opt}/>)
|
||||||
|
}
|
@ -1,82 +0,0 @@
|
|||||||
import { useEffect, useState} from 'react';
|
|
||||||
import {ChartTimeBase, ChartTimeData, ChartTimeDataParams} from './ChartTimeBase'
|
|
||||||
|
|
||||||
const GetRandomColor = () => "#" + Math.floor(Math.random()*16777215).toString(16)
|
|
||||||
|
|
||||||
|
|
||||||
function GetOrCreateDatasetByLineConfig(data: ChartTimeData, lineConfig: LineConfig) {
|
|
||||||
let dataset = data?.datasets.find(d => d.label === lineConfig.label)
|
|
||||||
if (!dataset) {
|
|
||||||
let color = lineConfig.borderColor
|
|
||||||
?? lineConfig.backgroundColor
|
|
||||||
?? lineConfig.color
|
|
||||||
?? GetRandomColor()
|
|
||||||
|
|
||||||
dataset = {
|
|
||||||
label: lineConfig.label,
|
|
||||||
data: [],
|
|
||||||
backgroundColor: lineConfig.backgroundColor ?? color,
|
|
||||||
borderColor: lineConfig.borderColor ?? color,
|
|
||||||
borderWidth: lineConfig.borderWidth ?? 1,
|
|
||||||
borderDash: lineConfig.dash ?? [],
|
|
||||||
}
|
|
||||||
data.datasets.push(dataset);
|
|
||||||
}
|
|
||||||
return dataset
|
|
||||||
}
|
|
||||||
|
|
||||||
export type LineConfig = {
|
|
||||||
type?: string
|
|
||||||
label: string
|
|
||||||
units?: string
|
|
||||||
xAccessorName: string
|
|
||||||
yAccessorName: string
|
|
||||||
color?: string
|
|
||||||
borderColor?: string
|
|
||||||
backgroundColor?: string
|
|
||||||
borderWidth?: number
|
|
||||||
dash?: number[]
|
|
||||||
labels?: any[]
|
|
||||||
}
|
|
||||||
|
|
||||||
export type ChartTimeProps = {
|
|
||||||
label?: string,
|
|
||||||
yDisplay: Boolean,
|
|
||||||
lines: LineConfig[],
|
|
||||||
data: any[],
|
|
||||||
interval: number,
|
|
||||||
}
|
|
||||||
|
|
||||||
export const ChartTimeArchive: React.FC<ChartTimeProps> = (props) => {
|
|
||||||
const [dataParams, setDataParams] = useState<ChartTimeDataParams>({ data: { datasets: [] }, yStart: new Date(), })
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if ((!props?.lines)
|
|
||||||
|| (!props?.data)
|
|
||||||
|| (props.lines.length === 0)
|
|
||||||
|| (props.data.length === 0))
|
|
||||||
return
|
|
||||||
|
|
||||||
setDataParams((preDataParams) => {
|
|
||||||
props.lines.forEach(lineCfg => {
|
|
||||||
let dataset = GetOrCreateDatasetByLineConfig(preDataParams.data, lineCfg)
|
|
||||||
let points = props.data.map(dataItem => {
|
|
||||||
return {
|
|
||||||
x: dataItem[lineCfg.xAccessorName],
|
|
||||||
y: new Date(dataItem[lineCfg.yAccessorName])
|
|
||||||
}
|
|
||||||
})
|
|
||||||
dataset.data = points;
|
|
||||||
});
|
|
||||||
|
|
||||||
preDataParams.yStart = new Date()
|
|
||||||
preDataParams.yStart.setSeconds(preDataParams.yStart.getSeconds() - props.interval)
|
|
||||||
preDataParams.yInterval = props.interval
|
|
||||||
preDataParams.displayLabels = props.yDisplay
|
|
||||||
return preDataParams
|
|
||||||
})
|
|
||||||
|
|
||||||
}, [props.data, props.lines, props.interval, props.yDisplay])
|
|
||||||
|
|
||||||
return (<ChartTimeBase dataParams={dataParams} />)
|
|
||||||
}
|
|
@ -16,8 +16,9 @@ Chart.register( TimeScale, LinearScale, LineController, LineElement, PointElemen
|
|||||||
|
|
||||||
const defaultOptions = {
|
const defaultOptions = {
|
||||||
//maintainAspectRatio: false,
|
//maintainAspectRatio: false,
|
||||||
aspectRatio:0.45,
|
aspectRatio: 0.45,
|
||||||
animation: false,
|
animation: false,
|
||||||
|
events: ['mousemove', 'mouseout', 'click', 'touchstart', 'touchmove'],
|
||||||
scales: {
|
scales: {
|
||||||
y:{
|
y:{
|
||||||
type: 'time',
|
type: 'time',
|
||||||
@ -38,12 +39,13 @@ const defaultOptions = {
|
|||||||
year: 'yyyy.MM',
|
year: 'yyyy.MM',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
position:{ x: 20 },
|
//position:'center',
|
||||||
grid:{
|
grid:{
|
||||||
drawTicks: false,
|
drawTicks: false,
|
||||||
},
|
},
|
||||||
ticks: {
|
ticks: {
|
||||||
z: 1,
|
z: 1,
|
||||||
|
display : false,
|
||||||
textStrokeColor : "#ffff",
|
textStrokeColor : "#ffff",
|
||||||
textStrokeWidth : 2,
|
textStrokeWidth : 2,
|
||||||
color:"#000",
|
color:"#000",
|
||||||
@ -98,49 +100,49 @@ const timeUnitByInterval = (intervalSec:number):String =>{
|
|||||||
return 'year'
|
return 'year'
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ChartTimeBase: React.FC<ChartTimeBaseProps> = (props) => {
|
export const ChartTimeBase: React.FC<ChartTimeBaseProps> = ({options, dataParams}) => {
|
||||||
const chartRef = useRef<HTMLCanvasElement>(null)
|
const chartRef = useRef<HTMLCanvasElement>(null)
|
||||||
|
|
||||||
const [chart, setChart] = useState<any>()
|
const [chart, setChart] = useState<any>()
|
||||||
|
|
||||||
useEffect(()=>{
|
useEffect(()=>{
|
||||||
if(chartRef.current && (!chart)){
|
if((chartRef.current)&&(!chart)){
|
||||||
let thisOptions = {}
|
let thisOptions = {}
|
||||||
Object.assign(thisOptions, defaultOptions, props.options)
|
Object.assign(thisOptions, defaultOptions, options)
|
||||||
|
|
||||||
let newChart = new Chart(chartRef.current, {
|
let newChart = new Chart(chartRef.current, {
|
||||||
type: 'line',
|
type: 'line',
|
||||||
options: thisOptions,
|
options: thisOptions,
|
||||||
data: props.dataParams.data
|
data: dataParams.data
|
||||||
})
|
})
|
||||||
setChart(newChart)
|
setChart(newChart)
|
||||||
|
|
||||||
return () => chart?.destroy()
|
return () => chart?.destroy()
|
||||||
}
|
}
|
||||||
}, [chart, props.options, props.dataParams])
|
}, [chart, options, dataParams])
|
||||||
|
|
||||||
useEffect(()=>{
|
useEffect(()=>{
|
||||||
if(!chart)
|
if(!chart)
|
||||||
return
|
return
|
||||||
|
|
||||||
chart.data = props.dataParams.data
|
chart.data = dataParams.data
|
||||||
|
chart.options.aspectRatio = options?.aspectRatio
|
||||||
|
|
||||||
if(props.dataParams.yStart){
|
if(dataParams.yStart){
|
||||||
let interval = Number(props.dataParams.yInterval ?? 600)
|
let interval = Number(dataParams.yInterval ?? 600)
|
||||||
let start = new Date(props.dataParams.yStart)
|
let start = new Date(dataParams.yStart)
|
||||||
let end = new Date(props.dataParams.yStart)
|
let end = new Date(dataParams.yStart)
|
||||||
end.setSeconds(end.getSeconds() + interval)
|
end.setSeconds(end.getSeconds() + interval)
|
||||||
|
|
||||||
if(chart.options.scales?.y){
|
if(chart.options.scales?.y){
|
||||||
chart.options.scales.y.max = end.getTime()
|
chart.options.scales.y.max = end.getTime()
|
||||||
chart.options.scales.y.min = start.getTime()
|
chart.options.scales.y.min = start.getTime()
|
||||||
chart.options.scales.y.ticks.display = props.dataParams.displayLabels ?? true
|
chart.options.scales.y.ticks.display = dataParams.displayLabels ?? true
|
||||||
chart.options.scales.y.time.unit = timeUnitByInterval(interval)
|
chart.options.scales.y.time.unit = timeUnitByInterval(interval)
|
||||||
chart.options.scales.y.time.stepSize = Math.round(interval/24)
|
chart.options.scales.y.time.stepSize = Math.round(interval/32)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
chart.update()
|
chart.update()
|
||||||
}, [chart, props])
|
}, [chart, dataParams, options])
|
||||||
|
|
||||||
return(<canvas ref={chartRef} />)
|
return(<canvas ref={chartRef} />)
|
||||||
}
|
}
|
@ -1,188 +1,139 @@
|
|||||||
import { useState, useEffect } from 'react'
|
import { useRef, useLayoutEffect, useState, useEffect } from 'react'
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
DatePicker,
|
DatePicker,
|
||||||
ConfigProvider,
|
ConfigProvider,
|
||||||
Select,
|
|
||||||
Row,
|
Row,
|
||||||
Col,
|
Col,
|
||||||
} from 'antd'
|
Tooltip} from 'antd'
|
||||||
import { useParams } from 'react-router-dom'
|
import { useParams } from 'react-router-dom'
|
||||||
import { Subscribe } from '../services/signalr'
|
|
||||||
import { DataService } from '../services/api'
|
import { DataService } from '../services/api'
|
||||||
import 'moment/locale/ru'
|
|
||||||
import locale from 'antd/lib/locale/ru_RU'
|
import locale from 'antd/lib/locale/ru_RU'
|
||||||
import { ChartTimeArchive } from '../components/charts/ChartTimeArchive'
|
import {generateUUID} from '../services/UidGenerator'
|
||||||
import { Display } from '../components/Display'
|
import { ArchiveColumn } from '../components/ArchiveColumn'
|
||||||
import { ChartTimeOnlineFooter } from '../components/ChartTimeOnlineFooter'
|
import moment from 'moment'
|
||||||
import { UserOfWells } from '../components/UserOfWells'
|
|
||||||
|
|
||||||
|
|
||||||
const { RangePicker } = DatePicker;
|
const { RangePicker } = DatePicker;
|
||||||
const { Option } = Select;
|
|
||||||
|
|
||||||
// Выбор периода времени
|
const SaveObject = (key, obj) => {
|
||||||
const RangePickerLocalized = ({locale, ...other}) => {
|
let json = JSON.stringify(obj)
|
||||||
return (
|
localStorage.setItem(key, json)
|
||||||
|
}
|
||||||
|
|
||||||
|
const LoadObject = (key) => {
|
||||||
|
let json = localStorage.getItem(key)
|
||||||
|
return json ? JSON.parse(json) : null
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function Archive() {
|
||||||
|
let { id } = useParams();
|
||||||
|
const [saubData, setSaubData] = useState([])
|
||||||
|
const [chartsCfgs, setChartsCfgs] = useState([])
|
||||||
|
const [rangeDate, setRangeDate] = useState([moment().subtract(3,'hours'), moment()])
|
||||||
|
const [geometry, setGeometry] = useState({ratioRest:1, ratio1st:1, wRest:.5, w1st:.5})
|
||||||
|
const chartsCfgsKey = 'chartsCfgs'
|
||||||
|
const chartsContainerRef = useRef();
|
||||||
|
|
||||||
|
const handleReceiveDataSaub = (data) => {
|
||||||
|
if (data)
|
||||||
|
setSaubData(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
const onAddChart = () => {
|
||||||
|
let newChartCfgs = [...chartsCfgs, {id: generateUUID(), yDisplay: false, aspectRatio:1}]
|
||||||
|
setChartsCfgs(newChartCfgs)
|
||||||
|
}
|
||||||
|
|
||||||
|
const onRemoveChart = (id) => {
|
||||||
|
let newChartCfgs = chartsCfgs.filter(cfg => cfg.id !== id )
|
||||||
|
setChartsCfgs(newChartCfgs)
|
||||||
|
}
|
||||||
|
|
||||||
|
const onSaveConfig = ()=>{
|
||||||
|
SaveObject(chartsCfgsKey, chartsCfgs)
|
||||||
|
}
|
||||||
|
|
||||||
|
const onChangeRange = (range) => {
|
||||||
|
setRangeDate(range)
|
||||||
|
}
|
||||||
|
|
||||||
|
useLayoutEffect(()=>{
|
||||||
|
if(chartsContainerRef.current && chartsCfgs?.length){
|
||||||
|
let w = chartsContainerRef.current.offsetWidth //1792
|
||||||
|
w = w > 0 ? w : 1792
|
||||||
|
let ot = chartsContainerRef.current.offsetTop
|
||||||
|
let ph = chartsContainerRef.current.offsetParent.offsetHeight
|
||||||
|
let h = ph - ot - 32 //761
|
||||||
|
h = h > 0 ? h : 761
|
||||||
|
|
||||||
|
let chartsCount = chartsCfgs.length
|
||||||
|
|
||||||
|
let labelLenght = 8
|
||||||
|
let borderWidth = 8
|
||||||
|
let wRest = Math.floor((w - labelLenght)/chartsCount) - borderWidth
|
||||||
|
let w1st = wRest + labelLenght
|
||||||
|
|
||||||
|
let ratio1st = w1st/h
|
||||||
|
let ratioRest = wRest/h
|
||||||
|
|
||||||
|
setGeometry({ratio1st, ratioRest, w1st, wRest})
|
||||||
|
}
|
||||||
|
},[chartsContainerRef, chartsCfgs])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
let cfgs = LoadObject(chartsCfgsKey)
|
||||||
|
if(cfgs)
|
||||||
|
setChartsCfgs(cfgs)
|
||||||
|
},[])
|
||||||
|
|
||||||
|
useEffect(()=>{
|
||||||
|
SaveObject(chartsCfgsKey, chartsCfgs)
|
||||||
|
},[chartsCfgs])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
let interval = (rangeDate[1] - rangeDate[0]) / 1000
|
||||||
|
let startDate = rangeDate[0].toISOString()
|
||||||
|
|
||||||
|
DataService.getData(id, startDate, interval, 2048)
|
||||||
|
.then(handleReceiveDataSaub)
|
||||||
|
.catch(error => console.error(error))
|
||||||
|
}, [id, rangeDate]);
|
||||||
|
|
||||||
|
let charts = null
|
||||||
|
if(chartsCfgs.length > 0){
|
||||||
|
chartsCfgs[0].yDisplay = true
|
||||||
|
|
||||||
|
charts = chartsCfgs.map((cfg, i) =>
|
||||||
|
<Col flex={`${i===0 ? geometry.w1st : geometry.wRest}px`}
|
||||||
|
key={cfg.id}>
|
||||||
|
<ArchiveColumn
|
||||||
|
data={saubData}
|
||||||
|
rangeDate={rangeDate}
|
||||||
|
chartRatio={i===0 ? geometry.ratio1st : geometry.ratioRest}
|
||||||
|
onRemoveChart={onRemoveChart}
|
||||||
|
onSaveConfig={onSaveConfig}
|
||||||
|
config={cfg}/>
|
||||||
|
</Col>)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (<>
|
||||||
|
<Tooltip title="Добавить график">
|
||||||
|
<Button
|
||||||
|
type="primary"
|
||||||
|
onClick={onAddChart}
|
||||||
|
disabled={chartsCfgs.length >= 6}>
|
||||||
|
+
|
||||||
|
</Button>
|
||||||
|
</Tooltip>
|
||||||
<ConfigProvider locale={locale}>
|
<ConfigProvider locale={locale}>
|
||||||
<RangePicker
|
<RangePicker
|
||||||
showTime
|
showTime
|
||||||
style={{ margin: '5px 5px', }}
|
onChange = {onChangeRange}
|
||||||
{...other}
|
value = {rangeDate}
|
||||||
/>
|
/>
|
||||||
</ConfigProvider>
|
</ConfigProvider>
|
||||||
)
|
<Row ref={chartsContainerRef}>
|
||||||
}
|
{charts}
|
||||||
|
|
||||||
// Выбор "перьев" для графиков - перенести в шапку графика
|
|
||||||
const SelectDataCharts = () => {
|
|
||||||
const linesCollection = [
|
|
||||||
{ label: "Глубина забоя", xAccessorName: "wellDepth", color: '#a0a' },
|
|
||||||
{ label: "Положение инструмента", xAccessorName: "bitDepth", color: '#a0a' },
|
|
||||||
{ label: "Высота талевого блока", xAccessorName: "blockPosition", color: '#a0a' },
|
|
||||||
{ label: "Талевый блок. Мин положение", xAccessorName: "blockPositionMin", color: '#a0a' },
|
|
||||||
{ label: "Талевый блок. Макс положение", xAccessorName: "blockPositionMax", color: '#a0a' },
|
|
||||||
{ label: "Скорость талевого блока", xAccessorName: "blockSpeed", color: '#a0a' },
|
|
||||||
{ label: "Скорости талевого блока. Задание", xAccessorName: "blockSpeedSp", color: '#a0a' },
|
|
||||||
{ label: "Талевый блок. Задание скорости для роторного бурения", xAccessorName: "blockSpeedSpRotor", color: '#a0a' },
|
|
||||||
{ label: "Талевый блок. Задание скорости для режима слайда", xAccessorName: "blockSpeedSpSlide", color: '#a0a' },
|
|
||||||
{ label: "Талевый блок. Задание скорости для проработки", xAccessorName: "blockSpeedSpDevelop", color: '#a0a' },
|
|
||||||
{ label: "Давление", xAccessorName: "pressure", color: '#a0a' },
|
|
||||||
{ label: "Давление. Холостой ход", xAccessorName: "pressureIdle", color: '#a0a' },
|
|
||||||
{ label: "Давление. Задание", xAccessorName: "pressureSp", color: '#a0a' },
|
|
||||||
{ label: "Давление. Задание для роторного бурения", xAccessorName: "pressureSpRotor", color: '#a0a' },
|
|
||||||
{ label: "Давление. Задание для режима слайда", xAccessorName: "pressureSpSlide", color: '#a0a' },
|
|
||||||
{ label: "Давление. Задание для проработки", xAccessorName: "pressureSpDevelop", color: '#a0a' },
|
|
||||||
{ label: "Давление дифф. Аварийное макс.", xAccessorName: "pressureDeltaLimitMax", color: '#a0a' },
|
|
||||||
{ label: "Осевая нагрузка", xAccessorName: "axialLoad", color: '#a0a' },
|
|
||||||
{ label: "Осевая нагрузка. Задание", xAccessorName: "axialLoadSp", color: '#a0a' },
|
|
||||||
{ label: "Осевая нагрузка. Аварийная макс.", xAccessorName: "axialLoadLimitMax", color: '#a0a' },
|
|
||||||
{ label: "Вес на крюке", xAccessorName: "hookWeight", color: '#a0a' },
|
|
||||||
{ label: "Вес на крюке. Холостой ход", xAccessorName: "hookWeightIdle", color: '#a0a' },
|
|
||||||
{ label: "Вес на крюке. Посадка", xAccessorName: "hookWeightLimitMin", color: '#a0a' },
|
|
||||||
{ label: "Вес на крюке. Затяжка", xAccessorName: "hookWeightLimitMax", color: '#a0a' },
|
|
||||||
{ label: "Момент на роторе", xAccessorName: "rotorTorque", color: '#a0a' },
|
|
||||||
{ label: "Момент на роторе. Холостой ход", xAccessorName: "rotorTorqueIdle", color: '#a0a' },
|
|
||||||
{ label: "Момент на роторе. Задание", xAccessorName: "rotorTorqueSp", color: '#a0a' },
|
|
||||||
{ label: "Момент на роторе. Аварийный макс.", xAccessorName: "rotorTorqueLimitMax", color: '#a0a' },
|
|
||||||
{ label: "Обороты ротора", xAccessorName: "rotorSpeed", color: '#a0a' },
|
|
||||||
{ label: "Расход", xAccessorName: "flow", color: '#a0a' },
|
|
||||||
{ label: "Расход. Холостой ход", xAccessorName: "flowIdle", color: '#a0a' },
|
|
||||||
{ label: "Расход. Аварийный макс.", xAccessorName: "flowDeltaLimitMax", color: '#a0a' },
|
|
||||||
]
|
|
||||||
|
|
||||||
const children = linesCollection.map((line) => (<Option key={line.xAccessorName}>{line.label}</Option>))
|
|
||||||
|
|
||||||
function handleChange(value) {
|
|
||||||
console.log(`selected ${value}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Select
|
|
||||||
mode="multiple"
|
|
||||||
allowClear
|
|
||||||
style={{ width: '50%' }}
|
|
||||||
placeholder="Выберите значения"
|
|
||||||
defaultValue={["wellDepth"]}
|
|
||||||
onChange={handleChange}
|
|
||||||
>
|
|
||||||
{children}
|
|
||||||
</Select>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const Column = ({ lineGroup, data, interval }) => {
|
|
||||||
let lines = [lineGroup.linePv]
|
|
||||||
|
|
||||||
if (lineGroup.lineSp)
|
|
||||||
lines.push(lineGroup.lineSp)
|
|
||||||
|
|
||||||
let dataLast = null
|
|
||||||
let pv = null
|
|
||||||
if (data?.length > 0) {
|
|
||||||
dataLast = data[data.length - 1];
|
|
||||||
if (lineGroup.linePv)
|
|
||||||
pv = dataLast[lineGroup.linePv?.xAccessorName]
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Display
|
|
||||||
label={lineGroup.label}
|
|
||||||
value={pv}
|
|
||||||
suffix={lineGroup.linePv?.units} />
|
|
||||||
<ChartTimeArchive
|
|
||||||
data={data}
|
|
||||||
yDisplay={lineGroup.yDisplay}
|
|
||||||
lines={lines}
|
|
||||||
interval={interval} />
|
|
||||||
<ChartTimeOnlineFooter
|
|
||||||
data={dataLast}
|
|
||||||
{...lineGroup} />
|
|
||||||
</>)
|
|
||||||
}
|
|
||||||
|
|
||||||
const paramsGroups = []
|
|
||||||
|
|
||||||
export default function Archive(props) {
|
|
||||||
let { id } = useParams();
|
|
||||||
const [saubData, setSaubData] = useState([])
|
|
||||||
const [chartInterval, setChartInterval] = useState(600)
|
|
||||||
|
|
||||||
const handleReceiveDataSaub = (data) => {
|
|
||||||
if (data) {
|
|
||||||
setSaubData(data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
DataService.getData(id)
|
|
||||||
.then(handleReceiveDataSaub)
|
|
||||||
.catch(error => console.error(error))
|
|
||||||
|
|
||||||
let unSubscribeMessages = Subscribe('ReceiveDataSaub', `well_${id}`, handleReceiveDataSaub)
|
|
||||||
return () => {
|
|
||||||
unSubscribeMessages()
|
|
||||||
}
|
|
||||||
}, [id]);
|
|
||||||
|
|
||||||
const colSpan = 24 / (paramsGroups.length)
|
|
||||||
|
|
||||||
return (<>
|
|
||||||
<div style={{ display: 'flex' }}>
|
|
||||||
<h2>Архив</h2>
|
|
||||||
<span style={{ flexGrow: 10 }}></span>
|
|
||||||
<UserOfWells data={saubData} />
|
|
||||||
</div>
|
|
||||||
<hr />
|
|
||||||
<Button type="primary" style={{
|
|
||||||
borderRadius: '5px',
|
|
||||||
font: 'bold',
|
|
||||||
textAlign: 'center',
|
|
||||||
margin: '5px 5px',
|
|
||||||
}}>Добавить график</Button>
|
|
||||||
<RangePickerLocalized />
|
|
||||||
<SelectDataCharts />
|
|
||||||
<Row style={{ marginBottom: '1rem' }}>
|
|
||||||
<Col>
|
|
||||||
Интервал:
|
|
||||||
<Select defaultValue="600" onChange={setChartInterval}>
|
|
||||||
<Option value='600'>10 минут</Option>
|
|
||||||
<Option value='1800'>30 минут</Option>
|
|
||||||
<Option value='3600'>1 час</Option>
|
|
||||||
<Option value='21600'>6 час</Option>
|
|
||||||
<Option value='86400'>1 день</Option>
|
|
||||||
</Select>
|
|
||||||
</Col>
|
|
||||||
</Row>
|
|
||||||
<Row>
|
|
||||||
<Col span={24 - 2}>
|
|
||||||
<Row>
|
|
||||||
{paramsGroups.map(group =>
|
|
||||||
<Col span={colSpan} className='border_small' key={group.label}>
|
|
||||||
<Column data={saubData} lineGroup={group} interval={chartInterval} />
|
|
||||||
</Col>)}
|
|
||||||
</Row>
|
|
||||||
</Col>
|
|
||||||
</Row>
|
</Row>
|
||||||
</>)
|
</>)
|
||||||
}
|
}
|
13
src/services/UidGenerator.ts
Normal file
13
src/services/UidGenerator.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
export function generateUUID():string {
|
||||||
|
let seed = (25869874412483
|
||||||
|
* new Date().getTime()
|
||||||
|
* (performance && performance.now && (performance.now()))) % 173395562924509
|
||||||
|
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
|
||||||
|
let random = Math.random()
|
||||||
|
seed = seed > 272
|
||||||
|
? seed = seed / 17
|
||||||
|
: seed = (seed * random * random * 557833831325167) % 173395562924509
|
||||||
|
random = Math.floor((random * seed) % 16)
|
||||||
|
return (c === 'x' ? random : (random & 0xB)).toString(16)
|
||||||
|
})
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user