From 539030e9fdfa6085c9c7ffc0f0c7cd9d6d2069d0 Mon Sep 17 00:00:00 2001 From: goodmice Date: Sun, 14 Aug 2022 15:03:48 +0500 Subject: [PATCH 1/7] =?UTF-8?q?=D0=9A=D0=BE=D0=BC=D0=BF=D0=BE=D0=BD=D0=B5?= =?UTF-8?q?=D0=BD=D1=82=D1=8B=20=D0=BC=D0=BE=D0=BD=D0=B8=D1=82=D0=BE=D1=80?= =?UTF-8?q?=D0=B8=D0=BD=D0=B3=D0=B0=20=D0=BF=D0=B5=D1=80=D0=B5=D0=BC=D0=B5?= =?UTF-8?q?=D1=89=D0=B5=D0=BD=D1=8B=20=D0=B2=20=D0=BE=D0=B1=D1=89=D1=83?= =?UTF-8?q?=D1=8E=20=D0=B4=D0=B8=D1=80=D0=B5=D0=BA=D1=82=D0=BE=D1=80=D0=B8?= =?UTF-8?q?=D1=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/d3/index.ts | 2 -- .../D3HorizontalCursor.tsx | 8 ++++---- .../D3MonitoringChartEditor.tsx | 5 +++-- .../d3/{ => monitoring}/D3MonitoringCharts.tsx | 17 +++++++++-------- .../D3MonitoringGroupsEditor.tsx | 2 +- src/components/d3/monitoring/index.ts | 1 + src/components/d3/plugins/index.ts | 1 - src/pages/Telemetry/Archive/index.jsx | 2 +- src/pages/Telemetry/TelemetryView/index.jsx | 2 +- 9 files changed, 20 insertions(+), 20 deletions(-) rename src/components/d3/{plugins => monitoring}/D3HorizontalCursor.tsx (95%) rename src/components/d3/{ => monitoring}/D3MonitoringChartEditor.tsx (97%) rename src/components/d3/{ => monitoring}/D3MonitoringCharts.tsx (98%) rename src/components/d3/{ => monitoring}/D3MonitoringGroupsEditor.tsx (99%) create mode 100644 src/components/d3/monitoring/index.ts diff --git a/src/components/d3/index.ts b/src/components/d3/index.ts index 8fd3688..4811ec3 100644 --- a/src/components/d3/index.ts +++ b/src/components/d3/index.ts @@ -1,6 +1,4 @@ export * from './D3Chart' export type { D3ChartProps } from './D3Chart' -export * from './D3MonitoringCharts' - export * from './types' diff --git a/src/components/d3/plugins/D3HorizontalCursor.tsx b/src/components/d3/monitoring/D3HorizontalCursor.tsx similarity index 95% rename from src/components/d3/plugins/D3HorizontalCursor.tsx rename to src/components/d3/monitoring/D3HorizontalCursor.tsx index 3b99157..a8a1d12 100644 --- a/src/components/d3/plugins/D3HorizontalCursor.tsx +++ b/src/components/d3/monitoring/D3HorizontalCursor.tsx @@ -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 { wrapPlugin } from '@components/d3/plugins/base' 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 { wrapPlugin } from './base' -import { D3TooltipPosition } from './D3Tooltip' +import { ChartGroup, ChartSizes } from './D3MonitoringCharts' import '@styles/d3.less' diff --git a/src/components/d3/D3MonitoringChartEditor.tsx b/src/components/d3/monitoring/D3MonitoringChartEditor.tsx similarity index 97% rename from src/components/d3/D3MonitoringChartEditor.tsx rename to src/components/d3/monitoring/D3MonitoringChartEditor.tsx index 462e9ff..86ffdeb 100644 --- a/src/components/d3/D3MonitoringChartEditor.tsx +++ b/src/components/d3/monitoring/D3MonitoringChartEditor.tsx @@ -1,9 +1,10 @@ import { Button, Form, FormItemProps, Input, InputNumber, Select, Tooltip } from 'antd' 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 { MinMax } from './types' const { Item: RawItem } = Form diff --git a/src/components/d3/D3MonitoringCharts.tsx b/src/components/d3/monitoring/D3MonitoringCharts.tsx similarity index 98% rename from src/components/d3/D3MonitoringCharts.tsx rename to src/components/d3/monitoring/D3MonitoringCharts.tsx index 86ee9c0..35db3ab 100644 --- a/src/components/d3/D3MonitoringCharts.tsx +++ b/src/components/d3/monitoring/D3MonitoringCharts.tsx @@ -16,18 +16,19 @@ import { ChartRegistry, ChartTick, MinMax -} from './types' +} from '@components/d3/types' import { BasePluginSettings, D3ContextMenu, D3ContextMenuSettings, - D3HorizontalCursor, - D3HorizontalCursorSettings -} from './plugins' -import D3MouseZone from './D3MouseZone' +} from '@components/d3/plugins' +import D3MouseZone from '@components/d3/D3MouseZone' +import { getByAccessor, getChartClass, getGroupClass, getTicks } from '@components/d3/functions' +import { renderArea, renderLine, renderNeedle, renderPoint, renderRectArea } from '@components/d3/renders' + import D3MonitoringGroupsEditor from './D3MonitoringGroupsEditor' -import { getByAccessor, getChartClass, getGroupClass, getTicks } from './functions' -import { renderArea, renderLine, renderNeedle, renderPoint, renderRectArea } from './renders' +import { D3HorizontalCursor, D3HorizontalCursorSettings } from './D3HorizontalCursor' +import D3MonitoringLimitChart from './D3MonitoringLimitChart' const roundTo = (v: number, to: number = 50) => { if (v === 0) return v @@ -73,7 +74,7 @@ const defaultOffsets: ChartOffset = { top: 10, bottom: 10, left: 100, - right: 10, + right: 20, } const getDefaultYAxisConfig = (): ChartAxis => ({ diff --git a/src/components/d3/D3MonitoringGroupsEditor.tsx b/src/components/d3/monitoring/D3MonitoringGroupsEditor.tsx similarity index 99% rename from src/components/d3/D3MonitoringGroupsEditor.tsx rename to src/components/d3/monitoring/D3MonitoringGroupsEditor.tsx index 25f9f13..90bbc4e 100644 --- a/src/components/d3/D3MonitoringGroupsEditor.tsx +++ b/src/components/d3/monitoring/D3MonitoringGroupsEditor.tsx @@ -3,11 +3,11 @@ import { Button, Divider, Empty, Modal, Popconfirm, Tooltip, Tree } from 'antd' import { UndoOutlined } from '@ant-design/icons' import { EventDataNode } from 'antd/lib/tree' +import { notify } from '@components/factory' import { getChartIcon } from '@utils' import { ExtendedChartDataset } from './D3MonitoringCharts' import D3MonitoringChartEditor from './D3MonitoringChartEditor' -import { notify } from '../factory' export type D3MonitoringGroupsEditorProps = { visible?: boolean diff --git a/src/components/d3/monitoring/index.ts b/src/components/d3/monitoring/index.ts new file mode 100644 index 0000000..59688d8 --- /dev/null +++ b/src/components/d3/monitoring/index.ts @@ -0,0 +1 @@ +export * from './D3MonitoringCharts' diff --git a/src/components/d3/plugins/index.ts b/src/components/d3/plugins/index.ts index c2fa895..40c5d84 100644 --- a/src/components/d3/plugins/index.ts +++ b/src/components/d3/plugins/index.ts @@ -1,6 +1,5 @@ export * from './base' export * from './D3ContextMenu' export * from './D3Cursor' -export * from './D3HorizontalCursor' export * from './D3Legend' export * from './D3Tooltip' diff --git a/src/pages/Telemetry/Archive/index.jsx b/src/pages/Telemetry/Archive/index.jsx index 03b20b9..cb57c64 100755 --- a/src/pages/Telemetry/Archive/index.jsx +++ b/src/pages/Telemetry/Archive/index.jsx @@ -5,7 +5,7 @@ import { Select } from 'antd' import { useIdWell } from '@asb/context' import { Flex } from '@components/Grid' -import { D3MonitoringCharts } from '@components/d3' +import { D3MonitoringCharts } from '@components/d3/monitoring' import { CopyUrlButton } from '@components/CopyUrl' import LoaderPortal from '@components/LoaderPortal' import { invokeWebApiWrapperAsync } from '@components/factory' diff --git a/src/pages/Telemetry/TelemetryView/index.jsx b/src/pages/Telemetry/TelemetryView/index.jsx index dca17a5..6e69057 100755 --- a/src/pages/Telemetry/TelemetryView/index.jsx +++ b/src/pages/Telemetry/TelemetryView/index.jsx @@ -4,7 +4,7 @@ import { Button, Select } from 'antd' import { useIdWell } from '@asb/context' import { makeDateSorter } from '@components/Table' -import { D3MonitoringCharts } from '@components/d3' +import { D3MonitoringCharts } from '@components/d3/monitoring' import LoaderPortal from '@components/LoaderPortal' import { Grid, GridItem, Flex } from '@components/Grid' import { invokeWebApiWrapperAsync } from '@components/factory' From 0a18b79416ae565ac06e22ddcfa88d3460ee0aab Mon Sep 17 00:00:00 2001 From: goodmice Date: Sun, 14 Aug 2022 15:05:48 +0500 Subject: [PATCH 2/7] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=D0=B0=20=D1=80=D0=B5=D0=B0=D0=BB=D0=B8=D0=B7=D0=B0=D1=86?= =?UTF-8?q?=D0=B8=D1=8F=20=D0=BE=D1=82=D1=80=D0=B8=D1=81=D0=BE=D0=B2=D0=BA?= =?UTF-8?q?=D0=B8=20=D0=B3=D1=80=D0=B0=D1=84=D0=B8=D0=BA=D0=B0=20=D0=BF?= =?UTF-8?q?=D0=B0=D1=80=D0=B0=D0=BC=D0=B5=D1=82=D1=80=D0=B0=20=D0=BE=D0=B3?= =?UTF-8?q?=D1=80=D0=B0=D0=BD=D0=B8=D1=87=D0=B8=D0=B2=D0=B0=D1=8E=D1=89?= =?UTF-8?q?=D0=B5=D0=B3=D0=BE=20=D0=BF=D0=BE=D0=B4=D0=B0=D1=87=D1=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../d3/monitoring/D3MonitoringCharts.tsx | 8 ++ .../d3/monitoring/D3MonitoringLimitChart.tsx | 95 +++++++++++++++++++ 2 files changed, 103 insertions(+) create mode 100644 src/components/d3/monitoring/D3MonitoringLimitChart.tsx diff --git a/src/components/d3/monitoring/D3MonitoringCharts.tsx b/src/components/d3/monitoring/D3MonitoringCharts.tsx index 35db3ab..c8c0e29 100644 --- a/src/components/d3/monitoring/D3MonitoringCharts.tsx +++ b/src/components/d3/monitoring/D3MonitoringCharts.tsx @@ -573,6 +573,14 @@ const _D3MonitoringCharts = >({ return })} + = { + yAxis?: d3.ScaleTime + data: DataType[] + width: number + height: number + left: number + top: number +} + +type LimitChartData = { + id: number + dateStart: Date + dateEnd: Date +} + +type LimitChartDataRaw = { + id?: number + dateStart?: string + dateEnd?: string +} + +const regulators: Record = { + 1: { color: '#007070', label: 'Расход' }, + 2: { color: '#59B359', label: 'Скорость блока' }, + 3: { color: '#FF0000', label: 'Давление' }, + 4: { color: '#0000CC', label: 'Осевая нагрузка' }, + 5: { color: '#00B3B3', label: 'Вес на крюке' }, + 6: { color: '#990099', label: 'Момент на роторе' }, +} + +const getLast = (out: LimitChartDataRaw[]) => out.at(-1) as LimitChartDataRaw +function isDataCorrect(value: LimitChartDataRaw): value is Required { + return typeof value.id !== 'undefined' +} + +const calcualteData = (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 + } else { + const n: LimitChartDataRaw = {} + if (row.idFeedRegulator) { + n.id = row.idFeedRegulator + n.dateStart = row.dateTime + n.dateEnd = row.dateTime + } + out.push(n) + } + return out + }, [{}] as LimitChartDataRaw[]) + + return out.filter(isDataCorrect).map((row) => ({ + id: row.id, + dateStart: new Date(row.dateStart), + dateEnd: new Date(row.dateEnd), + })) +} + +const _D3MonitoringLimitChart = ({ yAxis, data: chartData, width, height, left, top }: D3MonitoringLimitChartProps) => { + const [ref, setRef] = useState(null) + + const data = useMemo(() => calcualteData(chartData), [chartData]) + + useEffect(() => { + if (!ref || !yAxis) return + const elms = d3.select(ref).selectAll('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) + }, [yAxis, data, ref, width]) + + return ( + + + + + ) +} + +export const D3MonitoringLimitChart = memo(_D3MonitoringLimitChart) as typeof _D3MonitoringLimitChart + +export default D3MonitoringLimitChart From cc37bccacfaaa19ca8896826eb6a0f995e36f9e5 Mon Sep 17 00:00:00 2001 From: goodmice Date: Mon, 15 Aug 2022 06:57:20 +0500 Subject: [PATCH 3/7] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=D0=BE=20=D1=81=D0=BE=D1=85=D1=80=D0=B0=D0=BD=D0=B5=D0=BD?= =?UTF-8?q?=D0=B8=D0=B5=20=D0=B8=20=D1=80=D0=B5=D0=B4=D0=B0=D0=BA=D1=82?= =?UTF-8?q?=D0=B8=D1=80=D0=BE=D0=B2=D0=B0=D0=BD=D0=B8=D0=B5=20=D0=BA=D0=BE?= =?UTF-8?q?=D0=BB=D0=BE=D0=BD=D0=BA=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../d3/monitoring/D3MonitoringCharts.tsx | 41 +++++++++++++++---- ...roupsEditor.tsx => D3MonitoringEditor.tsx} | 31 ++++++++++---- .../d3/monitoring/D3MonitoringLimitChart.tsx | 41 +++++++++++-------- .../d3/monitoring/D3MonitoringLimitEditor.tsx | 18 ++++++++ 4 files changed, 96 insertions(+), 35 deletions(-) rename src/components/d3/monitoring/{D3MonitoringGroupsEditor.tsx => D3MonitoringEditor.tsx} (82%) create mode 100644 src/components/d3/monitoring/D3MonitoringLimitEditor.tsx diff --git a/src/components/d3/monitoring/D3MonitoringCharts.tsx b/src/components/d3/monitoring/D3MonitoringCharts.tsx index c8c0e29..d93633a 100644 --- a/src/components/d3/monitoring/D3MonitoringCharts.tsx +++ b/src/components/d3/monitoring/D3MonitoringCharts.tsx @@ -26,9 +26,9 @@ import D3MouseZone from '@components/d3/D3MouseZone' import { getByAccessor, getChartClass, getGroupClass, getTicks } from '@components/d3/functions' import { renderArea, renderLine, renderNeedle, renderPoint, renderRectArea } from '@components/d3/renders' -import D3MonitoringGroupsEditor from './D3MonitoringGroupsEditor' +import D3MonitoringEditor from './D3MonitoringEditor' import { D3HorizontalCursor, D3HorizontalCursorSettings } from './D3HorizontalCursor' -import D3MonitoringLimitChart from './D3MonitoringLimitChart' +import D3MonitoringLimitChart, { TelemetryRegulators } from './D3MonitoringLimitChart' const roundTo = (v: number, to: number = 50) => { if (v === 0) return v @@ -77,6 +77,15 @@ const defaultOffsets: ChartOffset = { right: 20, } +const defaultRegulators: TelemetryRegulators = { + 1: { color: '#007070', label: 'Расход' }, + 2: { color: '#59B359', label: 'Скорость блока' }, + 3: { color: '#FF0000', label: 'Давление' }, + 4: { color: '#0000CC', label: 'Осевая нагрузка' }, + 5: { color: '#00B3B3', label: 'Вес на крюке' }, + 6: { color: '#990099', label: 'Момент на роторе' }, +} + const getDefaultYAxisConfig = (): ChartAxis => ({ type: 'time', accessor: (d: any) => new Date(d.date) @@ -192,6 +201,7 @@ const _D3MonitoringCharts = >({ const [chartAreaRef, setChartAreaRef] = useState(null) const [axesAreaRef, setAxesAreaRef] = useState(null) const [settingsVisible, setSettingsVisible] = useState(false) + const [regulators, setRegulators] = useState(defaultRegulators) const offset = usePartialProps(_offset, defaultOffsets) const yTicks = usePartialProps>>(_yTicks, getDefaultYTicks) @@ -272,23 +282,28 @@ const _D3MonitoringCharts = >({ } ), [chartArea]) - const onGroupsChange = useCallback((sets: ExtendedChartDataset[][]) => { + const onGroupsChange = useCallback((settings: ExtendedChartDataset[][], regulators: TelemetryRegulators) => { if (chartName) { invokeWebApiWrapperAsync( async () => { - await UserSettingsService.update(chartName, sets) + await UserSettingsService.update(chartName, { + settings, + regulators, + }) }, undefined, 'Не удалось сохранить параметры графиков' ) } - setDatasets(sets) + setDatasets(settings) + setRegulators(regulators) setSettingsVisible(false) }, [chartName]) const onGroupsReset = useCallback(() => { setSettingsVisible(false) setDatasets(datasetGroups) + setRegulators(defaultRegulators) if (chartName) { invokeWebApiWrapperAsync( async () => await UserSettingsService.delete(chartName), @@ -306,13 +321,19 @@ const _D3MonitoringCharts = >({ let sets = chartName ? await UserSettingsService.get(chartName) : null if (typeof sets === 'string') sets = JSON.parse(sets) - if (Array.isArray(sets)) { - setDatasets(sets) + const { settings, regulators } = sets + if (regulators) + setRegulators(regulators) + if (Array.isArray(settings)) { + setDatasets(settings) } else if (Array.isArray(datasetGroups)) { setDatasets(datasetGroups) if (chartName) { invokeWebApiWrapperAsync( - async () => await UserSettingsService.insert(chartName, datasetGroups), + async () => await UserSettingsService.insert(chartName, { + settings: datasetGroups, + regulators: defaultRegulators, + }), undefined, 'Не удалось сохранить настройки графиков' ) @@ -574,6 +595,7 @@ const _D3MonitoringCharts = >({ })} >({ )} - setSettingsVisible(false)} diff --git a/src/components/d3/monitoring/D3MonitoringGroupsEditor.tsx b/src/components/d3/monitoring/D3MonitoringEditor.tsx similarity index 82% rename from src/components/d3/monitoring/D3MonitoringGroupsEditor.tsx rename to src/components/d3/monitoring/D3MonitoringEditor.tsx index 90bbc4e..bf904db 100644 --- a/src/components/d3/monitoring/D3MonitoringGroupsEditor.tsx +++ b/src/components/d3/monitoring/D3MonitoringEditor.tsx @@ -7,12 +7,15 @@ import { notify } from '@components/factory' import { getChartIcon } from '@utils' import { ExtendedChartDataset } from './D3MonitoringCharts' +import { TelemetryRegulators } from './D3MonitoringLimitChart' import D3MonitoringChartEditor from './D3MonitoringChartEditor' +import D3MonitoringLimitEditor from './D3MonitoringLimitEditor' export type D3MonitoringGroupsEditorProps = { visible?: boolean groups: ExtendedChartDataset[][] - onChange: (value: ExtendedChartDataset[][]) => void + regulators: TelemetryRegulators + onChange: (value: ExtendedChartDataset[][], regs: TelemetryRegulators) => void onCancel: () => void onReset: () => void } @@ -36,9 +39,12 @@ const getNodePos = (node: EventDataNode): { group: number, chart?: number } => { return { group: out[1], chart: out[2] } } -const _D3MonitoringGroupsEditor = ({ +type EditingMode = null | 'limit' | 'chart' + +const _D3MonitoringEditor = ({ visible, groups: oldGroups, + regulators: oldRegulators, onChange, onCancel, onReset, @@ -46,10 +52,13 @@ const _D3MonitoringGroupsEditor = ({ const [groups, setGroups] = useState[][]>([]) const [expand, setExpand] = useState([]) const [selected, setSelected] = useState([]) + const [mode, setMode] = useState(null) + const [regulators, setRegulators] = useState({}) 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: { node: EventDataNode @@ -148,23 +157,29 @@ const _D3MonitoringGroupsEditor = ({ selectedKeys={selected} treeData={treeItems} onDrop={onDrop} - onSelect={setSelected} + onSelect={(value) => { + setSelected(value) + setMode('chart') + }} height={250} /> +
- {selectedGroup && selectedChart ? ( + {mode === 'chart' && selectedGroup && selectedChart ? ( group={selectedGroup} chart={selectedChart} onChange={onChartChange} /> + ) : (mode === 'limit' ? ( + value={regulators} onChange={setRegulators} /> ) : ( - )} + ))}
) } -export const D3MonitoringGroupsEditor = memo(_D3MonitoringGroupsEditor) as typeof _D3MonitoringGroupsEditor +export const D3MonitoringEditor = memo(_D3MonitoringEditor) as typeof _D3MonitoringEditor -export default D3MonitoringGroupsEditor +export default D3MonitoringEditor diff --git a/src/components/d3/monitoring/D3MonitoringLimitChart.tsx b/src/components/d3/monitoring/D3MonitoringLimitChart.tsx index aea37d9..203041d 100644 --- a/src/components/d3/monitoring/D3MonitoringLimitChart.tsx +++ b/src/components/d3/monitoring/D3MonitoringLimitChart.tsx @@ -3,15 +3,6 @@ import * as d3 from 'd3' import { TelemetryDataSaubDto } from '@api' -export type D3MonitoringLimitChartProps = { - yAxis?: d3.ScaleTime - data: DataType[] - width: number - height: number - left: number - top: number -} - type LimitChartData = { id: number dateStart: Date @@ -24,14 +15,10 @@ type LimitChartDataRaw = { dateEnd?: string } -const regulators: Record = { - 1: { color: '#007070', label: 'Расход' }, - 2: { color: '#59B359', label: 'Скорость блока' }, - 3: { color: '#FF0000', label: 'Давление' }, - 4: { color: '#0000CC', label: 'Осевая нагрузка' }, - 5: { color: '#00B3B3', label: 'Вес на крюке' }, - 6: { color: '#990099', label: 'Момент на роторе' }, -} +export type TelemetryRegulators = Record const getLast = (out: LimitChartDataRaw[]) => out.at(-1) as LimitChartDataRaw function isDataCorrect(value: LimitChartDataRaw): value is Required { @@ -63,7 +50,25 @@ const calcualteData = (data: DataType[]) })) } -const _D3MonitoringLimitChart = ({ yAxis, data: chartData, width, height, left, top }: D3MonitoringLimitChartProps) => { +export type D3MonitoringLimitChartProps = { + yAxis?: d3.ScaleTime + regulators: TelemetryRegulators + data: DataType[] + width: number + height: number + left: number + top: number +} + +const _D3MonitoringLimitChart = ({ + yAxis, + data: chartData, + width, + height, + left, + top, + regulators +}: D3MonitoringLimitChartProps) => { const [ref, setRef] = useState(null) const data = useMemo(() => calcualteData(chartData), [chartData]) diff --git a/src/components/d3/monitoring/D3MonitoringLimitEditor.tsx b/src/components/d3/monitoring/D3MonitoringLimitEditor.tsx new file mode 100644 index 0000000..fbb64fb --- /dev/null +++ b/src/components/d3/monitoring/D3MonitoringLimitEditor.tsx @@ -0,0 +1,18 @@ +import { memo } from 'react' +import { TelemetryRegulators } from './D3MonitoringLimitChart' + +export type D3MonitoringLimitEditorProps = { + value: TelemetryRegulators + onChange: (value: TelemetryRegulators) => void +} + +const _D3MonitoringLimitEditor = ({ value, onChange }: D3MonitoringLimitEditorProps) => { + + return ( + <> + ) +} + +export const D3MonitoringLimitEditor = memo(_D3MonitoringLimitEditor) as typeof _D3MonitoringLimitEditor + +export default D3MonitoringLimitEditor From 48ee08acab4427eb11cfd2f907cf3ff0761ab290 Mon Sep 17 00:00:00 2001 From: goodmice Date: Mon, 15 Aug 2022 11:58:32 +0500 Subject: [PATCH 4/7] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=20=D1=82=D1=83=D0=BB=D1=82=D0=B8=D0=BF=20=D0=B4=D0=BB?= =?UTF-8?q?=D1=8F=20=D0=BA=D0=BE=D0=BB=D0=BE=D0=BD=D0=BA=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../d3/monitoring/D3MonitoringCharts.tsx | 22 ++++----- .../d3/monitoring/D3MonitoringLimitChart.tsx | 47 +++++++++++++++++-- src/styles/d3.less | 10 +++- 3 files changed, 63 insertions(+), 16 deletions(-) diff --git a/src/components/d3/monitoring/D3MonitoringCharts.tsx b/src/components/d3/monitoring/D3MonitoringCharts.tsx index d93633a..57dcde6 100644 --- a/src/components/d3/monitoring/D3MonitoringCharts.tsx +++ b/src/components/d3/monitoring/D3MonitoringCharts.tsx @@ -78,12 +78,11 @@ const defaultOffsets: ChartOffset = { } const defaultRegulators: TelemetryRegulators = { - 1: { color: '#007070', label: 'Расход' }, - 2: { color: '#59B359', label: 'Скорость блока' }, - 3: { color: '#FF0000', label: 'Давление' }, - 4: { color: '#0000CC', label: 'Осевая нагрузка' }, - 5: { color: '#00B3B3', label: 'Вес на крюке' }, - 6: { color: '#990099', label: 'Момент на роторе' }, + 1: { color: '#59B359', label: 'Скорость блока' }, + 2: { color: '#FF0000', label: 'Давление' }, + 3: { color: '#0000CC', label: 'Осевая нагрузка' }, + 4: { color: '#990099', label: 'Момент на роторе' }, + 5: { color: '#007070', label: 'Расход' }, } const getDefaultYAxisConfig = (): ChartAxis => ({ @@ -321,11 +320,12 @@ const _D3MonitoringCharts = >({ let sets = chartName ? await UserSettingsService.get(chartName) : null if (typeof sets === 'string') sets = JSON.parse(sets) - const { settings, regulators } = sets - if (regulators) - setRegulators(regulators) - if (Array.isArray(settings)) { - setDatasets(settings) + if (sets && Array.isArray(sets.settings)) { + if (sets.regulators) + setRegulators(sets.regulators) + if (Array.isArray(sets.settings)) { + setDatasets(sets.settings) + } } else if (Array.isArray(datasetGroups)) { setDatasets(datasetGroups) if (chartName) { diff --git a/src/components/d3/monitoring/D3MonitoringLimitChart.tsx b/src/components/d3/monitoring/D3MonitoringLimitChart.tsx index 203041d..9017b05 100644 --- a/src/components/d3/monitoring/D3MonitoringLimitChart.tsx +++ b/src/components/d3/monitoring/D3MonitoringLimitChart.tsx @@ -1,18 +1,24 @@ import { memo, useEffect, useMemo, useState } from 'react' import * as d3 from 'd3' +import { Grid, GridItem } from '@components/Grid' import { TelemetryDataSaubDto } from '@api' +import { formatDate } from '@utils' 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(data: DataType[]) if (last.id === row.idFeedRegulator) { if (!row.idFeedRegulator) return out last.dateEnd = row.dateTime + last.depthEnd = row.wellDepth } else { - const n: LimitChartDataRaw = {} + let n: LimitChartDataRaw = {} if (row.idFeedRegulator) { - n.id = row.idFeedRegulator - n.dateStart = row.dateTime - n.dateEnd = row.dateTime + n = { + id: row.idFeedRegulator, + dateStart: row.dateTime, + dateEnd: row.dateTime, + depthStart: row.wellDepth, + depthEnd: row.wellDepth, + } } out.push(n) } @@ -47,6 +58,8 @@ const calcualteData = (data: DataType[]) id: row.id, dateStart: new Date(row.dateStart), dateEnd: new Date(row.dateEnd), + depthStart: row.depthStart, + depthEnd: row.depthEnd, })) } @@ -60,6 +73,9 @@ export type D3MonitoringLimitChartProps = { top: number } +const tooltipWidth = 300 +const tooltipHeight = 130 + const _D3MonitoringLimitChart = ({ yAxis, data: chartData, @@ -70,6 +86,8 @@ const _D3MonitoringLimitChart = ({ regulators }: D3MonitoringLimitChartProps) => { const [ref, setRef] = useState(null) + const [tooltipRef, setTooltipRef] = useState(null) + const [selected, setSelected] = useState>({}) const data = useMemo(() => calcualteData(chartData), [chartData]) @@ -85,12 +103,33 @@ const _D3MonitoringLimitChart = ({ .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) + yAxis(d.dateEnd) - tooltipHeight) / 2 + setSelected({ ...d, y, x: -tooltipWidth, visible: true }) + }) + .on('mouseout', (_, d) => setSelected((pre) => ({ ...pre, visible: false }))) }, [yAxis, data, ref, width]) return ( + +
+ + Регулятор: + {selected?.id ? regulators[selected.id].label : '---'} + Начало: + {formatDate(selected?.dateStart) ?? '---'} + {selected?.depthStart?.toFixed(2) ?? '---'} + м. + Конец: + {formatDate(selected?.dateEnd) ?? '---'} + {selected?.depthEnd?.toFixed(2) ?? '---'} + м. + +
+
) } diff --git a/src/styles/d3.less b/src/styles/d3.less index be70bbe..6ec1bb0 100644 --- a/src/styles/d3.less +++ b/src/styles/d3.less @@ -8,7 +8,7 @@ @arrow-size: 8px; width: 100%; - height: 100% - @arrow-size; + height: 100%; font-size: 13px; color: @color; @@ -30,6 +30,7 @@ &.top { margin-top: @arrow-size; + height: 100% - @arrow-size; &::after { border-bottom-color: @bg-color; @@ -42,6 +43,7 @@ &.bottom { margin-bottom: @arrow-size; + height: 100% - @arrow-size; &::after { border-top-color: @bg-color; @@ -53,8 +55,11 @@ &.left { margin-left: @arrow-size; + width: 100% - @arrow-size; + &::after { border-right-color: @bg-color; + margin-top: -@arrow-size; top: 50%; right: 100%; } @@ -62,8 +67,11 @@ &.right { margin-right: @arrow-size; + width: 100% - @arrow-size; + &::after { border-left-color: @bg-color; + margin-top: -@arrow-size; top: 50%; left: 100%; } From cf274028104d63e6ad6e6eb058190f4379275a16 Mon Sep 17 00:00:00 2001 From: goodmice Date: Mon, 15 Aug 2022 12:28:07 +0500 Subject: [PATCH 5/7] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=D0=B0=20=D0=B3=D0=BE=D1=80=D0=B8=D0=B7=D0=BE=D0=BD=D1=82?= =?UTF-8?q?=D0=B0=D0=BB=D1=8C=D0=BD=D0=B0=D1=8F=20=D0=B7=D0=BE=D0=BD=D0=B0?= =?UTF-8?q?=20=D1=80=D0=B5=D0=B3=D1=83=D0=BB=D1=8F=D1=82=D0=BE=D1=80=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../d3/monitoring/D3MonitoringCharts.tsx | 1 + .../d3/monitoring/D3MonitoringLimitChart.tsx | 75 ++++++++++++------- 2 files changed, 51 insertions(+), 25 deletions(-) diff --git a/src/components/d3/monitoring/D3MonitoringCharts.tsx b/src/components/d3/monitoring/D3MonitoringCharts.tsx index 57dcde6..ae37671 100644 --- a/src/components/d3/monitoring/D3MonitoringCharts.tsx +++ b/src/components/d3/monitoring/D3MonitoringCharts.tsx @@ -602,6 +602,7 @@ const _D3MonitoringCharts = >({ height={sizes.chartsHeight} left={sizes.inlineWidth + sizes.left} top={sizes.chartsTop} + zoneWidth={sizes.inlineWidth} /> = { height: number left: number top: number + zoneWidth?: number } const tooltipWidth = 300 -const tooltipHeight = 130 +const tooltipHeight = 120 const _D3MonitoringLimitChart = ({ yAxis, @@ -83,17 +84,17 @@ const _D3MonitoringLimitChart = ({ height, left, top, - regulators + regulators, + zoneWidth = 0 }: D3MonitoringLimitChartProps) => { const [ref, setRef] = useState(null) - const [tooltipRef, setTooltipRef] = useState(null) - const [selected, setSelected] = useState>({}) + const [selected, setSelected] = useState() const data = useMemo(() => calcualteData(chartData), [chartData]) useEffect(() => { if (!ref || !yAxis) return - const elms = d3.select(ref).selectAll('rect').data(data) + const elms = d3.select(ref).select('.bars').selectAll('rect').data(data) elms.exit().remove() const newElms = elms.enter().append('rect') @@ -104,32 +105,56 @@ const _D3MonitoringLimitChart = ({ .attr('y', (d) => yAxis(d.dateStart)) .attr('fill', (d) => regulators[d.id].color) .on('mouseover', (_, d) => { - const y = (yAxis(d.dateStart) + yAxis(d.dateEnd) - tooltipHeight) / 2 - setSelected({ ...d, y, x: -tooltipWidth, visible: true }) + const y = yAxis(d.dateStart) - tooltipHeight + setSelected({ ...d, y, x: -tooltipWidth - 10, visible: true }) }) - .on('mouseout', (_, d) => setSelected((pre) => ({ ...pre, visible: false }))) + .on('mouseout', (_, d) => 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 tooltipStyle = useMemo(() => ({ transition: 'opacity .1s ease-in-out', opacity: selected?.visible ? 1 : 0 }), [selected]) + return ( - + + + {selected && ( + + + + + + )} + - -
- - Регулятор: - {selected?.id ? regulators[selected.id].label : '---'} - Начало: - {formatDate(selected?.dateStart) ?? '---'} - {selected?.depthStart?.toFixed(2) ?? '---'} - м. - Конец: - {formatDate(selected?.dateEnd) ?? '---'} - {selected?.depthEnd?.toFixed(2) ?? '---'} - м. - -
-
+ {selected && ( + +
+ + Регулятор: + {regulators[selected.id].label} + Начало: + {formatDate(selected.dateStart)} + {selected.depthStart?.toFixed(2) ?? '---'} + м. + Конец: + {formatDate(selected.dateEnd)} + {selected.depthEnd?.toFixed(2) ?? '---'} + м. + +
+
+ )}
) } From 8ade639e0e01b0cdcf43c0323ca54a1bcbb10e46 Mon Sep 17 00:00:00 2001 From: goodmice Date: Mon, 15 Aug 2022 12:35:31 +0500 Subject: [PATCH 6/7] =?UTF-8?q?=D0=98=D0=B7=D0=BC=D0=B5=D0=BD=D1=91=D0=BD?= =?UTF-8?q?=20=D0=B2=D0=B8=D0=B4=20=D1=82=D1=83=D0=BB=D1=82=D0=B8=D0=BF?= =?UTF-8?q?=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../d3/monitoring/D3MonitoringLimitChart.tsx | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/components/d3/monitoring/D3MonitoringLimitChart.tsx b/src/components/d3/monitoring/D3MonitoringLimitChart.tsx index e5f9be0..be88683 100644 --- a/src/components/d3/monitoring/D3MonitoringLimitChart.tsx +++ b/src/components/d3/monitoring/D3MonitoringLimitChart.tsx @@ -74,7 +74,7 @@ export type D3MonitoringLimitChartProps = { zoneWidth?: number } -const tooltipWidth = 300 +const tooltipWidth = 270 const tooltipHeight = 120 const _D3MonitoringLimitChart = ({ @@ -139,18 +139,18 @@ const _D3MonitoringLimitChart = ({ {selected && ( -
- - Регулятор: - {regulators[selected.id].label} - Начало: - {formatDate(selected.dateStart)} - {selected.depthStart?.toFixed(2) ?? '---'} +
+ Ограничивающий параметр + {regulators[selected.id].label} + + Начало: + {formatDate(selected.dateStart)} + {selected.depthStart?.toFixed(2) ?? '---'} + м. + Конец: + {formatDate(selected.dateEnd)} + {selected.depthEnd?.toFixed(2) ?? '---'} м. - Конец: - {formatDate(selected.dateEnd)} - {selected.depthEnd?.toFixed(2) ?? '---'} - м.
From 1de4e8d90934dbe4ae41429dabb2e526e3f637cd Mon Sep 17 00:00:00 2001 From: goodmice Date: Mon, 15 Aug 2022 12:49:11 +0500 Subject: [PATCH 7/7] =?UTF-8?q?=D0=9A=D0=BE=D1=81=D0=BC=D0=B5=D1=82=D0=B8?= =?UTF-8?q?=D1=87=D0=B5=D1=81=D0=BA=D0=B8=D0=B5=20=D1=83=D0=BB=D1=83=D1=87?= =?UTF-8?q?=D1=88=D0=B5=D0=BD=D0=B8=D1=8F=20=D0=BA=D0=BE=D0=B4=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../d3/monitoring/D3MonitoringLimitChart.tsx | 41 +++++++++++++------ src/utils/functions/numbers.tsx | 6 ++- 2 files changed, 34 insertions(+), 13 deletions(-) diff --git a/src/components/d3/monitoring/D3MonitoringLimitChart.tsx b/src/components/d3/monitoring/D3MonitoringLimitChart.tsx index be88683..97dc80c 100644 --- a/src/components/d3/monitoring/D3MonitoringLimitChart.tsx +++ b/src/components/d3/monitoring/D3MonitoringLimitChart.tsx @@ -1,9 +1,9 @@ -import { memo, useEffect, useMemo, useState } from 'react' +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' -import { formatDate } from '@utils' type LimitChartData = { id: number @@ -77,6 +77,8 @@ export type D3MonitoringLimitChartProps = { const tooltipWidth = 270 const tooltipHeight = 120 +const displayValue = makeDisplayValue() + const _D3MonitoringLimitChart = ({ yAxis, data: chartData, @@ -85,7 +87,7 @@ const _D3MonitoringLimitChart = ({ left, top, regulators, - zoneWidth = 0 + zoneWidth = 0, }: D3MonitoringLimitChartProps) => { const [ref, setRef] = useState(null) const [selected, setSelected] = useState() @@ -108,22 +110,37 @@ const _D3MonitoringLimitChart = ({ const y = yAxis(d.dateStart) - tooltipHeight setSelected({ ...d, y, x: -tooltipWidth - 10, visible: true }) }) - .on('mouseout', (_, d) => setSelected((pre) => pre ? ({ ...pre, visible: false }) : undefined)) + .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 tooltipStyle = useMemo(() => ({ transition: 'opacity .1s ease-in-out', opacity: selected?.visible ? 1 : 0 }), [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 ( {selected && ( - - - + + + ({ {selected && ( -
+
Ограничивающий параметр {regulators[selected.id].label} - + Начало: {formatDate(selected.dateStart)} - {selected.depthStart?.toFixed(2) ?? '---'} + {displayValue(selected.depthStart)} м. Конец: {formatDate(selected.dateEnd)} - {selected.depthEnd?.toFixed(2) ?? '---'} + {displayValue(selected.depthEnd)} м.
diff --git a/src/utils/functions/numbers.tsx b/src/utils/functions/numbers.tsx index c60a46e..6e6ca26 100644 --- a/src/utils/functions/numbers.tsx +++ b/src/utils/functions/numbers.tsx @@ -35,7 +35,11 @@ export const makeDisplayValue = ({ def = '----', inf = (v) => `${v < 0 ? '-' : ''}\u221E`, 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 let f = Number(v) if (typeof v === 'string') {