2019-07-28 04:56:12 +08:00
|
|
|
import {ITaskManager} from '../interfaces/ITaskManager';
|
|
|
|
import {TaskProgressDTO} from '../../../common/entities/settings/TaskProgressDTO';
|
|
|
|
import {ITask} from './ITask';
|
|
|
|
import {TaskRepository} from './TaskRepository';
|
|
|
|
import {Config} from '../../../common/config/private/Config';
|
2019-12-06 22:53:34 +08:00
|
|
|
import {TaskScheduleDTO, TaskTriggerType} from '../../../common/entities/task/TaskScheduleDTO';
|
|
|
|
import {Logger} from '../../Logger';
|
|
|
|
|
|
|
|
const LOG_TAG = '[TaskManager]';
|
2019-07-28 04:56:12 +08:00
|
|
|
|
|
|
|
export class TaskManager implements ITaskManager {
|
|
|
|
|
2019-12-06 22:53:34 +08:00
|
|
|
protected timers: NodeJS.Timeout[] = [];
|
|
|
|
|
|
|
|
constructor() {
|
|
|
|
this.runSchedules();
|
|
|
|
}
|
2019-07-28 04:56:12 +08:00
|
|
|
|
|
|
|
getProgresses(): { [id: string]: TaskProgressDTO } {
|
|
|
|
const m: { [id: string]: TaskProgressDTO } = {};
|
2019-12-08 02:28:57 +08:00
|
|
|
TaskRepository.Instance.getAvailableTasks()
|
|
|
|
.filter(t => t.Progress)
|
|
|
|
.forEach(t => m[t.Name] = t.Progress);
|
2019-07-28 04:56:12 +08:00
|
|
|
return m;
|
|
|
|
}
|
|
|
|
|
2019-12-06 22:53:34 +08:00
|
|
|
start<T>(taskName: string, config: T): void {
|
2019-07-28 04:56:12 +08:00
|
|
|
const t = this.findTask(taskName);
|
|
|
|
if (t) {
|
|
|
|
t.start(config);
|
2019-12-06 22:53:34 +08:00
|
|
|
} else {
|
|
|
|
Logger.warn(LOG_TAG, 'cannot find task to start:' + taskName);
|
2019-07-28 04:56:12 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
stop(taskName: string): void {
|
|
|
|
const t = this.findTask(taskName);
|
|
|
|
if (t) {
|
|
|
|
t.stop();
|
2019-12-08 02:28:57 +08:00
|
|
|
if (global.gc) {
|
|
|
|
global.gc();
|
|
|
|
}
|
2019-12-06 22:53:34 +08:00
|
|
|
} else {
|
|
|
|
Logger.warn(LOG_TAG, 'cannot find task to stop:' + taskName);
|
2019-07-28 04:56:12 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
getAvailableTasks(): ITask<any>[] {
|
|
|
|
return TaskRepository.Instance.getAvailableTasks();
|
|
|
|
}
|
|
|
|
|
2019-12-06 22:53:34 +08:00
|
|
|
public stopSchedules(): void {
|
|
|
|
this.timers.forEach(timer => clearTimeout(timer));
|
|
|
|
this.timers = [];
|
|
|
|
}
|
|
|
|
|
2019-08-20 18:54:45 +08:00
|
|
|
public runSchedules(): void {
|
2019-12-06 22:53:34 +08:00
|
|
|
this.stopSchedules();
|
|
|
|
Logger.info(LOG_TAG, 'Running task schedules');
|
2019-08-20 18:54:45 +08:00
|
|
|
Config.Server.tasks.scheduled.forEach(schedule => {
|
2019-12-06 22:53:34 +08:00
|
|
|
const nextDate = this.getDateFromSchedule(new Date(), schedule);
|
|
|
|
if (nextDate && nextDate.getTime() > Date.now()) {
|
2019-12-08 01:16:37 +08:00
|
|
|
Logger.debug(LOG_TAG, 'running schedule: ' + schedule.taskName +
|
2019-12-06 22:53:34 +08:00
|
|
|
' at ' + nextDate.toLocaleString(undefined, {hour12: false}));
|
2019-08-20 18:54:45 +08:00
|
|
|
|
2019-12-06 22:53:34 +08:00
|
|
|
const timer: NodeJS.Timeout = setTimeout(() => {
|
2019-08-20 18:54:45 +08:00
|
|
|
this.start(schedule.taskName, schedule.config);
|
2019-12-06 22:53:34 +08:00
|
|
|
this.timers = this.timers.filter(t => t !== timer);
|
|
|
|
}, nextDate.getTime() - Date.now());
|
|
|
|
this.timers.push(timer);
|
|
|
|
|
|
|
|
} else {
|
2019-12-08 01:16:37 +08:00
|
|
|
Logger.debug(LOG_TAG, 'skipping schedule:' + schedule.taskName);
|
2019-08-20 18:54:45 +08:00
|
|
|
}
|
2019-12-06 22:53:34 +08:00
|
|
|
|
2019-08-20 18:54:45 +08:00
|
|
|
});
|
|
|
|
}
|
2019-07-28 04:56:12 +08:00
|
|
|
|
2019-12-06 22:53:34 +08:00
|
|
|
protected getDateFromSchedule(refDate: Date, schedule: TaskScheduleDTO): Date {
|
|
|
|
switch (schedule.trigger.type) {
|
|
|
|
case TaskTriggerType.scheduled:
|
|
|
|
return new Date(schedule.trigger.time);
|
|
|
|
|
|
|
|
case TaskTriggerType.periodic:
|
|
|
|
const nextValidHM = (date: Date, h: number, m: number, dayDiff: number): Date => {
|
2019-07-28 04:56:12 +08:00
|
|
|
|
2019-12-06 22:53:34 +08:00
|
|
|
if (date.getHours() < h || (date.getHours() === h && date.getMinutes() <= m)) {
|
|
|
|
date.setHours(h);
|
|
|
|
date.setMinutes(m);
|
|
|
|
} else {
|
|
|
|
date.setTime(date.getTime() + dayDiff);
|
|
|
|
date.setHours(h);
|
|
|
|
date.setMinutes(m);
|
|
|
|
}
|
|
|
|
return date;
|
|
|
|
};
|
|
|
|
|
|
|
|
const getNextDayOfTheWeek = (dayOfWeek: number, h: number, m: number): Date => {
|
|
|
|
const date = new Date(refDate);
|
|
|
|
date.setDate(refDate.getDate() + (dayOfWeek + 1 + 7 - refDate.getDay()) % 7);
|
|
|
|
if (date.getDay() === refDate.getDay()) {
|
|
|
|
return new Date(refDate);
|
|
|
|
}
|
|
|
|
date.setHours(0, 0, 0, 0);
|
|
|
|
return date;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const hour = Math.floor(schedule.trigger.atTime / 1000 / (60 * 60));
|
|
|
|
const minute = (schedule.trigger.atTime / 1000 / 60) % 60;
|
|
|
|
|
|
|
|
if (schedule.trigger.periodicity <= 6) { // Between Monday and Sunday
|
|
|
|
const nextRunDate = getNextDayOfTheWeek(schedule.trigger.periodicity, hour, minute);
|
|
|
|
return nextValidHM(nextRunDate, hour, minute, 7 * 24 * 60 * 60 * 1000);
|
|
|
|
}
|
|
|
|
|
|
|
|
// every day
|
|
|
|
return nextValidHM(new Date(refDate), hour, minute, 24 * 60 * 60 * 1000);
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
protected findTask<T = any>(taskName: string): ITask<T> {
|
|
|
|
return this.getAvailableTasks().find(t => t.Name === taskName);
|
2019-07-28 04:56:12 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|