forked from ddrilling/asb_cloud_front
Merge branch 'feature/monitoring-limiter' into dev
This commit is contained in:
commit
a35c09e245
@ -1,6 +1,4 @@
|
|||||||
export * from './D3Chart'
|
export * from './D3Chart'
|
||||||
export type { D3ChartProps } from './D3Chart'
|
export type { D3ChartProps } from './D3Chart'
|
||||||
|
|
||||||
export * from './D3MonitoringCharts'
|
|
||||||
|
|
||||||
export * from './types'
|
export * from './types'
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
import { CSSProperties, ReactNode, SVGProps, useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
import { CSSProperties, ReactNode, SVGProps, useEffect, useMemo, useRef, useState } from 'react'
|
||||||
import * as d3 from 'd3'
|
import * as d3 from 'd3'
|
||||||
|
|
||||||
|
import { wrapPlugin } from '@components/d3/plugins/base'
|
||||||
import { useD3MouseZone } from '@components/d3/D3MouseZone'
|
import { useD3MouseZone } from '@components/d3/D3MouseZone'
|
||||||
import { ChartGroup, ChartSizes } from '@components/d3/D3MonitoringCharts'
|
import { D3TooltipPosition } from '@components/d3/plugins/D3Tooltip'
|
||||||
import { getChartIcon, isDev, usePartialProps } from '@utils'
|
import { getChartIcon, isDev, usePartialProps } from '@utils'
|
||||||
|
|
||||||
import { wrapPlugin } from './base'
|
import { ChartGroup, ChartSizes } from './D3MonitoringCharts'
|
||||||
import { D3TooltipPosition } from './D3Tooltip'
|
|
||||||
|
|
||||||
import '@styles/d3.less'
|
import '@styles/d3.less'
|
||||||
|
|
@ -1,9 +1,10 @@
|
|||||||
import { Button, Form, FormItemProps, Input, InputNumber, Select, Tooltip } from 'antd'
|
import { Button, Form, FormItemProps, Input, InputNumber, Select, Tooltip } from 'antd'
|
||||||
import { memo, useCallback, useEffect, useMemo } from 'react'
|
import { memo, useCallback, useEffect, useMemo } from 'react'
|
||||||
|
|
||||||
import { ColorPicker, Color } from '../ColorPicker'
|
import { MinMax } from '@components/d3/types'
|
||||||
|
import { ColorPicker, Color } from '@components/ColorPicker'
|
||||||
|
|
||||||
import { ExtendedChartDataset } from './D3MonitoringCharts'
|
import { ExtendedChartDataset } from './D3MonitoringCharts'
|
||||||
import { MinMax } from './types'
|
|
||||||
|
|
||||||
const { Item: RawItem } = Form
|
const { Item: RawItem } = Form
|
||||||
|
|
@ -16,18 +16,19 @@ import {
|
|||||||
ChartRegistry,
|
ChartRegistry,
|
||||||
ChartTick,
|
ChartTick,
|
||||||
MinMax
|
MinMax
|
||||||
} from './types'
|
} from '@components/d3/types'
|
||||||
import {
|
import {
|
||||||
BasePluginSettings,
|
BasePluginSettings,
|
||||||
D3ContextMenu,
|
D3ContextMenu,
|
||||||
D3ContextMenuSettings,
|
D3ContextMenuSettings,
|
||||||
D3HorizontalCursor,
|
} from '@components/d3/plugins'
|
||||||
D3HorizontalCursorSettings
|
import D3MouseZone from '@components/d3/D3MouseZone'
|
||||||
} from './plugins'
|
import { getByAccessor, getChartClass, getGroupClass, getTicks } from '@components/d3/functions'
|
||||||
import D3MouseZone from './D3MouseZone'
|
import { renderArea, renderLine, renderNeedle, renderPoint, renderRectArea } from '@components/d3/renders'
|
||||||
import D3MonitoringGroupsEditor from './D3MonitoringGroupsEditor'
|
|
||||||
import { getByAccessor, getChartClass, getGroupClass, getTicks } from './functions'
|
import D3MonitoringEditor from './D3MonitoringEditor'
|
||||||
import { renderArea, renderLine, renderNeedle, renderPoint, renderRectArea } from './renders'
|
import { D3HorizontalCursor, D3HorizontalCursorSettings } from './D3HorizontalCursor'
|
||||||
|
import D3MonitoringLimitChart, { TelemetryRegulators } from './D3MonitoringLimitChart'
|
||||||
|
|
||||||
const roundTo = (v: number, to: number = 50) => {
|
const roundTo = (v: number, to: number = 50) => {
|
||||||
if (v === 0) return v
|
if (v === 0) return v
|
||||||
@ -73,7 +74,15 @@ const defaultOffsets: ChartOffset = {
|
|||||||
top: 10,
|
top: 10,
|
||||||
bottom: 10,
|
bottom: 10,
|
||||||
left: 100,
|
left: 100,
|
||||||
right: 10,
|
right: 20,
|
||||||
|
}
|
||||||
|
|
||||||
|
const defaultRegulators: TelemetryRegulators = {
|
||||||
|
1: { color: '#59B359', label: 'Скорость блока' },
|
||||||
|
2: { color: '#FF0000', label: 'Давление' },
|
||||||
|
3: { color: '#0000CC', label: 'Осевая нагрузка' },
|
||||||
|
4: { color: '#990099', label: 'Момент на роторе' },
|
||||||
|
5: { color: '#007070', label: 'Расход' },
|
||||||
}
|
}
|
||||||
|
|
||||||
const getDefaultYAxisConfig = <DataType,>(): ChartAxis<DataType> => ({
|
const getDefaultYAxisConfig = <DataType,>(): ChartAxis<DataType> => ({
|
||||||
@ -191,6 +200,7 @@ const _D3MonitoringCharts = <DataType extends Record<string, unknown>>({
|
|||||||
const [chartAreaRef, setChartAreaRef] = useState<SVGGElement | null>(null)
|
const [chartAreaRef, setChartAreaRef] = useState<SVGGElement | null>(null)
|
||||||
const [axesAreaRef, setAxesAreaRef] = useState<SVGGElement | null>(null)
|
const [axesAreaRef, setAxesAreaRef] = useState<SVGGElement | null>(null)
|
||||||
const [settingsVisible, setSettingsVisible] = useState<boolean>(false)
|
const [settingsVisible, setSettingsVisible] = useState<boolean>(false)
|
||||||
|
const [regulators, setRegulators] = useState<TelemetryRegulators>(defaultRegulators)
|
||||||
|
|
||||||
const offset = usePartialProps(_offset, defaultOffsets)
|
const offset = usePartialProps(_offset, defaultOffsets)
|
||||||
const yTicks = usePartialProps<Required<ChartTick<DataType>>>(_yTicks, getDefaultYTicks)
|
const yTicks = usePartialProps<Required<ChartTick<DataType>>>(_yTicks, getDefaultYTicks)
|
||||||
@ -271,23 +281,28 @@ const _D3MonitoringCharts = <DataType extends Record<string, unknown>>({
|
|||||||
}
|
}
|
||||||
), [chartArea])
|
), [chartArea])
|
||||||
|
|
||||||
const onGroupsChange = useCallback((sets: ExtendedChartDataset<DataType>[][]) => {
|
const onGroupsChange = useCallback((settings: ExtendedChartDataset<DataType>[][], regulators: TelemetryRegulators) => {
|
||||||
if (chartName) {
|
if (chartName) {
|
||||||
invokeWebApiWrapperAsync(
|
invokeWebApiWrapperAsync(
|
||||||
async () => {
|
async () => {
|
||||||
await UserSettingsService.update(chartName, sets)
|
await UserSettingsService.update(chartName, {
|
||||||
|
settings,
|
||||||
|
regulators,
|
||||||
|
})
|
||||||
},
|
},
|
||||||
undefined,
|
undefined,
|
||||||
'Не удалось сохранить параметры графиков'
|
'Не удалось сохранить параметры графиков'
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
setDatasets(sets)
|
setDatasets(settings)
|
||||||
|
setRegulators(regulators)
|
||||||
setSettingsVisible(false)
|
setSettingsVisible(false)
|
||||||
}, [chartName])
|
}, [chartName])
|
||||||
|
|
||||||
const onGroupsReset = useCallback(() => {
|
const onGroupsReset = useCallback(() => {
|
||||||
setSettingsVisible(false)
|
setSettingsVisible(false)
|
||||||
setDatasets(datasetGroups)
|
setDatasets(datasetGroups)
|
||||||
|
setRegulators(defaultRegulators)
|
||||||
if (chartName) {
|
if (chartName) {
|
||||||
invokeWebApiWrapperAsync(
|
invokeWebApiWrapperAsync(
|
||||||
async () => await UserSettingsService.delete(chartName),
|
async () => await UserSettingsService.delete(chartName),
|
||||||
@ -305,13 +320,20 @@ const _D3MonitoringCharts = <DataType extends Record<string, unknown>>({
|
|||||||
let sets = chartName ? await UserSettingsService.get(chartName) : null
|
let sets = chartName ? await UserSettingsService.get(chartName) : null
|
||||||
if (typeof sets === 'string')
|
if (typeof sets === 'string')
|
||||||
sets = JSON.parse(sets)
|
sets = JSON.parse(sets)
|
||||||
if (Array.isArray(sets)) {
|
if (sets && Array.isArray(sets.settings)) {
|
||||||
setDatasets(sets)
|
if (sets.regulators)
|
||||||
|
setRegulators(sets.regulators)
|
||||||
|
if (Array.isArray(sets.settings)) {
|
||||||
|
setDatasets(sets.settings)
|
||||||
|
}
|
||||||
} else if (Array.isArray(datasetGroups)) {
|
} else if (Array.isArray(datasetGroups)) {
|
||||||
setDatasets(datasetGroups)
|
setDatasets(datasetGroups)
|
||||||
if (chartName) {
|
if (chartName) {
|
||||||
invokeWebApiWrapperAsync(
|
invokeWebApiWrapperAsync(
|
||||||
async () => await UserSettingsService.insert(chartName, datasetGroups),
|
async () => await UserSettingsService.insert(chartName, {
|
||||||
|
settings: datasetGroups,
|
||||||
|
regulators: defaultRegulators,
|
||||||
|
}),
|
||||||
undefined,
|
undefined,
|
||||||
'Не удалось сохранить настройки графиков'
|
'Не удалось сохранить настройки графиков'
|
||||||
)
|
)
|
||||||
@ -572,6 +594,16 @@ const _D3MonitoringCharts = <DataType extends Record<string, unknown>>({
|
|||||||
return <line key={`${i}`} x1={x} x2={x} y1={sizes.chartsTop} y2={offset.top + sizes.inlineHeight} />
|
return <line key={`${i}`} x1={x} x2={x} y1={sizes.chartsTop} y2={offset.top + sizes.inlineHeight} />
|
||||||
})}
|
})}
|
||||||
</g>
|
</g>
|
||||||
|
<D3MonitoringLimitChart
|
||||||
|
regulators={regulators}
|
||||||
|
data={data}
|
||||||
|
yAxis={yAxis}
|
||||||
|
width={20}
|
||||||
|
height={sizes.chartsHeight}
|
||||||
|
left={sizes.inlineWidth + sizes.left}
|
||||||
|
top={sizes.chartsTop}
|
||||||
|
zoneWidth={sizes.inlineWidth}
|
||||||
|
/>
|
||||||
<D3MouseZone width={width} height={height} offset={{ ...offset, top: sizes.chartsTop }}>
|
<D3MouseZone width={width} height={height} offset={{ ...offset, top: sizes.chartsTop }}>
|
||||||
<D3HorizontalCursor
|
<D3HorizontalCursor
|
||||||
{...plugins?.cursor}
|
{...plugins?.cursor}
|
||||||
@ -588,8 +620,9 @@ const _D3MonitoringCharts = <DataType extends Record<string, unknown>>({
|
|||||||
<Empty image={Empty.PRESENTED_IMAGE_SIMPLE} />
|
<Empty image={Empty.PRESENTED_IMAGE_SIMPLE} />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<D3MonitoringGroupsEditor
|
<D3MonitoringEditor
|
||||||
groups={datasets}
|
groups={datasets}
|
||||||
|
regulators={regulators}
|
||||||
visible={settingsVisible}
|
visible={settingsVisible}
|
||||||
onChange={onGroupsChange}
|
onChange={onGroupsChange}
|
||||||
onCancel={() => setSettingsVisible(false)}
|
onCancel={() => setSettingsVisible(false)}
|
@ -3,16 +3,19 @@ import { Button, Divider, Empty, Modal, Popconfirm, Tooltip, Tree } from 'antd'
|
|||||||
import { UndoOutlined } from '@ant-design/icons'
|
import { UndoOutlined } from '@ant-design/icons'
|
||||||
import { EventDataNode } from 'antd/lib/tree'
|
import { EventDataNode } from 'antd/lib/tree'
|
||||||
|
|
||||||
|
import { notify } from '@components/factory'
|
||||||
import { getChartIcon } from '@utils'
|
import { getChartIcon } from '@utils'
|
||||||
|
|
||||||
import { ExtendedChartDataset } from './D3MonitoringCharts'
|
import { ExtendedChartDataset } from './D3MonitoringCharts'
|
||||||
|
import { TelemetryRegulators } from './D3MonitoringLimitChart'
|
||||||
import D3MonitoringChartEditor from './D3MonitoringChartEditor'
|
import D3MonitoringChartEditor from './D3MonitoringChartEditor'
|
||||||
import { notify } from '../factory'
|
import D3MonitoringLimitEditor from './D3MonitoringLimitEditor'
|
||||||
|
|
||||||
export type D3MonitoringGroupsEditorProps<DataType> = {
|
export type D3MonitoringGroupsEditorProps<DataType> = {
|
||||||
visible?: boolean
|
visible?: boolean
|
||||||
groups: ExtendedChartDataset<DataType>[][]
|
groups: ExtendedChartDataset<DataType>[][]
|
||||||
onChange: (value: ExtendedChartDataset<DataType>[][]) => void
|
regulators: TelemetryRegulators
|
||||||
|
onChange: (value: ExtendedChartDataset<DataType>[][], regs: TelemetryRegulators) => void
|
||||||
onCancel: () => void
|
onCancel: () => void
|
||||||
onReset: () => void
|
onReset: () => void
|
||||||
}
|
}
|
||||||
@ -36,9 +39,12 @@ const getNodePos = (node: EventDataNode): { group: number, chart?: number } => {
|
|||||||
return { group: out[1], chart: out[2] }
|
return { group: out[1], chart: out[2] }
|
||||||
}
|
}
|
||||||
|
|
||||||
const _D3MonitoringGroupsEditor = <DataType,>({
|
type EditingMode = null | 'limit' | 'chart'
|
||||||
|
|
||||||
|
const _D3MonitoringEditor = <DataType,>({
|
||||||
visible,
|
visible,
|
||||||
groups: oldGroups,
|
groups: oldGroups,
|
||||||
|
regulators: oldRegulators,
|
||||||
onChange,
|
onChange,
|
||||||
onCancel,
|
onCancel,
|
||||||
onReset,
|
onReset,
|
||||||
@ -46,10 +52,13 @@ const _D3MonitoringGroupsEditor = <DataType,>({
|
|||||||
const [groups, setGroups] = useState<ExtendedChartDataset<DataType>[][]>([])
|
const [groups, setGroups] = useState<ExtendedChartDataset<DataType>[][]>([])
|
||||||
const [expand, setExpand] = useState<Key[]>([])
|
const [expand, setExpand] = useState<Key[]>([])
|
||||||
const [selected, setSelected] = useState<Key[]>([])
|
const [selected, setSelected] = useState<Key[]>([])
|
||||||
|
const [mode, setMode] = useState<EditingMode>(null)
|
||||||
|
const [regulators, setRegulators] = useState<TelemetryRegulators>({})
|
||||||
|
|
||||||
useEffect(() => setGroups(oldGroups), [oldGroups])
|
useEffect(() => setGroups(oldGroups), [oldGroups])
|
||||||
|
useEffect(() => setRegulators(oldRegulators), [oldRegulators])
|
||||||
|
|
||||||
const onModalOk = useCallback(() => onChange(groups), [groups])
|
const onModalOk = useCallback(() => onChange(groups, regulators), [groups, regulators])
|
||||||
|
|
||||||
const onDrop = useCallback((info: {
|
const onDrop = useCallback((info: {
|
||||||
node: EventDataNode
|
node: EventDataNode
|
||||||
@ -148,23 +157,29 @@ const _D3MonitoringGroupsEditor = <DataType,>({
|
|||||||
selectedKeys={selected}
|
selectedKeys={selected}
|
||||||
treeData={treeItems}
|
treeData={treeItems}
|
||||||
onDrop={onDrop}
|
onDrop={onDrop}
|
||||||
onSelect={setSelected}
|
onSelect={(value) => {
|
||||||
|
setSelected(value)
|
||||||
|
setMode('chart')
|
||||||
|
}}
|
||||||
height={250}
|
height={250}
|
||||||
/>
|
/>
|
||||||
|
<Button onClick={() => setMode('limit')}>Ограничение подачи</Button>
|
||||||
</div>
|
</div>
|
||||||
<Divider type={'vertical'} style={{ height: '100%', padding: '0 5px' }} />
|
<Divider type={'vertical'} style={{ height: '100%', padding: '0 5px' }} />
|
||||||
<div style={divStyle}>
|
<div style={divStyle}>
|
||||||
{selectedGroup && selectedChart ? (
|
{mode === 'chart' && selectedGroup && selectedChart ? (
|
||||||
<D3MonitoringChartEditor<DataType> group={selectedGroup} chart={selectedChart} onChange={onChartChange} />
|
<D3MonitoringChartEditor<DataType> group={selectedGroup} chart={selectedChart} onChange={onChartChange} />
|
||||||
|
) : (mode === 'limit' ? (
|
||||||
|
<D3MonitoringLimitEditor<DataType> value={regulators} onChange={setRegulators} />
|
||||||
) : (
|
) : (
|
||||||
<Empty description={'Выберите график для редактирования'} />
|
<Empty description={'Выберите график для редактирования'} />
|
||||||
)}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Modal>
|
</Modal>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const D3MonitoringGroupsEditor = memo(_D3MonitoringGroupsEditor) as typeof _D3MonitoringGroupsEditor
|
export const D3MonitoringEditor = memo(_D3MonitoringEditor) as typeof _D3MonitoringEditor
|
||||||
|
|
||||||
export default D3MonitoringGroupsEditor
|
export default D3MonitoringEditor
|
181
src/components/d3/monitoring/D3MonitoringLimitChart.tsx
Normal file
181
src/components/d3/monitoring/D3MonitoringLimitChart.tsx
Normal file
@ -0,0 +1,181 @@
|
|||||||
|
import { CSSProperties, memo, useEffect, useMemo, useState } from 'react'
|
||||||
|
import * as d3 from 'd3'
|
||||||
|
|
||||||
|
import { Grid, GridItem } from '@components/Grid'
|
||||||
|
import { formatDate, makeDisplayValue } from '@utils'
|
||||||
|
import { TelemetryDataSaubDto } from '@api'
|
||||||
|
|
||||||
|
type LimitChartData = {
|
||||||
|
id: number
|
||||||
|
dateStart: Date
|
||||||
|
dateEnd: Date
|
||||||
|
depthStart: number | null
|
||||||
|
depthEnd: number | null
|
||||||
|
}
|
||||||
|
|
||||||
|
type LimitChartDataRaw = {
|
||||||
|
id?: number
|
||||||
|
dateStart?: string
|
||||||
|
dateEnd?: string
|
||||||
|
depthStart?: number | null
|
||||||
|
depthEnd?: number | null
|
||||||
|
}
|
||||||
|
|
||||||
|
export type TelemetryRegulators = Record<number, {
|
||||||
|
color: string
|
||||||
|
label: string
|
||||||
|
}>
|
||||||
|
|
||||||
|
const getLast = (out: LimitChartDataRaw[]) => out.at(-1) as LimitChartDataRaw
|
||||||
|
function isDataCorrect(value: LimitChartDataRaw): value is Required<LimitChartDataRaw> {
|
||||||
|
return typeof value.id !== 'undefined'
|
||||||
|
}
|
||||||
|
|
||||||
|
const calcualteData = <DataType extends TelemetryDataSaubDto>(data: DataType[]) => {
|
||||||
|
const out = data.filter((row) => row.dateTime).reduce((out, row) => {
|
||||||
|
const last = getLast(out)
|
||||||
|
if (last.id === row.idFeedRegulator) {
|
||||||
|
if (!row.idFeedRegulator) return out
|
||||||
|
last.dateEnd = row.dateTime
|
||||||
|
last.depthEnd = row.wellDepth
|
||||||
|
} else {
|
||||||
|
let n: LimitChartDataRaw = {}
|
||||||
|
if (row.idFeedRegulator) {
|
||||||
|
n = {
|
||||||
|
id: row.idFeedRegulator,
|
||||||
|
dateStart: row.dateTime,
|
||||||
|
dateEnd: row.dateTime,
|
||||||
|
depthStart: row.wellDepth,
|
||||||
|
depthEnd: row.wellDepth,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out.push(n)
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}, [{}] as LimitChartDataRaw[])
|
||||||
|
|
||||||
|
return out.filter(isDataCorrect).map<LimitChartData>((row) => ({
|
||||||
|
id: row.id,
|
||||||
|
dateStart: new Date(row.dateStart),
|
||||||
|
dateEnd: new Date(row.dateEnd),
|
||||||
|
depthStart: row.depthStart,
|
||||||
|
depthEnd: row.depthEnd,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
export type D3MonitoringLimitChartProps<DataType> = {
|
||||||
|
yAxis?: d3.ScaleTime<number, number, never>
|
||||||
|
regulators: TelemetryRegulators
|
||||||
|
data: DataType[]
|
||||||
|
width: number
|
||||||
|
height: number
|
||||||
|
left: number
|
||||||
|
top: number
|
||||||
|
zoneWidth?: number
|
||||||
|
}
|
||||||
|
|
||||||
|
const tooltipWidth = 270
|
||||||
|
const tooltipHeight = 120
|
||||||
|
|
||||||
|
const displayValue = makeDisplayValue()
|
||||||
|
|
||||||
|
const _D3MonitoringLimitChart = <DataType extends TelemetryDataSaubDto>({
|
||||||
|
yAxis,
|
||||||
|
data: chartData,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
left,
|
||||||
|
top,
|
||||||
|
regulators,
|
||||||
|
zoneWidth = 0,
|
||||||
|
}: D3MonitoringLimitChartProps<DataType>) => {
|
||||||
|
const [ref, setRef] = useState<SVGGElement | null>(null)
|
||||||
|
const [selected, setSelected] = useState<LimitChartData & { x: number, y: number, visible: boolean }>()
|
||||||
|
|
||||||
|
const data = useMemo(() => calcualteData(chartData), [chartData])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!ref || !yAxis) return
|
||||||
|
const elms = d3.select(ref).select('.bars').selectAll<SVGRectElement, unknown>('rect').data(data)
|
||||||
|
|
||||||
|
elms.exit().remove()
|
||||||
|
const newElms = elms.enter().append('rect')
|
||||||
|
|
||||||
|
elms.merge(newElms)
|
||||||
|
.attr('width', width)
|
||||||
|
.attr('height', (d) => Math.max(yAxis(d.dateEnd) - yAxis(d.dateStart), 1))
|
||||||
|
.attr('y', (d) => yAxis(d.dateStart))
|
||||||
|
.attr('fill', (d) => regulators[d.id].color)
|
||||||
|
.on('mouseover', (_, d) => {
|
||||||
|
const y = yAxis(d.dateStart) - tooltipHeight
|
||||||
|
setSelected({ ...d, y, x: -tooltipWidth - 10, visible: true })
|
||||||
|
})
|
||||||
|
.on('mouseout', () => setSelected((pre) => pre ? ({ ...pre, visible: false }) : undefined))
|
||||||
|
}, [yAxis, data, ref, width])
|
||||||
|
|
||||||
|
const zoneY1 = useMemo(() => yAxis && selected ? yAxis(selected.dateStart) : 0, [yAxis, selected])
|
||||||
|
const zoneY2 = useMemo(() => yAxis && selected ? yAxis(selected.dateEnd) : 0, [yAxis, selected])
|
||||||
|
|
||||||
|
const opacityStyle: CSSProperties = useMemo(() => ({
|
||||||
|
transition: 'opacity .1s ease-in-out',
|
||||||
|
opacity: selected?.visible ? 1 : 0,
|
||||||
|
}), [selected])
|
||||||
|
|
||||||
|
const tooltipStyle: CSSProperties = useMemo(() => ({
|
||||||
|
...opacityStyle,
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
alignItems: 'center',
|
||||||
|
}), [opacityStyle])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<g transform={`translate(${left}, ${top})`} stroke={'#333'} strokeWidth={1} fill={'none'}>
|
||||||
|
<g ref={setRef} >
|
||||||
|
<g className={'bars'} strokeWidth={0} />
|
||||||
|
{selected && (
|
||||||
|
<g
|
||||||
|
style={opacityStyle}
|
||||||
|
pointerEvents={'none'}
|
||||||
|
strokeDasharray={'6, 3, 3, 3'}
|
||||||
|
stroke={regulators[selected.id].color}
|
||||||
|
>
|
||||||
|
<line x1={-zoneWidth} x2={0} y1={zoneY1} y2={zoneY1} />
|
||||||
|
<line x1={-zoneWidth} x2={0} y1={zoneY2} y2={zoneY2} />
|
||||||
|
<rect
|
||||||
|
opacity={0.1}
|
||||||
|
stroke={'none'}
|
||||||
|
x={-zoneWidth}
|
||||||
|
y={zoneY1}
|
||||||
|
width={zoneWidth}
|
||||||
|
height={zoneY2 - zoneY1}
|
||||||
|
fill={regulators[selected.id].color}
|
||||||
|
/>
|
||||||
|
</g>
|
||||||
|
)}
|
||||||
|
</g>
|
||||||
|
<rect x={0} y={0} width={width} height={height} />
|
||||||
|
{selected && (
|
||||||
|
<foreignObject width={tooltipWidth} height={tooltipHeight} x={selected.x} y={selected.y} pointerEvents={'none'}>
|
||||||
|
<div className={'tooltip bottom'} style={tooltipStyle}>
|
||||||
|
<span>Ограничивающий параметр</span>
|
||||||
|
<span>{regulators[selected.id].label}</span>
|
||||||
|
<Grid style={{ margin: 0, padding: 0 }}>
|
||||||
|
<GridItem row={1} col={1}>Начало:</GridItem>
|
||||||
|
<GridItem row={1} col={2}>{formatDate(selected.dateStart)}</GridItem>
|
||||||
|
<GridItem row={1} col={3}>{displayValue(selected.depthStart)}</GridItem>
|
||||||
|
<GridItem row={1} col={4}>м.</GridItem>
|
||||||
|
<GridItem row={2} col={1}>Конец:</GridItem>
|
||||||
|
<GridItem row={2} col={2}>{formatDate(selected.dateEnd)}</GridItem>
|
||||||
|
<GridItem row={2} col={3}>{displayValue(selected.depthEnd)}</GridItem>
|
||||||
|
<GridItem row={2} col={4}>м.</GridItem>
|
||||||
|
</Grid>
|
||||||
|
</div>
|
||||||
|
</foreignObject>
|
||||||
|
)}
|
||||||
|
</g>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const D3MonitoringLimitChart = memo(_D3MonitoringLimitChart) as typeof _D3MonitoringLimitChart
|
||||||
|
|
||||||
|
export default D3MonitoringLimitChart
|
18
src/components/d3/monitoring/D3MonitoringLimitEditor.tsx
Normal file
18
src/components/d3/monitoring/D3MonitoringLimitEditor.tsx
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import { memo } from 'react'
|
||||||
|
import { TelemetryRegulators } from './D3MonitoringLimitChart'
|
||||||
|
|
||||||
|
export type D3MonitoringLimitEditorProps = {
|
||||||
|
value: TelemetryRegulators
|
||||||
|
onChange: (value: TelemetryRegulators) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
const _D3MonitoringLimitEditor = <DataType,>({ value, onChange }: D3MonitoringLimitEditorProps) => {
|
||||||
|
|
||||||
|
return (
|
||||||
|
<></>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const D3MonitoringLimitEditor = memo(_D3MonitoringLimitEditor) as typeof _D3MonitoringLimitEditor
|
||||||
|
|
||||||
|
export default D3MonitoringLimitEditor
|
1
src/components/d3/monitoring/index.ts
Normal file
1
src/components/d3/monitoring/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * from './D3MonitoringCharts'
|
@ -1,6 +1,5 @@
|
|||||||
export * from './base'
|
export * from './base'
|
||||||
export * from './D3ContextMenu'
|
export * from './D3ContextMenu'
|
||||||
export * from './D3Cursor'
|
export * from './D3Cursor'
|
||||||
export * from './D3HorizontalCursor'
|
|
||||||
export * from './D3Legend'
|
export * from './D3Legend'
|
||||||
export * from './D3Tooltip'
|
export * from './D3Tooltip'
|
||||||
|
@ -5,7 +5,7 @@ import { Select } from 'antd'
|
|||||||
|
|
||||||
import { useIdWell } from '@asb/context'
|
import { useIdWell } from '@asb/context'
|
||||||
import { Flex } from '@components/Grid'
|
import { Flex } from '@components/Grid'
|
||||||
import { D3MonitoringCharts } from '@components/d3'
|
import { D3MonitoringCharts } from '@components/d3/monitoring'
|
||||||
import { CopyUrlButton } from '@components/CopyUrl'
|
import { CopyUrlButton } from '@components/CopyUrl'
|
||||||
import LoaderPortal from '@components/LoaderPortal'
|
import LoaderPortal from '@components/LoaderPortal'
|
||||||
import { invokeWebApiWrapperAsync } from '@components/factory'
|
import { invokeWebApiWrapperAsync } from '@components/factory'
|
||||||
|
@ -4,7 +4,7 @@ import { Button, Select } from 'antd'
|
|||||||
|
|
||||||
import { useIdWell } from '@asb/context'
|
import { useIdWell } from '@asb/context'
|
||||||
import { makeDateSorter } from '@components/Table'
|
import { makeDateSorter } from '@components/Table'
|
||||||
import { D3MonitoringCharts } from '@components/d3'
|
import { D3MonitoringCharts } from '@components/d3/monitoring'
|
||||||
import LoaderPortal from '@components/LoaderPortal'
|
import LoaderPortal from '@components/LoaderPortal'
|
||||||
import { Grid, GridItem, Flex } from '@components/Grid'
|
import { Grid, GridItem, Flex } from '@components/Grid'
|
||||||
import { invokeWebApiWrapperAsync } from '@components/factory'
|
import { invokeWebApiWrapperAsync } from '@components/factory'
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
@arrow-size: 8px;
|
@arrow-size: 8px;
|
||||||
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100% - @arrow-size;
|
height: 100%;
|
||||||
|
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
color: @color;
|
color: @color;
|
||||||
@ -30,6 +30,7 @@
|
|||||||
|
|
||||||
&.top {
|
&.top {
|
||||||
margin-top: @arrow-size;
|
margin-top: @arrow-size;
|
||||||
|
height: 100% - @arrow-size;
|
||||||
|
|
||||||
&::after {
|
&::after {
|
||||||
border-bottom-color: @bg-color;
|
border-bottom-color: @bg-color;
|
||||||
@ -42,6 +43,7 @@
|
|||||||
|
|
||||||
&.bottom {
|
&.bottom {
|
||||||
margin-bottom: @arrow-size;
|
margin-bottom: @arrow-size;
|
||||||
|
height: 100% - @arrow-size;
|
||||||
|
|
||||||
&::after {
|
&::after {
|
||||||
border-top-color: @bg-color;
|
border-top-color: @bg-color;
|
||||||
@ -53,8 +55,11 @@
|
|||||||
|
|
||||||
&.left {
|
&.left {
|
||||||
margin-left: @arrow-size;
|
margin-left: @arrow-size;
|
||||||
|
width: 100% - @arrow-size;
|
||||||
|
|
||||||
&::after {
|
&::after {
|
||||||
border-right-color: @bg-color;
|
border-right-color: @bg-color;
|
||||||
|
margin-top: -@arrow-size;
|
||||||
top: 50%;
|
top: 50%;
|
||||||
right: 100%;
|
right: 100%;
|
||||||
}
|
}
|
||||||
@ -62,8 +67,11 @@
|
|||||||
|
|
||||||
&.right {
|
&.right {
|
||||||
margin-right: @arrow-size;
|
margin-right: @arrow-size;
|
||||||
|
width: 100% - @arrow-size;
|
||||||
|
|
||||||
&::after {
|
&::after {
|
||||||
border-left-color: @bg-color;
|
border-left-color: @bg-color;
|
||||||
|
margin-top: -@arrow-size;
|
||||||
top: 50%;
|
top: 50%;
|
||||||
left: 100%;
|
left: 100%;
|
||||||
}
|
}
|
||||||
|
@ -35,7 +35,11 @@ export const makeDisplayValue = ({
|
|||||||
def = '----',
|
def = '----',
|
||||||
inf = (v) => `${v < 0 ? '-' : ''}\u221E`,
|
inf = (v) => `${v < 0 ? '-' : ''}\u221E`,
|
||||||
fixed = 2
|
fixed = 2
|
||||||
}: DisplayValueOptions) => (v: unknown): ReactNode => {
|
}: DisplayValueOptions = {
|
||||||
|
def: '----',
|
||||||
|
inf: (v) => `${v < 0 ? '-' : ''}\u221E`,
|
||||||
|
fixed: 2
|
||||||
|
}) => (v: unknown): ReactNode => {
|
||||||
if (typeof v === 'undefined' || v === null || String(v) === 'NaN') return def
|
if (typeof v === 'undefined' || v === null || String(v) === 'NaN') return def
|
||||||
let f = Number(v)
|
let f = Number(v)
|
||||||
if (typeof v === 'string') {
|
if (typeof v === 'string') {
|
||||||
|
Loading…
Reference in New Issue
Block a user