skynet框架之日程表設計

參考雲風大神的例子,對其進行了改進,支持屢次提交單個日程,改變時間後,提早日程觸發時間。api

--[[
    t提供了兩種方案
    方案1和2
]]

local skynet = require "skynet"
local service  = require "skynet.service"

local schedule = {}
local service_addr

-- ttime = { month=, day=, wday=, hour= , min= }
function schedule.submit(ttime)
    
    return skynet.call(service_addr, "lua", ttime)
    -- TOO(do somthing)
    -- skynet.error(" schedule.submit")
end

function schedule.changetime(ttime)
    local tmp = {}
    for k, v in pairs(ttime) do
        tmp[k] = v
    end
    tmp.changetime = true
    return skynet.call(service_addr, "lua", tmp)

end

skynet.init(function()
    local schedule_service = function()
        local skynet = require "skynet"
        local task  = {session = 0,difftime = 0}
        local tasksession = {}

        local function next_time(now, ttime) -- 參數如今的時間,日程時間
            local ntime = {
                year = now.year,
                month = now.month,
                day = now.day,
                hour = ttime.hour or 0,
                min = ttime.min or 0,
                sec = ttime.sec,
            }
            -- skynet.error(now.year,now.month,now.day,now.hour,now.min,now.sec,now.wday)
            -- skynet.error(ttime.year,ttime.month,ttime.day,ttime.hour,ttime.min,ttime.sec,ttime.wday)
            -- skynet.error(ntime.year,ntime.month,ntime.day,ntime.hour,ntime.min,ntime.sec,ntime.wday)
            
            if ttime.wday then -- 每週的活動
                -- set week
                assert(ttime.day == nil and ttime.month == nil)
                ntime.day = ntime.day + ttime.wday - now.wday
                local t = os.time(ntime)
                if t < now.time then
                    ntime.day = ntime.day + 7
                end
            else -- 若是日程時間使日期的話
                -- set day,no week day
                if ttime.day then
                    ntime.day = ttime.day
                end
                if ttime.month then
                    ntime.month = ttime.month
                end
                local t = os.time(ntime) -- 換成秒
                if t < now.time then --時間和如今比是過去的時間變成下個月或者下一年
                    if ttime.month then
                        ntime.year = ntime.year + 1
                    else
                        ntime.month = ntime.month + 1
                    end
                end
            end
            skynet.error("next_time ",ntime.year,ntime.month,ntime.day,ntime.hour,ntime.min,ntime.sec,ntime.wday)
            return os.time(ntime) -- 將下第二天程的時間換成秒返回
        end

        local function changetime(ttime)
            local ctime = math.floor(skynet.time()) --向下取整
            --local now = math.floor(skynet.time())
            skynet.error("changetime",ctime)
            local current = os.date("*t",ctime) --將ctime 轉換成*t的表結構
            current.time = ctime -- 保存原來的時間
            if not ttime.hour then -- 若是原來表結構中沒有小時,將如今的小時賦值給它
                ttime.hour = current.hour
            end
            if not ttime.min then -- 若是原來表結構中沒有分鐘,將如今的分鐘賦值給它
                ttime.min = current.min
            end
            ttime.sec = current.sec -- 如今的秒賦值給它
            local ntime = next_time(current,ttime) -- 計算出下一第二天程的時間

            skynet.error(string.format("Change time to nex %s",os.date(nil,ntime)))
            skynet.error(string.format("Change time to cur %s",os.date(nil,ctime)))
            local unique_diff = os.difftime(ntime,ctime) -- 方案1計算出下第二天程距離如今時間還有多少秒
            --task.difftime = os.difftime(ntime,ctime)
            --skynet.error("changetime task.difftime",task.difftime)
            skynet.error("changetime unique_diff",unique_diff)
            for k, v in pairs(task) do  -- 日期改變了才 遍歷日程表,喚醒全部掛起的線程
                local ttask_diff = 0 --方案1
                if type(v) == "table" then
                    ttask_diff =  os.difftime(v.time, math.floor(skynet.time())) -- 方案1
                    
                    if unique_diff > ttask_diff then --方案1
                        task[k].difftime = ttask_diff --方案1
                        --task.difftime = ttask_diff 
                    end
                    skynet.error("changetime unique_diff",k,unique_diff)
                    skynet.error("changetime task.difftime",k,task.difftime)
                    skynet.error("changetime ttask_diff",k,ttask_diff)
                    skynet.error("changetime session",k, type(v.co))
                    skynet.wakeup(v.co) -- 喚醒服務
                end
            end
            skynet.ret()
        end

        -- {year = 2020, month=12, day=31, hour=20 , min=30}
        local function submit(_, addr, ttime)
            if ttime.changetime then
                return changetime(ttime)
            end
            local session = task.session + 1
            task.session = session
            task[session] = {time = 0, address = addr, difftime = 0}-- 方案1
            skynet.error("submit task.session",task.session)
            repeat -- 循環 直到時間到了
                
                local now = math.floor(skynet.time()) -- 真的當前時間戳
                skynet.error(string.format("submit now  %s",os.date(nil,now)))
                local ctime = now  + task[session].difftime -- 方案1如今的時間+日程距離如今的秒數 = 假的當前的時間戳
                local ctime = now  + task.difftime -- 方案2
                skynet.error("submit task[session].difftime",session,task[session].difftime)
                -- if  ctime > task[session].time then -- 方案2
                --     break -- 方案2
                -- end-- 方案2
                local current = os.date("*t",ctime) -- 變成結構
                current.time = ctime -- 增長一項time

                local ntime = next_time(current,ttime) -- 計算下第二天程時間
                skynet.error(string.format("submit ntime  %s",os.date(nil,ntime)))
                skynet.error(string.format("submit ntime  %d",ntime))
                skynet.error(string.format("submit ctime  %s",os.date(nil,ctime)))

                task[session].time = ntime -- 方案1
                task[session].co =  coroutine.running()-- 方案1
                

                local diff =  os.difftime(ntime,ctime)
                skynet.error("submit sleep",diff,session)
            until skynet.sleep(diff * 100) ~= "BREAK"  -- 休眠diff * 100

            task[session] = nil
            skynet.ret() 
        end


        skynet.start(function()
            skynet.dispatch("lua", submit)
            skynet.info_func(function()
                local info = {}
                for k, v in pairs(task) do 
                    if type(v) == "table" then
                        table.insert(info, {
                            time =  os.date(nil, v.time),
                            address =  skynet.address(v.address),
                        })
                    end
                    return  info
                end
            end)
        end)
    end
    service_addr = service.new("schedule", schedule_service) -- 啓動一個服務
end)
return  schedule

方案一主要思路是:在repeat循環中每次都會計算一個假的當前時間。經過假的當前時間計算出日程的下次觸發時間,而後計算下第二天程的觸發時間和假的當前時間的差值來決定線程掛起的時長。如何改變假的當前時間是經過changetime接口改變的。在這個api中會改變task.difftime的值,而後把所有線程全都喚醒,繼續執行repeat循環,從新計算下第二天程的觸發時間,而後從新計算假的當時間和下次觸發的時差=線程掛起的時長。若是改變的時刻在日程觸發時刻的後面,要求改變時刻以前的日程所有觸發,方案1和2均可以,若是提交了樂意日程觸發時刻比如今真實的會見還靠前,則方案2不可行(不過這種狀況通常不會有)session

相關文章
相關標籤/搜索