Merge branch 'dev' into dev2

This commit is contained in:
MichaelDvP
2024-01-21 09:35:16 +01:00
76 changed files with 963 additions and 958 deletions

View File

@@ -50,8 +50,10 @@ const SingleUpload: FC<SingleUploadProps> = ({ onDrop, onCancel, isUploading, pr
const progressText = () => {
if (uploading) {
if (progress.total) {
return LL.UPLOADING() + ': ' + Math.round((progress.loaded * 100) / progress.total) + '%';
if (progress.total && progress.loaded) {
return progress.loaded <= progress.total
? LL.UPLOADING() + ': ' + Math.round((progress.loaded * 100) / progress.total) + '%'
: LL.UPLOADING() + ': ' + Math.round((progress.total * 100) / progress.loaded) + '%';
}
}
return LL.UPLOAD_DROP_TEXT();
@@ -83,7 +85,13 @@ const SingleUpload: FC<SingleUploadProps> = ({ onDrop, onCancel, isUploading, pr
<Box width="100%" p={2}>
<LinearProgress
variant="determinate"
value={progress.total === 0 ? 0 : Math.round((progress.loaded * 100) / progress.total)}
value={
progress.total === 0 || progress.loaded === 0
? 0
: progress.loaded <= progress.total
? Math.round((progress.loaded * 100) / progress.total)
: Math.round((progress.total * 100) / progress.loaded)
}
/>
</Box>
<Button startIcon={<CancelIcon />} variant="outlined" color="secondary" onClick={onCancel}>

View File

@@ -37,7 +37,8 @@ const GenerateToken: FC<GenerateTokenProps> = ({ username, onClose }) => {
if (open) {
void generateToken();
}
}, [open, generateToken]);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [open]);
return (
<Dialog sx={dialogStyle} onClose={onClose} open={!!username} fullWidth maxWidth="sm">

View File

@@ -2,8 +2,8 @@ import { Tab } from '@mui/material';
import { Navigate, Route, Routes } from 'react-router-dom';
import SettingsApplication from './SettingsApplication';
import SettingsCustomEntities from './SettingsCustomEntities';
import SettingsCustomization from './SettingsCustomization';
import SettingsEntities from './SettingsEntities';
import SettingsScheduler from './SettingsScheduler';
import type { FC } from 'react';
import { RouterTabs, useRouterTab, useLayoutTitle } from 'components';
@@ -27,7 +27,7 @@ const Settings: FC = () => {
<Route path="application" element={<SettingsApplication />} />
<Route path="customization" element={<SettingsCustomization />} />
<Route path="scheduler" element={<SettingsScheduler />} />
<Route path="customentities" element={<SettingsEntities />} />
<Route path="customentities" element={<SettingsCustomEntities />} />
<Route path="*" element={<Navigate replace to="/settings/application" />} />
</Routes>
</>

View File

@@ -1,5 +1,7 @@
import AddIcon from '@mui/icons-material/Add';
import CancelIcon from '@mui/icons-material/Cancel';
import RefreshIcon from '@mui/icons-material/Refresh';
import EditOutlinedIcon from '@mui/icons-material/EditOutlined';
import WarningIcon from '@mui/icons-material/Warning';
import { Button, Typography, Box } from '@mui/material';
import { Table, Header, HeaderRow, HeaderCell, Body, Row, Cell } from '@table-library/react-table-library/table';
@@ -11,7 +13,7 @@ import { useBlocker } from 'react-router-dom';
import { toast } from 'react-toastify';
import SettingsEntitiesDialog from './SettingsEntitiesDialog';
import SettingsCustomEntitiesDialog from './SettingsCustomEntitiesDialog';
import * as EMSESP from './api';
import { DeviceValueTypeNames, DeviceValueUOM_s } from './types';
import { entityItemValidation } from './validators';
@@ -21,7 +23,7 @@ import { ButtonRow, FormLoader, SectionContent, BlockNavigation } from 'componen
import { useI18nContext } from 'i18n/i18n-react';
const SettingsEntities: FC = () => {
const SettingsCustomEntities: FC = () => {
const { LL } = useI18nContext();
const [numChanges, setNumChanges] = useState<number>(0);
const blocker = useBlocker(numChanges !== 0);
@@ -43,6 +45,7 @@ const SettingsEntities: FC = () => {
function hasEntityChanged(ei: EntityItem) {
return (
ei.id !== ei.o_id ||
ei.ram !== ei.o_ram ||
(ei?.name || '') !== (ei?.o_name || '') ||
ei.device_id !== ei.o_device_id ||
ei.type_id !== ei.o_type_id ||
@@ -51,7 +54,8 @@ const SettingsEntities: FC = () => {
ei.factor !== ei.o_factor ||
ei.value_type !== ei.o_value_type ||
ei.writeable !== ei.o_writeable ||
ei.deleted !== ei.o_deleted
ei.deleted !== ei.o_deleted ||
(ei.value || '') !== (ei.o_value || '')
);
}
@@ -118,6 +122,7 @@ const SettingsEntities: FC = () => {
.filter((ei) => !ei.deleted)
.map((condensed_ei) => ({
id: condensed_ei.id,
ram: condensed_ei.ram,
name: condensed_ei.name,
device_id: condensed_ei.device_id,
type_id: condensed_ei.type_id,
@@ -125,7 +130,8 @@ const SettingsEntities: FC = () => {
factor: condensed_ei.factor,
uom: condensed_ei.uom,
writeable: condensed_ei.writeable,
value_type: condensed_ei.value_type
value_type: condensed_ei.value_type,
value: condensed_ei.value
}))
})
.then(() => {
@@ -173,14 +179,16 @@ const SettingsEntities: FC = () => {
setSelectedEntityItem({
id: Math.floor(Math.random() * (Math.floor(200) - 100) + 100),
name: '',
device_id: '',
type_id: '',
ram: 0,
device_id: '0',
type_id: '0',
offset: 0,
factor: 1,
uom: 0,
value_type: 0,
writeable: false,
deleted: false
deleted: false,
value: ''
});
setDialogOpen(true);
};
@@ -212,18 +220,21 @@ const SettingsEntities: FC = () => {
<HeaderCell stiff>{LL.ID_OF(LL.DEVICE())}</HeaderCell>
<HeaderCell stiff>{LL.ID_OF(LL.TYPE(1))}</HeaderCell>
<HeaderCell stiff>{LL.OFFSET()}</HeaderCell>
<HeaderCell stiff>{LL.VALUE(1) + ' ' + LL.TYPE(1)}</HeaderCell>
<HeaderCell stiff>{LL.TYPE(1)}</HeaderCell>
<HeaderCell stiff>{LL.VALUE(1)}</HeaderCell>
</HeaderRow>
</Header>
<Body>
{tableList.map((ei: EntityItem) => (
<Row key={ei.name} item={ei} onClick={() => editEntityItem(ei)}>
<Cell>{ei.name}</Cell>
<Cell>{showHex(ei.device_id as number, 2)}</Cell>
<Cell>{showHex(ei.type_id as number, 3)}</Cell>
<Cell>{ei.offset}</Cell>
<Cell>{DeviceValueTypeNames[ei.value_type]}</Cell>
<Cell>
{ei.name}&nbsp;
{ei.writeable && <EditOutlinedIcon color="primary" sx={{ fontSize: 12 }} />}
</Cell>
<Cell>{ei.ram === 1 ? '' : showHex(ei.device_id as number, 2)}</Cell>
<Cell>{ei.ram === 1 ? '' : showHex(ei.type_id as number, 3)}</Cell>
<Cell>{ei.ram === 1 ? '' : ei.offset}</Cell>
<Cell>{ei.ram === 1 ? 'RAM' : DeviceValueTypeNames[ei.value_type]}</Cell>
<Cell>{formatValue(ei.value, ei.uom)}</Cell>
</Row>
))}
@@ -244,7 +255,7 @@ const SettingsEntities: FC = () => {
{renderEntity()}
{selectedEntityItem && (
<SettingsEntitiesDialog
<SettingsCustomEntitiesDialog
open={dialogOpen}
creating={creating}
onClose={onDialogClose}
@@ -274,7 +285,10 @@ const SettingsEntities: FC = () => {
</Box>
<Box flexWrap="nowrap" whiteSpace="nowrap">
<ButtonRow>
<Button startIcon={<AddIcon />} variant="outlined" color="secondary" onClick={addEntityItem}>
<Button startIcon={<RefreshIcon />} variant="outlined" color="secondary" onClick={fetchEntities}>
{LL.REFRESH()}
</Button>
<Button startIcon={<AddIcon />} variant="outlined" color="primary" onClick={addEntityItem}>
{LL.ADD(0)}
</Button>
</ButtonRow>
@@ -284,4 +298,4 @@ const SettingsEntities: FC = () => {
);
};
export default SettingsEntities;
export default SettingsCustomEntities;

View File

@@ -30,7 +30,7 @@ import { useI18nContext } from 'i18n/i18n-react';
import { updateValue } from 'utils';
import { validate } from 'validators';
type SettingsEntitiesDialogProps = {
type SettingsCustomEntitiesDialogProps = {
open: boolean;
creating: boolean;
onClose: () => void;
@@ -39,14 +39,14 @@ type SettingsEntitiesDialogProps = {
validator: Schema;
};
const SettingsEntitiesDialog = ({
const SettingsCustomEntitiesDialog = ({
open,
creating,
onClose,
onSave,
selectedItem,
validator
}: SettingsEntitiesDialogProps) => {
}: SettingsCustomEntitiesDialogProps) => {
const { LL } = useI18nContext();
const [editItem, setEditItem] = useState<EntityItem>(selectedItem);
const [fieldErrors, setFieldErrors] = useState<ValidateFieldsError>();
@@ -100,7 +100,7 @@ const SettingsEntitiesDialog = ({
<Box flexWrap="nowrap" whiteSpace="nowrap" />
</Box>
<Grid container spacing={2}>
<Grid item xs={8}>
<Grid item xs={4}>
<ValidatedTextField
fieldErrors={fieldErrors}
name="name"
@@ -111,122 +111,154 @@ const SettingsEntitiesDialog = ({
onChange={updateFormValue}
/>
</Grid>
<Grid item xs={4} mt={3}>
<BlockFormControlLabel
control={<Checkbox checked={editItem.writeable} onChange={updateFormValue} name="writeable" />}
label={LL.WRITEABLE()}
/>
</Grid>
<Grid item xs={4}>
<ValidatedTextField
fieldErrors={fieldErrors}
name="device_id"
label={LL.ID_OF(LL.DEVICE())}
margin="normal"
type="string"
fullWidth
value={editItem.device_id as string}
onChange={updateFormValue}
inputProps={{ style: { textTransform: 'uppercase' } }}
InputProps={{ startAdornment: <InputAdornment position="start">0x</InputAdornment> }}
/>
</Grid>
<Grid item xs={4}>
<ValidatedTextField
fieldErrors={fieldErrors}
name="type_id"
label={LL.ID_OF(LL.TYPE(1))}
margin="normal"
fullWidth
value={editItem.type_id}
onChange={updateFormValue}
inputProps={{ style: { textTransform: 'uppercase' } }}
InputProps={{ startAdornment: <InputAdornment position="start">0x</InputAdornment> }}
/>
</Grid>
<Grid item xs={4}>
<ValidatedTextField
fieldErrors={fieldErrors}
name="offset"
label={LL.OFFSET()}
margin="normal"
fullWidth
type="number"
value={editItem.offset}
onChange={updateFormValue}
/>
</Grid>
<Grid item xs={4}>
<TextField
name="value_type"
name="ram"
label={LL.VALUE(1) + ' ' + LL.TYPE(1)}
value={editItem.value_type}
value={editItem.ram}
variant="outlined"
onChange={updateFormValue}
margin="normal"
fullWidth
select
>
<MenuItem value={DeviceValueType.BOOL}>BOOL</MenuItem>
<MenuItem value={DeviceValueType.INT}>INT</MenuItem>
<MenuItem value={DeviceValueType.UINT}>UINT</MenuItem>
<MenuItem value={DeviceValueType.SHORT}>SHORT</MenuItem>
<MenuItem value={DeviceValueType.USHORT}>USHORT</MenuItem>
<MenuItem value={DeviceValueType.ULONG}>ULONG</MenuItem>
<MenuItem value={DeviceValueType.TIME}>TIME</MenuItem>
<MenuItem value={DeviceValueType.STRING}>RAW</MenuItem>
<MenuItem value={0}>EMS-{LL.VALUE(1)}</MenuItem>
<MenuItem value={1}>RAM-{LL.VALUE(1)}</MenuItem>
</TextField>
</Grid>
{editItem.value_type !== DeviceValueType.BOOL && editItem.value_type !== DeviceValueType.STRING && (
<>
<Grid item xs={4}>
<TextField
name="factor"
label={LL.FACTOR()}
value={editItem.factor}
variant="outlined"
onChange={updateFormValue}
fullWidth
margin="normal"
type="number"
inputProps={{ step: '0.001' }}
/>
</Grid>
<Grid item xs={4}>
<TextField
name="uom"
label={LL.UNIT()}
value={editItem.uom}
margin="normal"
fullWidth
onChange={updateFormValue}
select
>
{DeviceValueUOM_s.map((val, i) => (
<MenuItem key={i} value={i}>
{val}
</MenuItem>
))}
</TextField>
</Grid>
</>
)}
{editItem.value_type === DeviceValueType.STRING && (
{editItem.ram === 1 && (
<Grid item xs={4}>
<TextField
name="factor"
label="Bytes"
value={editItem.factor}
name="value"
label={LL.STARTVALUE()}
value={editItem.value}
variant="outlined"
onChange={updateFormValue}
fullWidth
margin="normal"
type="number"
inputProps={{ min: '1', max: '27', step: '1' }}
/>
</Grid>
)}
{editItem.ram === 0 && (
<>
<Grid item xs={4} mt={3}>
<BlockFormControlLabel
control={<Checkbox checked={editItem.writeable} onChange={updateFormValue} name="writeable" />}
label={LL.WRITEABLE()}
/>
</Grid>
<Grid item xs={4}>
<ValidatedTextField
fieldErrors={fieldErrors}
name="device_id"
label={LL.ID_OF(LL.DEVICE())}
margin="normal"
type="string"
fullWidth
value={editItem.device_id as string}
onChange={updateFormValue}
inputProps={{ style: { textTransform: 'uppercase' } }}
InputProps={{ startAdornment: <InputAdornment position="start">0x</InputAdornment> }}
/>
</Grid>
<Grid item xs={4}>
<ValidatedTextField
fieldErrors={fieldErrors}
name="type_id"
label={LL.ID_OF(LL.TYPE(1))}
margin="normal"
fullWidth
value={editItem.type_id}
onChange={updateFormValue}
inputProps={{ style: { textTransform: 'uppercase' } }}
InputProps={{ startAdornment: <InputAdornment position="start">0x</InputAdornment> }}
/>
</Grid>
<Grid item xs={4}>
<ValidatedTextField
fieldErrors={fieldErrors}
name="offset"
label={LL.OFFSET()}
margin="normal"
fullWidth
type="number"
value={editItem.offset}
onChange={updateFormValue}
/>
</Grid>
<Grid item xs={4}>
<TextField
name="value_type"
label={LL.VALUE(1) + ' ' + LL.TYPE(1)}
value={editItem.value_type}
variant="outlined"
onChange={updateFormValue}
margin="normal"
fullWidth
select
>
<MenuItem value={DeviceValueType.BOOL}>BOOL</MenuItem>
<MenuItem value={DeviceValueType.INT}>INT</MenuItem>
<MenuItem value={DeviceValueType.UINT}>UINT</MenuItem>
<MenuItem value={DeviceValueType.SHORT}>SHORT</MenuItem>
<MenuItem value={DeviceValueType.USHORT}>USHORT</MenuItem>
<MenuItem value={DeviceValueType.ULONG}>ULONG</MenuItem>
<MenuItem value={DeviceValueType.TIME}>TIME</MenuItem>
<MenuItem value={DeviceValueType.STRING}>RAW</MenuItem>
</TextField>
</Grid>
{editItem.value_type !== DeviceValueType.BOOL && editItem.value_type !== DeviceValueType.STRING && (
<>
<Grid item xs={4}>
<TextField
name="factor"
label={LL.FACTOR()}
value={editItem.factor}
variant="outlined"
onChange={updateFormValue}
fullWidth
margin="normal"
type="number"
inputProps={{ step: '0.001' }}
/>
</Grid>
<Grid item xs={4}>
<TextField
name="uom"
label={LL.UNIT()}
value={editItem.uom}
margin="normal"
fullWidth
onChange={updateFormValue}
select
>
{DeviceValueUOM_s.map((val, i) => (
<MenuItem key={i} value={i}>
{val}
</MenuItem>
))}
</TextField>
</Grid>
</>
)}
{editItem.value_type === DeviceValueType.STRING && editItem.device_id !== '0' && (
<Grid item xs={4}>
<TextField
name="factor"
label="Bytes"
value={editItem.factor}
variant="outlined"
onChange={updateFormValue}
fullWidth
margin="normal"
type="number"
inputProps={{ min: '1', max: '27', step: '1' }}
/>
</Grid>
)}
</>
)}
</Grid>
</DialogContent>
@@ -249,4 +281,4 @@ const SettingsEntitiesDialog = ({
);
};
export default SettingsEntitiesDialog;
export default SettingsCustomEntitiesDialog;

View File

@@ -88,7 +88,7 @@ export const writeSchedule = (data: any) => alovaInstance.Post('/rest/schedule',
// SettingsEntities
export const readCustomEntities = () =>
alovaInstance.Get<EntityItem[]>('/rest/customentities', {
alovaInstance.Get<EntityItem[]>('/rest/customEntities', {
name: 'entities',
transformData(data: any) {
return data.entities.map((ei: EntityItem) => ({
@@ -106,4 +106,4 @@ export const readCustomEntities = () =>
}));
}
});
export const writeCustomEntities = (data: any) => alovaInstance.Post('/rest/customentities', data);
export const writeCustomEntities = (data: any) => alovaInstance.Post('/rest/customEntities', data);

View File

@@ -177,7 +177,8 @@ export enum DeviceValueUOM {
L,
KMIN,
K,
VOLTS
VOLTS,
MBAR
}
export const DeviceValueUOM_s = [
@@ -204,7 +205,8 @@ export const DeviceValueUOM_s = [
'l',
'K*min',
'K',
'V'
'V',
'mbar'
];
export enum AnalogType {
@@ -323,6 +325,7 @@ export enum ScheduleFlag {
export interface EntityItem {
id: number; // unique number
ram: number;
name: string;
device_id: number | string;
type_id: number | string;
@@ -334,6 +337,7 @@ export interface EntityItem {
writeable: boolean;
deleted?: boolean;
o_id?: number;
o_ram?: number;
o_name?: string;
o_device_id?: number | string;
o_type_id?: number | string;
@@ -343,6 +347,7 @@ export interface EntityItem {
o_value_type?: number;
o_deleted?: boolean;
o_writeable?: boolean;
o_value?: any;
}
export interface Entities {
@@ -398,6 +403,6 @@ export const DeviceValueTypeNames = [
'ULONG',
'TIME',
'ENUM',
'STRING',
'RAW',
'CMD'
];

View File

@@ -9,7 +9,6 @@ export interface NTPStatus {
utc_time: string;
local_time: string;
server: string;
uptime: number;
}
export interface NTPSettings {