@bbc/sofie-server-core-integration
Version:
Library for connecting to Core
139 lines • 5.67 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.TimeSync = void 0;
const tslib_1 = require("tslib");
const underscore_1 = tslib_1.__importDefault(require("underscore"));
class TimeSync {
// public serverDelayTime: number = 0.0008 // the minimum time we think the server needs to process our request
_options;
_invalidationCallback;
_timeSource;
_syncDiff; // difference local time vs server time
_syncQuality; // how good the synced time probably is (in ms)
_lastSyncTime; // timestamp (in local time)
_timeInterval = undefined;
constructor(options, timeSource, invalidationCallback) {
this._timeSource = timeSource;
this._options = {
syncPeriod: options.syncPeriod || 1000 * 60 * 10,
minSyncQuality: options.minSyncQuality || 1000 / 50,
minTryCount: options.minTryCount || 3,
maxTryCount: options.maxTryCount || 10,
retryWaitTime: options.retryWaitTime || 300,
serverDelayTime: options.serverDelayTime || 0,
};
this._syncDiff = 0;
this._lastSyncTime = 0;
this._syncQuality = null;
this._invalidationCallback = invalidationCallback;
}
localTime() {
return Date.now();
}
currentTime() {
return this.localTime() + this._syncDiff;
}
get quality() {
return this._syncQuality;
}
get diff() {
return this._syncDiff;
}
isGood() {
return !!(this.quality && this.quality < this._options.minSyncQuality);
}
async init() {
this._timeInterval = setInterval(() => {
this.maybeTriggerSync();
}, this._options.syncPeriod / 2);
return this.syncTime();
}
stop() {
if (this._timeInterval) {
clearInterval(this._timeInterval);
}
}
maybeTriggerSync() {
if (this.localTime() - this._lastSyncTime > this._options.syncPeriod) {
// It's time to do a sync
// log.verbose('triggerSync ' + (this.localTime() - this._lastSyncTime))
this._lastSyncTime = this.localTime();
setTimeout(() => {
this.syncTime().catch((err) => {
console.log(err);
});
}, 1);
}
}
async syncTime() {
const syncResults = [];
let selectedSyncResult = null;
let haveGoodSyncResult = false;
const doSync = async () => {
const startTime = this.localTime(); // Local time at the start of the query
const serverTime = await this._timeSource(); // Server time at some point during the query
if (serverTime) {
const endTime = this.localTime(); // Local time at the end of the query
const transportDuration = endTime - startTime;
const calcLocalTime = startTime + transportDuration / 2 + this._options.serverDelayTime; // Our best guess of the point in time the server probably calculated serverTime
const quality = transportDuration / 2; // The estimated quality of our estimate
const diff = serverTime - calcLocalTime;
return {
diff: diff,
quality: quality,
};
}
return {
diff: 0,
quality: null,
};
};
for (let tryCount = 0; tryCount < this._options.maxTryCount; tryCount++) {
const syncResult = await doSync();
if (!underscore_1.default.isNull(syncResult.quality)) {
syncResults.push(syncResult);
}
if (tryCount >= this._options.minTryCount) {
// Evaluate our progress:
// The best result is the one earliest in time, since the best quality is
// caused by the lowest delay:
let bestResult = underscore_1.default.min(syncResults, (r) => {
return !underscore_1.default.isNull(r.quality) ? r.quality : 999999;
});
if (!bestResult)
bestResult = { diff: 0, quality: null };
if (!underscore_1.default.isNull(bestResult.quality) && bestResult.quality < this._options.minSyncQuality) {
// Our result is good enough
selectedSyncResult = bestResult;
haveGoodSyncResult = true;
break;
}
}
}
if (!selectedSyncResult) {
// We don't have a good sync result.
const bestResult = underscore_1.default.min(syncResults, (r) => {
return !underscore_1.default.isNull(r.quality) ? r.quality : 999999;
});
if (!underscore_1.default.isNull(bestResult.quality) && bestResult.quality < (this._syncQuality || 99990)) {
// It's not a good result, but it's better than what we currently have
selectedSyncResult = bestResult;
}
}
if (selectedSyncResult) {
// we've got a sync result
this._syncDiff = selectedSyncResult.diff;
this._syncQuality = selectedSyncResult.quality;
this._lastSyncTime = this.localTime();
if (this._invalidationCallback)
this._invalidationCallback();
return haveGoodSyncResult;
}
else {
// we never got a result that was good enough
return false;
}
}
}
exports.TimeSync = TimeSync;
//# sourceMappingURL=timeSync.js.map