mirror of
https://github.com/tiqi-group/pydase.git
synced 2025-06-07 22:10:41 +02:00
272 lines
8.4 KiB
TypeScript
272 lines
8.4 KiB
TypeScript
import React, { useContext } from 'react';
|
|
import { ButtonComponent } from './ButtonComponent';
|
|
import { NumberComponent } from './NumberComponent';
|
|
import { SliderComponent } from './SliderComponent';
|
|
import { EnumComponent, EnumSerialization } from './EnumComponent';
|
|
import { MethodComponent } from './MethodComponent';
|
|
import { AsyncMethodComponent } from './AsyncMethodComponent';
|
|
import { StringComponent } from './StringComponent';
|
|
import { ListComponent } from './ListComponent';
|
|
import { DataServiceComponent, DataServiceJSON } from './DataServiceComponent';
|
|
import { DeviceConnectionComponent } from './DeviceConnection';
|
|
import { ImageComponent } from './ImageComponent';
|
|
import { LevelName } from './NotificationsComponent';
|
|
import { getIdFromFullAccessPath } from '../utils/stringUtils';
|
|
import { WebSettingsContext } from '../WebSettings';
|
|
import { updateValue } from '../socket';
|
|
import { DictComponent } from './DictComponent';
|
|
import { parseFullAccessPath } from '../utils/stateUtils';
|
|
|
|
type AttributeType =
|
|
| 'str'
|
|
| 'bool'
|
|
| 'float'
|
|
| 'int'
|
|
| 'Quantity'
|
|
| 'None'
|
|
| 'list'
|
|
| 'dict'
|
|
| 'method'
|
|
| 'DataService'
|
|
| 'DeviceConnection'
|
|
| 'Enum'
|
|
| 'NumberSlider'
|
|
| 'Image'
|
|
| 'ColouredEnum';
|
|
|
|
type ValueType = boolean | string | number | Record<string, unknown>;
|
|
export type SerializedValue = {
|
|
type: AttributeType;
|
|
full_access_path: string;
|
|
name?: string;
|
|
value?: ValueType | ValueType[];
|
|
readonly: boolean;
|
|
doc?: string | null;
|
|
async?: boolean;
|
|
frontend_render?: boolean;
|
|
enum?: Record<string, string>;
|
|
};
|
|
type GenericComponentProps = {
|
|
attribute: SerializedValue;
|
|
isInstantUpdate: boolean;
|
|
addNotification: (message: string, levelname?: LevelName) => void;
|
|
};
|
|
|
|
const getPathFromPathParts = (pathParts: string[]): string => {
|
|
let path = '';
|
|
for (const pathPart of pathParts) {
|
|
if (!pathPart.startsWith('[') && path !== '') {
|
|
path += '.';
|
|
}
|
|
path += pathPart;
|
|
}
|
|
return path;
|
|
};
|
|
|
|
const createDisplayNameFromAccessPath = (fullAccessPath: string): string => {
|
|
const displayNameParts = [];
|
|
const parsedFullAccessPath = parseFullAccessPath(fullAccessPath);
|
|
for (let i = parsedFullAccessPath.length - 1; i >= 0; i--) {
|
|
const item = parsedFullAccessPath[i];
|
|
displayNameParts.unshift(item);
|
|
if (!item.startsWith('[')) {
|
|
break;
|
|
}
|
|
}
|
|
return getPathFromPathParts(displayNameParts);
|
|
};
|
|
|
|
export const GenericComponent = React.memo(
|
|
({ attribute, isInstantUpdate, addNotification }: GenericComponentProps) => {
|
|
const { full_access_path: fullAccessPath } = attribute;
|
|
const id = getIdFromFullAccessPath(fullAccessPath);
|
|
const webSettings = useContext(WebSettingsContext);
|
|
|
|
let displayName = createDisplayNameFromAccessPath(fullAccessPath);
|
|
|
|
if (webSettings[fullAccessPath]) {
|
|
if (webSettings[fullAccessPath].display === false) {
|
|
return null;
|
|
}
|
|
if (webSettings[fullAccessPath].displayName) {
|
|
displayName = webSettings[fullAccessPath].displayName;
|
|
}
|
|
}
|
|
|
|
function changeCallback(
|
|
value: SerializedValue,
|
|
callback: (ack: unknown) => void = undefined
|
|
) {
|
|
updateValue(value, callback);
|
|
}
|
|
|
|
if (attribute.type === 'bool') {
|
|
return (
|
|
<ButtonComponent
|
|
fullAccessPath={fullAccessPath}
|
|
docString={attribute.doc}
|
|
readOnly={attribute.readonly}
|
|
value={Boolean(attribute.value)}
|
|
addNotification={addNotification}
|
|
changeCallback={changeCallback}
|
|
displayName={displayName}
|
|
id={id}
|
|
/>
|
|
);
|
|
} else if (attribute.type === 'float' || attribute.type === 'int') {
|
|
return (
|
|
<NumberComponent
|
|
type={attribute.type}
|
|
fullAccessPath={fullAccessPath}
|
|
docString={attribute.doc}
|
|
readOnly={attribute.readonly}
|
|
value={Number(attribute.value)}
|
|
isInstantUpdate={isInstantUpdate}
|
|
addNotification={addNotification}
|
|
changeCallback={changeCallback}
|
|
displayName={displayName}
|
|
id={id}
|
|
/>
|
|
);
|
|
} else if (attribute.type === 'Quantity') {
|
|
return (
|
|
<NumberComponent
|
|
type="Quantity"
|
|
fullAccessPath={fullAccessPath}
|
|
docString={attribute.doc}
|
|
readOnly={attribute.readonly}
|
|
value={Number(attribute.value['magnitude'])}
|
|
unit={attribute.value['unit']}
|
|
isInstantUpdate={isInstantUpdate}
|
|
addNotification={addNotification}
|
|
changeCallback={changeCallback}
|
|
displayName={displayName}
|
|
id={id}
|
|
/>
|
|
);
|
|
} else if (attribute.type === 'NumberSlider') {
|
|
return (
|
|
<SliderComponent
|
|
fullAccessPath={fullAccessPath}
|
|
docString={attribute.value['value'].doc}
|
|
readOnly={attribute.readonly}
|
|
value={attribute.value['value']}
|
|
min={attribute.value['min']}
|
|
max={attribute.value['max']}
|
|
stepSize={attribute.value['step_size']}
|
|
isInstantUpdate={isInstantUpdate}
|
|
addNotification={addNotification}
|
|
changeCallback={changeCallback}
|
|
displayName={displayName}
|
|
id={id}
|
|
/>
|
|
);
|
|
} else if (attribute.type === 'Enum' || attribute.type === 'ColouredEnum') {
|
|
return (
|
|
<EnumComponent
|
|
attribute={attribute as EnumSerialization}
|
|
addNotification={addNotification}
|
|
changeCallback={changeCallback}
|
|
displayName={displayName}
|
|
id={id}
|
|
/>
|
|
);
|
|
} else if (attribute.type === 'method') {
|
|
if (!attribute.async) {
|
|
return (
|
|
<MethodComponent
|
|
fullAccessPath={fullAccessPath}
|
|
docString={attribute.doc}
|
|
addNotification={addNotification}
|
|
displayName={displayName}
|
|
id={id}
|
|
render={attribute.frontend_render}
|
|
/>
|
|
);
|
|
} else {
|
|
return (
|
|
<AsyncMethodComponent
|
|
fullAccessPath={fullAccessPath}
|
|
docString={attribute.doc}
|
|
value={attribute.value as 'RUNNING' | null}
|
|
addNotification={addNotification}
|
|
displayName={displayName}
|
|
id={id}
|
|
render={attribute.frontend_render}
|
|
/>
|
|
);
|
|
}
|
|
} else if (attribute.type === 'str') {
|
|
return (
|
|
<StringComponent
|
|
fullAccessPath={fullAccessPath}
|
|
value={attribute.value as string}
|
|
readOnly={attribute.readonly}
|
|
docString={attribute.doc}
|
|
isInstantUpdate={isInstantUpdate}
|
|
addNotification={addNotification}
|
|
changeCallback={changeCallback}
|
|
displayName={displayName}
|
|
id={id}
|
|
/>
|
|
);
|
|
} else if (attribute.type === 'DataService') {
|
|
return (
|
|
<DataServiceComponent
|
|
props={attribute.value as DataServiceJSON}
|
|
isInstantUpdate={isInstantUpdate}
|
|
addNotification={addNotification}
|
|
displayName={displayName}
|
|
id={id}
|
|
/>
|
|
);
|
|
} else if (attribute.type === 'DeviceConnection') {
|
|
return (
|
|
<DeviceConnectionComponent
|
|
fullAccessPath={fullAccessPath}
|
|
props={attribute.value as DataServiceJSON}
|
|
isInstantUpdate={isInstantUpdate}
|
|
addNotification={addNotification}
|
|
displayName={displayName}
|
|
id={id}
|
|
/>
|
|
);
|
|
} else if (attribute.type === 'list') {
|
|
return (
|
|
<ListComponent
|
|
value={attribute.value as SerializedValue[]}
|
|
docString={attribute.doc}
|
|
isInstantUpdate={isInstantUpdate}
|
|
addNotification={addNotification}
|
|
id={id}
|
|
/>
|
|
);
|
|
} else if (attribute.type === 'dict') {
|
|
return (
|
|
<DictComponent
|
|
value={attribute.value as Record<string, SerializedValue>}
|
|
docString={attribute.doc}
|
|
isInstantUpdate={isInstantUpdate}
|
|
addNotification={addNotification}
|
|
id={id}
|
|
/>
|
|
);
|
|
} else if (attribute.type === 'Image') {
|
|
return (
|
|
<ImageComponent
|
|
fullAccessPath={fullAccessPath}
|
|
docString={attribute.value['value'].doc}
|
|
displayName={displayName}
|
|
id={id}
|
|
addNotification={addNotification}
|
|
// Add any other specific props for the ImageComponent here
|
|
value={attribute.value['value']['value'] as string}
|
|
format={attribute.value['format']['value'] as string}
|
|
/>
|
|
);
|
|
} else {
|
|
return <div key={fullAccessPath}>{fullAccessPath}</div>;
|
|
}
|
|
}
|
|
);
|