import { tsLog } from '../logger/logger' import { tsString } from '../utils/string' import { tsEntry } from '../entry/entry' import { consts } from '../consts' /** * 时间模块 */ export module tsTime { /** * 当前时间unix时间戳/秒 * @returns */ export function unixSec(): number { return Math.floor(Date.now() / 1000) } /** * 当前时间unix时间戳/毫秒 * @returns */ export function unixMsec(): number { return Date.now() } /** * 定时器对象 */ export class Timer extends tsEntry.Entry { private _running: boolean = false get running() { return this._running } private _stopped: boolean = true get stopped() { return this._stopped } private _interval: number = 0 get interval() { return this._interval } private _execLimit: number = -1 get execLimit() { return this._execLimit } private _permanent: boolean = true get permanent(): boolean { return this._permanent } private _execCount: number = 0 get execCount() { return this._execCount } private callback: (timer: Timer, caller: any) => void = null private caller: any = null constructor(id: number, name: string, interval: number, callback: (timer: Timer, caller: any) => void, caller: any = null, limit: number = -1, permanent: boolean = false) { super(id, name) this.entryName = 'Timer' this._interval = interval this.callback = callback this.caller = caller this._execLimit = limit this._permanent = permanent } destroy(): void { this.callback = null this.caller = null } start(): boolean { if (this.running) { this.error('start err:already running') return false } this.debug('start') this._running = true this._stopped = false return true } /** * 立即停止并销毁 * @returns 成功:true 失败:false */ stop(): boolean { if (this.stopped) { this.error('stop err:already stopped') return false } this.debug('stop') this._running = false this._stopped = true this.destroy() return true } /** * 暂停 * @returns 成功:true 失败:false */ pause(): boolean { if (this.stopped) { return false } if (!this.running) { this.error('pause err:not running') return false } this.debug('pause') this._running = false return true } /** * 恢复 * @returns 成功:true 失败:false */ resume(): boolean { if (this.stopped) { return false } if (this.running) { // this.error("resume err:is running"); return false } this.debug('resume') return TimeManager.resumeTimer(this) } private async _exec() { if (!this.running) { return } let begin = tsTime.unixMsec() this.callback(this, this.caller) let use = tsTime.unixMsec() - begin if (use >= tsLog.ExecTimeLevel.Error) { this.errorf('exec limit:{0} count:{1} use:{2}', this.execLimit, this._execCount, use) } else if (use >= tsLog.ExecTimeLevel.Warn) { this.warnf('exec limit:{0} count:{1} use:{2}', this.execLimit, this._execCount, use) } else { this.tracef('exec limit:{0} count:{1} use:{2}', this.execLimit, this._execCount, use) } } exec(): boolean { if (this.stopped || !this.running) { this.error('exec err:is stopped') return false } if (!this.callback) { this.error('exec err:callback is null') return false } this._execCount++ this._exec() return true } checkLimit(): boolean { if (this.execLimit == -1) { return false } return this.execCount >= this.execLimit } } /** * 闹钟对象 */ export class Clocker extends tsEntry.Entry { private _running: boolean = false get running() { return this._running } private _stopped: boolean = true get stopped() { return this._stopped } private _interval: string = '' get interval() { return this._interval } private callback: (clocker: Clocker, caller: any) => void = null private caller: any = null constructor(id: number, name: string, interval: string, callback: (clocker: Clocker, caller: any) => void, caller: any = null) { super(id, name) this.entryName = 'Clocker' this._interval = interval this.callback = callback this.caller = caller } destroy(): void { this.callback = null this.caller = null } start(): boolean { if (this.running) { this.error('start err:already running') return false } this.debug('start') this._running = true this._stopped = false return true } /** * 立即停止并销毁 * @returns */ stop(): boolean { if (this.stopped) { this.error('stop err:already stopped') return false } this.debug('stop') this._running = false this._stopped = true this.destroy() return true } /** * 暂停 * @returns */ pause(): boolean { if (!this.running) { this.error('pause err:not running') return false } this.debug('pause') this._running = false return true } /** * 恢复 * @returns */ resume(): boolean { if (this.running) { this.error('resume err:is running') return false } this.debug('resume') this._running = true } private async _exec() { if (!this.running) { return } let begin = tsTime.unixMsec() this.callback(this, this.caller) let use = tsTime.unixMsec() - begin if (use >= tsLog.ExecTimeLevel.Error) { this.errorf('exec use:{0}', use) } else if (use >= tsLog.ExecTimeLevel.Warn) { this.warnf('exec use:{0}', use) } else { this.tracef('exec use:{0}', use) } } exec(): boolean { if (this.stopped || !this.running) { this.error('exec err:is stopped') return false } if (!this.callback) { this.error('exec err:callback is null') return false } this._exec() return true } } export class TimeManager { private static tag: string = 'TimeManager' private static autoIncIdStart: number = consts.autoIncIdStart private static nameTimerMap: Map = new Map() private static intervalTimersMap: Map = new Map() private static autoIncInterval: number = 0 private static nameClockerMap: Map = new Map() private static intervalClockersMap: Map = new Map() private static nameTimerHistoryMap: Map = new Map() static start(): void { setInterval(() => { this.autoIncInterval++ let timers = this.intervalTimersMap.get(this.autoIncInterval) if (timers && timers.length > 0) { let begin = tsTime.unixMsec() let execCount = 0 let delCount = 0 this.intervalTimersMap.delete(this.autoIncInterval) let len = timers.length for (let index = 0; index < len; index++) { const element = timers[index] if (element.stopped) { this.delTimer(element) delCount++ continue } if (!element.running) { continue } execCount++ element.exec() if (element.checkLimit()) { this.delTimer(element) delCount++ continue } let nextInterval = this.autoIncInterval + element.interval if (this.intervalTimersMap.has(nextInterval)) { this.intervalTimersMap.get(nextInterval).push(element) } else { this.intervalTimersMap.set(nextInterval, [element]) } } let use = tsTime.unixMsec() - begin if (use >= tsLog.ExecTimeLevel.Error) { tsLog.Logger.errorf(this.tag + ' loop exec:{0} dels:{1} use:{2}', execCount, delCount, use) } else if (use >= tsLog.ExecTimeLevel.Warn) { tsLog.Logger.warnf(this.tag + ' loop exec:{0} dels:{1} use:{2}', execCount, delCount, use) } else { tsLog.Logger.tracef(this.tag + ' loop exec:{0} dels:{1} use:{2}', execCount, delCount, use) } } let date = new Date() let key = tsString.formatNumber(date.getHours(), 2) + ':' + tsString.formatNumber(date.getMinutes(), 2) + ':' + tsString.formatNumber(date.getSeconds(), 2) let clockers = this.intervalClockersMap.get(key) if (clockers && clockers.length > 0) { let clockersNew: Clocker[] = null for (let index = 0; index < clockers.length; index++) { const element = clockers[index] if (!element.running) { if (element.stopped) { this.delClocker(element) } continue } element.exec() if (!clockersNew) { clockersNew = [] } clockersNew.push(element) } this.intervalClockersMap.set(key, clockersNew) } }, 1000) } static hasTimer(name: string): boolean { return this.nameTimerMap.has(name) } static getTimer(name: string): Timer { return this.nameTimerMap.get(name) } static addTimer(name: string, interval: number, callback: (timer: Timer, caller: any) => void, caller: any = null, limit: number = -1, permanent: boolean = false): Timer { if (tsString.isNullOrEmpty(name) || interval < 1 || Math.ceil(interval) > interval) { tsLog.Logger.errorf(this.tag + ' addTimer name:{0} interval:{1} limit:{2} fail err: invalid params', name, interval, limit) return null } let old = this.getTimer(name) if (old) { if (!old.stopped) { old.stop() } tsLog.Logger.warnf(this.tag + ' addTimer name:{0} interval:{1} limit:{2} replace {3}', name, interval, limit, old.fullName) } if (this.autoIncIdStart == consts.autoIncIdStart) { this.start() } let id = this.autoIncIdStart this.autoIncIdStart++ let timer = new Timer(id, name, interval, callback, caller, limit, permanent) let timerName = timer.fullName this.nameTimerMap.set(name, timer) let nextInterval = this.autoIncInterval + interval if (this.intervalTimersMap.has(nextInterval)) { this.intervalTimersMap.get(nextInterval).push(timer) } else { this.intervalTimersMap.set(nextInterval, [timer]) } if (!timer) { let errMsg = tsString.format(this.tag + ' addTimer {0} instance is {1}', timerName, timer) tsLog.Logger.error(errMsg) throw new Error(errMsg) } else { tsLog.Logger.debugf(this.tag + ' addTimer {0} size:{1} pocessing:{2}', timer.fullName, this.nameTimerMap.size, this.intervalTimersMap.size) timer.start() } this.nameTimerHistoryMap.set(name, interval) // tsLog.Logger.warn(this.tag + " addTimer history:", this.nameTimerHistoryMap); return timer } static delTimer(timer: Timer): boolean { if (!timer || !timer.id) { tsLog.Logger.warnf(this.tag + ' delTimer {0} err: invalid instance', timer) return false } if (timer.running) { timer.stop() } let old = this.getTimer(timer.name) if (!old || timer.id != old.id) { tsLog.Logger.warnf(this.tag + ' delTimer del:{0} old:{1} err: invalid instance', timer.fullName, old ? old.fullName : 'null') return false } let ok = this.nameTimerMap.delete(timer.name) if (ok) { tsLog.Logger.debugf(this.tag + ' delTimer {0} size:{1} processing:{2}', timer.fullName, this.nameTimerMap.size, this.intervalTimersMap.size) } else { tsLog.Logger.errorf(this.tag + ' delTimer {0} err: does not exist', timer.fullName) } return ok } static delTimerByName(name: string): boolean { let timer = this.getTimer(name) return this.delTimer(timer) } static resumeTimer(timer: Timer): boolean { if (!this.hasTimer(timer.name)) { tsLog.Logger.errorf(this.tag + ' resumeTimer {0} does not exist', timer.fullName) return false } if (timer.running) { tsLog.Logger.errorf(this.tag + ' resumeTimer {0} is running', timer.fullName) return false } let nextInterval = this.autoIncInterval + timer.interval if (this.intervalTimersMap.has(nextInterval)) { this.intervalTimersMap.get(nextInterval).push(timer) } else { this.intervalTimersMap.set(nextInterval, [timer]) } tsLog.Logger.debugf(this.tag + ' resumeTimer {0} size:{1} pocessing:{2}', timer.fullName, this.nameTimerMap.size, this.intervalTimersMap.size) timer.start() return true } static pause(): void { this.nameTimerMap.forEach(function (timer: Timer) { if (!timer.permanent) { timer.pause() } }) } static resume(): void { this.nameTimerMap.forEach(function (timer: Timer) { if (!timer.permanent) { timer.resume() } }) } static getStatistics(): string[] { let arr: string[] = [] this.nameTimerMap.forEach(function (timer: Timer) { let str = tsString.format('名称:{0} 间隔:{1}s 状态:{2} 执行计数:{3}', timer.name, timer.interval, timer.running, timer.execCount) arr.push(str) }) return arr } static hasClocker(name: string): boolean { return this.nameClockerMap.has(name) } static getClocker(name: string): Clocker { return this.nameClockerMap.get(name) } static addClocker(name: string, interval: string, callback: (clocker: Clocker, caller: any) => void, caller: any = null): Clocker { if (tsString.isNullOrEmpty(name) || interval.split(':').length != 3) { tsLog.Logger.errorf(this.tag + ' addClocker name:{0} interval:{1} fail err: invalid params', name, interval) return null } let old = this.getClocker(name) if (old) { if (!old.stopped) { old.stop() } tsLog.Logger.warnf(this.tag + ' addClocker name:{0} interval:{1} replace {2}', name, interval, old.fullName) } if (this.autoIncIdStart == consts.autoIncIdStart) { this.start() } let id = this.autoIncIdStart this.autoIncIdStart++ let clocker = new Clocker(id, name, interval, callback, caller) let clockerName = clocker.fullName this.nameClockerMap.set(name, clocker) if (this.intervalClockersMap.has(interval)) { this.intervalClockersMap.get(interval).push(clocker) } else { this.intervalClockersMap.set(interval, [clocker]) } if (!clocker) { let errMsg = tsString.format(this.tag + ' addClocker {0} instance is {1}', clockerName, clocker) tsLog.Logger.error(errMsg) throw new Error(errMsg) } else { tsLog.Logger.debugf(this.tag + ' addClocker {0} size:{1} pocessing:{2}', clocker.fullName, this.nameClockerMap.size, this.intervalClockersMap.size) clocker.start() } return clocker } static delClocker(clocker: Clocker): boolean { if (!clocker || !clocker.id) { tsLog.Logger.warnf(this.tag + ' delClocker {0} err: invalid instance', clocker) return false } if (clocker.running) { clocker.stop() } let old = this.getClocker(clocker.name) if (!old || clocker.id != old.id) { return false } let ok = this.nameClockerMap.delete(clocker.name) if (ok) { tsLog.Logger.debugf(this.tag + ' delClocker {0} size:{1} processing:{2}', clocker.fullName, this.nameClockerMap.size, this.intervalClockersMap.size) } else { tsLog.Logger.errorf(this.tag + ' delClocker {0} err: does not exist', clocker.fullName) } return ok } static delClockerByName(name: string): boolean { let clocker = this.getClocker(name) return this.delClocker(clocker) } } /** * 通过名称获取定时器 * @param name 名称 * @returns Timer实例 */ export function getTimer(name: string): Timer { return TimeManager.getTimer(name) } /** * 添加秒级定时器 * @param name 名称,全局唯一,同名覆盖 * @param interval 间隔 单位:秒 * @param callback 回调 * @param caller 调用者(回传参数,可用于解决this指针问题) * @param limit 执行次数(次数达到自动删除 默认-1(无限次数)) * @param permanent 常驻状态(true: 不受全局暂停影响 默认false) * @returns Timer实例 */ export function addTimer( name: string, interval: number, callback: (timer: Timer, caller: any) => void, caller: any = null, limit: number = -1, permanent: boolean = false ): Timer { return TimeManager.addTimer(name, interval, callback, caller, limit, permanent) } /** * 删除定时器 * @param timer Timer实例 * @returns 成功:true 失败:false */ export function delTimer(timer: Timer): boolean { return TimeManager.delTimer(timer) } /** * 通过名称删除定时器 * @param name 名称 * @returns 成功:true 失败:false */ export function delTimerByName(name: string): boolean { return TimeManager.delTimerByName(name) } /** * 暂停所有定时器 */ export function pauseAll(): void { TimeManager.pause() } /** * 恢复所有定时器 */ export function resumeAll(): void { TimeManager.resume() } /** * 获取统计信息 * @returns ["名称:app_askBetListTimer 间隔:300s 状态:true 执行计数:1", ...] */ export function getStatistics(): string[] { return TimeManager.getStatistics() } /** * 通过名称获取闹钟 * @param name 名称 * @returns Clocker实例 */ export function getClocker(name: string): Clocker { return TimeManager.getClocker(name) } /** * 添加时分秒级闹钟 * @param name 名称 * @param interval 间隔 示例: 12:00:00 * @param callback 回调 * @param caller 调用者(回传参数,可用于解决this指针问题) * @returns Clocker实例 */ export function addClocker(name: string, interval: string, callback: (clocker: Clocker, caller: any) => void, caller: any = null): Clocker { return TimeManager.addClocker(name, interval, callback, caller) } /** * 删除闹钟 * @param clocker Clocker实例 * @returns 成功:true 失败:false */ export function delClocker(clocker: Clocker): boolean { return TimeManager.delClocker(clocker) } /** * 通过名称删除闹钟 * @param name 名称 * @returns 成功:true 失败:false */ export function delClockerByName(name: string): boolean { return TimeManager.delClockerByName(name) } }