Skip to content

Commit

Permalink
Merge pull request #32 from m-abdulhak/28-add-state-indicators-to-qui…
Browse files Browse the repository at this point in the history
…ck-action-buttons

#28 #29 #30 #31 Add State Indicators and Debugging Options
  • Loading branch information
m-abdulhak committed Jun 18, 2023
2 parents ef8e8ff + 7d9767d commit e317bde
Show file tree
Hide file tree
Showing 19 changed files with 375 additions and 88 deletions.
72 changes: 59 additions & 13 deletions src/client/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import {
uniqueRenderingElements,
resetRenderer,
setElementEnabled,
toggleElementEnabled,
isElementEnabled,
createFieldCanvas,
changeBackgroundField
} from '@common/rendering/renderer';
Expand All @@ -33,21 +33,26 @@ import TabContainer from './components/Layouts/TabContainer';
import Options from './components/Options/index';
import Benchmark from './components/Benchmark';
import CodeEditor from './components/Editors/CodeEditor';
import DebugPanel from './components/Debug';
import TitledSlider from './components/Inputs/TitledSlider';
import CodeEditorSection from './components/Editors/CodeEditor/CodeEditorSection';

import exampleConfigs from '../scenes';

import { getSceneFromUrlQuery } from './utils';

const options = Object.values(exampleConfigs).map((v) => ({
label: v.title, value: v.name
label: v.title,
value: v.name
}));

