Add lines scheduler for reliable railway-time scheduling(which is also safer than the atlatc scheduler) and document new atlatc functions
This commit is contained in:
parent
d867cd723f
commit
7b488f40d9
|
@ -19,6 +19,7 @@ advtrains.lines = {
|
|||
local modpath = minetest.get_modpath(minetest.get_current_modname()) .. DIR_DELIM
|
||||
|
||||
dofile(modpath.."railwaytime.lua")
|
||||
dofile(modpath.."scheduler.lua")
|
||||
dofile(modpath.."stoprail.lua")
|
||||
|
||||
|
||||
|
@ -27,6 +28,7 @@ function advtrains.lines.load(data)
|
|||
advtrains.lines.stations = data.stations or {}
|
||||
advtrains.lines.stops = data.stops or {}
|
||||
advtrains.lines.rwt.set_time(data.rwt_time)
|
||||
advtrains.lines.sched.load(data.scheduler_queue)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -34,10 +36,12 @@ function advtrains.lines.save()
|
|||
return {
|
||||
stations = advtrains.lines.stations,
|
||||
stops = advtrains.lines.stops,
|
||||
rwt_time = advtrains.lines.rwt.get_time()
|
||||
rwt_time = advtrains.lines.rwt.get_time(),
|
||||
scheduler_queue = advtrains.lines.sched.save()
|
||||
}
|
||||
end
|
||||
|
||||
function advtrains.lines.step(dtime)
|
||||
advtrains.lines.rwt.step(dtime)
|
||||
advtrains.lines.sched.run()
|
||||
end
|
||||
|
|
|
@ -0,0 +1,104 @@
|
|||
-- scheduler.lua
|
||||
-- Implementation of a Railway time schedule queue
|
||||
-- In contrast to the LuaATC interrupt queue, this one can handle many different
|
||||
-- event receivers. This is done by registering a callback with the scheduler
|
||||
|
||||
local ln = advtrains.lines
|
||||
local sched = {}
|
||||
|
||||
local UNITS_THRESH = 10
|
||||
local MAX_PER_ITER = 10
|
||||
|
||||
local callbacks = {}
|
||||
--Function signature is function(d)
|
||||
function sched.register_callback(e, func)
|
||||
callbacks[e] = func
|
||||
end
|
||||
|
||||
--[[
|
||||
{
|
||||
t = <railway time in seconds>
|
||||
e = <handler callback>
|
||||
d = <data table>
|
||||
u = <unit identifier>
|
||||
}
|
||||
The "unit identifier" is there to prevent schedule overflows. It can be, for example, the position hash
|
||||
of a node or a train ID. If the number of schedules for a unit exceeds UNITS_THRESH, further schedules are
|
||||
blocked.
|
||||
]]--
|
||||
local queue = {}
|
||||
|
||||
local units_cnt = {}
|
||||
|
||||
function sched.load(data)
|
||||
if data then
|
||||
for i,elem in ipairs(data) do
|
||||
table.insert(queue, elem)
|
||||
units_cnt[elem.u] = (units_cnt[elem.u] or 0) + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
function sched.save()
|
||||
return queue
|
||||
end
|
||||
|
||||
function sched.run()
|
||||
local ctime = ln.rwt.get_time()
|
||||
local cnt = 0
|
||||
local ucn, elem
|
||||
while cnt <= MAX_PER_ITER do
|
||||
elem = queue[1]
|
||||
if elem and elem.t <= ctime then
|
||||
table.remove(queue, 1)
|
||||
if callbacks[elem.e] then
|
||||
-- run it
|
||||
callbacks[elem.e](elem.d)
|
||||
else
|
||||
atwarn("No callback to handle schedule",elem)
|
||||
end
|
||||
cnt=cnt+1
|
||||
ucn = units_cnt[elem.u]
|
||||
if ucn and ucn>0 then
|
||||
units_cnt[elem.u] = ucn - 1
|
||||
end
|
||||
else
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function sched.enqueue(rwtime, handler, evtdata, unitid, unitlim)
|
||||
local qtime = ln.rwt.to_secs(rwtime)
|
||||
|
||||
local cnt=1
|
||||
local ucn, elem
|
||||
|
||||
ucn = (units_cnt[unitid] or 0)
|
||||
local ulim=(unitlim or UNITS_THRESH)
|
||||
if ucn >= ulim then
|
||||
atwarn("Scheduler: discarding enqueue for",handler,"(limit",ulim,") because unit",unitid,"has already",ucn,"schedules enqueued")
|
||||
return false
|
||||
end
|
||||
|
||||
while true do
|
||||
elem = queue[cnt]
|
||||
if not elem or elem.t > qtime then
|
||||
table.insert(queue, cnt, {
|
||||
t=qtime,
|
||||
e=handler,
|
||||
d=evtdata,
|
||||
u=unitid,
|
||||
})
|
||||
units_cnt[unitid] = ucn + 1
|
||||
return true
|
||||
end
|
||||
cnt = cnt+1
|
||||
end
|
||||
end
|
||||
|
||||
function sched.enqueue_in(rwtime, handler, evtdata, unitid, unitlim)
|
||||
local ctime = ln.rwt.get_time()
|
||||
sched.enqueue(ctime + rwtime, handler, evtdata, unitid, unitlim)
|
||||
end
|
||||
|
||||
ln.sched = sched
|
|
@ -114,6 +114,18 @@ aspect = {
|
|||
}
|
||||
As of August 2018, only the aspect.main.free field is ever used by the interlocking system.
|
||||
|
||||
# Lines
|
||||
|
||||
The advtrains_line_automation component adds a few contraptions that should make creating timeable systems easier.
|
||||
Part of its functionality is also available in LuaATC:
|
||||
|
||||
- rwt.* - all Railway Time functions are included as documented in https://advtrains.de/wiki/doku.php?id=dev:lines:rwt
|
||||
|
||||
- schedule(rw_time, msg)
|
||||
- schedule_in(rw_dtime, msg)
|
||||
Schedules an event of type {type="schedule", schedule=true, msg=msg} at (resp. after) the specified railway time.
|
||||
(which can be in any format). You can only schedule one event this way. (uses the new lines-internal scheduler)
|
||||
|
||||
## Components and events
|
||||
|
||||
The event table is a table of the following format:
|
||||
|
|
|
@ -123,6 +123,15 @@ function ac.run_in_env(pos, evtdata, customfct_p)
|
|||
digiline:receptor_send(pos, digiline.rules.default, channel, msg)
|
||||
end
|
||||
end
|
||||
-- add lines scheduler if enabled
|
||||
if advtrains.lines and advtrains.lines.sched then
|
||||
customfct.schedule = function(rwtime, msg)
|
||||
advtrains.lines.sched.enqueue(rwtime, "atlatc_env", {pos=pos, msg=msg}, advtrains.encode_pos(pos), 1)
|
||||
end
|
||||
customfct.schedule_in = function(rwtime, msg)
|
||||
advtrains.lines.sched.enqueue_in(rwtime, "atlatc_env", {pos=pos, msg=msg}, advtrains.encode_pos(pos), 1)
|
||||
end
|
||||
end
|
||||
|
||||
local datain=nodetbl.data or {}
|
||||
local succ, dataout = atlatc.envs[nodetbl.env]:execute_code(datain, nodetbl.code, evtdata, customfct)
|
||||
|
@ -144,4 +153,11 @@ function ac.on_digiline_receive(pos, node, channel, msg)
|
|||
atlatc.interrupt.add(0, pos, {type="digiline", digiline=true, channel = channel, msg = msg})
|
||||
end
|
||||
|
||||
if advtrains.lines and advtrains.lines.sched then
|
||||
advtrains.lines.sched.register_callback("atlatc_env", function(data)
|
||||
-- This adds another interrupt to the atlatc queue... there might be a better way
|
||||
atlatc.interrupt.add(0, data.pos, {type="schedule",schedule=true, msg=data.msg})
|
||||
end)
|
||||
end
|
||||
|
||||
atlatc.active=ac
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
advtrains
|
||||
advtrains_interlocking?
|
||||
advtrains_line_automation?
|
||||
mesecons_switch?
|
Loading…
Reference in New Issue