aboutsummaryrefslogtreecommitdiff
path: root/advtrains_interlocking
diff options
context:
space:
mode:
Diffstat (limited to 'advtrains_interlocking')
-rw-r--r--advtrains_interlocking/database.lua30
-rw-r--r--advtrains_interlocking/init.lua1
-rw-r--r--advtrains_interlocking/route_prog.lua2
-rw-r--r--advtrains_interlocking/routesetting.lua214
-rw-r--r--advtrains_interlocking/tcb_ts_ui.lua25
-rw-r--r--advtrains_interlocking/train_related.lua22
6 files changed, 284 insertions, 10 deletions
diff --git a/advtrains_interlocking/database.lua b/advtrains_interlocking/database.lua
index 6d86e39..c19f516 100644
--- a/advtrains_interlocking/database.lua
+++ b/advtrains_interlocking/database.lua
@@ -64,6 +64,7 @@ Whenever train leaves a TC
-> Clearing any routes set from this TC outward recursively - see "Reversing problem"
Whenever train enters a TC
-> Clear route status from the just entered TC
+Note that this prohibits by design that the train clears the route ahead of it.
== Reversing Problem ==
Encountered at the Royston simulation in SimSig. It is solved there by imposing a time limit on the set route. Call-on routes can somehow be set anyway.
Imagine this setup: (T=Train, R=Route, >=in_dir TCB)
@@ -108,10 +109,18 @@ function ildb.load(data)
if data.signalass then
signal_assignments = data.signalass
end
+ if data.rs_locks then
+ advtrains.interlocking.route.rte_locks = data.rs_locks
+ end
end
function ildb.save()
- return {tcbs = track_circuit_breaks, ts=track_sections, signalass = signal_assignments}
+ return {
+ tcbs = track_circuit_breaks,
+ ts=track_sections,
+ signalass = signal_assignments,
+ rs_locks = advtrains.interlocking.route.rte_locks,
+ }
end
--
@@ -144,9 +153,21 @@ Track section
name = "Some human-readable name"
tc_breaks = { <signal specifier>,... } -- Bounding TC's (signal specifiers)
-- Can be direct ends (auto-detected), conflicting routes or TCBs that are too far away from each other
- route = {origin = <signal>, from_tcb = <index>}
+ route = {
+ origin = <signal>, -- route origin
+ entry = <sigd>, -- supposed train entry point
+ rsn = <string>,
+ first = <bool>
+ }
+ route_post = {
+ locks = {[n] = <pts>}
+ next = <sigd>
+ }
-- Set whenever a route has been set through this TC. It saves the origin tcb id and side
- -- (=the origin signal). index is the TCB of the route origin
+ -- (=the origin signal). rsn is some description to be shown to the user
+ -- first says whether to clear the routesetting status from the origin signal.
+ -- locks contains the positions where locks are held by this ts.
+ -- 'route' is cleared when train enters the section, while 'route_post' cleared when train leaves section.
trains = {<id>, ...} -- Set whenever a train (or more) reside in this TC
}
@@ -379,10 +400,11 @@ function ildb.remove_from_interlocking(sigd)
end
function ildb.remove_tcb(pos)
+ local pts = advtrains.roundfloorpts(pos)
+ if not track_circuit_breaks[pts] then return end
for connid=1,2 do
ildb.remove_from_interlocking({p=pos, s=connid})
end
- local pts = advtrains.roundfloorpts(pos)
track_circuit_breaks[pts] = nil
end
diff --git a/advtrains_interlocking/init.lua b/advtrains_interlocking/init.lua
index 05665e3..9bd6669 100644
--- a/advtrains_interlocking/init.lua
+++ b/advtrains_interlocking/init.lua
@@ -11,3 +11,4 @@ dofile(modpath.."signal_api.lua")
dofile(modpath.."demosignals.lua")
dofile(modpath.."train_related.lua")
dofile(modpath.."route_prog.lua")
+dofile(modpath.."routesetting.lua")
diff --git a/advtrains_interlocking/route_prog.lua b/advtrains_interlocking/route_prog.lua
index 2573ba4..fb12548 100644
--- a/advtrains_interlocking/route_prog.lua
+++ b/advtrains_interlocking/route_prog.lua
@@ -356,6 +356,6 @@ minetest.register_chatcommand("at_rp_discard",
--TODO on route setting
--- locked turnouts need to somehow know the TS they're associated to, which isn't possible with the current route programming and saving method
-- unify luaautomation get/setstate interface to the core
-- privileges for route programming
+-- routes should end at signals. complete route setting by punching a signal, and command as exceptional route completion
diff --git a/advtrains_interlocking/routesetting.lua b/advtrains_interlocking/routesetting.lua
index bee183c..181b82d 100644
--- a/advtrains_interlocking/routesetting.lua
+++ b/advtrains_interlocking/routesetting.lua
@@ -1,7 +1,20 @@
-- Setting and clearing routes
+-- TODO duplicate
+local lntrans = { "A", "B" }
+local function sigd_to_string(sigd)
+ return minetest.pos_to_string(sigd.p).." / "..lntrans[sigd.s]
+end
+
+local ildb = advtrains.interlocking.db
local ilrs = {}
+-- table containing locked points
+-- also manual locks (maintenance a.s.o.) are recorded here
+-- [pts] = {
+-- [n] = { [by = <ts_id>], rsn = <human-readable text>, [origin = <sigd>] }
+-- }
+ilrs.rte_locks = {}
-- Requests the given route
-- This function will try to set the designated route.
@@ -10,3 +23,204 @@ local ilrs = {}
function ilrs.request_route(signal, tcbs, routeid)
end
+
+-- main route setting. First checks if everything can be set as designated,
+-- then (if "try" is not set) actually sets it
+-- returns:
+-- true - route can be/was successfully set
+-- false, message - something went wrong, what is contained in the message.
+function ilrs.set_route(signal, route, try)
+ if not try then
+ atdebug("rteset real-run")
+ local tsuc, trsn = ilrs.set_route(signal, route, true)
+ if not tsuc then
+ return false, trsn
+ end
+ atdebug("doing stuff")
+ else
+ atdebug("rteset try-run")
+ end
+
+ -- we start at the tc designated by signal
+ local c_sigd = signal
+ local first = true
+ local i = 1
+ local rtename = route.name
+ local signalname = ildb.get_tcbs(signal).signal_name
+ local c_tcbs, c_ts_id, c_ts, c_rseg, c_lckp
+ while c_sigd and i<=#route do
+ c_tcbs = ildb.get_tcbs(c_sigd)
+ c_ts_id = c_tcbs.ts_id
+ if not c_ts_id then
+ if not try then atwarn("Encountered End-Of-Interlocking while setting route",rtename,"of",signal) end
+ return false, "No track section adjacent to "..sigd_to_string(c_sigd).."!"
+ end
+ c_ts = ildb.get_ts(c_ts_id)
+ c_rseg = route[i]
+ c_lckp = {}
+
+ if c_ts.route then
+ if not try then atwarn("Encountered ts lock while a real run of routesetting routine, at ts=",c_ts_id,"while setting route",rtename,"of",signal) end
+ return false, "Section '"..c_ts.name.."' already has route set from "..sigd_to_string(c_ts.route.origin).."!"
+ end
+ if c_ts.trains and #c_ts.trains>0 then
+ if not try then atwarn("Encountered ts occupied while a real run of routesetting routine, at ts=",c_ts_id,"while setting route",rtename,"of",signal) end
+ return false, "Section '"..c_ts.name.."' is occupied!"
+ end
+
+ for pts, state in pairs(c_rseg.locks) do
+ local confl = ilrs.has_route_lock(pts, state)
+
+ local pos = minetest.string_to_pos(pts)
+ local node = advtrains.ndb.get_node(pos)
+ local ndef = minetest.registered_nodes[node.name]
+ if ndef and ndef.luaautomation and ndef.luaautomation.setstate and ndef.luaautomation.getstate then
+ local cstate = ndef.luaautomation.getstate
+ if type(cstate)=="function" then cstate = cstate(pos) end
+ if cstate ~= state then
+ local confl = ilrs.has_route_lock(pts)
+ if confl then
+ if not try then atwarn("Encountered route lock while a real run of routesetting routine, at position",pts,"while setting route",rtename,"of",signal) end
+ return false, "Lock conflict at "..pts..", Held locked by:\n"..confl
+ elseif not try then
+ ndef.luaautomation.setstate(pos, state)
+ end
+ end
+ if not try then
+ ilrs.add_route_lock(pts, c_ts_id, "Route '"..rtename.."' from signal '"..signalname.."'", signal)
+ c_lckp[#c_lckp+1] = pts
+ end
+ else
+ if not try then atwarn("Encountered route lock misconfiguration (no passive component) while a real run of routesetting routine, at position",pts,"while setting route",rtename,"of",signal) end
+ return false, "Route misconfiguration: No passive component at "..pts..". Please reconfigure route!"
+ end
+ end
+ -- reserve ts and write locks
+ if not try then
+ c_ts.route = {
+ origin = signal,
+ entry = c_sigd,
+ rsn = "Route '"..rtename.."' from signal '"..signalname.."', segment #"..i,
+ first = first,
+ }
+ c_ts.route_post = {
+ locks = c_lckp,
+ next = c_rseg.next,
+ }
+ end
+ -- advance
+ first = nil
+ c_sigd = c_rseg.next
+ i = i + 1
+ end
+
+ return true
+end
+
+-- Checks whether there is a route lock that prohibits setting the component
+-- to the wanted state. returns string with reasons on conflict
+function ilrs.has_route_lock(pts)
+ -- look this up
+ local e = ilrs.rte_locks[pts]
+ if not e then return nil
+ elseif #e==0 then
+ ilrs.rte_locks[pts] = nil
+ return nil
+ end
+ local txts = {}
+ for _, ent in ipairs(e) do
+ txts[#txts+1] = ent.rsn
+ end
+ return table.concat(txts, "\n")
+end
+
+-- adds route lock for position
+function ilrs.add_route_lock(pts, ts, rsn, origin)
+ ilrs.free_route_locks_indiv(pts, ts, true)
+ local elm = {by=ts, rsn=rsn, origin=origin}
+ if not ilrs.rte_locks[pts] then
+ ilrs.rte_locks[pts] = { elm }
+ else
+ table.insert(ilrs.rte_locks[pts], elm)
+ end
+end
+
+-- adds route lock for position
+function ilrs.add_manual_route_lock(pts, rsn)
+ local elm = {rsn=rsn}
+ if not ilrs.rte_locks[pts] then
+ ilrs.rte_locks[pts] = { elm }
+ else
+ table.insert(ilrs.rte_locks[pts], elm)
+ end
+end
+
+-- frees route locking for all points (components) that were set by this ts
+function ilrs.free_route_locks(ts, lcks, nocallbacks)
+ for _,pts in pairs(lcks) do
+ ilrs.free_route_locks_indiv(pts, ts, nocallbacks)
+ end
+end
+
+function ilrs.free_route_locks_indiv(pts, ts, nocallbacks)
+ local e = ilrs.rte_locks[pts]
+ if not e then return nil
+ elseif #e==0 then
+ ilrs.rte_locks[pts] = nil
+ return nil
+ end
+ local i = 1
+ while i <= #e do
+ if e[i].by == ts then
+ atdebug("free_route_locks_indiv",pts,"clearing entry",e[i].by,e[i].rsn)
+ table.remove(e,i)
+ else
+ i = i + 1
+ end
+ end
+
+ --TODO callbacks
+end
+-- frees all route locks, even manual ones set with the tool, at a specific position
+function ilrs.remove_route_locks(pts, nocallbacks)
+ ilrs.rte_locks[pts] = nil
+ --TODO callbacks
+end
+
+local function sigd_equal(sigd, cmp)
+ return vector.equals(sigd.p, cmp.p) and sigd.s==cmp.s
+end
+
+-- starting from the designated sigd, clears all subsequent route and route_post
+-- information from the track sections.
+-- note that this does not clear the routesetting status from the entry signal,
+-- only from the ts's
+function ilrs.cancel_route_from(sigd)
+ -- we start at the tc designated by signal
+ local c_sigd = sigd
+ local c_tcbs, c_ts_id, c_ts, c_rseg, c_lckp
+ while c_sigd do
+ c_tcbs = ildb.get_tcbs(c_sigd)
+ c_ts_id = c_tcbs.ts_id
+ c_ts = ildb.get_ts(c_ts_id)
+
+ if not c_ts
+ or not c_ts.route
+ or not sigd_equal(c_ts.route.entry, c_sigd) then
+ return
+ end
+
+ c_ts.route = nil
+
+ if c_ts.route_post then
+ advtrains.interlocking.route.free_route_locks(c_ts_id, c_ts.route_post.locks)
+ c_sigd = c_ts.route_post.next
+ else
+ c_sigd = nil
+ end
+ c_ts.route_post = nil
+ end
+end
+
+advtrains.interlocking.route = ilrs
+
diff --git a/advtrains_interlocking/tcb_ts_ui.lua b/advtrains_interlocking/tcb_ts_ui.lua
index 8745626..e35e5b9 100644
--- a/advtrains_interlocking/tcb_ts_ui.lua
+++ b/advtrains_interlocking/tcb_ts_ui.lua
@@ -457,12 +457,12 @@ function advtrains.interlocking.show_signalling_form(sigd, pname, sel_rte)
form = form.."label[0.5,2.5;A route is requested from this signal:]"
form = form.."label[0.5,3.0;"..rte.name.."]"
if tcbs.route_committed then
- form = form.."label[0.5,2.5;Route has been set.]"
+ form = form.."label[0.5,3.5;Route has been set.]"
else
- form = form.."label[0.5,2.5;Waiting for route to be set...]"
+ form = form.."label[0.5,3.5;Waiting for route to be set...]"
end
- form = form.."button[0.5,6.5;1,6;cancelroute;Cancel Route]"
+ form = form.."button[0.5,6; 5,1;cancelroute;Cancel Route]"
else
local strtab = {}
for idx, route in ipairs(tcbs.routes) do
@@ -515,7 +515,14 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
tcbs.signal_name = fields.name
end
if tcbs.routeset and fields.cancelroute then
- --TODO
+ -- if route committed, cancel route ts info
+ if tcbs.route_committed then
+ advtrains.interlocking.route.cancel_route_from(sigd)
+ end
+ -- then clear own routeset state
+ --TODO callbacks
+ tcbs.routeset = nil
+ tcbs.route_committed = nil
end
if not tcbs.routeset then
if fields.newroute then
@@ -525,7 +532,15 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
end
if sel_rte and tcbs.routes[sel_rte] then
if fields.setroute then
- --TODO
+ tcbs.routeset = sel_rte
+ local succ, rsn = advtrains.interlocking.route.set_route(sigd, tcbs.routes[sel_rte])
+ if not succ then
+ atwarn("Setting route failed:")
+ atwarn(rsn)
+ tcbs.routeset = nil
+ else
+ tcbs.route_committed = true
+ end
end
if fields.dsproute then
local t = os.clock()
diff --git a/advtrains_interlocking/train_related.lua b/advtrains_interlocking/train_related.lua
index 3d7e280..2dfbd5c 100644
--- a/advtrains_interlocking/train_related.lua
+++ b/advtrains_interlocking/train_related.lua
@@ -73,6 +73,19 @@ local function setsection(tid, train, ts_id, ts, origin)
table.insert(ts.trains, tid)
end
+ -- route setting - clear route state
+ if ts.route then
+ if ts.route.first then
+ local tcbs = advtrains.interlocking.db.get_tcbs(ts.route.origin)
+ if tcbs then
+ --TODO callbacks
+ tcbs.routeset = nil
+ tcbs.route_committed = nil
+ end
+ end
+ ts.route = nil
+ end
+
end
local function freesection(tid, train, ts_id, ts)
@@ -84,6 +97,15 @@ local function freesection(tid, train, ts_id, ts)
if not ts.trains then ts.trains = {} end
itremove(ts.trains, tid)
+ if ts.route_post then
+ advtrains.interlocking.route.free_route_locks(ts_id, ts.route_post.locks)
+ if ts.route_post.next then
+ --this does nothing when the train went the right way, because
+ -- "route" info is already cleared.
+ advtrains.interlocking.route.cancel_route_from(ts.route_post.next)
+ end
+ ts.route_post = nil
+ end
end