const App = () => {
const [loading, setLoading] = useState(true);
const [selectedScene, setSelectedScene] = useState(options[0].value);
const [selectedScene, setSelectedScene] = useState(getSceneFromUrlQuery(options));
const [config, setConfig] = useState(exampleConfigs[selectedScene].simConfig);
const [benchSettings, setBenchSettings] = useState(exampleConfigs[selectedScene].benchmarkConfig);
const [description, setDescription] = useState(exampleConfigs[selectedScene].description);
const [uiEnabled, setUiEnabled] = useState(true);
const [uiEnabled, setUiEnabled] = useState(false);
const [time, setTime] = useState(0);
const [robotParams, setRobotParams] = useState({ velocityScale: 1 });
const [renderSkip, setRenderSkip] = useState(1);
Expand All @@ -56,6 +61,11 @@ const App = () => {
const svgRef = useRef(null);
const fieldsElemRef = useRef(null);

const availableFieldTitles = Object.values(exampleConfigs[selectedScene]?.simConfig?.env?.fields || {})
.map((field) => field.title);

const [selectedBackgroundField, setSelectedBackgroundField] = useState(availableFieldTitles?.[0] ?? null);

// User Defined Robot Velocity Controller
const [defaultOnLoopCode, setDefaultOnLoopCode] = useState('');
const [onLoopCode, setOnLoopCode] = useState(null);
Expand Down Expand Up @@ -102,7 +112,7 @@ const App = () => {
}

resetSimulation(usedConfig, onUpdate, setDefaultOnLoopCode, setDefaultOnInitCode);
onRobotParamsChange({ velocityScale: newConfig.robots.params.velocityScale });
onRobotParamsChange({ velocityScale: newConfig?.robots?.params?.velocityScale || 1 });
onRenderSkipChange(newConfig.env.renderSkip);
setPaused(false);
resetRenderer();
Expand All @@ -111,22 +121,32 @@ const App = () => {
fieldsElemRef.current.innerHTML = '';
}

if (newConfig.env.fields && typeof newConfig.env.fields === 'object') {
if (
newConfig.env.fields
&& typeof newConfig.env.fields === 'object'
&& Object.keys(newConfig.env.fields).length > 0
) {
for (const [fieldKey, field] of Object.entries(newConfig.env.fields)) {
if (!field.url) {
console.error(`Field ${fieldKey} has no url!`);
return;
}

if (!field.title) {
field.title = fieldKey;
}

const imageElemOnload = (canvasElem, context) => {
field.canvasElem = canvasElem;
field.context = context;
fieldsElemRef?.current?.appendChild(canvasElem);
};

createFieldCanvas(fieldKey, field, imageElemOnload);
createFieldCanvas(field, imageElemOnload);
}
}

setSelectedBackgroundField(Object.values(newConfig?.env?.fields || {})?.[0]?.title ?? null);
};

const onTogglePause = () => {
Expand All @@ -153,10 +173,10 @@ const App = () => {
const initialized = simulationIsInitialized();

const selectElem = (
<div id='simulation-selection'>
<div id='scene-selection'>
<p>Scene: </p>
<Select
id='simulation-select'
id='scene-select'
name={selectedScene.label}
value={selectedScene}
onChange={(event) => {
Expand Down Expand Up @@ -190,7 +210,14 @@ const App = () => {
setValue={(newV) => onRobotParamsChange({ velocityScale: newV })}
tooltTip='Controls robots velocity, only works when supported in robot controller.'
/>
<p> TODO: Change other runtime parameters, simulation configuration, and benchmarking configuration.</p>
<CodeEditorSection
title='Scene Configuration'
code={JSON.stringify(config, null, 2)}
setCode={() => {
// TODO: update current configuration
}}
/>
{/* <p> TODO: Change other runtime parameters, simulation configuration, and benchmarking configuration.</p> */}
</>
);

Expand Down Expand Up @@ -222,7 +249,16 @@ const App = () => {
{ label: 'Options', content: optionsElem },
{ label: 'Configuration', content: configurationsElem },
{ label: 'Benchmark', content: benchElem },
{ label: 'Controller', content: controllerCodeEditor }
{ label: 'Controller', content: controllerCodeEditor },
{
label: 'Debug',
content: (
<DebugPanel
title='Scene State'
getSceneState={() => window.scene}
/>
)
}
];

const ui = uiEnabled ? (
Expand All @@ -242,12 +278,22 @@ const App = () => {
<div style={{ width: '100%' }}>
{selectElem}
<QuickActions
toggleElementEnabled={toggleElementEnabled}
setElementEnabled={setElementEnabled}
isElementEnabled={isElementEnabled}
renderSkip={renderSkip}
setRenderSkip={onRenderSkipChange}
setUiEnabled={setUiEnabled}
uiEnabled={uiEnabled}
changeBackground={() => changeBackgroundField(fieldsElemRef.current)}
changeBackground={(fieldTitle) => {
setSelectedBackgroundField(fieldTitle);
changeBackgroundField(fieldsElemRef.current, fieldTitle);
}}
setSelectedBackgroundField={setSelectedBackgroundField}
availableFields={availableFieldTitles}
selectedBackgroundField={selectedBackgroundField}
reset={reset}
onTogglePause={onTogglePause}
paused={paused}
time={time}
benchmarkData={benchmarkData}
simConfig={config}
Expand Down
67 changes: 67 additions & 0 deletions src/client/components/Debug/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import React, { useState } from 'react';
import PropTypes from 'prop-types';

import CodeEditorSection from '../Editors/CodeEditor/CodeEditorSection';

const getCircularReplacer = () => {
const seen = new WeakSet();
return (key, value) => {
if (typeof value === 'object' && value !== null) {
if (seen.has(value)) {
return;
}
seen.add(value);
}

return value;
};
};

const stripScene = (scene) => ({
...scene,
engine: undefined,
world: undefined,
staticObjects: undefined,
robots: scene?.robots?.map((robot) => ({
...robot,
scene: undefined,
engine: undefined,
world: undefined,
sensorManager: undefined,
actuatorManager: undefined
})),
pucks: scene?.pucks?.map((puck) => ({
...puck,
scene: undefined,
engine: undefined,
world: undefined,
sensorManager: undefined,
actuatorManager: undefined
}))
});

function DebugPanel({
title,
getSceneState
}) {
const [debugInfo, setDebugInfo] = useState('');

return (
<CodeEditorSection
key='DebugPanel'
title={title}
setCode={(d) => setDebugInfo(d)}
code={debugInfo}
defaultCode=''
foldAll
getDefaultCode={() => JSON.stringify(stripScene(getSceneState()), getCircularReplacer(), 2)}
/>
);
}

DebugPanel.propTypes = {
title: PropTypes.string,
getSceneState: PropTypes.func
};

export default DebugPanel;
31 changes: 23 additions & 8 deletions src/client/components/Editors/CodeEditor/CodeEditorSection.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* eslint-disable no-console */
/* eslint-disable no-eval */
import React from 'react';
import React, { useRef, useEffect } from 'react';
import PropTypes from 'prop-types';
import AceEditor from 'react-ace';
import IconButton from '@mui/material/IconButton';
Expand All @@ -18,12 +18,26 @@ function CodeEditorSection({
title,
code,
setCode,
defaultCode
defaultCode,
getDefaultCode,
foldAll
}) {
const editorRef = useRef(null);

const resetCode = () => {
setCode(defaultCode);
if (getDefaultCode && typeof getDefaultCode === 'function') {
setCode(getDefaultCode());
} else {
setCode(defaultCode);
}
};

useEffect(() => {
if (foldAll) {
editorRef.current.editor.session.foldAll(1, editorRef.current.editor.session.doc.getAllLines().length);
}
}, [code]);

return (
<Grid container item xs={12} md={12} lg={12} spacing={1}>
<Grid item xs={10} md={11} lg={11}>
Expand All @@ -35,7 +49,7 @@ function CodeEditorSection({
</Grid>
<Grid item xs={2} md={1} lg={1}>
<div className='code-editor-btn-container'>
<Tooltip title="Reset Code">
<Tooltip title="Reset">
<IconButton
color="secondary"
onClick={() => resetCode()}
Expand All @@ -47,10 +61,9 @@ function CodeEditorSection({
</Grid>
<Grid item xs={12} md={12}>
<AceEditor
ref={editorRef}
className='code-editor'
name="robot-controller-code-editor"
placeholder="Robot Controller Code"
value={code ?? defaultCode}
value={code || defaultCode}
onChange={(newCode) => setCode(newCode)}
fontSize={16}
mode='javascript'
Expand All @@ -73,7 +86,9 @@ CodeEditorSection.propTypes = {
title: PropTypes.string,
code: PropTypes.string,
setCode: PropTypes.func,
defaultCode: PropTypes.string
defaultCode: PropTypes.string,
getDefaultCode: PropTypes.func,
foldAll: PropTypes.bool
};

export default CodeEditorSection;
2 changes: 1 addition & 1 deletion src/client/components/Editors/CodeEditor/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import Grid from '@mui/material/Grid';
import Tooltip from '@mui/material/Tooltip';

import CodeEditorSection from './CodeEditorSection';
import downLoadTextAsFile from '../../../utils/download';
import { downLoadTextAsFile } from '../../../utils';

import 'ace-builds/webpack-resolver';
import 'ace-builds/src-noconflict/mode-javascript';
Expand Down
8 changes: 5 additions & 3 deletions src/client/components/Inputs/TitledSlider.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React from 'react';
import propTypes from 'prop-types';
import { Box, Grid, Slider, Typography, Tooltip } from '@mui/material';

const TitledSlider = ({ title, value, setValue, tooltTip }) => (
const TitledSlider = ({ title, value, setValue, tooltTip, minValue, maxValue }) => (
<Box sx={{ width: '100%', height: '75px' }}>
<Grid container spacing={2} item sm={12} lg={10} xl={8}>
<Grid item sm={3} md={2}>
Expand All @@ -14,8 +14,8 @@ const TitledSlider = ({ title, value, setValue, tooltTip }) => (
</Grid>
<Grid item xs sm={9} md={10}>
<Slider
min={0.1}
max={50}
min={minValue || 0}
max={maxValue || 50}
step={0.1}
value={value}
valueLabelDisplay='auto'
Expand All @@ -29,6 +29,8 @@ const TitledSlider = ({ title, value, setValue, tooltTip }) => (
TitledSlider.propTypes = {
title: propTypes.string.isRequired,
value: propTypes.number.isRequired,
minValue: propTypes.number,
maxValue: propTypes.number,
setValue: propTypes.func.isRequired,
tooltTip: propTypes.string
};
Expand Down
2 changes: 2 additions & 0 deletions src/client/components/Options/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ const Options = ({
<TitledSlider
title='Render Skip'
value={renderSkip}
minValue={1}
maxValue={100}
setValue={setRenderSkip}
tooltTip='Number of simulation steps to run between frames, speeds up simulation but can cause app lag.'
/>
Expand Down
Loading

0 comments on commit e317bde

Please sign in to comment.