165 lines
5.2 KiB
TypeScript
165 lines
5.2 KiB
TypeScript
import { markPage, sleepShort, timestamp } from './utils';
|
|
import UpgradeBuildingTask from './Task/UpgradeBuildingTask';
|
|
import UpgradeBuildingAction from './Action/UpgradeBuildingAction';
|
|
import { BuildingQueueFullError, TryLaterError } from './Errors';
|
|
import { TaskQueue, TaskList, Task, TaskId } from './Storage/TaskQueue';
|
|
import ActionQueue from './Storage/ActionQueue';
|
|
import { Args, Command } from './Common';
|
|
import TaskQueueRenderer from './TaskQueueRenderer';
|
|
import ActionController from './Action/ActionController';
|
|
import TaskController from './Task/TaskController';
|
|
import GoToPageAction from './Action/GoToPageAction';
|
|
import CheckBuildingRemainingTimeAction from './Action/CheckBuildingRemainingTimeAction';
|
|
|
|
export default class Scheduler {
|
|
private readonly version: string;
|
|
private taskQueue: TaskQueue;
|
|
private actionQueue: ActionQueue;
|
|
|
|
constructor(version: string) {
|
|
this.version = version;
|
|
this.taskQueue = new TaskQueue();
|
|
this.actionQueue = new ActionQueue();
|
|
}
|
|
|
|
async run() {
|
|
await sleepShort();
|
|
markPage('Executor', this.version);
|
|
|
|
this.renderTaskQueue();
|
|
setInterval(() => this.renderTaskQueue(), 5000);
|
|
|
|
while (true) {
|
|
await this.doLoopStep();
|
|
}
|
|
}
|
|
|
|
private renderTaskQueue() {
|
|
this.log('RENDER TASK QUEUE');
|
|
new TaskQueueRenderer().render(this.taskQueue.seeItems());
|
|
}
|
|
|
|
private async doLoopStep() {
|
|
await sleepShort();
|
|
const currentTs = timestamp();
|
|
const taskCommand = this.taskQueue.get(currentTs);
|
|
|
|
// текущего таска нет, очищаем очередь действий по таску
|
|
if (taskCommand === undefined) {
|
|
this.log('NO ACTIVE TASK');
|
|
this.actionQueue.clear();
|
|
return;
|
|
}
|
|
|
|
const actionCommand = this.popActionCommand();
|
|
|
|
this.log('CURRENT TASK', taskCommand);
|
|
this.log('CURRENT ACTION', actionCommand);
|
|
|
|
if (actionCommand) {
|
|
return await this.processActionCommand(actionCommand, taskCommand);
|
|
}
|
|
|
|
if (taskCommand) {
|
|
return await this.processTaskCommand(taskCommand);
|
|
}
|
|
}
|
|
|
|
private async processTaskCommand(task: Task) {
|
|
const taskController = this.createTaskControllerByName(task.cmd.name);
|
|
this.log('PROCESS TASK CONTROLLER', taskController, task);
|
|
if (taskController) {
|
|
taskController.run(task);
|
|
}
|
|
}
|
|
|
|
private async processActionCommand(cmd: Command, task: Task) {
|
|
const actionController = this.createActionControllerByName(cmd.name);
|
|
this.log('PROCESS ACTION CONTROLLER', cmd.name, actionController);
|
|
if (actionController) {
|
|
await this.runAction(actionController, cmd.args, task);
|
|
}
|
|
}
|
|
|
|
getTaskItems(): TaskList {
|
|
return this.taskQueue.seeItems();
|
|
}
|
|
|
|
completeTask(id: TaskId) {
|
|
this.taskQueue.complete(id);
|
|
}
|
|
|
|
scheduleTask(task: Command): void {
|
|
this.log('PUSH TASK', task);
|
|
this.taskQueue.push(task, timestamp());
|
|
}
|
|
|
|
scheduleActions(actions: Array<Command>): void {
|
|
this.actionQueue.assign(actions);
|
|
}
|
|
|
|
private createTaskControllerByName(
|
|
taskName: string
|
|
): TaskController | undefined {
|
|
switch (taskName) {
|
|
case UpgradeBuildingTask.NAME:
|
|
return new UpgradeBuildingTask(this);
|
|
}
|
|
this.logError('TASK NOT FOUND', taskName);
|
|
return undefined;
|
|
}
|
|
|
|
private popActionCommand(): Command | undefined {
|
|
const actionItem = this.actionQueue.pop();
|
|
if (actionItem === undefined) {
|
|
return undefined;
|
|
}
|
|
return actionItem;
|
|
}
|
|
|
|
private createActionControllerByName(
|
|
actonName: string
|
|
): ActionController | undefined {
|
|
if (actonName === UpgradeBuildingAction.NAME) {
|
|
return new UpgradeBuildingAction(this);
|
|
}
|
|
if (actonName === GoToPageAction.NAME) {
|
|
return new GoToPageAction();
|
|
}
|
|
if (actonName === CheckBuildingRemainingTimeAction.NAME) {
|
|
return new CheckBuildingRemainingTimeAction();
|
|
}
|
|
this.logError('ACTION NOT FOUND', actonName);
|
|
return undefined;
|
|
}
|
|
|
|
private async runAction(action: ActionController, args: Args, task: Task) {
|
|
try {
|
|
await action.run(args, task);
|
|
} catch (e) {
|
|
console.warn('ACTION ABORTED', e.message);
|
|
if (e instanceof TryLaterError) {
|
|
console.warn('TRY', task.id, 'AFTER', e.seconds);
|
|
this.actionQueue.clear();
|
|
this.taskQueue.postpone(task.id, timestamp() + e.seconds);
|
|
}
|
|
if (e instanceof BuildingQueueFullError) {
|
|
console.warn('BUILDING QUEUE FULL, TRY ALL AFTER', e.seconds);
|
|
this.actionQueue.clear();
|
|
this.taskQueue.modify(
|
|
t => t.cmd.name === UpgradeBuildingTask.NAME,
|
|
t => t.withTime(timestamp() + e.seconds)
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
private log(...args) {
|
|
console.log('SCHEDULER:', ...args);
|
|
}
|
|
|
|
private logError(...args) {
|
|
console.error(...args);
|
|
}
|
|
}
|