Implement routesetting

Missing things: signal aspect updating, waiting routes handling, management /info tool
master
orwell96 2018-07-21 16:31:00 +02:00
parent 5fc5eb9c2a
commit c34794e8a1
7 changed files with 286 additions and 11 deletions

View File

@ -304,7 +304,8 @@ function advtrains.register_tracks(tracktype, def, preset)
if var.switchalt and var.switchst then
local switchfunc=function(pos, node, newstate)
if newstate~=var.switchst and not advtrains.get_train_at_pos(pos) then
if newstate~=var.switchst and not advtrains.get_train_at_pos(pos)
and not (advtrains.interlocking and advtrains.interlocking.route.has_route_lock(advtrains.roundfloorpts(pos)) ) then --TODO schöner machen
advtrains.ndb.swap_node(pos, {name=def.nodename_prefix.."_"..var.switchalt..rotation, param2=node.param2})
advtrains.invalidate_all_paths(pos)
end

View File

@ -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

View File

@ -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")

View File

@ -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

View File

@ -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

View File

@ -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()

View File

@ -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