import * as React from 'react'; import { Outlet } from 'react-router-dom'; import { Stack } from '@fluentui/react'; import { SlideNavBtns } from '@components/nav/slideNav/SlideNavBtns'; import { EXPERIMENT, TRIALS } from '@static/datamodel'; import NavCon from '@components/nav/Nav'; import MessageInfo from '@components/common/MessageInfo'; import { COLUMN } from '@static/const'; import { isManagerExperimentPage } from '@static/function'; import '@style/App.scss'; import '@style/common/common.scss'; import '@style/experiment/trialdetail/trialsDetail.scss'; const echarts = require('echarts/lib/echarts'); echarts.registerTheme('nni_theme', { color: '#3c8dbc' }); export const AppContext = React.createContext({ interval: 10, // sendons columnList: COLUMN, experimentUpdateBroadcast: 0, trialsUpdateBroadcast: 0, metricGraphMode: 'max', bestTrialEntries: '10', maxDurationUnit: 'm', expandRowIDs: new Set(['']), // eslint-disable-next-line @typescript-eslint/no-empty-function changeColumn: (_val: string[]): void => {}, // eslint-disable-next-line @typescript-eslint/no-empty-function changeMetricGraphMode: (_val: 'max' | 'min'): void => {}, // eslint-disable-next-line @typescript-eslint/no-empty-function changeMaxDurationUnit: (_val: string): void => {}, // eslint-disable-next-line @typescript-eslint/no-empty-function changeEntries: (_val: string): void => {}, // eslint-disable-next-line @typescript-eslint/no-empty-function updateOverviewPage: () => {}, // eslint-disable-next-line @typescript-eslint/no-empty-function updateDetailPage: () => {}, // eslint-disable-next-line @typescript-eslint/no-empty-function changeExpandRowIDs: (_val: string, _type?: string): void => {}, // eslint-disable-next-line @typescript-eslint/no-empty-function startTimer: () => {}, // eslint-disable-next-line @typescript-eslint/no-empty-function closeTimer: (): void => {}, // eslint-disable-next-line @typescript-eslint/no-empty-function refreshDetailTable: (): void => {} }); interface AppState { interval: number; columnList: string[]; experimentUpdateBroadcast: number; trialsUpdateBroadcast: number; maxDurationUnit: string; metricGraphMode: 'max' | 'min'; // tuner's optimize_mode filed isillegalFinal: boolean; expWarningMessage: string; bestTrialEntries: string; // for overview page: best trial entreis expandRowIDs: Set; timerIdList: number[]; } class App extends React.Component<{}, AppState> { private timerId = 0; constructor(props: {}) { super(props); this.state = { interval: 10, // sendons columnList: COLUMN, experimentUpdateBroadcast: 0, trialsUpdateBroadcast: 0, metricGraphMode: 'max', maxDurationUnit: 'm', isillegalFinal: false, expWarningMessage: '', bestTrialEntries: '10', expandRowIDs: new Set(), timerIdList: [] }; } async componentDidMount(): Promise { await Promise.all([EXPERIMENT.init(), TRIALS.init()]); this.setState(state => ({ experimentUpdateBroadcast: state.experimentUpdateBroadcast + 1, trialsUpdateBroadcast: state.trialsUpdateBroadcast + 1, metricGraphMode: EXPERIMENT.optimizeMode === 'minimize' ? 'min' : 'max' })); this.startTimer(); } render(): React.ReactNode { const { interval, columnList, experimentUpdateBroadcast, trialsUpdateBroadcast, metricGraphMode, isillegalFinal, expWarningMessage, bestTrialEntries, maxDurationUnit, expandRowIDs } = this.state; if (experimentUpdateBroadcast === 0 || trialsUpdateBroadcast === 0) { return null; } const errorList = [ { errorWhere: TRIALS.jobListError(), errorMessage: TRIALS.getJobErrorMessage() }, { errorWhere: EXPERIMENT.experimentError(), errorMessage: EXPERIMENT.getExperimentMessage() }, { errorWhere: EXPERIMENT.statusError(), errorMessage: EXPERIMENT.getStatusMessage() }, { errorWhere: TRIALS.MetricDataError(), errorMessage: TRIALS.getMetricDataErrorMessage() }, { errorWhere: TRIALS.latestMetricDataError(), errorMessage: TRIALS.getLatestMetricDataErrorMessage() }, { errorWhere: TRIALS.metricDataRangeError(), errorMessage: TRIALS.metricDataRangeErrorMessage() } ]; return ( {isManagerExperimentPage() ? null : (
{/* search space & config & dispatcher, nnimanagerlog*/} {/* if api has error field, show error message */} {errorList.map( (item, key) => item.errorWhere && (
) )} {isillegalFinal && (
)} {/* {this.props.children}
)}
); } 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 })); } // experiment status and /trial-jobs api's status could decide website update if (['DONE', 'ERROR', 'STOPPED', 'VIEWED'].includes(EXPERIMENT.status) || TRIALS.jobListError()) { // experiment finished, refresh once more to ensure consistency this.setState(() => ({ interval: 0 })); this.closeTimer(); return; } this.startTimer(); }; public lastRefresh = async (): Promise => { await EXPERIMENT.update(); await TRIALS.update(true); this.setState(state => ({ experimentUpdateBroadcast: state.experimentUpdateBroadcast + 1, trialsUpdateBroadcast: state.trialsUpdateBroadcast + 1 })); }; public changeInterval = (interval: number): void => { this.setState(() => ({ interval: interval })); // reset interval val this.closeTimer(); // close page auto refresh if (interval !== 0) { this.refresh(); } }; public changeColumn = (columnList: string[]): void => { this.setState({ columnList: columnList }); }; public changeExpandRowIDs = (id: string, type?: string): void => { const currentExpandRowIDs = this.state.expandRowIDs; if (!currentExpandRowIDs.has(id)) { currentExpandRowIDs.add(id); } else { if (!(type !== undefined && type === 'chart')) { currentExpandRowIDs.delete(id); } } this.setState({ expandRowIDs: currentExpandRowIDs }); }; public changeMetricGraphMode = (val: 'max' | 'min'): void => { this.setState({ metricGraphMode: val }); }; // overview best trial module public changeEntries = (entries: string): void => { this.setState({ bestTrialEntries: entries }); }; // overview max duration unit public changeMaxDurationUnit = (unit: string): void => { this.setState({ maxDurationUnit: unit }); }; public updateOverviewPage = (): void => { this.setState(state => ({ experimentUpdateBroadcast: state.experimentUpdateBroadcast + 1 })); }; public updateDetailPage = async (): Promise => { this.setState(state => ({ trialsUpdateBroadcast: state.trialsUpdateBroadcast + 1 })); }; // fetch api to update table record data public refreshDetailTable = async (): Promise => { await TRIALS.update(true); this.setState(state => ({ trialsUpdateBroadcast: state.trialsUpdateBroadcast + 1 })); }; // start to refresh page automatically public startTimer = (): void => { this.timerId = window.setTimeout(this.refresh, this.state.interval * 1000); const storeTimerList = this.state.timerIdList; storeTimerList.push(this.timerId); this.setState(() => ({ timerIdList: storeTimerList })); }; public closeTimer = (): void => { const { timerIdList } = this.state; timerIdList.forEach(item => { window.clearTimeout(item); }); }; } export default App;