перед выходными:)

This commit is contained in:
Фролов 2021-04-09 17:59:35 +05:00
parent d4d51941d7
commit ea426b16ef
18 changed files with 338 additions and 29 deletions

16
package-lock.json generated
View File

@ -4189,6 +4189,16 @@
"resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz",
"integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw=="
},
"chart.js": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/chart.js/-/chart.js-3.0.2.tgz",
"integrity": "sha512-DR0GmFSlxcFJp/w//ZmbxSduAkH/AqwxoiZxK97KHnWZf6gvsKWS3160WvNMMHYvzW9OXqGWjPjVh1Qu+xDabg=="
},
"chartjs-adapter-date-fns": {
"version": "1.1.0-beta.1",
"resolved": "https://registry.npmjs.org/chartjs-adapter-date-fns/-/chartjs-adapter-date-fns-1.1.0-beta.1.tgz",
"integrity": "sha512-VNhuZ86kXKOwh61CyRLP7hoFqAR7+gjnrtf7KYLt/Wfh3jIQs14l1h+nagtQoFaabIYIo6UD5/jJb2/J6zOPcw=="
},
"check-types": {
"version": "11.1.2",
"resolved": "https://registry.npmjs.org/check-types/-/check-types-11.1.2.tgz",
@ -5060,9 +5070,9 @@
}
},
"date-fns": {
"version": "2.19.0",
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.19.0.tgz",
"integrity": "sha512-X3bf2iTPgCAQp9wvjOQytnf5vO5rESYRXlPIVcgSbtT5OTScPcsf9eZU+B/YIkKAtYr5WeCii58BgATrNitlWg=="
"version": "2.20.0",
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.20.0.tgz",
"integrity": "sha512-nmA7y6aDH5+fknfJ0G77HQzUSfTPpq4ifq+c9blP9d+X9zs3kNjxC+t3pcbBMGTp262a6PJB3RVjLlxIgoMI+Q=="
},
"debug": {
"version": "4.3.1",

View File

@ -9,7 +9,10 @@
"@testing-library/react": "^11.2.6",
"@testing-library/user-event": "^12.8.3",
"antd": "^4.15.0",
"chart.js": "^3.0.2",
"chartjs-adapter-date-fns": "^1.1.0-beta.1",
"craco-less": "^1.17.1",
"date-fns": "^2.20.0",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-router-dom": "^5.2.0",

View File

@ -3,30 +3,15 @@ import React /*, { useContext, createContext, useState }*/ from "react"
import {
BrowserRouter as Router,
Switch,
Route,
Redirect
} from "react-router-dom"
Route} from "react-router-dom"
import Login from './pages/Login'
import Main from './pages/Main'
import {OpenAPI} from './services/api'
import { OpenAPI } from './services/api'
import { PrivateRoute } from './components/PrivateRoute'
OpenAPI.BASE = 'http://localhost:3000'
OpenAPI.TOKEN = localStorage['token']
function PrivateRoute({ children, ...rest }) {
let token = localStorage['token']
return (
<Route
{...rest}
render={({ location }) =>
token
? (children)
: (<Redirect to={{pathname: "/login", state: { from: location }}}/>)
}
/>
);
}
export default function App() {
return (
<Router>

View File

@ -0,0 +1,16 @@
import React /*, { useContext, createContext, useState }*/ from "react";
import {
Route,
Redirect
} from "react-router-dom";
export function PrivateRoute({ children, ...rest }) {
let token = localStorage['token'];
return (
<Route
{...rest}
render={({ location }) => token
? (children)
: (<Redirect to={{ pathname: "/login", state: { from: location } }} />)} />
);
}

View File

@ -0,0 +1,69 @@
import { Chart, TimeScale, Legend, LineController, LineElement, PointElement } from 'chart.js'
import 'chartjs-adapter-date-fns';
import React, { useEffect, useRef} from 'react';
Chart.register( TimeScale, LineController, LineElement, PointElement, Legend );
const options = {
//showLine :true,
indexAxis:'y',
//maintainAspectRatio: false,
//responsive:false,
scales: {
y:{
type: 'time',
time: {
unit: 'second',
displayFormats: {
millisecond: 'HH:mm:ss.SSS',
second: 'HH:mm:ss',
minute: 'HH:mm:ss',
hour: 'dd HH:mm:ss',
day: 'MM.dd HH:mm',
week: 'yy.MM.dd HH:mm',
month: 'yyyy.MM.dd',
quarter: 'yyyy.MM.dd',
year: 'yyyy.MM',
},
},
},
},
}
const data= {
datasets: [{
label: 'Torque',
data: [
{x: 10, y: '2021-04-09T17:50:34.021679+05:00'},
{x: 15, y: '2021-04-09T17:50:33.021679+05:00'},
{x: 20, y: '2021-04-09T17:50:32.021679+05:00'}],
boderColor: 'rgba(0, 99, 132, 1)',
backgroundColor: 'rgba(0, 99, 132, 1)',
},
{
label: 'Pressure',
data: [
{x: 11, y: '2021-04-09T17:50:34.021679+05:00'},
{x: 14, y: '2021-04-09T17:50:33.021679+05:00'},
{x: 21, y: '2021-04-09T17:50:32.021679+05:00'}],
boderColor: 'rgba(255, 99, 132, 1)',
backgroundColor: 'rgba(255, 99, 132, 1)',
borderDash: [5, 5],
}
]
}
export function ChartSaubDataOnline(props){
const chartRef = useRef(null)
useEffect(()=>{
let chart = new Chart(chartRef.current, {
type: 'line',
data,
options})
//chart.canvas.parentNode.style.height = '128px';
return _ => chart.destroy()
},[])
return(<canvas ref={chartRef} />)
}

View File

@ -32,13 +32,14 @@ export default function Login() {
try{
let user = await login(formData)
setUser(user)
setLoader(false)
history.push('well')
}catch(e){
if(e.status === 400)
openNotificationError(e.message)
console.error(`Error ${e}`)
setLoader(false)
}
setLoader(false)
}
return (

View File

@ -1,6 +1,30 @@
import React, { useState, useEffect} from 'react';
import {ChartSaubDataOnline} from '../components/charts/ChartSaubDataOnline'
import {useParams} from 'react-router-dom'
import {Subscribe} from '../services/signalr'
import {DataService} from '../services/api'
export default function Well(props){
let { id } = useParams();
return(<div>Well id: {id}</div>)
const [saubData, setSaubData] = useState([])
const handleReceiveDataSaub = (data)=>{
setSaubData(pre => [...pre, ...data].slice(-1024))
}
useEffect( ()=> {
DataService.get(id)
.then(handleReceiveDataSaub)
.catch(error=>console.error(error))
return Subscribe('ReceiveDataSaub', `well_${id}`, handleReceiveDataSaub)
},
[id]);
return(<div>
Well id: {id}; points count: {saubData.length}
<ChartSaubDataOnline/>
</div>)
}

View File

@ -35,8 +35,8 @@ export default function Wells(props){
let updateWellsList = async () => {
setLoader(true)
try{
setWells( await WellService.get())
var newWells = (await WellService.get()).map(w =>{return {key:w.id, ...w}})
setWells( newWells )
}
catch(e){
console.error(`${e.message}`);

View File

@ -1 +1 @@
npx openapi -i http://localhost:5000/swagger/v1/swagger.json -o src/services/openapi
npx openapi -i http://localhost:5000/swagger/v1/swagger.json -o src/services/api

View File

@ -5,8 +5,13 @@ export { ApiError } from './core/ApiError';
export { OpenAPI } from './core/OpenAPI';
export type { AuthDto } from './models/AuthDto';
export type { DataSaubBaseDto } from './models/DataSaubBaseDto';
export type { TelemetryDataDto } from './models/TelemetryDataDto';
export type { TelemetryInfoDto } from './models/TelemetryInfoDto';
export type { UserTokenDto } from './models/UserTokenDto';
export type { WellDto } from './models/WellDto';
export { AuthService } from './services/AuthService';
export { DataService } from './services/DataService';
export { TelemetryService } from './services/TelemetryService';
export { WellService } from './services/WellService';

View File

@ -0,0 +1,32 @@
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
export type DataSaubBaseDto = {
date?: string;
mode?: number | null;
wellDepth?: number | null;
bitDepth?: number | null;
blockHeight?: number | null;
blockSpeed?: number | null;
blockSpeedSp?: number | null;
pressure?: number | null;
pressureIdle?: number | null;
pressureSp?: number | null;
pressureDeltaLimitMax?: number | null;
axialLoad?: number | null;
axialLoadSp?: number | null;
axialLoadLimitMax?: number | null;
hookWeight?: number | null;
hookWeightIdle?: number | null;
hookWeightLimitMin?: number | null;
hookWeightLimitMax?: number | null;
rotorTorque?: number | null;
rotorTorqueIdle?: number | null;
rotorTorqueSp?: number | null;
rotorTorqueLimitMax?: number | null;
rotorSpeed?: number | null;
flow?: number | null;
flowIdle?: number | null;
flowDeltaLimitMax?: number | null;
}

View File

@ -0,0 +1,12 @@
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
import type { DataSaubBaseDto } from './DataSaubBaseDto';
export type TelemetryDataDto = {
date?: string;
hmiVersion?: string | null;
userName?: string | null;
dataSaub?: Array<DataSaubBaseDto> | null;
}

View File

@ -0,0 +1,9 @@
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
export type TelemetryInfoDto = {
caption?: string | null;
cluster?: string | null;
deposit?: string | null;
}

View File

@ -3,9 +3,9 @@
/* eslint-disable */
export type WellDto = {
id?: number;
caption?: string | null;
cluster?: string | null;
deposit?: string | null;
id?: number;
lastData?: any;
}

View File

@ -0,0 +1,25 @@
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
import type { DataSaubBaseDto } from '../models/DataSaubBaseDto';
import { request as __request } from '../core/request';
export class DataService {
/**
* Возвращает данные САУБ по скважине
* @param wellId
* @returns DataSaubBaseDto Success
* @throws ApiError
*/
public static async get(
wellId: number,
): Promise<Array<DataSaubBaseDto>> {
const result = await __request({
method: 'GET',
path: `/api/well/${wellId}/data`,
});
return result.body;
}
}

View File

@ -0,0 +1,48 @@
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
import type { TelemetryDataDto } from '../models/TelemetryDataDto';
import type { TelemetryInfoDto } from '../models/TelemetryInfoDto';
import { request as __request } from '../core/request';
export class TelemetryService {
/**
* Принимает общую информацию по скважине
* @param uid Уникальный идентификатор отправителя
* @param requestBody нформация об отправителе
* @returns any Success
* @throws ApiError
*/
public static async info(
uid: string,
requestBody?: TelemetryInfoDto,
): Promise<any> {
const result = await __request({
method: 'POST',
path: `/api/telemetry/${uid}/info`,
body: requestBody,
});
return result.body;
}
/**
* Принимает данные от разных систем по скважине
* @param uid Уникальный идентификатор отправителя
* @param requestBody Данные
* @returns any Success
* @throws ApiError
*/
public static async data(
uid: string,
requestBody?: TelemetryDataDto,
): Promise<any> {
const result = await __request({
method: 'POST',
path: `/api/telemetry/${uid}/data`,
body: requestBody,
});
return result.body;
}
}

View File

@ -13,7 +13,7 @@ export class WellService {
public static async get(): Promise<Array<WellDto>> {
const result = await __request({
method: 'GET',
path: `/api/Well`,
path: `/api/well`,
});
return result.body;
}

View File

@ -0,0 +1,70 @@
import { HubConnectionBuilder, HubConnectionState } from '@microsoft/signalr';
// SignalR js api:
//https://docs.microsoft.com/ru-ru/javascript/api/@aspnet/signalr/?view=signalr-js-latest
const ConnectionOptions = {
accessTokenFactory: () => localStorage['token'],
//transport:1,
}
const Connection = new HubConnectionBuilder()
.withUrl(`http://localhost:5000/hubs/telemetry`, ConnectionOptions)
.withAutomaticReconnect()
.build();
const GetConnectionAsync = async ()=>{
if(Connection.state !== HubConnectionState.Connected)
await Connection.start()
if(Connection.state === HubConnectionState.Connected)
return Connection
else
throw new Error('Can`t connect to websocket')
}
type handlerFunction = (...args: any[]) => void;
type cleanFunction = (...args: any[]) => void;
/** Subscribe on some SignalR method (topic).
* @example useEffect(() => Subscribe('methodName', `${id}`, handleNewData), [id]);
* @param {string} methodName name of the method
* @param {string} groupName name of the group
* @param {handlerFunction} handler fires when got new data by subscription
* @return {cleanFunction} unsubscribe function for useEffect cleanup.
*/
const Subscribe = (
methodName: string,
groupName: string = '',
handler: handlerFunction ):cleanFunction=>{
GetConnectionAsync().then(async connection => {
if(groupName)
await connection.send('AddToGroup', groupName)
connection.on(methodName, handler)
})
if(groupName)
return () => {
Connection.send('RemoveFromGroup', groupName)
.finally(()=>Connection.off(methodName))
}
return () => Connection.off(methodName)
}
/** Invokes some SignalR method.
* @param {string} methodName name of the method
* @param {any[]} args methods arguments
* @return {Promise<void>} Promise
*/
const InvokeAsync = async (methodName:string, ...args:any[]) => {
await GetConnectionAsync()
await Connection.send(methodName, ...args)
}
export {
Subscribe,
InvokeAsync,
GetConnectionAsync,
}