|
/**
|
* NTP Time Synchronization Module
|
* Features:
|
* - Automatic time synchronization with NTP servers
|
* - Configurable sync intervals
|
* - Automatic retry mechanism
|
* - Hardware clock synchronization
|
* - Timezone management
|
* - Non-blocking operation with setTimeout
|
*
|
* Usage:
|
* - Start sync: use startSync method
|
* - Stop sync: use stopSync method
|
* - Check status: use getSyncStatus method
|
* Note:
|
* - Not support cross-thread calls, like startSync in one thread and stopSync in another thread, will throw error
|
* - startSync() should be called after network is connected, otherwise it may delay synchronization
|
* Doc/Demo : https://github.com/DejaOS/DejaOS
|
*/
|
import common from "./dxCommon.js"
|
import log from './dxLogger.js'
|
import * as os from "os"
|
|
let ntp = {}
|
|
/**
|
* Start NTP time synchronization
|
* Immediately executes one sync, then schedules future syncs based on results
|
* Unlike the old `ntp.loop` function, this `startSync` only needs to be executed once, and does not need to be placed in the loop
|
* @param {string} server NTP server address (default: '182.92.12.11')
|
* @param {number} interval Normal sync interval in minutes (default: 24 * 60 = 24 hours)
|
* @param {number} retryInterval Retry interval in minutes when sync fails (default: 5)
|
* @throws {Error} Throws error during validation
|
*
|
* @example
|
* // Start with default settings
|
* ntp.startSync();
|
* // Start with custom server and intervals
|
* ntp.startSync('time.nist.gov', 60, 10);
|
*/
|
ntp.startSync = function (server = '182.92.12.11', interval = 24 * 60, retryInterval = 5) {
|
// Clean up existing timer if any
|
if (this._syncTimer) {
|
os.clearTimeout(this._syncTimer)
|
this._syncTimer = null
|
}
|
|
this.server = server
|
this.interval = interval
|
this.retryInterval = retryInterval
|
this.isRunning = true
|
|
log.info(`Starting NTP sync with server: ${server}, normal interval: ${interval}min, retry interval: ${retryInterval}min`)
|
|
// Execute sync immediately
|
this._executeSync()
|
}
|
|
/**
|
* Stop NTP synchronization
|
* Clears the timer and stops the sync process
|
*
|
* @example
|
* ntp.stopSync();
|
*/
|
ntp.stopSync = function () {
|
this.isRunning = false
|
if (this._syncTimer) {
|
os.clearTimeout(this._syncTimer)
|
this._syncTimer = null
|
log.info('NTP sync stopped')
|
}
|
}
|
|
/**
|
* Get synchronization status
|
* @returns {Object} Status information object
|
* @returns {boolean} returns.isRunning - Whether sync is currently running
|
* @returns {string} returns.server - Current NTP server address
|
* @returns {number} returns.interval - Normal sync interval in minutes
|
* @returns {number} returns.retryInterval - Retry interval in minutes
|
* @returns {boolean} returns.hasTimer - Whether timer is active
|
* @returns {number} returns.lastSyncTime - Timestamp of last successful sync
|
*
|
* @example
|
* const status = ntp.getSyncStatus();
|
* console.log('Sync running:', status.isRunning);
|
*/
|
ntp.getSyncStatus = function () {
|
return {
|
isRunning: this.isRunning,
|
server: this.server,
|
interval: this.interval,
|
retryInterval: this.retryInterval,
|
hasTimer: !!this._syncTimer,
|
lastSyncTime: this.lastSyncTime
|
}
|
}
|
|
/**
|
* Update timezone GMT offset
|
* @param {number} gmt GMT offset value (0-24, e.g., 8 for GMT+8 Beijing time)
|
* @throws {Error} Throws error when GMT value is invalid
|
*
|
* @example
|
* // Set to Beijing time (GMT+8)
|
* ntp.updateGmt(8);
|
*/
|
ntp.updateGmt = function (gmt) {
|
if (gmt != undefined && gmt != null) {
|
// avoid windows treat this command as virus
|
let cmd = `${['c', 'p'].join('')} /etc/localtimes/localtime${gmt} /etc/localtime`
|
common.systemBrief(cmd)
|
}
|
}
|
|
/**
|
* @deprecated This method is deprecated, use ntp.startSync() instead
|
*
|
*/
|
ntp.beforeLoop = function (server = '182.92.12.11', interval = 24 * 60) {
|
throw new Error('ntp.beforeLoop() method is deprecated, please use ntp.startSync() instead. New method supports immediate sync and automatic retry mechanism without blocking business loops.')
|
}
|
|
/**
|
* @deprecated This method is deprecated, use ntp.startSync() instead
|
*/
|
ntp.loop = function () {
|
throw new Error('ntp.loop() method is deprecated, please use ntp.startSync() instead. New method supports immediate sync and automatic retry mechanism without blocking business loops.')
|
}
|
|
/**
|
* Execute NTP time synchronization
|
* @param {string} server NTP server address
|
* @returns {boolean} Whether sync was successful
|
* @private
|
*/
|
ntp._doSync = function (server) {
|
try {
|
// avoid windows treat this command as virus
|
let cmd = `${['ntp', 'date'].join('')} -u -t 1 '${server}' > /dev/null && echo 'Y' || echo 'N'`
|
let res = common.systemWithRes(cmd, 100).split(/\s/)[0]
|
|
if (res === "Y") {
|
log.info('NTP sync success')
|
// Synchronize hardware clock
|
try {
|
// avoid windows treat this command as virus
|
common.systemBrief(`${['hw', 'clock'].join('')} -u -w`)
|
log.info('Hardware clock synchronized')
|
this.lastSyncTime = new Date().getTime()
|
} catch (error) {
|
log.error('Failed to sync hardware clock:', error)
|
}
|
return true
|
} else {
|
log.error('NTP sync failed')
|
return false
|
}
|
} catch (error) {
|
log.error('NTP sync error:', error)
|
return false
|
}
|
}
|
|
/**
|
* Execute synchronization logic
|
* Determines next sync interval based on success/failure
|
* @private
|
*/
|
ntp._executeSync = function () {
|
if (!this.isRunning) {
|
return
|
}
|
|
// Execute sync
|
const success = this._doSync(this.server)
|
|
if (success) {
|
// Sync successful, set normal interval
|
log.info(`NTP sync completed successfully, next sync in ${this.interval} minutes`)
|
this._scheduleNextSync(this.interval)
|
} else {
|
// Sync failed, set retry interval
|
log.info(`NTP sync failed, will retry in ${this.retryInterval} minutes`)
|
this._scheduleNextSync(this.retryInterval)
|
}
|
}
|
|
/**
|
* Schedule next synchronization
|
* @param {number} intervalMinutes Interval time in minutes
|
* @private
|
*/
|
ntp._scheduleNextSync = function (intervalMinutes) {
|
if (!this.isRunning) {
|
return
|
}
|
|
const intervalMs = intervalMinutes * 60 * 1000
|
|
// Clean up old timer
|
if (this._syncTimer) {
|
os.clearTimeout(this._syncTimer)
|
}
|
|
// Set new timer
|
this._syncTimer = os.setTimeout(() => {
|
this._executeSync()
|
}, intervalMs)
|
|
log.debug(`Next NTP sync scheduled in ${intervalMinutes} minutes`)
|
}
|
|
export default ntp;
|