Skip to content
Snippets Groups Projects
Commit b3afb8b5 authored by David Sehnal's avatar David Sehnal
Browse files

mol-plugin: Task progress tracking

parent a1359477
No related branches found
No related tags found
No related merge requests found
...@@ -18,10 +18,12 @@ import { Loci, EmptyLoci } from 'mol-model/loci'; ...@@ -18,10 +18,12 @@ import { Loci, EmptyLoci } from 'mol-model/loci';
import { Representation } from 'mol-repr'; import { Representation } from 'mol-repr';
import { CreateStructureFromPDBe } from './state/actions/basic'; import { CreateStructureFromPDBe } from './state/actions/basic';
import { LogEntry } from 'mol-util/log-entry'; import { LogEntry } from 'mol-util/log-entry';
import { TaskManager } from './util/task-manager';
export class PluginContext { export class PluginContext {
private disposed = false; private disposed = false;
private ev = RxEventHelper.create(); private ev = RxEventHelper.create();
private tasks = new TaskManager();
readonly state = new PluginState(this); readonly state = new PluginState(this);
readonly commands = new PluginCommand.Manager(); readonly commands = new PluginCommand.Manager();
...@@ -33,7 +35,8 @@ export class PluginContext { ...@@ -33,7 +35,8 @@ export class PluginContext {
cameraSnapshots: this.state.cameraSnapshots.events, cameraSnapshots: this.state.cameraSnapshots.events,
snapshots: this.state.snapshots.events, snapshots: this.state.snapshots.events,
}, },
log: this.ev<LogEntry>() log: this.ev<LogEntry>(),
task: this.tasks.events
}; };
readonly behaviors = { readonly behaviors = {
...@@ -76,8 +79,8 @@ export class PluginContext { ...@@ -76,8 +79,8 @@ export class PluginContext {
return type === 'string' ? await req.text() : new Uint8Array(await req.arrayBuffer()); return type === 'string' ? await req.text() : new Uint8Array(await req.arrayBuffer());
} }
async runTask<T>(task: Task<T>) { runTask<T>(task: Task<T>) {
return await task.run(p => console.log(p.root.progress.message), 250); return this.tasks.run(task);
} }
dispose() { dispose() {
...@@ -86,6 +89,7 @@ export class PluginContext { ...@@ -86,6 +89,7 @@ export class PluginContext {
this.canvas3d.dispose(); this.canvas3d.dispose();
this.ev.dispose(); this.ev.dispose();
this.state.dispose(); this.state.dispose();
this.tasks.dispose();
this.disposed = true; this.disposed = true;
} }
......
...@@ -17,6 +17,7 @@ import { StateSnapshots } from './state'; ...@@ -17,6 +17,7 @@ import { StateSnapshots } from './state';
import { List } from 'immutable'; import { List } from 'immutable';
import { LogEntry } from 'mol-util/log-entry'; import { LogEntry } from 'mol-util/log-entry';
import { formatTime } from 'mol-util'; import { formatTime } from 'mol-util';
import { BackgroundTaskProgress } from './task';
export class Plugin extends React.Component<{ plugin: PluginContext }, {}> { export class Plugin extends React.Component<{ plugin: PluginContext }, {}> {
render() { render() {
...@@ -33,6 +34,9 @@ export class Plugin extends React.Component<{ plugin: PluginContext }, {}> { ...@@ -33,6 +34,9 @@ export class Plugin extends React.Component<{ plugin: PluginContext }, {}> {
<TrajectoryControls /> <TrajectoryControls />
</div> </div>
<ViewportControls /> <ViewportControls />
<div style={{ position: 'absolute', left: '10px', bottom: '10px', color: 'white' }}>
<BackgroundTaskProgress />
</div>
</div> </div>
<div style={{ position: 'absolute', width: '300px', right: '0', top: '0', padding: '10px', overflowY: 'scroll' }}> <div style={{ position: 'absolute', width: '300px', right: '0', top: '0', padding: '10px', overflowY: 'scroll' }}>
<CurrentObject /> <CurrentObject />
......
/**
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
*/
import * as React from 'react';
import { PluginComponent } from './base';
import { OrderedMap } from 'immutable';
import { TaskManager } from 'mol-plugin/util/task-manager';
import { filter } from 'rxjs/operators';
import { Progress } from 'mol-task';
export class BackgroundTaskProgress extends PluginComponent<{ }, { tracked: OrderedMap<number, TaskManager.ProgressEvent> }> {
componentDidMount() {
this.subscribe(this.plugin.events.task.progress.pipe(filter(e => e.level !== 'none')), e => {
this.setState({ tracked: this.state.tracked.set(e.id, e) })
});
this.subscribe(this.plugin.events.task.finished, ({ id }) => {
this.setState({ tracked: this.state.tracked.delete(id) })
})
}
state = { tracked: OrderedMap<number, TaskManager.ProgressEvent>() };
render() {
return <div>
{this.state.tracked.valueSeq().map(e => <ProgressEntry key={e!.id} event={e!} />)}
</div>;
}
}
class ProgressEntry extends PluginComponent<{ event: TaskManager.ProgressEvent }> {
render() {
const root = this.props.event.progress.root;
const subtaskCount = countSubtasks(this.props.event.progress.root) - 1;
const pr = root.progress.isIndeterminate
? void 0
: <>[{root.progress.current}/{root.progress.max}]</>;
const subtasks = subtaskCount > 0
? <>[{subtaskCount} subtask(s)]</>
: void 0
return <div>
{root.progress.message} {pr} {subtasks}
</div>;
}
}
function countSubtasks(progress: Progress.Node) {
if (progress.children.length === 0) return 1;
let sum = 0;
for (const c of progress.children) sum += countSubtasks(c);
return sum;
}
\ No newline at end of file
/**
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
*/
import { Task, Progress } from 'mol-task';
import { RxEventHelper } from 'mol-util/rx-event-helper';
import { now } from 'mol-util/now';
export { TaskManager }
class TaskManager {
private ev = RxEventHelper.create();
private id = 0;
readonly events = {
progress: this.ev<TaskManager.ProgressEvent>(),
finished: this.ev<{ id: number }>()
};
private track(id: number) {
return (progress: Progress) => {
const elapsed = now() - progress.root.progress.startedTime;
progress.root.progress.startedTime
this.events.progress.next({
id,
level: elapsed < 250 ? 'none' : elapsed < 1500 ? 'background' : 'overlay',
progress
});
};
}
async run<T>(task: Task<T>): Promise<T> {
const id = this.id++;
try {
const ret = await task.run(this.track(id), 100);
return ret;
} finally {
this.events.finished.next({ id });
}
}
dispose() {
this.ev.dispose();
}
}
namespace TaskManager {
export type ReportLevel = 'none' | 'background' | 'overlay'
export interface ProgressEvent {
id: number,
level: ReportLevel,
progress: Progress
}
function delay(time: number): Promise<void> {
return new Promise(res => setTimeout(res, time));
}
export function testTask(N: number) {
return Task.create('Test', async ctx => {
let i = 0;
while (i < N) {
await delay(100 + Math.random() * 200);
if (ctx.shouldUpdate) {
await ctx.update({ message: 'Step ' + i, current: i, max: N, isIndeterminate: false });
}
i++;
}
})
}
}
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment