import * as React from 'react'; import { Stack } from 'office-ui-fabric-react'; import { COLUMN } from './static/const'; import { EXPERIMENT, TRIALS } from './static/datamodel'; import NavCon from './components/NavCon'; import MessageInfo from './components/Modals/MessageInfo'; import './App.scss'; interface AppState { interval: number; columnList: string[]; experimentUpdateBroadcast: number; trialsUpdateBroadcast: number; metricGraphMode: 'max' | 'min'; // tuner's optimize_mode filed isillegalFinal: boolean; expWarningMessage: string; } class App extends React.Component<{}, AppState> { private timerId!: number | null; private dataFormatimer!: number; constructor(props: {}) { super(props); this.state = { interval: 10, // sendons columnList: COLUMN, experimentUpdateBroadcast: 0, trialsUpdateBroadcast: 0, metricGraphMode: 'max', isillegalFinal: false, expWarningMessage: '' }; } async componentDidMount(): Promise { await Promise.all([EXPERIMENT.init(), TRIALS.init()]); this.setState(state => ({ experimentUpdateBroadcast: state.experimentUpdateBroadcast + 1 })); this.setState(state => ({ trialsUpdateBroadcast: state.trialsUpdateBroadcast + 1 })); this.timerId = window.setTimeout(this.refresh, this.state.interval * 1000); this.setState({ metricGraphMode: (EXPERIMENT.optimizeMode === 'minimize' ? 'min' : 'max') }); // final result is legal // get a succeed trial,see final result data's format // eslint-disable-next-line @typescript-eslint/no-non-null-assertion this.dataFormatimer = window.setInterval(this.getFinalDataFormat, this.state.interval * 1000); } getFinalDataFormat = (): void => { for(let i = 0; this.state.isillegalFinal === false; i++){ if(TRIALS.succeededTrials()[0] !== undefined && TRIALS.succeededTrials()[0].final !== undefined){ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const oneSucceedTrial = JSON.parse(JSON.parse(TRIALS.succeededTrials()[0].final!.data)); if (typeof oneSucceedTrial === 'number' || oneSucceedTrial.hasOwnProperty('default')) { window.clearInterval(this.dataFormatimer); break; } else { // illegal final data this.setState(() => ({ isillegalFinal: true, expWarningMessage: 'WebUI support final result as number and dictornary includes default keys, your experiment final result is illegal, please check your data.' })); window.clearInterval(this.dataFormatimer); } } else { break; } } } changeInterval = (interval: number): void => { this.setState({ interval }); if (this.timerId === null && interval !== 0) { window.setTimeout(this.refresh); } else if (this.timerId !== null && interval === 0) { window.clearTimeout(this.timerId); } } // TODO: use local storage changeColumn = (columnList: string[]): void => { this.setState({ columnList: columnList }); } changeMetricGraphMode = (val: 'max' | 'min'): void => { this.setState({ metricGraphMode: val }); } render(): React.ReactNode { const { interval, columnList, experimentUpdateBroadcast, trialsUpdateBroadcast, metricGraphMode, isillegalFinal, expWarningMessage } = this.state; if (experimentUpdateBroadcast === 0 || trialsUpdateBroadcast === 0) { return null; // TODO: render a loading page } const reactPropsChildren = React.Children.map(this.props.children, child => React.cloneElement( child as React.ReactElement, { interval, columnList, changeColumn: this.changeColumn, experimentUpdateBroadcast, trialsUpdateBroadcast, metricGraphMode, changeMetricGraphMode: this.changeMetricGraphMode }) ); return (
{isillegalFinal &&
} {reactPropsChildren}
); } private refresh = async (): Promise => { const [experimentUpdated, trialsUpdated] = await Promise.all([EXPERIMENT.update(), TRIALS.update()]); if (experimentUpdated) { this.setState(state => ({ experimentUpdateBroadcast: state.experimentUpdateBroadcast + 1 })); } if (trialsUpdated) { this.setState(state => ({ trialsUpdateBroadcast: state.trialsUpdateBroadcast + 1 })); } if (['DONE', 'ERROR', 'STOPPED'].includes(EXPERIMENT.status)) { // experiment finished, refresh once more to ensure consistency if (this.state.interval > 0) { this.setState({ interval: 0 }); this.lastRefresh(); } } else if (this.state.interval !== 0) { this.timerId = window.setTimeout(this.refresh, this.state.interval * 1000); } } public async lastRefresh(): Promise { await EXPERIMENT.update(); await TRIALS.update(true); this.setState(state => ({ experimentUpdateBroadcast: state.experimentUpdateBroadcast + 1 })); this.setState(state => ({ trialsUpdateBroadcast: state.trialsUpdateBroadcast + 1 })); } } export default App;