forked from ddrilling/asb_cloud_front
Добавлено отображение последних 3х сообщений, Имени пользователя, просмотр сообщений, перенесён выбор скважин в заголовок
This commit is contained in:
parent
d59e5a0fe4
commit
0c351640cd
14
.vscode/launch.json
vendored
14
.vscode/launch.json
vendored
@ -5,19 +5,11 @@
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Launch Edge",
|
||||
"name": "Launch Chrome",
|
||||
"request": "launch",
|
||||
"type": "pwa-msedge",
|
||||
"url": "http://localhost:3000",
|
||||
"type": "pwa-chrome",
|
||||
"url": "http://localhost:3000/",
|
||||
"webRoot": "${workspaceFolder}"
|
||||
},
|
||||
|
||||
{
|
||||
"type": "pwa-chrome",
|
||||
"request": "launch",
|
||||
"name": "Launch Chrome against localhost",
|
||||
"url": "http://localhost:3000",
|
||||
"webRoot": "${workspaceFolder}"
|
||||
}
|
||||
]
|
||||
}
|
5
.vscode/settings.json
vendored
Normal file
5
.vscode/settings.json
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"cSpell.words": [
|
||||
"день"
|
||||
]
|
||||
}
|
@ -18,7 +18,7 @@ export const CustomColumn = ({data}) => {
|
||||
|
||||
return (<>
|
||||
{lines.map(line => <Display className='border_small display_flex_container'
|
||||
kay={line.label}
|
||||
key={line.label}
|
||||
label={line.label}
|
||||
value={line.value}
|
||||
suffix={line.units}/>)}
|
||||
|
22
src/components/UserOfWells.jsx
Normal file
22
src/components/UserOfWells.jsx
Normal file
@ -0,0 +1,22 @@
|
||||
import { Display } from './Display'
|
||||
|
||||
export const UserOfWells = ({ data }) => {
|
||||
const dataLast = data[data.length - 1]
|
||||
|
||||
const lines = [
|
||||
{ label: 'Пользователь', accessorName: 'user' },
|
||||
]
|
||||
|
||||
if (dataLast)
|
||||
lines.forEach(line => line.value = dataLast[line.accessorName] ?? '-')
|
||||
else
|
||||
lines.forEach(line => line.value = '-')
|
||||
|
||||
return (<>
|
||||
{lines.map(line => <Display className='border_small display_flex_container user_card'
|
||||
key={line.label}
|
||||
label={line.label}
|
||||
value={line.value}
|
||||
suffix={line.units} />)}
|
||||
</>)
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
import { useState, useEffect } from 'react'
|
||||
import { WellService } from '../services/api'
|
||||
import Loader from '../components/Loader'
|
||||
import { TreeSelect } from 'antd' // TreeSelect
|
||||
import { TreeSelect } from 'antd'
|
||||
import { useHistory } from 'react-router-dom'
|
||||
|
||||
const groupBy = (table, ...keys) => {
|
||||
@ -10,13 +10,13 @@ const groupBy = (table, ...keys) => {
|
||||
let groups = table.reduce((rv, item) => {
|
||||
let keyValue = item[key]
|
||||
let group = rv.find(o => o.title === keyValue)
|
||||
if(!group)
|
||||
{
|
||||
if (!group) {
|
||||
group = {
|
||||
title: keyValue,
|
||||
value: keys.length === 1 ? item : `${key} ${keyValue} ${item['id']}`,
|
||||
value: keys.length === 1 ? `${item['id']}` : `${key} ${keyValue} ${item['id']}`,
|
||||
selectable: keys.length === 1,
|
||||
children:[]}
|
||||
children: []
|
||||
}
|
||||
rv.push(group)
|
||||
}
|
||||
if (keys.length > 1)
|
||||
@ -34,8 +34,8 @@ const groupBy = (table, ...keys) => {
|
||||
};
|
||||
|
||||
export default function WellTreeSelector(props) {
|
||||
const [wells, setWells] = useState([])
|
||||
const [wellsTree, setWellsTree] = useState([]) // wellsTree,
|
||||
// const [wells, setWells] = useState([])
|
||||
const [wellsTree, setWellsTree] = useState([])
|
||||
const [loader, setLoader] = useState(false)
|
||||
const history = useHistory()
|
||||
|
||||
@ -44,8 +44,7 @@ export default function WellTreeSelector(props){
|
||||
try {
|
||||
let newWells = (await WellService.getWells()).map(w => { return { key: w.id, ...w } })
|
||||
let wellsTree = groupBy(newWells, 'deposit', 'cluster', 'caption')
|
||||
console.log(wellsTree)
|
||||
setWells( newWells )
|
||||
// setWells( newWells )
|
||||
setWellsTree(wellsTree)
|
||||
}
|
||||
catch (e) {
|
||||
@ -56,19 +55,23 @@ export default function WellTreeSelector(props){
|
||||
|
||||
useEffect(() => { updateWellsList() }, [])
|
||||
|
||||
const onChange = (value) =>{
|
||||
;
|
||||
const onSelect = (value) => {
|
||||
if (value)
|
||||
history.push(`/well/${value}`);
|
||||
console.log(value)
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<TreeSelect /* не работает пока */
|
||||
style={{ width: '25%',
|
||||
alignItems: 'center' }}
|
||||
<TreeSelect
|
||||
style={{
|
||||
width: '25%',
|
||||
alignItems: 'center'
|
||||
}}
|
||||
placeholder="Выберите месторождение"
|
||||
treeData={wellsTree}
|
||||
treeDefaultExpandAll
|
||||
onChange={onChange}
|
||||
onSelect={onSelect}
|
||||
/>
|
||||
{loader && <Loader />}
|
||||
</>
|
||||
|
82
src/components/charts/ChartTimeArchive.tsx
Normal file
82
src/components/charts/ChartTimeArchive.tsx
Normal file
@ -0,0 +1,82 @@
|
||||
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} />)
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
export default function Analise(props){
|
||||
return(<h2>Анализ</h2>)
|
||||
}
|
3
src/pages/Analysis.jsx
Normal file
3
src/pages/Analysis.jsx
Normal file
@ -0,0 +1,3 @@
|
||||
export default function Analysis(props){
|
||||
return(<h2>Анализ</h2>)
|
||||
}
|
@ -1,5 +1,187 @@
|
||||
import { useState, useEffect } from 'react'
|
||||
import {
|
||||
Button,
|
||||
DatePicker,
|
||||
ConfigProvider,
|
||||
Select,
|
||||
Row,
|
||||
Col,
|
||||
} from 'antd'
|
||||
import { useParams } from 'react-router-dom'
|
||||
import { Subscribe } from '../services/signalr'
|
||||
import { DataService } from '../services/api'
|
||||
import 'moment/locale/ru'
|
||||
import locale from 'antd/lib/locale/ru_RU'
|
||||
import { ChartTimeArchive } from '../components/charts/ChartTimeArchive'
|
||||
import { Display } from '../components/Display'
|
||||
import { ChartTimeOnlineFooter } from '../components/ChartTimeOnlineFooter'
|
||||
import { UserOfWells } from '../components/UserOfWells'
|
||||
|
||||
|
||||
const { RangePicker } = DatePicker;
|
||||
const { Option } = Select;
|
||||
|
||||
// Выбор периода времени
|
||||
const PeriodOfTime = () => {
|
||||
// const [startDate, setStartDate] = useState(new Date());
|
||||
|
||||
return (
|
||||
<ConfigProvider locale={locale}>
|
||||
<RangePicker
|
||||
showTime
|
||||
style={{ margin: '5px 5px', }}
|
||||
/>
|
||||
</ConfigProvider>
|
||||
)
|
||||
}
|
||||
|
||||
// Выбор "перьев" для графиков - перенести в шапку графика
|
||||
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) {
|
||||
return (<h2>Архив</h2>)
|
||||
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>
|
||||
<PeriodOfTime />
|
||||
<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>
|
||||
</>)
|
||||
}
|
@ -7,35 +7,6 @@ import WellTreeSelector from '../components/WellTreeSelector'
|
||||
|
||||
const { Header } = Layout
|
||||
|
||||
const groupBy = (table, ...keys) => {
|
||||
let key = keys[0]
|
||||
|
||||
let groups = table.reduce((rv, item) => {
|
||||
let keyValue = item[key]
|
||||
let group = rv.find(o=>o.title === keyValue)
|
||||
if(!group)
|
||||
{
|
||||
group = {
|
||||
title: keyValue,
|
||||
value: keys.length === 1 ? item : `${key} ${keyValue} ${item['id']}`,
|
||||
selectable: keys.length === 1,
|
||||
children:[]}
|
||||
rv.push(group)
|
||||
}
|
||||
if(keys.length > 1)
|
||||
group.children.push(item);
|
||||
return rv;
|
||||
}, []);
|
||||
|
||||
if(keys.length > 1){
|
||||
for(let group of groups){
|
||||
group.children = groupBy(group.children, ...keys.slice(1))
|
||||
}
|
||||
}
|
||||
|
||||
return groups
|
||||
};
|
||||
|
||||
export default function PageHeader(props){
|
||||
const [sidebarVisible, setSidebarVisible] = useState(true)
|
||||
const login = localStorage['login']
|
||||
|
@ -8,10 +8,10 @@ import Files from './Files'
|
||||
import Archive from './Archive'
|
||||
import Messages from './Messages'
|
||||
import Report from './Report'
|
||||
import Analise from './Analise'
|
||||
import Analysis from './Analysis'
|
||||
import PageHeader from './Header'
|
||||
|
||||
const { Content, Sider } = Layout // Header
|
||||
const { Content, Sider } = Layout
|
||||
|
||||
export default function Main(){
|
||||
const [sidebarVisible] = useState(true) // setSidebarVisible
|
||||
@ -47,7 +47,7 @@ export default function Main(){
|
||||
<Link to="/report">Рапорт</Link>
|
||||
</Menu.Item>
|
||||
<Menu.Item key="5" icon= {<FolderOutlined />}>
|
||||
<Link to="/analise">Анализ</Link>
|
||||
<Link to="/analysis">Анализ</Link>
|
||||
</Menu.Item>
|
||||
<Menu.Item key="6" icon= {<FolderOutlined />}>
|
||||
<Link to="/file">Файлы</Link>
|
||||
@ -65,14 +65,14 @@ export default function Main(){
|
||||
<Route path="/archive">
|
||||
<Archive />
|
||||
</Route>
|
||||
<Route path="/messages">
|
||||
<Route path="/well/:id/messages">
|
||||
<Messages />
|
||||
</Route>
|
||||
<Route path="/report">
|
||||
<Report />
|
||||
</Route>
|
||||
<Route path="/analise">
|
||||
<Analise />
|
||||
<Route path="/analysis">
|
||||
<Analysis />
|
||||
</Route>
|
||||
<Route path="/well/:id">
|
||||
<TelemetryView/>
|
||||
|
@ -1,3 +1,110 @@
|
||||
export default function Messages(props){
|
||||
return(<h2>Сообщения</h2>)
|
||||
import { Table } from 'antd';
|
||||
import { MessageService } from '../services/api'
|
||||
import { useState, useEffect } from 'react'
|
||||
import { useParams } from 'react-router-dom'
|
||||
import { Subscribe } from '../services/signalr'
|
||||
import Loader from '../components/Loader'
|
||||
import moment from 'moment';
|
||||
import '../styles/message.css'
|
||||
|
||||
|
||||
// Словарь категорий для строк таблицы
|
||||
const categoryDictionary = {
|
||||
1: {title:'Авария'},
|
||||
2: {title:'Предупреждение'},
|
||||
3: {title:'Информация'},
|
||||
}
|
||||
|
||||
// Конфигурация таблицы
|
||||
const columns = [
|
||||
{
|
||||
title: 'Дата',
|
||||
key: 'date',
|
||||
dataIndex: 'date',
|
||||
render: (item) => moment(item).format('DD MMM YYYY, HH:MM:ss'),
|
||||
sorter: (a, b) => new Date(b.date) - new Date(a.date),
|
||||
sortDirections: ['descend', 'ascend'],
|
||||
},
|
||||
{
|
||||
title: 'Категория',
|
||||
key: 'categoryId',
|
||||
dataIndex: 'categoryId',
|
||||
render: (_, item) => categoryDictionary[item.categoryId].title,
|
||||
style: (_, item) => categoryDictionary[item.categoryId].style,
|
||||
sorter: (a, b) => a.categoryId - b.categoryId,
|
||||
sortDirections: ['descend', 'ascend'],
|
||||
},
|
||||
{
|
||||
title: 'Сообщение',
|
||||
key: 'message',
|
||||
dataIndex: 'message',
|
||||
onFilter: (value, record) => record.name.indexOf(value) === 0,
|
||||
},
|
||||
{
|
||||
title: 'Пользователь',
|
||||
key: 'user',
|
||||
dataIndex: 'user',
|
||||
},
|
||||
];
|
||||
|
||||
// Данные для таблицы
|
||||
export default function Messages(props) {
|
||||
let { id } = useParams()
|
||||
const [messages, setMessages] = useState([])
|
||||
const [loader] = useState(false)
|
||||
|
||||
// const handleMessages = (messages) => {
|
||||
// if (messages) {
|
||||
// messages.map(m => { return { key: m.id, ...m } })
|
||||
// }
|
||||
// }
|
||||
|
||||
const handleReceiveMessages = (messages) => {
|
||||
if (messages) {
|
||||
setMessages(messages.items)
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
MessageService.getMessage(id)
|
||||
.then(handleReceiveMessages)
|
||||
.catch(error => console.error(error))
|
||||
let unSubscribeMessagesHub = Subscribe('ReceiveMessages', `well_${id}`, handleReceiveMessages)
|
||||
return () => {
|
||||
unSubscribeMessagesHub()
|
||||
}
|
||||
}, [id]);
|
||||
|
||||
// let updateMessagesTable = async () => {
|
||||
// setLoader(true)
|
||||
// try {
|
||||
// let newMessages = await MessageService.getMessage(1, 0, 32, [], '2020-01-01T00:00:00', '2025-01-01T00:00:00')
|
||||
// setMessages(newMessages.items)
|
||||
// } catch (e) {
|
||||
// console.error(`${e.message}`);
|
||||
// }
|
||||
// setLoader(false)
|
||||
// }
|
||||
|
||||
// useEffect(() => {
|
||||
// updateMessagesTable()
|
||||
// }, [])
|
||||
|
||||
return (
|
||||
<>
|
||||
<h2>Сообщения</h2>
|
||||
<hr />
|
||||
<Table
|
||||
columns={columns}
|
||||
dataSource={messages}
|
||||
rowClassName={(record) => `event_message_${record.categoryId}`}
|
||||
size={'small'}
|
||||
pagination={{ pageSize: 10 }}
|
||||
rowKey={(record) => record.id}
|
||||
/>
|
||||
{loader && <Loader />}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
// TODO Стили для отсортированных страниц
|
@ -1,4 +1,4 @@
|
||||
import React, { useState } from "react";
|
||||
import React from "react"; // , { useState }
|
||||
import {
|
||||
DatePicker,
|
||||
Radio,
|
||||
@ -6,18 +6,19 @@ import {
|
||||
Button,
|
||||
Select,
|
||||
Checkbox,
|
||||
Pagination
|
||||
} from 'antd';
|
||||
import 'moment/locale/ru';
|
||||
import locale from 'antd/lib/locale/ru_RU';
|
||||
|
||||
import moment from "moment";
|
||||
|
||||
const { RangePicker } = DatePicker;
|
||||
const { Option } = Select;
|
||||
|
||||
|
||||
|
||||
|
||||
// Расширение файла для составленного рапорта
|
||||
const ExeptionFileChecker = () => {
|
||||
const ExceptionFileChecker = () => {
|
||||
const [value, setValue] = React.useState(1);
|
||||
const onChange = e => {
|
||||
console.log('radio checked', e.target.value);
|
||||
@ -33,12 +34,18 @@ const ExeptionFileChecker = () => {
|
||||
|
||||
// Выбор периода времени
|
||||
const PeriodOfTime = () => {
|
||||
const [startDate, setStartDate] = useState(new Date());
|
||||
function disabledDate(current) {
|
||||
return current && current < moment().subtract(3, 'days'); // Наверно, не надо)
|
||||
}
|
||||
// const [startDate, setStartDate] = useState(new Date());
|
||||
|
||||
|
||||
return (
|
||||
<ConfigProvider locale={locale}>
|
||||
<ConfigProvider locale={locale}
|
||||
disabledDate>
|
||||
<RangePicker
|
||||
showTime
|
||||
disabledDate={disabledDate}
|
||||
/>
|
||||
</ConfigProvider>
|
||||
)
|
||||
@ -64,7 +71,9 @@ const GraphicStep = () => {
|
||||
}
|
||||
|
||||
return (
|
||||
<Select defaultValue="1 minute" style={{ width: 100 }} onChange={handleChange}>
|
||||
<Select defaultValue="1 minute"
|
||||
style={{ width: 100 }}
|
||||
onChange={handleChange}>
|
||||
<Option value="1 minute">1 минута</Option>
|
||||
<Option value="1 day">1 день</Option>
|
||||
<Option value="1 week">1 неделя</Option>
|
||||
@ -81,14 +90,19 @@ export default function Report(props) {
|
||||
<PeriodOfTime />
|
||||
<h3>Шаг графиков</h3>
|
||||
<GraphicStep />
|
||||
{/* <h3>Содержимое отчёта</h3>
|
||||
<ContentSelection /> */}
|
||||
<h3>Содержимое отчёта</h3>
|
||||
<ContentSelection />
|
||||
<div></div>
|
||||
<ExeptionFileChecker />
|
||||
<ExceptionFileChecker />
|
||||
<p>*Предполагаемое колличество страниц</p>
|
||||
<hr />
|
||||
<div>
|
||||
<Button type="primary" style={{ display: 'flex', alignItems: 'center', padding: '20px', borderRadius: '5px' }}>Получить рапорт</Button>
|
||||
<Button type="primary" style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
padding: '20px',
|
||||
borderRadius: '5px'
|
||||
}}>Получить рапорт</Button>
|
||||
</div>
|
||||
|
||||
</>
|
||||
|
@ -3,12 +3,18 @@ import { Row, Col, Select, } from 'antd'
|
||||
import { ChartTimeOnline } from '../components/charts/ChartTimeOnline'
|
||||
import { ChartTimeOnlineFooter } from '../components/ChartTimeOnlineFooter'
|
||||
import { CustomColumn } from '../components/CustomColumn'
|
||||
import { ModeDisplay } from '../components/ModeDisplay';
|
||||
import { Display } from '../components/Display';
|
||||
import { UserOfWells } from '../components/UserOfWells'
|
||||
import { ModeDisplay } from '../components/ModeDisplay'
|
||||
import { Display } from '../components/Display'
|
||||
import moment from 'moment'
|
||||
import { Table } from 'antd';
|
||||
import { MessageService } from '../services/api'
|
||||
import Loader from '../components/Loader'
|
||||
|
||||
import { useParams } from 'react-router-dom'
|
||||
import { Subscribe } from '../services/signalr'
|
||||
import { DataService } from '../services/api'
|
||||
import '../styles/message_telemetry.css'
|
||||
|
||||
const { Option } = Select
|
||||
|
||||
@ -28,7 +34,7 @@ const blockSpeedGroup = {
|
||||
}
|
||||
|
||||
const pressureGroup = {
|
||||
label:"Давтение",
|
||||
label: "Давление",
|
||||
yDisplay: false,
|
||||
linePv: { label: "pressure", units: 'атм', xAccessorName: "pressure", yAccessorName: "date", color: '#c00' },
|
||||
lineSp: { label: "pressureSp", units: 'атм', xAccessorName: "pressureSp", yAccessorName: "date", color: '#c00' },
|
||||
@ -103,10 +109,48 @@ export const Column = ({lineGroup, data, interval})=>{
|
||||
</>)
|
||||
}
|
||||
|
||||
// Словарь категорий для строк таблицы
|
||||
const categoryDictionary = {
|
||||
1: { title: 'Авария' },
|
||||
2: { title: 'Предупреждение' },
|
||||
3: { title: 'Информация' },
|
||||
}
|
||||
|
||||
// Конфигурация таблицы
|
||||
const columns = [
|
||||
{
|
||||
title: 'Дата',
|
||||
dataIndex: 'date',
|
||||
render: (item) => moment(item).format('DD MMM YYYY, HH:MM:ss'),
|
||||
sorter: (a, b) => new Date(b.date) - new Date(a.date),
|
||||
sortDirections: ['descend', 'ascend'],
|
||||
},
|
||||
{
|
||||
title: 'Категория',
|
||||
dataIndex: 'categoryId',
|
||||
render: (_, item) => categoryDictionary[item.categoryId].title,
|
||||
style: (_, item) => categoryDictionary[item.categoryId].style,
|
||||
sorter: (a, b) => a.categoryId - b.categoryId,
|
||||
sortDirections: ['descend', 'ascend'],
|
||||
},
|
||||
{
|
||||
title: 'Сообщение',
|
||||
dataIndex: 'message',
|
||||
onFilter: (value, record) => record.name.indexOf(value) === 0,
|
||||
},
|
||||
{
|
||||
title: 'Пользователь',
|
||||
dataIndex: 'user',
|
||||
},
|
||||
];
|
||||
|
||||
export default function TelemetryView(props) {
|
||||
let { id } = useParams();
|
||||
let { id } = useParams()
|
||||
const [saubData, setSaubData] = useState([])
|
||||
const [chartInterval, setchartInterval] = useState(600)
|
||||
const [chartInterval, setChartInterval] = useState(600)
|
||||
const [messages, setMessages] = useState([])
|
||||
|
||||
const [loader] = useState(false) // , setLoader
|
||||
|
||||
const handleReceiveDataSaub = (data) => {
|
||||
if (data) {
|
||||
@ -114,12 +158,27 @@ export default function TelemetryView(props){
|
||||
}
|
||||
}
|
||||
|
||||
const handleReceiveMessages = (messages) => {
|
||||
if (messages) {
|
||||
setMessages(messages.items.splice(0, 3))
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
DataService.getData(id)
|
||||
.then(handleReceiveDataSaub)
|
||||
.catch(error => console.error(error))
|
||||
|
||||
return Subscribe('ReceiveDataSaub', `well_${id}`, handleReceiveDataSaub)
|
||||
MessageService.getMessage(id)
|
||||
.then(handleReceiveMessages)
|
||||
.catch(error => console.error(error))
|
||||
|
||||
let unSubscribeDataSaubHub = Subscribe('ReceiveDataSaub', `well_${id}`, handleReceiveDataSaub)
|
||||
let unSubscribeMessagesHub = Subscribe('ReceiveMessages', `well_${id}`, handleReceiveMessages)
|
||||
return () => {
|
||||
unSubscribeDataSaubHub()
|
||||
unSubscribeMessagesHub()
|
||||
}
|
||||
}, [id]);
|
||||
|
||||
const colSpan = 24 / (paramsGroups.length)
|
||||
@ -132,7 +191,7 @@ export default function TelemetryView(props){
|
||||
</Col>
|
||||
<Col>
|
||||
Интервал:
|
||||
<Select defaultValue="600" onChange={setchartInterval}>
|
||||
<Select defaultValue="600" onChange={setChartInterval}>
|
||||
<Option value='600'>10 минут</Option>
|
||||
<Option value='1800'>30 минут</Option>
|
||||
<Option value='3600'>1 час</Option>
|
||||
@ -140,6 +199,10 @@ export default function TelemetryView(props){
|
||||
<Option value='86400'>1 день</Option>
|
||||
</Select>
|
||||
</Col>
|
||||
<span style={{ flexGrow: 1 }}></span>
|
||||
<Col>
|
||||
<UserOfWells data={saubData}/>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row>
|
||||
<Col span={2} >
|
||||
@ -154,5 +217,15 @@ export default function TelemetryView(props){
|
||||
</Row>
|
||||
</Col>
|
||||
</Row>
|
||||
<Table
|
||||
showHeader={false}
|
||||
columns={columns}
|
||||
dataSource={messages}
|
||||
rowClassName={(record) => `event_message_${record.categoryId}`}
|
||||
size={'small'}
|
||||
pagination={false}
|
||||
rowKey={(record) => record.id}
|
||||
/>
|
||||
{loader && <Loader />}
|
||||
</div>)
|
||||
}
|
@ -68,7 +68,7 @@ export default function Wells(props){
|
||||
try{
|
||||
let newWells = (await WellService.getWells()).map(w =>{return {key:w.id, ...w}})
|
||||
let wellsTree = groupBy(newWells, 'deposit', 'cluster', 'caption')
|
||||
console.log(wellsTree)
|
||||
console.log(Wells.wellsTree)
|
||||
setWells( newWells )
|
||||
setWellsTree(wellsTree)
|
||||
}
|
||||
@ -80,10 +80,6 @@ export default function Wells(props){
|
||||
|
||||
useEffect(()=>{updateWellsList()}, [])
|
||||
|
||||
const onChange = (value) =>{
|
||||
;
|
||||
}
|
||||
|
||||
return(<>
|
||||
<h2>Скважины</h2>
|
||||
<Table
|
||||
@ -94,13 +90,6 @@ export default function Wells(props){
|
||||
onClick: event => {history.push(`well/${record.id}`)},
|
||||
};
|
||||
}}/>
|
||||
{/* <TreeSelect
|
||||
style={{ width: '100%' }}
|
||||
placeholder="Please select"
|
||||
treeData={wellsTree}
|
||||
treeDefaultExpandAll
|
||||
onChange={onChange}
|
||||
/> */}
|
||||
{loader&&<Loader/>}
|
||||
</>)
|
||||
}
|
@ -9,20 +9,58 @@ const ConnectionOptions = {
|
||||
}
|
||||
|
||||
const Connection = new HubConnectionBuilder()
|
||||
.withUrl(`http://localhost:5000/hubs/telemetry`, ConnectionOptions)
|
||||
.withUrl(`http://192.168.1.70:5000/hubs/telemetry`, ConnectionOptions)
|
||||
.withAutomaticReconnect()
|
||||
.build();
|
||||
|
||||
const GetConnectionAsync = async ()=>{
|
||||
if(Connection.state !== HubConnectionState.Connected)
|
||||
await Connection.start()
|
||||
let connectionPromise: Promise<void>
|
||||
|
||||
// const GetConnectionAsync = async () => {
|
||||
// try {
|
||||
// await Connection.start();
|
||||
// console.log(Connection.state);
|
||||
// } catch (err) {
|
||||
// console.log(err);
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
const GetConnectionAsync = async () => {
|
||||
if (Connection.state === HubConnectionState.Disconnected)
|
||||
connectionPromise = Connection.start()
|
||||
|
||||
if (Connection.state !== HubConnectionState.Connected)
|
||||
await connectionPromise
|
||||
|
||||
if(Connection.state === HubConnectionState.Connected)
|
||||
return Connection
|
||||
else
|
||||
throw new Error('Can`t connect to websocket')
|
||||
}
|
||||
|
||||
|
||||
// const GetConnectionAsync = async () => {
|
||||
// if (Connection.state === HubConnectionState.Disconnected)
|
||||
// connectionPromise = Connection.start()
|
||||
|
||||
// if (Connection.state !== HubConnectionState.Connected)
|
||||
// await connectionPromise
|
||||
|
||||
// return Connection
|
||||
// }
|
||||
|
||||
// const GetConnectionAsync = async ()=>{
|
||||
// if(Connection.state === HubConnectionState.Disconnected)
|
||||
// await Connection.start()
|
||||
|
||||
// if(Connection.state === HubConnectionState.Connecting)
|
||||
// await Connection.onreconnected
|
||||
|
||||
// // if(Connection.state === HubConnectionState.Disconnecting)
|
||||
// // await Connection.onclose
|
||||
|
||||
// // if(Connection.state === HubConnectionState.Connected)
|
||||
// console.log(Connection.state)
|
||||
// return Connection
|
||||
// }
|
||||
|
||||
type handlerFunction = (...args: any[]) => void;
|
||||
|
||||
type cleanFunction = (...args: any[]) => void;
|
||||
@ -38,11 +76,10 @@ const Subscribe = (
|
||||
methodName: string,
|
||||
groupName: string = '',
|
||||
handler: handlerFunction ):cleanFunction=>{
|
||||
|
||||
GetConnectionAsync().then(async connection => {
|
||||
if(groupName)
|
||||
await connection.send('AddToGroup', groupName)
|
||||
connection.on(methodName, handler)
|
||||
await Connection.send('AddToGroup', groupName)
|
||||
Connection.on(methodName, handler)
|
||||
})
|
||||
|
||||
if(groupName)
|
||||
|
@ -88,7 +88,7 @@
|
||||
text-align: right;
|
||||
justify-content: flex-end;
|
||||
align-items:baseline;
|
||||
margin: 1px 1rem 1px 1rem;
|
||||
margin: 1px 1rem;
|
||||
flex: auto;
|
||||
}
|
||||
|
||||
|
35
src/styles/message.css
Normal file
35
src/styles/message.css
Normal file
@ -0,0 +1,35 @@
|
||||
.event_message {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.event_message_off {
|
||||
color: #505050;
|
||||
background: #b5b5b5;
|
||||
}
|
||||
|
||||
.event_message_disabled {
|
||||
color: #656565;
|
||||
background: #b5b5b5;
|
||||
}
|
||||
|
||||
.event_message_1 {
|
||||
color: white;
|
||||
background: #ff3737;
|
||||
}
|
||||
|
||||
.event_message_2 {
|
||||
color: black;
|
||||
background: gold;
|
||||
}
|
||||
|
||||
.event_message_3,
|
||||
.event_message_3:hover {
|
||||
color: #c0c0c0;
|
||||
background: #505060;
|
||||
height: 1px;
|
||||
}
|
||||
|
||||
td.ant-table-column-sort {
|
||||
color: black;
|
||||
background-color: rgb(221, 247, 221);
|
||||
}
|
30
src/styles/message_telemetry.css
Normal file
30
src/styles/message_telemetry.css
Normal file
@ -0,0 +1,30 @@
|
||||
.event_message {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.event_message_off {
|
||||
color: #505050;
|
||||
background: #b5b5b5;
|
||||
}
|
||||
|
||||
.event_message_disabled {
|
||||
color: #656565;
|
||||
background: #b5b5b5;
|
||||
}
|
||||
|
||||
.event_message_1 {
|
||||
color: white;
|
||||
background: #ff3737;
|
||||
}
|
||||
|
||||
.event_message_2 {
|
||||
color: black;
|
||||
background: gold;
|
||||
}
|
||||
|
||||
.event_message_3,
|
||||
.event_message_3:hover {
|
||||
color: #c0c0c0;
|
||||
background: #505060;
|
||||
height: 1px;
|
||||
}
|
Loading…
Reference in New Issue
Block a user