如何在系统启动期间知道系统时间何时从 NTP 变为正确

jfr*_*d00 5 startup ntpd clock raspbian

我有一个运行 Raspbian(Debian 衍生产品)的 Raspberry Pi,它可以记录实时温度。作为其中的一部分,我需要一个精确到几秒钟的时钟。在服务器启动时的正常操作中,我知道 Raspberry Pi 会定期通过网络连接到 NTP 服务器,以确保其本地时钟合理地接近正确的时间。这种精度水平适合我的应用程序。Raspberry Pi 没有电池供电的时钟来在服务器关闭或断电时保持时间,因此当它第一次启动时,系统时间不正确,直到它建立互联网连接并通过 NTP 获取正确时间。

我发现一个问题是当停电并且 Raspberry Pi 在没有电源的情况下关闭一段时间,然后在某个时间后(比如 30 分钟)恢复供电。Pi 启动,我的应用程序启动并再次开始记录温度,但 Pi 上的时钟不正确,因此它以错误的时间戳记录温度。它似乎以某种方式保存了最后一个已知时间并从那里恢复(重新启动时它不会重置为纪元时间)。最终,当 Pi 所在的 LAN 恢复并重新获得互联网连接时,Pi 将更正其时间(通过 NTP),但在此之前,我记录了不准确的时间戳。

我想弄清楚解决这个问题的最佳行动方案是什么?

出于维护原因,我宁愿不添加电池支持的附加时钟(不希望任何人必须更换电池,因为这本质上是嵌入式设备,用户不易访问)。

我愿意让我的应用程序推迟记录温度,直到我知道从网络中准确检索了时间,但我什至不知道如何检测该状态。任何人都知道如何知道现在何时从 NTP 服务器更新时间并且现在是正确的?

我的应用程序是通过在启动时运行脚本来启动的。

关于如何解决这个问题的任何其他想法?

jfr*_*d00 0

我决定在我的解决方案中需要一个函数,该函数将返回一个在系统时间准确时解决的承诺。然后,我可以在启动时调用该函数,并使用其反馈来了解何时再次开始记录温度。为此,我决定使用 ntpClient 自己获取准确的时间并将其与本地系统时间进行比较。当两者达到期望的精度时,我就可以解决承诺。如果不是,我设置一个计时器并重新检查时间,继续直到最终本地系统时间变得足够准确。到目前为止,在几次停电期间(这也是最初发现时间不准确问题的地方),该功能运行得很好。

这是我使用的代码:

const Promise = require('bluebird');
const ntpClient = Promise.promisifyAll(require('ntp-client'));
const log = require('./log');

function Decay(startT, maxT, decayAmount, decayTimes) {
    // startT is initial delay (e.g. 5 seconds)
    // maxT is the max delay this ever returns (e.g. 5 minutes)
    // decayAmount is how much to decay when a threshold is crossed (e.g. increase by 0.5)
    // decayTimes is how many invocations should trigger a decayAmount (e.g. every 5 times)

    // example: var d = new Decay(5000, 5*60*1000, .5, 5);
    // each 5 seconds, to a max of 5 minutes, getting 50% longer every 5 invocations

    // make sure decayTimes is at least 1 and not negative
    decayTimes = Math.max(decayTimes, 1);
    var num = 0;
    var currentDelay = startT;
    var start = Date.now();

    this.val = function() {
        var elapsed = Date.now() - start;
        // if evenly divisible by decayTimes, then bump the increment
        if (num !== 0 && num % decayTimes === 0) {
            currentDelay = Math.min(Math.round((1 + decayAmount) * currentDelay), maxT);
        }
        ++num;
        return currentDelay;
    };
}

function checkSystemTime(precision) {
    precision = precision || 5000;
    return ntpClient.getNetworkTimeAsync("pool.ntp.org", 123).then(function(ntpTime) {
        return Math.abs(ntpTime.getTime() - Date.now()) <= precision;
    });
}

function waitForAccurateSystemTime(precision, howLong) {
    var start = Date.now();
    // retry starts every 5 seconds, repeats 5 times, then increases by 50% 
    //   up until longest retry time of once every 15 minutes
    var decay = new Decay(5000, 15*60*1000, .5, 5);
    var errCntr = 0;
    var inaccurateCntr = 0;

    function logRetries() {
        // only log anything if there were more than five consecutive errors
        if (errCntr > 5 || inaccurateCntr > 0) {
            log(7, "Time synchronization issue, errCntr = " + errCntr + ", inaccurateCntr = " + inaccurateCntr);
        }
    }

    return new Promise(function(resolve, reject) {

        function check() {
            checkSystemTime(precision).then(function(accurate) {
                if (accurate) {
                    resolve(true);
                } else {
                    ++inaccurateCntr;
                    again();
                }
            }, again);
        }

        function again() {
            ++errCntr;
            if (errCntr == 10) {
                // only log once here that we're in a retry loop on 10th retry
                // final logging will be done later
                log(7, "In retry loop waiting for system time to agree with ntp server time");
            }
            // if we're only supposed to go for a certain amount of time, then check to see
            // if we exceeded that amount of time.  If not, set timer for next decay() value.
            if (!howLong || Date.now() - start <= howLong) {
                setTimeout(check, decay.val());
            } else {
                var err = "timeout waiting for accurate system time";
                log(7, err);
                reject(err);
            }
        }

        check();
    }).then(function(result) {
        logRetries();
        return result;
    }).catch(function(err) {
        logRetries();
        throw err;
    });
}

module.exports = {
    checkSystemTime: checkSystemTime,
    waitForAccurateSystemTime: waitForAccurateSystemTime,
    Decay: Decay
};
Run Code Online (Sandbox Code Playgroud)

而且,我这样使用它:

const validTime = require("./valid-time");
validTime.waitForAccurateSystemTime(2 * 60 * 1000, 0).then({
    // start operation here that requires accurate system time
}).catch({
    // abort process, no accurate system time could be found
});
Run Code Online (Sandbox Code Playgroud)