Files
BR_YKC/4G/源代码/lib/ntp.lua

151 lines
5.6 KiB
Lua
Raw Normal View History

2026-03-31 15:46:04 +08:00
--- 模块功能:网络授时.
-- 重要提醒!!!!!!
-- 本功能模块采用多个免费公共的NTP服务器来同步时间
-- 并不能保证任何时间任何地点都能百分百同步到正确的时间
-- 所以,如果用户项目中的业务逻辑严格依赖于时间同步功能
-- 则不要使用使用本功能模块,建议使用自己的应用服务器来同步时间
-- 参考 http://ask.openluat.com/article/912 加深对授时功能的理解
-- @module ntp
-- @author openLuat
-- @license MIT
-- @copyright openLuat
-- @release 2017.10.21
require "misc"
require "socket"
require "utils"
require "log"
local sbyte, ssub = string.byte, string.sub
module(..., package.seeall)
-- NTP服务器域名集合
local timeServer = {
"cn.pool.ntp.org",
"edu.ntp.org.cn",
"cn.ntp.org.cn",
"s2c.time.edu.cn",
"time1.aliyun.com",
"tw.pool.ntp.org",
"0.cn.pool.ntp.org",
"0.tw.pool.ntp.org",
"1.cn.pool.ntp.org",
"1.tw.pool.ntp.org",
"3.cn.pool.ntp.org",
"3.tw.pool.ntp.org",
}
-- 同步超时等待时间
local NTP_TIMEOUT = 8000
-- 同步是否完成标记
local ntpEnd = false
-- 获取NTP服务器地址列表
-- @return table,服务器地址列表
-- @usage local addtable = ntp.getServers()
function getServers()
return timeServer
end
-- 设置NTP服务器地址列表
-- @table st,tab类型服务器地址列表
-- @return 无
-- @usage ntp.getServers({"1edu.ntp.org.cn","cn.ntp.org.cn"})
function setServers(st)
timeServer = st
end
-- NTP同步标志
-- @return bool,NTP的同步状态true为成功,fasle为失败
-- @usage local sta = ntp.isEnd()
function isEnd()
return ntpEnd
end
local sTs,sFnc,sFun
-- 同步时间随机每个NTP服务器尝试1次超时8秒,适用于被任务函数调用
-- @number ts,每隔ts小时同步1次
-- @function fnc,同步成功后回调函数
-- @function fun,同步成功前回调函数
-- @return nil
-- @usage ntp.ntpTime() -- 只同步1次
-- @usage ntp.ntpTime(1) -- 1小时同步1次
-- @usage ntp.ntpTime(nil,fnc) -- 只同步1次同步成功后执行fnc()
-- @usage ntp.ntpTime(24,fnc) -- 24小时同步1次同步成功后执行fnc()
function ntpTime(ts, fnc, fun)
local rc, data, ntim
local sTs,sFnc,sFun = ts or sTs, fnc or sFnc, fun or sFun
ntpEnd = false
while true do
local tUnusedSvr = {}
for i = 1, #timeServer do
tUnusedSvr[i] = timeServer[i]
end
for i = 1, #timeServer do
while not socket.isReady() do sys.waitUntil('IP_READY_IND') end
local c = socket.udp()
local idx = rtos.tick() % #tUnusedSvr + 1
if c:connect(tUnusedSvr[idx], "123") then
if c:send(string.fromHex("E30006EC0000000000000000314E31340000000000000000000000000000000000000000000000000000000000000000")) then
rc, data = c:recv(NTP_TIMEOUT)
if rc and #data == 48 then
ntim = os.date("*t", (sbyte(ssub(data, 41, 41)) - 0x83) * 2 ^ 24 + (sbyte(ssub(data, 42, 42)) - 0xAA) * 2 ^ 16 + (sbyte(ssub(data, 43, 43)) - 0x7E) * 2 ^ 8 + (sbyte(ssub(data, 44, 44)) - 0x80) + 1)
if type(sFun) == "function" then sFun() end
misc.setClock(ntim, sFnc)
ntpEnd = true
c:close()
break
end
end
end
local cnt, n, m = #tUnusedSvr, 1
for m = 1, cnt do
if m ~= idx then
tUnusedSvr[n] = tUnusedSvr[m]
n = n + 1
end
end
tUnusedSvr[cnt] = nil
c:close()
sys.wait(1000)
end
if ntpEnd then
sys.publish("NTP_SUCCEED")
log.info("ntp.timeSync is date:", ntim.year .. "/" .. ntim.month .. "/" .. ntim.day .. "," .. ntim.hour .. ":" .. ntim.min .. ":" .. ntim.sec)
else
log.warn("ntp.timeSync is error!")
end
if sTs == nil or type(sTs) ~= "number" then break end
sys.wait(sTs * 3600 * 1000)
end
end
--- ntp同步时间任务.
-- 重要提醒!!!!!!
-- 本功能模块采用多个免费公共的NTP服务器来同步时间
-- 并不能保证任何时间任何地点都能百分百同步到正确的时间
-- 所以,如果用户项目中的业务逻辑严格依赖于时间同步功能
-- 则不要使用使用本功能模块,建议使用自己的应用服务器来同步时间
-- @number[opt=nil] period 调用本接口会立即同步一次每隔period小时再自动同步1次nil表示仅同步一次
-- @function[opt=nil] fnc 同步结束,设置系统时间后的回调函数,回调函数的调用形式为:
-- fnc(timeresult)
-- time表示设置之后的系统时间table类型例如{year=2017,month=2,day=14,hour=14,min=19,sec=23}
-- result为true表示成功false或者nil为失败
-- @function[opt=nil] fun 同步结束设置系统时间前的回调函数回调函数的调用形式为fun()
-- @return nil
--
-- @usage
-- 立即同步一次(仅同步这一次):
-- ntp.timeSync()
--
-- 立即同步一次之后每隔1小时自动同步一次
-- ntp.timeSync(1)
--
-- 立即同步一次仅同步这一次同步结束后执行fnc(time,result)
-- ntp.timeSync(nil,fnc)
--
-- 立即同步一次之后每隔24小时自动同步一次每次同步结束后执行fnc(time,result)
-- ntp.timeSync(24,fnc)
function timeSync(period, fnc, fun)
sTs,sFnc,sFun = period, fnc, fun
sys.taskInit(ntpTime)
end