diff options
Diffstat (limited to 'advtrains_interlocking/routesetting.lua')
-rw-r--r-- | advtrains_interlocking/routesetting.lua | 165 |
1 files changed, 139 insertions, 26 deletions
diff --git a/advtrains_interlocking/routesetting.lua b/advtrains_interlocking/routesetting.lua index 67efaea..6544a92 100644 --- a/advtrains_interlocking/routesetting.lua +++ b/advtrains_interlocking/routesetting.lua @@ -43,13 +43,19 @@ function ilrs.set_route(signal, route, try) local first = true local i = 1 local rtename = route.name - local signalname = ildb.get_tcbs(signal).signal_name + local signalname = (ildb.get_tcbs(signal).signal_name or "") .. sigd_to_string(signal) local c_tcbs, c_ts_id, c_ts, c_rseg, c_lckp + -- signals = { { pos = ., tcbs_ref = <tcbs>, role = "main_distant", main_aspect = nil, dst_type = "next_main" or "none" } + local signals = {} + local nodst while c_sigd and i<=#route do c_tcbs = ildb.get_tcbs(c_sigd) if not c_tcbs then if not try then atwarn("Did not find TCBS",c_sigd,"while setting route",rtename,"of",signal) end - return false, "No TCB found at "..sigd_to_string(c_sigd)..". Please reconfigure route!" + return false, "No TCB found at "..sigd_to_string(c_sigd)..". Please update or reconfigure route!" + end + if i == 1 then + nodst = c_tcbs.nodst end c_ts_id = c_tcbs.ts_id if not c_ts_id then @@ -60,42 +66,79 @@ function ilrs.set_route(signal, route, try) c_rseg = route[i] c_lckp = {} - if c_ts.route then + if not c_ts then + if not try then atwarn("Encountered ts missing during a real run of routesetting routine, at ts=",c_ts_id,"while setting route",rtename,"of",signal) end + return false, "Section '"..(c_ts_id).."' not found!", c_ts_id, nil + elseif c_ts.route then if not try then atwarn("Encountered ts lock during 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)..":\n"..c_ts.route.rsn, c_ts_id, nil + return false, "Section '"..(c_ts.name or c_ts_id).."' already has route set from "..sigd_to_string(c_ts.route.origin)..":\n"..c_ts.route.rsn, c_ts_id, nil end if c_ts.trains and #c_ts.trains>0 then - if not try then atwarn("Encountered ts occupied during 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!", c_ts_id, nil + if c_rseg.call_on then + --atdebug("Routesetting: Call-on situation in", c_ts_id) + else + if not try then atwarn("Encountered ts occupied during a real run of routesetting routine, at ts=",c_ts_id,"while setting route",rtename,"of",signal) end + return false, "Section '"..(c_ts.name or c_ts_id).."' is occupied!", c_ts_id, nil + end end - for pts, state in pairs(c_rseg.locks) do - local confl = ilrs.has_route_lock(pts, state) + -- collect locks from rs cache and from route def + local c_locks = {} + if route.use_rscache and c_ts.rs_cache and c_rseg.next then + -- rscache needs to be enabled, present and next must be defined + start_pkey = advtrains.encode_pos(c_sigd.p) + end_pkey = advtrains.encode_pos(c_rseg.next.p) + if c_ts.rs_cache[start_pkey] and c_ts.rs_cache[start_pkey][end_pkey] then + for lp,lst in pairs(c_ts.rs_cache[start_pkey][end_pkey]) do + --atdebug("Add lock from RSCache:",lp,"->",lst) + c_locks[lp] = lst + end + elseif not try then + atwarn("While setting route",rtename,"of",signal,"segment "..i..",required path from",c_tcbs,"to",c_rseg.next,"was not found in the track section's RS cache. Please check!") + end + end + -- add all from locks, these override the rscache + for lpts,lst in pairs(c_rseg.locks) do + --atdebug("Add lock from Routedef:",lpts,"->",lst,"overrides",c_locks[lpts] or "none") + c_locks[lpts] = lst + end + + for lp, state in pairs(c_locks) do + local confl = ilrs.has_route_lock(lp, state) - local pos = minetest.string_to_pos(pts) + local pos = advtrains.decode_pos(lp) if advtrains.is_passive(pos) then local cstate = advtrains.getstate(pos) if cstate ~= state then - local confl = ilrs.has_route_lock(pts) + local confl = ilrs.has_route_lock(lp) 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, nil, pts + if not try then atwarn("Encountered route lock while a real run of routesetting routine, at position",pos,"while setting route",rtename,"of",signal) end + return false, "Lock conflict at "..minetest.pos_to_string(pos)..", Held locked by:\n"..confl, nil, lp elseif not try then advtrains.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 + ilrs.add_route_lock(lp, c_ts_id, "Route '"..rtename.."' from signal '"..signalname.."'", signal) + c_lckp[#c_lckp+1] = lp 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, "No passive component at "..pts..". Please reconfigure route!" + return false, "No passive component at "..minetest.pos_to_string(pos)..". Please update track section or reconfigure route!" + end + end + -- sanity check, is section at next the same as the current? + local nvar = c_rseg.next + if nvar then + local re_tcbs = ildb.get_tcbs({p = nvar.p, s = (nvar.s==1) and 2 or 1}) + if (not re_tcbs or not re_tcbs.ts_id or re_tcbs.ts_id~=c_ts_id) + and route[i+1] then --FIX 2025-01-08: in old worlds the final TCB may be wrong (it didn't matter back then), don't error out here (route still shown invalid in UI) + if not try then atwarn("Encountered inconsistent ts (front~=back) while a real run of routesetting routine, at position",pts,"while setting route",rtename,"of",signal) end + return false, "TCB at "..minetest.pos_to_string(nvar.p).." has different section than previous TCB. Please update track section or reconfigure route!" end end -- reserve ts and write locks if not try then - local nvar = c_rseg.next if not route[i+1] then -- We shouldn't use the "next" value of the final route segment, because this can lead to accidental route-cancelling of already set routes from another signal. nvar = nil @@ -112,9 +155,22 @@ function ilrs.set_route(signal, route, try) } if c_tcbs.signal then c_tcbs.route_committed = true - c_tcbs.aspect = route.aspect or advtrains.interlocking.GENERIC_FREE c_tcbs.route_origin = signal - advtrains.interlocking.update_signal_aspect(c_tcbs) + -- determine route role + local ndef = advtrains.ndb.get_ndef(c_tcbs.signal) + local assign_dst = c_rseg.assign_dst + if assign_dst == nil then + assign_dst = (i~=1) -- special behavior when assign_dst is nil (and not false): + -- defaults to false for the very first signal and true for all others (= minimal user configuration overhead) + end + local sig_table = { + pos = c_tcbs.signal, + tcbs_ref = c_tcbs, + role = ndef and ndef.advtrains and ndef.advtrains.route_role, + main_aspect = c_rseg.main_aspect, + assign_dst = assign_dst + } + signals[#signals+1] = sig_table end end -- advance @@ -122,10 +178,47 @@ function ilrs.set_route(signal, route, try) c_sigd = c_rseg.next i = i + 1 end + + -- Get reference to signal at end of route + local last_mainsig = nil + if c_sigd then + local e_tcbs = ildb.get_tcbs(c_sigd) + local pos = e_tcbs and e_tcbs.signal + if pos then + last_mainsig = pos + end + end + for i = #signals, 1, -1 do + -- note the signals are iterated backwards. Switch depending on the role + local sig = signals[i] + -- apply mainaspect + sig.tcbs_ref.route_aspect = sig.main_aspect or "_default" -- or route.main_aspect : TODO this does not work if a distant signal is on the path! Implement per-sig aspects! + if sig.role == "distant" or sig.role == "distant_repeater" or sig.role == "main_distant" then + if last_mainsig then + -- assign the remote as the last mainsig if desired + if sig.assign_dst then + sig.tcbs_ref.route_remote = last_mainsig + end + -- if it wasn't a distant_repeater clear the mainsig + if sig.role ~= "distant_repeater" then + last_mainsig = false + end + end + end + if sig.role == "main" or sig.role == "main_distant" or sig.role == "end" then + -- record this as the new last mainsig + last_mainsig = sig.pos + end + -- for shunt signals nothing happens + -- update the signal aspect on map + advtrains.interlocking.signal.update_route_aspect(sig.tcbs_ref, i ~= 1) + end return true end +-- Change 2024-01-27: pts is not an encoded pos, not a pos-to-string! + -- 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) @@ -172,6 +265,11 @@ function ilrs.free_route_locks(ts, lcks, nocallbacks) end function ilrs.free_route_locks_indiv(pts, ts, nocallbacks) + -- legacy: if starts with bracket then pts is still in old pos_to_string format (may happen because ts.route_post is not migrated) + if string.match(pts, "^%(") then + atdebug("free_route_locks_indiv: converting position",pts) + pts = advtrains.encode_pos(minetest.string_to_pos(pts)) + end local e = ilrs.rte_locks[pts] if not e then return nil elseif #e==0 then @@ -191,7 +289,7 @@ function ilrs.free_route_locks_indiv(pts, ts, nocallbacks) -- TODO use luaautomation timers? if not nocallbacks then minetest.after(0, ilrs.update_waiting, "lck", pts) - minetest.after(0.5, advtrains.set_fallback_state, minetest.string_to_pos(pts)) + minetest.after(0.5, advtrains.set_fallback_state, advtrains.decode_pos(pts)) end end -- frees all route locks, even manual ones set with the tool, at a specific position @@ -224,12 +322,13 @@ function ilrs.cancel_route_from(sigd) --atdebug("cancelling",c_ts.route.rsn) -- clear signal aspect and routesetting state c_tcbs.route_committed = nil - c_tcbs.aspect = nil + c_tcbs.route_aspect = nil + c_tcbs.route_remote = nil c_tcbs.routeset = nil c_tcbs.route_auto = nil c_tcbs.route_origin = nil - advtrains.interlocking.update_signal_aspect(c_tcbs) + advtrains.interlocking.signal.update_route_aspect(c_tcbs) c_ts_id = c_tcbs.ts_id if not c_tcbs then @@ -273,17 +372,19 @@ function ilrs.update_route(sigd, tcbs, newrte, cancel) --atdebug("Signal not in control, held by",tcbs.signal_name) return end + -- clear route_rsn, it will be set again if needed + tcbs.route_rsn = nil if (newrte and tcbs.routeset and tcbs.routeset ~= newrte) or cancel then if tcbs.route_committed then --atdebug("Cancelling:",tcbs.routeset) advtrains.interlocking.route.cancel_route_from(sigd) end tcbs.route_committed = nil - tcbs.aspect = nil + tcbs.route_aspect = nil + tcbs.route_remote = nil has_changed_aspect = true tcbs.routeset = nil tcbs.route_auto = nil - tcbs.route_rsn = nil end if newrte or tcbs.routeset then if tcbs.route_committed then @@ -291,7 +392,14 @@ function ilrs.update_route(sigd, tcbs, newrte, cancel) end if newrte then tcbs.routeset = newrte end --atdebug("Setting:",tcbs.routeset) - local succ, rsn, cbts, cblk = ilrs.set_route(sigd, tcbs.routes[tcbs.routeset]) + local succ, rsn, cbts, cblk + local route = tcbs.routes[tcbs.routeset] + if route then + succ, rsn, cbts, cblk = ilrs.set_route(sigd, route) + else + succ = false + rsn = attrans("Route state changed.") + end if not succ then tcbs.route_rsn = rsn --atdebug("Routesetting failed:",rsn) @@ -308,12 +416,17 @@ function ilrs.update_route(sigd, tcbs, newrte, cancel) end else --atdebug("Committed Route:",tcbs.routeset) - has_changed_aspect = true + -- set_route now sets the signal aspects + --has_changed_aspect = true + -- route success. apply default_autoworking flag if requested + if route.default_autoworking then + tcbs.route_auto = true --FIX 2025-01-08: never set it to false if it was true! + end end end if has_changed_aspect then -- FIX: prevent an minetest.after() loop caused by update_signal_aspect dispatching path invalidation, which in turn calls ARS again - advtrains.interlocking.update_signal_aspect(tcbs) + advtrains.interlocking.signal.update_route_aspect(tcbs) end advtrains.interlocking.update_player_forms(sigd) end |