Add Monitoring files in Python and Mui/React

This commit is contained in:
2024-06-29 17:12:58 +02:00
parent 5285f8b26e
commit dbae0a776d
23 changed files with 726 additions and 102 deletions

File diff suppressed because one or more lines are too long

View File

@@ -22,7 +22,7 @@
<title>
Markus Lehr | VR Predictive Maintenance Device.
</title>
<script type="module" crossorigin src="/assets/index-40b528e3.js"></script>
<script type="module" crossorigin src="/assets/index-a894fc13.js"></script>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>

View File

@@ -44,14 +44,17 @@ import { MonitoringList } from "./pages/monitorings/list";
import { MonitoringCreate } from "./pages/monitorings/create";
import { MonitoringEdit } from "./pages/monitorings/edit";
import { VR_API_URL } from './env';
import { MonDatafileList } from "./pages/monitorings/mondatfilelist";
const API_URL = "https://api.fake-rest.refine.dev";
// Test URL
//const MONITORINGS_API_URL = 'http://127.0.0.1:5000/vrpmdvapi/1_0';
const MONITORINGS_API_URL = 'http://127.0.0.1:5000/vrpmdvapi/1_0';
const MONITORINGFILES_API_URL = 'http://127.0.0.1:5000/vrpmdvapi/1_0';
// Embedded URL
const MONITORINGS_API_URL = '/vrpmdvapi/1_0';
//const MONITORINGS_API_URL = '/vrpmdvapi/1_0';
//const MONDATAFILES_API_URL = '/vrpmdvapi/1_0';
// .get(`${API_URL}/exams`)
const MONITORINGSTATUS_API_URL = 'http://127.0.0.1:5000/vrpmdvapi/1_0';
@@ -69,6 +72,7 @@ function App() {
dataProvider={{
default: dataProvider(API_URL),
monitorings: dataProvider(MONITORINGS_API_URL),
monitoringfiles: dataProvider(MONITORINGFILES_API_URL),
}}
notificationProvider={notificationProvider}
authProvider={authProvider}
@@ -79,11 +83,21 @@ function App() {
list: "/monitorings",
create: "/monitorings/create",
edit: "/monitorings/edit/:id",
// show: "/monitorings/datafiles/:id",
meta: {
canDelete: true,
dataProviderName: "monitorings"
},
},
{
name: "monitoringfiles",
list: "/monitoringfiles",
meta: {
canDelete: true,
dataProviderName: "monitoringfiles"
},
},
]}
options={{
syncWithLocation: true,
@@ -122,6 +136,10 @@ function App() {
<Route index element={<MonitoringList />} />
<Route path="create" element={<MonitoringCreate />} />
<Route path="edit/:id" element={<MonitoringEdit />} />
<Route path="datafiles/:id" element={<MonDatafileList />} />
</Route>
<Route path="/monitoringfiles">
<Route index element={<MonDatafileList />} />
</Route>
<Route path="*" element={<ErrorComponent />} />
</Route>

View File

@@ -0,0 +1,190 @@
import React, { useState } from "react";
import { QueryObserverResult, UseQueryOptions } from "@tanstack/react-query";
import warnOnce from "warn-once";
import { useMeta, useList } from "@refinedev/core";
import {
BaseRecord,
GetListResponse,
SuccessErrorNotification,
MetaQuery,
LiveModeProps,
BaseKey,
HttpError,
Prettify,
} from "@refinedev/core";
import { useResource } from "@refinedev/core";
import { pickNotDeprecated } from "@refinedev/core";
import {
useLoadingOvertime,
UseLoadingOvertimeOptionsProps,
UseLoadingOvertimeReturnType,
} from "@refinedev/core";
export type useDownloadReturnType<
TData extends BaseRecord = BaseRecord,
TError extends HttpError = HttpError,
> = {
downloadResult: QueryObserverResult<GetListResponse<TData>, TError>;
// // showId?: BaseKey;
// setShowId: React.Dispatch<React.SetStateAction<BaseKey | undefined>>;
} & UseLoadingOvertimeReturnType;
export type useDownloadProps<
TQueryFnData extends BaseRecord = BaseRecord,
TError extends HttpError = HttpError,
TData extends BaseRecord = TQueryFnData,
> = {
/**
* Resource name for API data interactions
* @default Reads `:resource` from the URL
*/
resource?: string;
/**
* Data item ID for API data interactions
* @default Reads `:id` from the URL
*/
id?: BaseKey;
/**
* react-query's [useQuery](https://tanstack.com/query/v4/docs/reference/useQuery) options
*/
queryOptions?: UseQueryOptions<
GetListResponse<TQueryFnData>,
TError,
GetListResponse<TData>
>;
/**
* Additional meta data to pass to the data provider's `getOne`
*/
// meta?: MetaQuery;
// /**
// * Additional meta data to pass to the data provider's `getOne`
// * @deprecated `metaData` is deprecated with refine@4, refine will pass `meta` instead, however, we still support `metaData` for backward compatibility.
// */
metaData?: MetaQuery;
/**
* Target data provider name for API call to be made
* @default `"default"`
*/
dataProviderName?: string;
} & LiveModeProps &
SuccessErrorNotification<
GetListResponse<TData>,
TError,
Prettify<{ id?: BaseKey } & MetaQuery>
> &
UseLoadingOvertimeOptionsProps;
/**
* `useDownload` hook allows you to fetch the desired record.
* It uses `getList` method as query function from the dataProvider that is
* passed to {@link https://refine.dev/docs/api-reference/core/components/refine-config/ `<Refine>`}.
*
* @see {@link https://refine.dev/docs/api-reference/core/hooks/show/useShow} for more details. => TODO ML: change to own link
*
* @typeParam TQueryFnData - Result data returned by the query function. Extends {@link https://refine.dev/docs/api-reference/core/interfaceReferences#baserecord `BaseRecord`}
* @typeParam TError - Custom error object that extends {@link https://refine.dev/docs/api-reference/core/interfaceReferences#httperror `HttpError`}
* @typeParam TData - Result data returned by the `select` function. Extends {@link https://refine.dev/docs/api-reference/core/interfaceReferences#baserecord `BaseRecord`}. Defaults to `TQueryFnData`
*
*/
export const useDownload = <
TQueryFnData extends BaseRecord = BaseRecord,
TError extends HttpError = HttpError,
TData extends BaseRecord = TQueryFnData,
>({
resource: resourceFromProp,
id,
successNotification,
errorNotification,
//meta,
metaData,
liveMode,
onLiveEvent,
dataProviderName,
queryOptions,
overtimeOptions,
}: useDownloadProps<TQueryFnData, TError, TData> = {}): useDownloadReturnType<
TData,
TError
> => {
const {
resource,
id: idFromRoute,
identifier,
} = useResource(resourceFromProp);
const { identifier: inferredIdentifier } = useResource();
const getMeta = useMeta();
const getDefaultId = () => {
const idFromPropsOrRoute = id ?? idFromRoute;
if (resourceFromProp && resourceFromProp !== inferredIdentifier) {
return id;
}
return idFromPropsOrRoute;
};
const defaultId = getDefaultId();
//const [showId, setShowId] = useState<BaseKey | undefined>(defaultId);
// React.useEffect(() => {
// setShowId(defaultId);
// }, [defaultId]);
// const combinedMeta = getMeta({
// resource,
// meta: pickNotDeprecated(meta, metaData),
// });
warnOnce(
Boolean(resourceFromProp) && !id,
`[useShow]: resource: "${identifier}", id: ${id} \n\nIf you don't use the \`setShowId\` method to set the \`showId\`, you should pass the \`id\` prop to \`useShow\`. Otherwise, \`useShow\` will not be able to infer the \`id\` from the current URL. \n\nSee https://refine.dev/docs/api-reference/core/hooks/show/useShow/#resource`,
);
// const downloadResult = () : QueryObserverResult<GetListResponse<TData>, TError> => {
// const res = useList<TQueryFnData, TError, TData>({
// resource: identifier,
// successNotification,
// errorNotification,
// metaData: metaData,
// liveMode,
// onLiveEvent,
// dataProviderName,
// });
// return { res };
// };
const downloadResult = useList<TQueryFnData, TError, TData>({
resource: identifier,
// queryOptions: {
// enabled: showId !== undefined,
// ...queryOptions,
// },
successNotification,
errorNotification,
metaData: metaData,
liveMode,
onLiveEvent,
dataProviderName,
});
const { elapsedTime } = useLoadingOvertime({
isLoading: downloadResult.isFetching,
interval: overtimeOptions?.interval,
onInterval: overtimeOptions?.onInterval,
});
return {
downloadResult,
overtime: { elapsedTime },
};
};

View File

@@ -0,0 +1,24 @@
import {
useModal,
} from "@refinedev/core";
import Button from "@refinedev/mui";
export const DownloadButton = () => {
const { visible, show, close } = useModal();
return (
<Button>
Download
</Button>
);
};
// {visible && (
// <YourModalComponent>
// <p>Dummy Modal Content</p>
// <button onClick={close}>Close Modal</button>
// </YourModalComponent>
// )}

View File

@@ -147,6 +147,7 @@ export const MonitoringList = () => {
return (
<>
<EditButton hideText recordItemId={row.id} />
<ShowButton hideText recordItemId={row.id} />
<DeleteButton hideText recordItemId={row.id} />
</>
);

View File

@@ -0,0 +1,261 @@
import { DataGrid, GridColDef, GridRowSelectionModel } from "@mui/x-data-grid";
import { Download, Delete } from "@mui/icons-material";
import {
DateField,
DeleteButton,
EditButton,
List,
MarkdownField,
NumberField,
ShowButton,
useDataGrid,
} from "@refinedev/mui";
import React from "react";
import { IMonitoring, IMonitoringFile } from "./monitorings.types";
//import { MonitoringStatus } from "./status/monitoringstatus";
// import { MonitoringStatusEditChip } from "./status/monitoringstatusedit";
import { useUpdate } from "@refinedev/core";
import Button from "@mui/material/Button/Button";
import Stack from "@mui/material/Stack/Stack";
export const MonDatafileList = () => {
const dataProvider = "monitoringfiles";
//const dataProvider = "monitorings";
const { dataGridProps } = useDataGrid<IMonitoringFile>({
syncWithLocation: true,
dataProviderName: dataProvider,
pagination: {
mode: "client",
pageSize: 10,
},
});
const [selectedRowKeys, setSelectedRowKeys] = React.useState<GridRowSelectionModel>([]);
const hasSelected = selectedRowKeys.length > 0;
// const updateSelectedItems = () => {
// mutate(
// {
// resource: "posts",
// ids: selectedRowKeys.map(String),
// values: {
// status: "approved",
// },
// },
// {
// onSuccess: () => {
// setSelectedRowKeys([]);
// },
// },
// );
// };
// const { data: categoryData, isLoading: categoryIsLoading } = useMany({
// resource: "categories",
// ids:
// dataGridProps?.rows
// ?.map((item: any) => item?.category?.id)
// .filter(Boolean) ?? [],
// queryOptions: {
// enabled: !!dataGridProps?.rows,
// },
// });
const { mutate } = useUpdate();
//dataGridProps.disableRowSelectionOnClick: true;
const columns = React.useMemo<GridColDef<IMonitoringFile>[]>(
() => [
{
field: "name",
flex: 1,
headerName: "Name",
type:"string",
minWidth: 250,
},
{
field: "timestamp",
flex: 1,
headerName: "Timestamp",
minWidth: 30,
renderCell: function render({ value }) {
let d = new Date();
const unixtime = new Date(value);
//return <DateField value={unixtime} />;
return <DateField value={unixtime} format={'DD-MM-YYYY hh:mm:ss A'}/>;
},
},
{
field: "samplerate",
flex: 0.3,
headerName: "Samplerate/Hz",
renderCell: function render({ row }) {
return (
<NumberField
value={row.samplerate}
options={{
minimumIntegerDigits: 1,
minimumFractionDigits:0,
maximumFractionDigits: 0,
}}
/>
);
},
minWidth: 120,
},
{
field: "sampleperiod",
flex: 0.3,
headerName: "Period/s",
renderCell: function render({ row }) {
return (
<NumberField
value={row.sampleperiod}
options={{
minimumIntegerDigits: 1,
minimumFractionDigits:1,
maximumFractionDigits: 3,
}}
/>
);
},
minWidth: 120,
},
{
field: "downtime",
flex: 0.3,
headerName: "Downtime/s",
renderCell: function render({ row }) {
return (
<NumberField
value={row.downtime}
options={{
minimumIntegerDigits: 1,
minimumFractionDigits:1,
maximumFractionDigits: 3,
}}
/>
);
},
minWidth: 120,
},
{
field: "actions",
headerName: "Actions",
sortable: false,
renderCell: function render({ row }) {
return (
<>
<ShowButton hideText recordItemId={row.id} />
<DeleteButton hideText recordItemId={row.id} />
</>
);
},
align: "center",
headerAlign: "center",
minWidth: 80,
},
],
[]
);
return (
<List
cardProps={{ sx: { paddingX: { xs: 2, md: 0 } } }}
cardHeaderProps={{
subheader: hasSelected && (
<Stack direction="row">
<Button
startIcon={<Download color="success" />}
onClick={() => updateSelectedItems("approved")}
>
Download
</Button>
<Button
startIcon={<Delete color="error" />}
onClick={() => updateSelectedItems("rejected")}
>
{t("buttons.rejectAll")}
</Button>
</Stack>
),
}}
>
<DataGrid
{...dataGridProps}
columns={columns}
autoHeight
checkboxSelection
onRowSelectionModelChange={(newSelectionModel) => {
setSelectedRowKeys(newSelectionModel);
}}
rowSelectionModel={selectedRowKeys}
/>
</List>
);
};
/*
<List
cardProps={{ sx: { paddingX: { xs: 2, md: 0 } } }}
cardHeaderProps={{
subheader: hasSelected && (
<Stack direction="row">
<Button
startIcon={<Download color="success" />}
onClick={() => updateSelectedItems("approved")}
>
{t("buttons.acceptAll")}
</Button>
<Button
startIcon={<Delete color="error" />}
onClick={() => updateSelectedItems("rejected")}
>
{t("buttons.rejectAll")}
</Button>
</Stack>
),
}}
>
<DataGrid
{...dataGridProps}
columns={columns}
autoHeight
checkboxSelection
onRowSelectionModelChange={(newSelectionModel) => {
setSelectedRowKeys(newSelectionModel);
}}
rowSelectionModel={selectedRowKeys}
/>
</List>
);
*/
// onRowSelectionModelChange={(newSelectionModel) => {
// setSelectedRowKeys(newSelectionModel);
// }}
// rowSelectionModel={selectedRowKeys}
// {
// field: "monstate",
// headerName: t("orders.fields.status"),
// width: 124,
// renderCell: function render({ row }) {
// return <MonitoringStatus status={row.monstate} />;
// },
// },
// renderEditCell: function render({ row }) {
// return <MonitoringStatusEditChip value={row.status} />;
// },

View File

@@ -12,5 +12,15 @@ export interface IMonitoring {
status: "off" | "started" | "stopped";//MonitoringStatus;
}
export interface IMonitoringFile {
id: string;
name: string;
samplerate: number;
sampleperiod: number;
downtime: number;
timestamp: string;
}
//string;

Binary file not shown.

View File

@@ -1 +1 @@
[{"no": 0, "id": "cb842c0d-51fc-433f-baf4-0f45af0fc0c5", "created_at": "06/14/2024, 16:30", "name": "MM1", "samplerate": 500, "sampleperiod": 1, "downtime": 10, "owner": "ML", "status": "stopped"}]
[{"no": 0, "id": "cb842c0d-51fc-433f-baf4-0f45af0fc0c5", "created_at": "06/14/2024, 16:30", "name": "MM1", "samplerate": 500, "sampleperiod": 1, "downtime": 10, "owner": "ML", "status": "stopped"}, {"no": 0, "id": "a1931936-5e6d-4286-851a-7c1327495139", "created_at": "06/27/2024, 13:42", "name": "MM2", "samplerate": 3750, "sampleperiod": 1, "downtime": 10, "owner": "ML", "status": "stopped"}]

View File

@@ -1,16 +1,20 @@
import os
import time
from uuid import uuid4
import uuid
from flask import jsonify
import json
from marshmallow import EXCLUDE
#from vrpmdvcreatemoncmd import VRPMDV_CreateMonCmd
#from vrpmdvcreatemonschema import VRPMDV_CreateMonSchema
#from vrpmdvdeletemoncmd import VRPMDV_DeleteMonCmd
#from vrpmdvdeletemonschema import VRPMDV_DeleteMonSchema
from vrpmdvmonfilesschema import VRPMDV_MonfilesSchema
from vrpmdvmonitoring import VRPMDV_Monitoring
from vrpmdvmonitoringschema import VRPMDV_MonitoringSchema
from vrpmdvmonreqschema import VRPMDV_MonReqSchema
from vrpmdvntlink import trySend
# from vrpmdvntlink import trySend
from extensions.rt_service import rt_service as rts
from vrpmdvmonitoringState import VRPMDVMonitoringState
@@ -25,10 +29,13 @@ class VRPMDV_Data:
format = "%(asctime)s: %(message)s"
logging.basicConfig(format=format, level=logging.INFO, datefmt="%H:%M:%S")
self.mons:list = []
self.rtservice = rts.RT_Service()
#TODO ML: uncomment this lines
self.rtservice = None #rts.RT_Service()
if (self.rtservice != None):
self.rtservice.initCoproFW("home/root/elffile","zephyr_openamp_rsc_table.elf")
self.logTask = None #createCoproLoggingTask()
self.loaded = self.loadFile()
self.monfilespath = '/home/markus/monfiles/'
#start the monitorings
time.sleep(1)
self.startMons();
@@ -341,3 +348,66 @@ class VRPMDV_Data:
except:
return "no Item found"
def getMonitoring2files(self):
#search in the given directory
dirs = [d for d in os.listdir(self.monfilespath) if os.path.isdir(d)]
#return for the filter
def getMonitoringfiles(self, id):
#search in the given directory
dirname = os.path.dirname(self.monfilespath)
dirs = []
for d in os.listdir(dirname) :
# if os.path.isdir(d) :
dirs.append(d)
#dirs = [d for d in os.listdir(dirname) if os.path.isdir(d)]
id = '0'
#currently we return the first one
for dir in dirs:
mon = self.findMonitoring(dir)
id = mon.id
#TODO ML change to array
#for file in files:
filename = self.monfilespath + str(id) + '/'
monfiles = self.loadMonfiles(filename)
schema = VRPMDV_MonfilesSchema()
result = schema.dumps(monfiles, many=True)
return result
def loadMonfiles(self, id):
#files = [f for f in os.listdir() if os.path.isfile(f)]
completeMonPath = os.path.dirname(id)
monfiles = []
for f in os.listdir(completeMonPath) :
# if (os.path.isfile(f)) :
try:
completeFilepath = id + str(f)
with open(completeFilepath, 'r') as f:
fmonfile = f.read()
if fmonfile:
schema = VRPMDV_MonfilesSchema()
monfile = schema.loads(fmonfile, unknown=EXCLUDE)
monfiles.append(monfile)
except:
#nothing todo ML we should create a file later the database
print("file not found")
return monfiles
# This is to get the directory that the program
# is currently running in.
# dir_path = os.path.dirname(os.path.realpath(__file__))
# for root, dirs, files in os.walk(dir_path):
# for file in files:
# # change the extension from '.mp3' to
# # the one of your choice.
# if file.endswith('.mp3'):
# print (root+'/'+str(file))

View File

@@ -0,0 +1,14 @@
import datetime as dt
class VRPMDV_Monfiles:
def __init__(self, id, name , samplerate, sampleperiod, downtime, timestamp) :
self.id = id
self.name = name
self.samplerate = samplerate
self.sampleperiod = sampleperiod
self.downtime = downtime
self.timestamp = timestamp

View File

@@ -0,0 +1,22 @@
import uuid
import datetime as dt
from marshmallow import Schema, fields, post_load, EXCLUDE
from vrpmdvmonfiles import VRPMDV_Monfiles
class VRPMDV_MonfilesSchema(Schema):
id = fields.String(required=True)
name = fields.String(required=True)
samplerate = fields.Integer(required=True)
sampleperiod = fields.Integer(required=True)
downtime = fields.Integer(required=True)
timestamp = fields.Integer(required=True)
#samples = fields.List()
@post_load
def make_vrpmdv_Monfiles(self, data, **kwargs):
return VRPMDV_Monfiles(**data)

View File

@@ -10,7 +10,7 @@ from vrpmdvmonreq import VRPMDV_MonReq
from extensions.rt_service import rt_service as rts
from vrpmdvmonitoringState import VRPMDVMonitoringState
from vrpmdvmondata import createCoproLoggingTask
from vrpmdvntlink import trySend
# from vrpmdvntlink import trySend
import logging
import threading
import time
@@ -78,8 +78,9 @@ class VRPMDV_Monitoring(VRPMDV_MonReq):
self.no = no
self.id = id
self.created_at = created_at
self.rtservice = rts.RT_Service()
self.rtMon = rts.RTSMonitoringTask()
#TODO ML: uncomment
self.rtservice = None #rts.RT_Service()
self.rtMon = None #rts.RTSMonitoringTask()
self.monTask = None
self.run_thread = False
self.fdesc = -1
@@ -95,7 +96,9 @@ class VRPMDV_Monitoring(VRPMDV_MonReq):
logging.info("MainThread: createMonOnDevice => before schemaCreateCmd")
schemaCreateCmd = VRPMDV_CreateMonSchema()
logging.info("MainThread: try send start monitoring starting %s", schemaCreateCmd.dumps(vrpmdCreateCmd))
res = trySend(schemaCreateCmd.dumps(vrpmdCreateCmd))
# TODO ML: uncomment this
# res = trySend(schemaCreateCmd.dumps(vrpmdCreateCmd))
res = 'Test'
logging.info("MainThread: try send start monitoring done %s", res)
# start the receiving thread
@@ -133,7 +136,9 @@ class VRPMDV_Monitoring(VRPMDV_MonReq):
vrpmdDeleteCmd = VRPMDV_DeleteMonCmd(self.no)
schemaDeleteCmd = VRPMDV_DeleteMonSchema()
res = trySend(schemaDeleteCmd.dumps(vrpmdDeleteCmd))
# TODO ML: uncomment this
# res = trySend(schemaDeleteCmd.dumps(vrpmdDeleteCmd))
res = 'Stop'
logging.info("MainThread: try send stop monitoring done %s", res)
# if (isstopped) :

View File

@@ -79,6 +79,15 @@ def setStatus(id):
return resp
@app.route('/vrpmdvapi/1_0/monitoringfiles', methods=['GET'])
def get_monitoringfiles():
#vrpmreq = VRPMDV_MonReqSchema().load(request.get_json())
#data = vrpmdvdata.createMonitoring(vrpmreq)
data = vrpmdvdata.getMonitoringfiles(0)
resp = Response(data, status=200, mimetype='application/json')
return resp
if __name__ == "__main__":