aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authororwell96 <orwell@bleipb.de>2019-08-26 23:08:02 +0200
committerorwell96 <orwell@bleipb.de>2019-08-26 23:08:02 +0200
commit7b488f40d95c2d68db898d7cb228e17e001cea73 (patch)
tree686712ef9e75631feebdbac4218a26fa73a72975
parentd867cd723f373b0fc52bd2cdd19176a29598f8be (diff)
downloadadvtrains-7b488f40d95c2d68db898d7cb228e17e001cea73.tar.gz
advtrains-7b488f40d95c2d68db898d7cb228e17e001cea73.tar.bz2
advtrains-7b488f40d95c2d68db898d7cb228e17e001cea73.zip
Add lines scheduler for reliable railway-time scheduling(which is also safer than the atlatc scheduler) and document new atlatc functions
-rw-r--r--advtrains_line_automation/init.lua6
-rw-r--r--advtrains_line_automation/scheduler.lua104
-rw-r--r--advtrains_luaautomation/README.txt12
-rw-r--r--advtrains_luaautomation/active_common.lua16
-rw-r--r--advtrains_luaautomation/depends.txt1
5 files changed, 138 insertions, 1 deletions
diff --git a/advtrains_line_automation/init.lua b/advtrains_line_automation/init.lua
index 0f4a4eb..7b758bc 100644
--- a/advtrains_line_automation/init.lua
+++ b/advtrains_line_automation/init.lua
@@ -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
diff --git a/advtrains_line_automation/scheduler.lua b/advtrains_line_automation/scheduler.lua
new file mode 100644
index 0000000..da639df
--- /dev/null
+++ b/advtrains_line_automation/scheduler.lua
@@ -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
diff --git a/advtrains_luaautomation/README.txt b/advtrains_luaautomation/README.txt
index a07bbb4..20ef816 100644
--- a/advtrains_luaautomation/README.txt
+++ b/advtrains_luaautomation/README.txt
@@ -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:
diff --git a/advtrains_luaautomation/active_common.lua b/advtrains_luaautomation/active_common.lua
index 5d8cc48..8d0975f 100644
--- a/advtrains_luaautomation/active_common.lua
+++ b/advtrains_luaautomation/active_common.lua
@@ -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
diff --git a/advtrains_luaautomation/depends.txt b/advtrains_luaautomation/depends.txt
index 1b4fd87..d5523e1 100644
--- a/advtrains_luaautomation/depends.txt
+++ b/advtrains_luaautomation/depends.txt
@@ -1,3 +1,4 @@
advtrains
advtrains_interlocking?
+advtrains_line_automation?
mesecons_switch? \ No newline at end of file