2025-11-15 18:58:30 +00:00
|
|
|
import { MinPriorityQueue } from '@datastructures-js/priority-queue';
|
|
|
|
|
import { Arrays } from '../../../browser/tools/Arrays';
|
|
|
|
|
import { DateHelper } from '../../../browser/date/DateHelper';
|
|
|
|
|
import { DateMath } from '../../../browser/date/DateMath';
|
|
|
|
|
import { Task } from './Task';
|
|
|
|
|
import { iTaskScheduler } from './iTaskScheduler';
|
|
|
|
|
import { RJLog } from '../../log/RJLog';
|
2025-11-16 13:30:12 +00:00
|
|
|
import { DateFormatter } from '../../../browser/date/DateFormatter';
|
|
|
|
|
import { Duration } from '../../../browser/date/Duration';
|
2025-11-15 18:58:30 +00:00
|
|
|
|
|
|
|
|
export class Scheduler
|
|
|
|
|
{
|
|
|
|
|
// [ Tasks ]
|
|
|
|
|
_queue:Task[] = [];
|
|
|
|
|
_scheduledTasks = new Set<string>();
|
|
|
|
|
|
|
|
|
|
// [ Next Task ]
|
|
|
|
|
_taskTimerCallback:NodeJS.Timeout;
|
|
|
|
|
_nextTaskID:string;
|
|
|
|
|
|
|
|
|
|
// [ Settings ]
|
|
|
|
|
_maxScheduleDurationDays:number = 1;
|
|
|
|
|
|
|
|
|
|
// [ Schedulers ]
|
|
|
|
|
_taskSchedulers:iTaskScheduler[] = [];
|
|
|
|
|
get taskSchedulers(){ return this._taskSchedulers; }
|
|
|
|
|
|
|
|
|
|
constructor()
|
|
|
|
|
{
|
|
|
|
|
this._queue =[];
|
|
|
|
|
this._taskTimerCallback = null;
|
|
|
|
|
this._nextTaskID = null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
get maxDate():Date
|
|
|
|
|
{
|
|
|
|
|
return DateMath.fromNowAddDays( this._maxScheduleDurationDays );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async update()
|
|
|
|
|
{
|
|
|
|
|
let maxDate = this.maxDate;
|
|
|
|
|
|
|
|
|
|
let allTasks:Task[] = [];
|
|
|
|
|
|
|
|
|
|
for ( let ts of this._taskSchedulers )
|
|
|
|
|
{
|
|
|
|
|
let tasks = await ts.getTasksToSchedule( maxDate );
|
|
|
|
|
allTasks = allTasks.concat( tasks );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this.scheduleTasks( allTasks );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
scheduleTasks( tasks:Task[] )
|
|
|
|
|
{
|
|
|
|
|
let maxDate = DateMath.fromNowAddDays( this._maxScheduleDurationDays );
|
2025-11-24 14:25:10 +00:00
|
|
|
tasks = tasks.filter( t => ! this._scheduledTasks.has( t.id ) && ! DateMath.isAfter( t.utcDate, maxDate ) );
|
2025-11-15 18:58:30 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
this._queue = this._queue.concat( tasks );
|
|
|
|
|
tasks.forEach( t => this._scheduledTasks.add( t.id ) );
|
|
|
|
|
|
2025-11-24 14:25:10 +00:00
|
|
|
this._queue.sort( ( a, b ) => { return a.utcDate.getTime() - b.utcDate.getTime() } );
|
2025-11-15 18:58:30 +00:00
|
|
|
|
|
|
|
|
let newNextTask = this._queue[ 0 ];
|
|
|
|
|
|
|
|
|
|
if ( this._nextTaskID && newNextTask.id != this._nextTaskID )
|
|
|
|
|
{
|
|
|
|
|
this._resetTimer();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this._updateTimer();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected _resetTimer()
|
|
|
|
|
{
|
|
|
|
|
if ( this._taskTimerCallback )
|
|
|
|
|
{
|
|
|
|
|
clearTimeout( this._taskTimerCallback );
|
|
|
|
|
this._taskTimerCallback = null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this._nextTaskID = null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected _updateTimer()
|
|
|
|
|
{
|
|
|
|
|
// RJLog.log( "_updateTimer:" );
|
|
|
|
|
|
|
|
|
|
if ( this._taskTimerCallback || this._queue.length == 0 )
|
|
|
|
|
{
|
|
|
|
|
// RJLog.log( "Nothing to do. Has Timer:", this._taskTimerCallback, "Queue Length:", this._queue.length );
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let task = this._queue[ 0 ];
|
|
|
|
|
this._nextTaskID = task.id;
|
|
|
|
|
|
2025-11-24 14:25:10 +00:00
|
|
|
let delay = Math.max( 0, task.utcDate.getTime() - Date.now() );
|
2025-11-15 18:58:30 +00:00
|
|
|
|
2025-11-24 14:25:10 +00:00
|
|
|
RJLog.log( task.id, DateFormatter.HMS( task.utcDate ), "delaying:", Duration.fromMilliSeconds( delay ) + " sec" );
|
2025-11-15 18:58:30 +00:00
|
|
|
|
|
|
|
|
this._taskTimerCallback = setTimeout(
|
|
|
|
|
() =>
|
|
|
|
|
{
|
|
|
|
|
this._taskTimerCallback = null;
|
|
|
|
|
Arrays.remove( this._queue, task );
|
|
|
|
|
this._scheduledTasks.delete( task.id );
|
|
|
|
|
this._nextTaskID = null;
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
task.action();
|
|
|
|
|
}
|
|
|
|
|
catch( e )
|
|
|
|
|
{
|
|
|
|
|
RJLog.log( e );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this._updateTimer();
|
|
|
|
|
|
|
|
|
|
},
|
|
|
|
|
delay
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-16 13:30:12 +00:00
|
|
|
cancelUserAppTask( taskID:string, userID:string, appID:string ):boolean
|
2025-11-15 18:58:30 +00:00
|
|
|
{
|
2025-11-16 13:30:12 +00:00
|
|
|
if ( ! taskID || ! userID || ! appID )
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let index = this._queue.findIndex( e => e.id === taskID );
|
2025-11-15 18:58:30 +00:00
|
|
|
|
2025-11-16 13:30:12 +00:00
|
|
|
if ( index == -1 )
|
|
|
|
|
{
|
|
|
|
|
RJLog.log( "Task not found:", taskID );
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let foundTask = this._queue[ index ];
|
|
|
|
|
|
|
|
|
|
if ( foundTask.userID !== userID || foundTask.appID !== appID )
|
|
|
|
|
{
|
|
|
|
|
RJLog.log( "Found task has not matching user:", foundTask.userID, foundTask.appID );
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-15 18:58:30 +00:00
|
|
|
if ( index === -1 )
|
|
|
|
|
{
|
2025-11-16 13:30:12 +00:00
|
|
|
return false;
|
2025-11-15 18:58:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Arrays.removeAt( this._queue, index );
|
|
|
|
|
|
2025-11-16 13:30:12 +00:00
|
|
|
if ( taskID !== this._nextTaskID )
|
|
|
|
|
{
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-15 18:58:30 +00:00
|
|
|
this._resetTimer();
|
|
|
|
|
this._updateTimer();
|
2025-11-16 13:30:12 +00:00
|
|
|
|
|
|
|
|
return true;
|
2025-11-15 18:58:30 +00:00
|
|
|
}
|
2025-11-16 13:30:12 +00:00
|
|
|
|
2025-11-15 18:58:30 +00:00
|
|
|
}
|