Implement routesetting incorporating tscache, other improvements

This commit is contained in:
orwell 2024-01-28 00:42:28 +01:00
parent f8c2ec60d6
commit 9fa43cb7bf
6 changed files with 185 additions and 94 deletions

View File

@ -57,7 +57,7 @@ function advtrains.setstate(parpos, newstate, pnode)
return false, "train_here" return false, "train_here"
end end
if advtrains.interlocking and advtrains.interlocking.route.has_route_lock(minetest.pos_to_string(pos)) then if advtrains.interlocking and advtrains.interlocking.route.has_route_lock(minetest.encode_pos(pos)) then
return false, "route_lock_here" return false, "route_lock_here"
end end

View File

@ -84,11 +84,34 @@ function ildb.load(data)
if pos then if pos then
-- that was a pos_to_string -- that was a pos_to_string
local epos = advtrains.encode_pos(pos) local epos = advtrains.encode_pos(pos)
atdebug("ILDB converting TCB position format",pts,"->",epos)
track_circuit_breaks[epos] = tcb track_circuit_breaks[epos] = tcb
else else
-- keep entry, it is already new -- keep entry, it is already new
track_circuit_breaks[pts] = tcb track_circuit_breaks[pts] = tcb
end end
-- convert the routes.[].locks table keys
for t_side,tcbs in pairs(tcb) do
if tcbs.routes then
for t_rnum,route in pairs(tcbs.routes) do
for t_rsnm,rseg in ipairs(route) do
local locks_n = {}
for lpts,state in pairs(rseg.locks) do
local lpos = minetest.string_to_pos(lpts)
if lpos then
local epos = advtrains.encode_pos(lpos)
atdebug("ILDB converting tcb",pts,"side",t_side,"route",t_route,"lock position format",lpts,"->",epos)
locks_n[epos] = state
else
-- already correct format
locks_n[lpts] = state
end
end
rseg.locks = locks_n
end
end
end
end
end end
end end
end end
@ -99,7 +122,23 @@ function ildb.load(data)
signal_assignments = data.signalass signal_assignments = data.signalass
end end
if data.rs_locks then if data.rs_locks then
advtrains.interlocking.route.rte_locks = data.rs_locks if data.tcbpts_conversion_applied then
advtrains.interlocking.route.rte_locks = data.rs_locks
else
advtrains.interlocking.route.rte_locks = {}
for pts, lta in pairs(data.rs_locks) do
local pos = minetest.string_to_pos(pts)
if pos then
-- that was a pos_to_string
local epos = advtrains.encode_pos(pos)
atdebug("ILDB converting Route Lock position format",pts,"->",epos)
advtrains.interlocking.route.rte_locks[epos] = lta
else
-- keep entry, it is already new
advtrains.interlocking.route.rte_locks[pts] = lta
end
end
end
end end
if data.rs_callbacks then if data.rs_callbacks then
advtrains.interlocking.route.rte_callbacks = data.rs_callbacks advtrains.interlocking.route.rte_callbacks = data.rs_callbacks
@ -197,8 +236,8 @@ routes = {
-- one table for each track section on the route -- one table for each track section on the route
-- Note that the section ID is implicitly inferred from the TCB -- Note that the section ID is implicitly inferred from the TCB
1 = { 1 = {
locks = { -- component locks for this section of the route. Not used when use_rscache is true. locks = { -- component locks for this section of the route.
(-16,9,0) = st 800080008000 = st
} }
next = S[(-23,9,0)/2] -- the start TCB of the next route segment (pointing forward) next = S[(-23,9,0)/2] -- the start TCB of the next route segment (pointing forward)
} }
@ -208,7 +247,8 @@ routes = {
} }
name = "<the route name>" name = "<the route name>"
ars = { <ARS rule definition table> } ars = { <ARS rule definition table> }
use_rscache = false -- if true, instead of "locks", the track section's rs_cache will be used to set locks use_rscache = false -- if true, the track section's rs_cache will be used to set locks in addition to the locks table
-- this is disabled for legacy routes, but enabled for all new routes by default
-- Fields used by the autorouter: -- Fields used by the autorouter:
ar_end_sigd = <sigd> -- the sigd describing the end of the route. Used for merging route options on recalculation ar_end_sigd = <sigd> -- the sigd describing the end of the route. Used for merging route options on recalculation
} }
@ -218,10 +258,12 @@ Track section
[id] = { [id] = {
name = "Some human-readable name" name = "Some human-readable name"
tc_breaks = { <signal specifier>,... } -- Bounding TC's (signal specifiers) tc_breaks = { <signal specifier>,... } -- Bounding TC's (signal specifiers)
rs_cache = { [<x>] = { [<y>] = { [<encoded pos>] = "state" } } } rs_cache = { [startTcbPosEnc] = { [endTcbPosEnc] = { [componentPosEnc] = "state" } } }
-- Saves the turnout states that need to be locked when a route is set from tcb#x to tcb#y -- Saves the turnout states that need to be locked when a route is set from tcb#x to tcb#y
-- e.g. 1 = { 2 = { "800080008000" = "st" } } -- e.g. "800080008005" = { "800080007EEA" = { "800080008000" = "st" } }
-- start TCB end TCB switch pos
-- Recalculated on every change via update_rs_cache -- Recalculated on every change via update_rs_cache
-- Note that the tcb side number is not saved because it is unnecessary
route = { route = {
origin = <signal>, -- route origin origin = <signal>, -- route origin
@ -281,17 +323,24 @@ function ildb.get_all_ts()
return track_sections return track_sections
end end
function tsrepair_notify(notify_pname, ...)
if notify_pname then
minetest.chat_send_player(notify_pname, advtrains.print_concat_table({"TS Check:",...}))
end
end
-- Checks the consistency of the track section at the given position, attempts to autorepair track sections if they are inconsistent -- Checks the consistency of the track section at the given position, attempts to autorepair track sections if they are inconsistent
-- There are 2 operation modes: -- There are 2 operation modes:
-- 1: pos is NOT a TCB, tcb_connid MUST be nil -- 1: pos is NOT a TCB, tcb_connid MUST be nil
-- 2: pos is a TCB, tcb_connid MUST be given -- 2: pos is a TCB, tcb_connid MUST be given
-- @param pos: the position to start from -- @param pos: the position to start from
-- @param tcb_connid: If provided node is a TCB, -- @param tcb_connid: If provided node is a TCB, the direction in which to search
-- @param notify_pname: the player to notify about reparations
-- Returns: -- Returns:
-- ts_id - the track section that was found -- ts_id - the track section that was found
-- nil - No track section exists -- nil - No track section exists
function ildb.check_and_repair_ts_at_pos(pos, tcb_connid) function ildb.check_and_repair_ts_at_pos(pos, tcb_connid, notify_pname)
atdebug("check_and_repair_ts_at_pos", pos, tcb_connid) --atdebug("check_and_repair_ts_at_pos", pos, tcb_connid)
-- check prereqs -- check prereqs
if ildb.get_tcb(pos) then if ildb.get_tcb(pos) then
if not tcb_connid then error("check_and_repair_ts_at_pos: Startpoint is TCB, must provide tcb_connid!") end if not tcb_connid then error("check_and_repair_ts_at_pos: Startpoint is TCB, must provide tcb_connid!") end
@ -315,15 +364,17 @@ function ildb.check_and_repair_ts_at_pos(pos, tcb_connid)
-- these must be the same as the first -- these must be the same as the first
if ts_id ~= tcbs_ts_id then if ts_id ~= tcbs_ts_id then
-- inconsistency is found, repair it -- inconsistency is found, repair it
atdebug("check_and_repair_ts_at_pos: Inconsistency is found!") --atdebug("check_and_repair_ts_at_pos: Inconsistency is found!")
return ildb.repair_ts_merge_all(all_tcbs) tsrepair_notify(notify_pname, "Track section inconsistent here, repairing...")
return ildb.repair_ts_merge_all(all_tcbs, false, notify_pname)
-- Step2 check is no longer necessary since we just created that new section -- Step2 check is no longer necessary since we just created that new section
end end
end end
end end
-- only one found (it is either nil or a ts id) -- only one found (it is either nil or a ts id)
atdebug("check_and_repair_ts_at_pos: TS consistent id=",ts_id,"") --atdebug("check_and_repair_ts_at_pos: TS consistent id=",ts_id,"")
if not ts_id then if not ts_id then
tsrepair_notify(notify_pname, "No track section found here.")
return return
-- All TCBs agreed that there is no section here. -- All TCBs agreed that there is no section here.
end end
@ -338,9 +389,11 @@ function ildb.check_and_repair_ts_at_pos(pos, tcb_connid)
-- ildb.tcbs_ensure_ts_ref_exists(sigd) has already make sure that all tcbs are found in the ts's tc_breaks list -- ildb.tcbs_ensure_ts_ref_exists(sigd) has already make sure that all tcbs are found in the ts's tc_breaks list
-- That means it is sufficient to compare the LENGTHS of both lists, if one is longer then it is inconsistent -- That means it is sufficient to compare the LENGTHS of both lists, if one is longer then it is inconsistent
if #ts.tc_breaks ~= #all_tcbs then if #ts.tc_breaks ~= #all_tcbs then
atdebug("check_and_repair_ts_at_pos: Partition is found!") --atdebug("check_and_repair_ts_at_pos: Partition is found!")
return ildb.repair_ts_merge_all(all_tcbs) tsrepair_notify(notify_pname, "Track section partition found, repairing...")
return ildb.repair_ts_merge_all(all_tcbs, false, notify_pname)
end end
tsrepair_notify(notify_pname, "Found section", ts.name or ts_id, "here.")
return ts_id return ts_id
end end
@ -367,7 +420,7 @@ end
-- Returns: a list of sigd's describing the TCBs found (sigd's point inward): -- Returns: a list of sigd's describing the TCBs found (sigd's point inward):
-- {p=<pos>, s=<side>, tcbs=<ref to tcbs table>} -- {p=<pos>, s=<side>, tcbs=<ref to tcbs table>}
function ildb.get_all_tcbs_adjacent(inipos, inidir, per_track_callback) function ildb.get_all_tcbs_adjacent(inipos, inidir, per_track_callback)
atdebug("get_all_tcbs_adjacent: inipos",inipos,"inidir",inidir,"") --atdebug("get_all_tcbs_adjacent: inipos",inipos,"inidir",inidir,"")
local found_sigd = {} local found_sigd = {}
local ti = advtrains.get_track_iterator(inipos, inidir, TS_MAX_SCAN, true) local ti = advtrains.get_track_iterator(inipos, inidir, TS_MAX_SCAN, true)
-- if initial start is TCBS and has xlink, need to add that to the TI -- if initial start is TCBS and has xlink, need to add that to the TI
@ -377,7 +430,7 @@ function ildb.get_all_tcbs_adjacent(inipos, inidir, per_track_callback)
ildb.validate_tcb_xlink(inisi, true) ildb.validate_tcb_xlink(inisi, true)
if initcbs.xlink then if initcbs.xlink then
-- adding the tcb will happen when this branch is retrieved again using ti:next_branch() -- adding the tcb will happen when this branch is retrieved again using ti:next_branch()
atdebug("get_all_tcbs_adjacent: Putting xlink Branch for initial node",initcbs.xlink) --atdebug("get_all_tcbs_adjacent: Putting xlink Branch for initial node",initcbs.xlink)
ti:add_branch(initcbs.xlink.p, initcbs.xlink.s) ti:add_branch(initcbs.xlink.p, initcbs.xlink.s)
end end
end end
@ -400,17 +453,17 @@ function ildb.get_all_tcbs_adjacent(inipos, inidir, per_track_callback)
-- A branch cannot be a TCB, as that would imply that it was a turnout/crossing (illegal) -- A branch cannot be a TCB, as that would imply that it was a turnout/crossing (illegal)
-- UNLESS: (a) it is the start point or (b) it was added via xlink -- UNLESS: (a) it is the start point or (b) it was added via xlink
-- Then the correct conn to use is connid (pointing forward) -- Then the correct conn to use is connid (pointing forward)
atdebug("get_all_tcbs_adjacent: Inserting TCB at branch start",pos, connid) --atdebug("get_all_tcbs_adjacent: Inserting TCB at branch start",pos, connid)
using_connid = connid using_connid = connid
end end
-- add the sigd of this tcb and a reference to the tcb table in it -- add the sigd of this tcb and a reference to the tcb table in it
atdebug("get_all_tcbs_adjacent: Found TCB: ",pos, using_connid, "ts=", tcb[using_connid].ts_id) --atdebug("get_all_tcbs_adjacent: Found TCB: ",pos, using_connid, "ts=", tcb[using_connid].ts_id)
local si = {p=pos, s=using_connid, tcbs=tcb[using_connid]} local si = {p=pos, s=using_connid, tcbs=tcb[using_connid]}
-- if xlink exists, add it now (only if we are not in branch start) -- if xlink exists, add it now (only if we are not in branch start)
ildb.validate_tcb_xlink(si, true) ildb.validate_tcb_xlink(si, true)
if not is_branch_start and si.tcbs.xlink then if not is_branch_start and si.tcbs.xlink then
-- adding the tcb will happen when this branch is retrieved again using ti:next_branch() -- adding the tcb will happen when this branch is retrieved again using ti:next_branch()
atdebug("get_all_tcbs_adjacent: Putting xlink Branch",si.tcbs.xlink) --atdebug("get_all_tcbs_adjacent: Putting xlink Branch",si.tcbs.xlink)
ti:add_branch(si.tcbs.xlink.p, si.tcbs.xlink.s) ti:add_branch(si.tcbs.xlink.p, si.tcbs.xlink.s)
end end
insert_sigd_if_not_present(found_sigd, si) insert_sigd_if_not_present(found_sigd, si)
@ -429,8 +482,8 @@ end
-- Called by frontend functions when multiple tcbs's that logically belong to one section have been determined to have different sections -- Called by frontend functions when multiple tcbs's that logically belong to one section have been determined to have different sections
-- Parameter is the output of ildb.get_all_tcbs_adjacent(pos) -- Parameter is the output of ildb.get_all_tcbs_adjacent(pos)
-- Returns the ID of the track section that results after the merge -- Returns the ID of the track section that results after the merge
function ildb.repair_ts_merge_all(all_tcbs, force_create) function ildb.repair_ts_merge_all(all_tcbs, force_create, notify_pname)
atdebug("repair_ts_merge_all: Instructed to merge sections of following TCBs:") --atdebug("repair_ts_merge_all: Instructed to merge sections of following TCBs:")
-- The first loop does the following for each TCBS: -- The first loop does the following for each TCBS:
-- a) Store the TS ID in the set of TS to update -- a) Store the TS ID in the set of TS to update
-- b) Set the TS ID to nil, so that the TCBS gets removed from the section -- b) Set the TS ID to nil, so that the TCBS gets removed from the section
@ -439,7 +492,7 @@ function ildb.repair_ts_merge_all(all_tcbs, force_create)
local any_ts = false local any_ts = false
for _,sigd in ipairs(all_tcbs) do for _,sigd in ipairs(all_tcbs) do
local ts_id = sigd.tcbs.ts_id local ts_id = sigd.tcbs.ts_id
atdebug(sigd, "ts=", ts_id) --atdebug(sigd, "ts=", ts_id)
if ts_id then if ts_id then
local ts = track_sections[ts_id] local ts = track_sections[ts_id]
if ts then if ts then
@ -455,7 +508,7 @@ function ildb.repair_ts_merge_all(all_tcbs, force_create)
end end
if not any_ts and not force_create then if not any_ts and not force_create then
-- nothing to do at all, just no interlocking. Why were we even called -- nothing to do at all, just no interlocking. Why were we even called
atdebug("repair_ts_merge_all: No track section present, will not create a new one") --atdebug("repair_ts_merge_all: No track section present, will not create a new one")
return nil return nil
end end
-- Purge every TS in turn. TS's that are now empty will be deleted. TS's that still have TCBs will be kept -- Purge every TS in turn. TS's that are now empty will be deleted. TS's that still have TCBs will be kept
@ -464,6 +517,7 @@ function ildb.repair_ts_merge_all(all_tcbs, force_create)
end end
-- Create a new fresh track section with all the TCBs we have in our collection -- Create a new fresh track section with all the TCBs we have in our collection
local new_ts_id, new_ts = ildb.create_ts_from_tcb_list(all_tcbs) local new_ts_id, new_ts = ildb.create_ts_from_tcb_list(all_tcbs)
tsrepair_notify(notify_pname, "Created track section",new_ts_id,"from TCBs:", all_tcbs)
return new_ts_id return new_ts_id
end end
@ -487,20 +541,20 @@ function ildb.purge_ts_tcb_refs(ts_id)
i = i+1 i = i+1
else else
-- this one is to be purged -- this one is to be purged
atdebug("purge_ts_tcb_refs(",ts_id,"): purging",sigd,"(backreference = ",tcbs.ts_id,")") --atdebug("purge_ts_tcb_refs(",ts_id,"): purging",sigd,"(backreference = ",tcbs.ts_id,")")
table.remove(ts.tc_breaks, i) table.remove(ts.tc_breaks, i)
has_changed = true has_changed = true
end end
else else
-- if not tcbs: was anyway an orphan, remove it -- if not tcbs: was anyway an orphan, remove it
atdebug("purge_ts_tcb_refs(",ts_id,"): purging",sigd,"(referred nonexisting TCB)") --atdebug("purge_ts_tcb_refs(",ts_id,"): purging",sigd,"(referred nonexisting TCB)")
table.remove(ts.tc_breaks, i) table.remove(ts.tc_breaks, i)
has_changed = true has_changed = true
end end
end end
if #ts.tc_breaks == 0 then if #ts.tc_breaks == 0 then
-- remove the section completely -- remove the section completely
atdebug("purge_ts_tcb_refs(",ts_id,"): after purging, the section is empty, is being deleted") --atdebug("purge_ts_tcb_refs(",ts_id,"): after purging, the section is empty, is being deleted")
track_sections[ts_id] = nil track_sections[ts_id] = nil
return nil return nil
else else
@ -520,14 +574,14 @@ function ildb.tcbs_ensure_ts_ref_exists(sigd)
if not tcbs or not tcbs.ts_id then return end if not tcbs or not tcbs.ts_id then return end
local ts = ildb.get_ts(tcbs.ts_id) local ts = ildb.get_ts(tcbs.ts_id)
if not ts then if not ts then
atdebug("tcbs_ensure_ts_ref_exists(",sigd,"): TS does not exist, setting to nil") --atdebug("tcbs_ensure_ts_ref_exists(",sigd,"): TS does not exist, setting to nil")
-- TS is deleted, clear own ts id -- TS is deleted, clear own ts id
tcbs.ts_id = nil tcbs.ts_id = nil
return return
end end
local did_insert = insert_sigd_if_not_present(ts.tc_breaks, {p=sigd.p, s=sigd.s}) local did_insert = insert_sigd_if_not_present(ts.tc_breaks, {p=sigd.p, s=sigd.s})
if did_insert then if did_insert then
atdebug("tcbs_ensure_ts_ref_exists(",sigd,"): TCBS was missing reference in TS",tcbs.ts_id) --atdebug("tcbs_ensure_ts_ref_exists(",sigd,"): TCBS was missing reference in TS",tcbs.ts_id)
ildb.update_rs_cache(tcbs.ts_id) ildb.update_rs_cache(tcbs.ts_id)
end end
end end
@ -538,7 +592,7 @@ function ildb.create_ts_from_tcb_list(sigd_list)
while track_sections[id] do while track_sections[id] do
id = advtrains.random_id(8) id = advtrains.random_id(8)
end end
atdebug("create_ts_from_tcb_list: sigd_list=",sigd_list, "new ID will be ",id) --atdebug("create_ts_from_tcb_list: sigd_list=",sigd_list, "new ID will be ",id)
local tcbr = {} local tcbr = {}
-- makes a copy of the sigd list, for use in repair mechanisms where sigd may contain a tcbs field which we dont want -- makes a copy of the sigd list, for use in repair mechanisms where sigd may contain a tcbs field which we dont want
@ -585,23 +639,23 @@ function ildb.get_possible_out_connids(node_name, in_connid)
local nt = node_from_to_state_cache[node_name] local nt = node_from_to_state_cache[node_name]
if not nt[in_connid] then if not nt[in_connid] then
local ta = {} local ta = {}
atdebug("Node From/To State Cache: Caching for ",node_name,"connid",in_connid) --atdebug("Node From/To State Cache: Caching for ",node_name,"connid",in_connid)
local ndef = minetest.registered_nodes[node_name] local ndef = minetest.registered_nodes[node_name]
if ndef.advtrains.node_state_map then if ndef.advtrains.node_state_map then
for state, tnode in pairs(ndef.advtrains.node_state_map) do for state, tnode in pairs(ndef.advtrains.node_state_map) do
local tndef = minetest.registered_nodes[tnode] local tndef = minetest.registered_nodes[tnode]
-- verify that the conns table is identical - this is purely to catch setup errors! -- verify that the conns table is identical - this is purely to catch setup errors!
if not tndef.at_conns or not tndef.at_conn_map then if not tndef.at_conns or not tndef.at_conn_map then
atdebug("ndef:",ndef,", tndef:",tndef) --atdebug("ndef:",ndef,", tndef:",tndef)
error("In AT setup for node "..tnode..": Node set as state "..state.." of "..node_name.." in state_map, but is missing at_conns/at_conn/map!") error("In AT setup for node "..tnode..": Node set as state "..state.." of "..node_name.." in state_map, but is missing at_conns/at_conn_map!")
end end
if #ndef.at_conns ~= #tndef.at_conns then if #ndef.at_conns ~= #tndef.at_conns then
atdebug("ndef:",ndef,", tndef:",tndef) --atdebug("ndef:",ndef,", tndef:",tndef)
error("In AT setup for node "..tnode..": Conns table does not match that of "..node_name.." (of which this is state "..state..")") error("In AT setup for node "..tnode..": Conns table does not match that of "..node_name.." (of which this is state "..state..")")
end end
for connid=1,#ndef.at_conns do for connid=1,#ndef.at_conns do
if ndef.at_conns[connid].c ~= tndef.at_conns[connid].c then if ndef.at_conns[connid].c ~= tndef.at_conns[connid].c then
atdebug("ndef:",ndef,", tndef:",tndef) --atdebug("ndef:",ndef,", tndef:",tndef)
error("In AT setup for node "..tnode..": Conns table does not match that of "..node_name.." (of which this is state "..state..")") error("In AT setup for node "..tnode..": Conns table does not match that of "..node_name.." (of which this is state "..state..")")
end end
end end
@ -610,13 +664,13 @@ function ildb.get_possible_out_connids(node_name, in_connid)
if ta[target_connid] then if ta[target_connid] then
-- Select the variant for which the other way would back-connect. This way, turnouts will switch to the appropriate branch if the train joins -- Select the variant for which the other way would back-connect. This way, turnouts will switch to the appropriate branch if the train joins
local have_back_conn = (tndef.at_conn_map[target_connid])==in_connid local have_back_conn = (tndef.at_conn_map[target_connid])==in_connid
atdebug("Found second state mapping",in_connid,"-",target_connid,"have_back_conn=",have_back_conn) --atdebug("Found second state mapping",in_connid,"-",target_connid,"have_back_conn=",have_back_conn)
if have_back_conn then if have_back_conn then
atdebug("Overwriting",in_connid,"-",target_connid,"=",state) --atdebug("Overwriting",in_connid,"-",target_connid,"=",state)
ta[target_connid] = state ta[target_connid] = state
end end
else else
atdebug("Setting",in_connid,"-",target_connid,"=",state) --atdebug("Setting",in_connid,"-",target_connid,"=",state)
ta[target_connid] = state ta[target_connid] = state
end end
end end
@ -629,26 +683,27 @@ function ildb.get_possible_out_connids(node_name, in_connid)
end end
local function recursively_find_routes(s_pos, s_connid, locks_found, result_table, scan_limit) local function recursively_find_routes(s_pos, s_connid, locks_found, result_table, scan_limit)
atdebug("Recursively restarting at ",s_pos, s_connid, "limit left", scan_limit) --atdebug("Recursively restarting at ",s_pos, s_connid, "limit left", scan_limit)
local ti = advtrains.get_track_iterator(s_pos, s_connid, scan_limit, false) local ti = advtrains.get_track_iterator(s_pos, s_connid, scan_limit, false)
local pos, connid, bconnid = ti:next_branch() local pos, connid, bconnid = ti:next_branch()
pos, connid, bconnid = ti:next_track()-- step once to get ahead of previous turnout pos, connid, bconnid = ti:next_track()-- step once to get ahead of previous turnout
local last_pos
repeat repeat
local node = advtrains.ndb.get_node_or_nil(pos) local node = advtrains.ndb.get_node_or_nil(pos)
atdebug("Walk ",pos, "nodename", node.name, "entering at conn",bconnid) --atdebug("Walk ",pos, "nodename", node.name, "entering at conn",bconnid)
local ndef = minetest.registered_nodes[node.name] local ndef = minetest.registered_nodes[node.name]
if ndef.advtrains and ndef.advtrains.node_state_map then if ndef.advtrains and ndef.advtrains.node_state_map then
-- Stop, this is a switchable node. Find out which conns we can go at -- Stop, this is a switchable node. Find out which conns we can go at
atdebug("Found turnout ",pos, "nodename", node.name, "entering at conn",bconnid) --atdebug("Found turnout ",pos, "nodename", node.name, "entering at conn",bconnid)
local pts = advtrains.encode_pos(pos) local pts = advtrains.encode_pos(pos)
if locks_found[pts] then if locks_found[pts] then
-- we've been here before. Stop -- we've been here before. Stop
atdebug("Was already seen! returning") --atdebug("Was already seen! returning")
return return
end end
local out_conns = ildb.get_possible_out_connids(node.name, bconnid) local out_conns = ildb.get_possible_out_connids(node.name, bconnid)
for oconnid, state in pairs(out_conns) do for oconnid, state in pairs(out_conns) do
atdebug("Going in direction",oconnid,"state",state) --atdebug("Going in direction",oconnid,"state",state)
locks_found[pts] = state locks_found[pts] = state
recursively_find_routes(pos, oconnid, locks_found, result_table, ti.limit) recursively_find_routes(pos, oconnid, locks_found, result_table, ti.limit)
locks_found[pts] = nil locks_found[pts] = nil
@ -656,21 +711,23 @@ local function recursively_find_routes(s_pos, s_connid, locks_found, result_tabl
return return
end end
--otherwise, this might be a tcb --otherwise, this might be a tcb
local tcbs = ildb.get_tcbs({p=pos, s=bconnid}) local tcb = ildb.get_tcb(pos)
if tcbs then if tcb then
-- we found a tcb, store the current locks in the result_table -- we found a tcb, store the current locks in the result_table
local table_key = advtrains.encode_pos(pos).."_"..bconnid local end_pkey = advtrains.encode_pos(pos)
atdebug("Found end TCB", table_key,", returning") --atdebug("Found end TCB", pos, end_pkey,", returning")
if result_table[table_key] then if result_table[end_pkey] then
atwarn("While caching track section routing, found multiple route paths within same track section. Only first one found will be used") atwarn("While caching track section routing, found multiple route paths within same track section. Only first one found will be used")
else else
result_table[table_key] = table.copy(locks_found) result_table[end_pkey] = table.copy(locks_found)
end end
return return
end end
-- Go forward -- Go forward
last_pos = pos
pos, connid, bconnid = ti:next_track() pos, connid, bconnid = ti:next_track()
until not pos -- this stops the loop when either the track end is reached or the limit is hit until not pos -- this stops the loop when either the track end is reached or the limit is hit
--atdebug("recursively_find_routes: Reached track end or limit at", last_pos, ". This path is not saved, returning")
end end
-- Updates the turnout cache of the given track section -- Updates the turnout cache of the given track section
@ -680,25 +737,33 @@ function ildb.update_rs_cache(ts_id)
error("Update TS Cache called with nonexisting ts_id "..(ts_id or "nil")) error("Update TS Cache called with nonexisting ts_id "..(ts_id or "nil"))
end end
local rscache = {} local rscache = {}
atdebug("== Running update_rs_cache for ",ts_id) --atdebug("== Running update_rs_cache for ",ts_id)
-- start on every of the TS's TCBs, walk the track forward and store locks along the way -- start on every of the TS's TCBs, walk the track forward and store locks along the way
for start_tcbi, start_tcbs in ipairs(ts.tc_breaks) do for start_tcbi, start_tcbs in ipairs(ts.tc_breaks) do
rscache[start_tcbi] = {} start_pkey = advtrains.encode_pos(start_tcbs.p)
atdebug("Starting for ",start_tcbi, start_tcbs) rscache[start_pkey] = {}
--atdebug("Starting for ",start_tcbi, start_tcbs)
local locks_found = {} local locks_found = {}
local result_table = {} local result_table = {}
recursively_find_routes(start_tcbs.p, start_tcbs.s, locks_found, result_table, TS_MAX_SCAN) recursively_find_routes(start_tcbs.p, start_tcbs.s, locks_found, result_table, TS_MAX_SCAN)
-- now result_table contains found route locks. Match them with the other TCBs we have in this section -- now result_table contains found route locks. Match them with the other TCBs we have in this section
for end_tcbi, end_tcbs in ipairs(ts.tc_breaks) do for end_tcbi, end_tcbs in ipairs(ts.tc_breaks) do
local table_key = advtrains.encode_pos(end_tcbs.p).."_"..end_tcbs.s if end_tcbi ~= start_tcbi then
if result_table[table_key] then end_pkey = advtrains.encode_pos(end_tcbs.p)
atdebug("Set RSCache entry",start_tcbi.."-"..end_tcbi,"=",result_table[table_key]) if result_table[end_pkey] then
rscache[start_tcbi][end_tcbi] = result_table[table_key] --atdebug("Set RSCache entry",end_pkey.."-"..end_pkey,"=",result_table[end_pkey])
rscache[start_pkey][end_pkey] = result_table[end_pkey]
result_table[end_pkey] = nil
end
end end
end end
-- warn about superfluous entry
for sup_end_pkey, sup_entry in pairs(result_table) do
--atwarn("In update_rs_cache for section",ts_id,"found superfluous endpoint",sup_end_pkey,"->",sup_entry)
end
end end
ts.rs_cache = rscache ts.rs_cache = rscache
atdebug("== Done update_rs_cache for ",ts_id, "result:",rscache) --atdebug("== Done update_rs_cache for ",ts_id, "result:",rscache)
end end
@ -711,21 +776,21 @@ end
-- Create a new TCB at the position and update/repair the adjoining sections -- Create a new TCB at the position and update/repair the adjoining sections
function ildb.create_tcb_at(pos) function ildb.create_tcb_at(pos)
atdebug("create_tcb_at",pos) --atdebug("create_tcb_at",pos)
local pts = advtrains.encode_pos(pos) local pts = advtrains.encode_pos(pos)
track_circuit_breaks[pts] = {[1] = {}, [2] = {}} track_circuit_breaks[pts] = {[1] = {}, [2] = {}}
local all_tcbs_1 = ildb.get_all_tcbs_adjacent(pos, 1) local all_tcbs_1 = ildb.get_all_tcbs_adjacent(pos, 1)
atdebug("TCBs on A side",all_tcbs_1) --atdebug("TCBs on A side",all_tcbs_1)
local all_tcbs_2 = ildb.get_all_tcbs_adjacent(pos, 2) local all_tcbs_2 = ildb.get_all_tcbs_adjacent(pos, 2)
atdebug("TCBs on B side",all_tcbs_2) --atdebug("TCBs on B side",all_tcbs_2)
-- perform TS repair -- perform TS repair
ildb.repair_ts_merge_all(all_tcbs_1) ildb.repair_ts_merge_all(all_tcbs_1, false)
ildb.repair_ts_merge_all(all_tcbs_2) ildb.repair_ts_merge_all(all_tcbs_2, false)
end end
-- Create a new TCB at the position and update/repair the now joined section -- Remove TCB at the position and update/repair the now joined section
function ildb.remove_tcb_at(pos) function ildb.remove_tcb_at(pos)
atdebug("remove_tcb_at",pos) --atdebug("remove_tcb_at",pos)
local pts = advtrains.encode_pos(pos) local pts = advtrains.encode_pos(pos)
local old_tcb = track_circuit_breaks[pts] local old_tcb = track_circuit_breaks[pts]
track_circuit_breaks[pts] = nil track_circuit_breaks[pts] = nil
@ -745,7 +810,7 @@ function ildb.remove_tcb_at(pos)
end end
advtrains.interlocking.remove_tcb_marker(pos) advtrains.interlocking.remove_tcb_marker(pos)
-- If needed, merge the track sections here -- If needed, merge the track sections here
ildb.check_and_repair_ts_at_pos(pos) ildb.check_and_repair_ts_at_pos(pos, nil)
return true return true
end end
@ -758,7 +823,7 @@ function ildb.validate_tcb_xlink(sigd, suppress_repairs)
if not osigd then return end if not osigd then return end
local otcbs = ildb.get_tcbs(tcbs.xlink) local otcbs = ildb.get_tcbs(tcbs.xlink)
if not otcbs then if not otcbs then
atdebug("validate_tcb_xlink",sigd,": Link partner ",osigd,"orphaned") --atdebug("validate_tcb_xlink",sigd,": Link partner ",osigd,"orphaned")
tcbs.xlink = nil tcbs.xlink = nil
if not suppress_repairs then if not suppress_repairs then
ildb.check_and_repair_ts_at_pos(sigd.p, sigd.s) ildb.check_and_repair_ts_at_pos(sigd.p, sigd.s)
@ -767,16 +832,16 @@ function ildb.validate_tcb_xlink(sigd, suppress_repairs)
end end
if otcbs.xlink then if otcbs.xlink then
if not vector.equals(otcbs.xlink.p, sigd.p) or otcbs.xlink.s~=sigd.s then if not vector.equals(otcbs.xlink.p, sigd.p) or otcbs.xlink.s~=sigd.s then
atdebug("validate_tcb_xlink",sigd,": Link partner ",osigd,"backreferencing to someone else (namely",otcbs.xlink,") clearing it") --atdebug("validate_tcb_xlink",sigd,": Link partner ",osigd,"backreferencing to someone else (namely",otcbs.xlink,") clearing it")
tcbs.xlink = nil tcbs.xlink = nil
if not suppress_repairs then if not suppress_repairs then
ildb.check_and_repair_ts_at_pos(sigd.p, sigd.s) ildb.check_and_repair_ts_at_pos(sigd.p, sigd.s)
atdebug("validate_tcb_xlink",sigd,": Link partner ",osigd," was backreferencing to someone else, now updating that") --atdebug("validate_tcb_xlink",sigd,": Link partner ",osigd," was backreferencing to someone else, now updating that")
ildb.validate_tcb_xlink(osigd) ildb.validate_tcb_xlink(osigd)
end end
end end
else else
atdebug("validate_tcb_xlink",sigd,": Link partner ",osigd,"wasn't backreferencing, clearing it") --atdebug("validate_tcb_xlink",sigd,": Link partner ",osigd,"wasn't backreferencing, clearing it")
tcbs.xlink = nil tcbs.xlink = nil
if not suppress_repairs then if not suppress_repairs then
ildb.check_and_repair_ts_at_pos(sigd.p, sigd.s) ildb.check_and_repair_ts_at_pos(sigd.p, sigd.s)
@ -785,19 +850,19 @@ function ildb.validate_tcb_xlink(sigd, suppress_repairs)
end end
function ildb.add_tcb_xlink(sigd1, sigd2) function ildb.add_tcb_xlink(sigd1, sigd2)
atdebug("add_tcb_xlink",sigd1, sigd2) --("add_tcb_xlink",sigd1, sigd2)
local tcbs1 = sigd1.tcbs or ildb.get_tcbs(sigd1) local tcbs1 = sigd1.tcbs or ildb.get_tcbs(sigd1)
local tcbs2 = sigd2.tcbs or ildb.get_tcbs(sigd2) local tcbs2 = sigd2.tcbs or ildb.get_tcbs(sigd2)
if vector.equals(sigd1.p, sigd2.p) then if vector.equals(sigd1.p, sigd2.p) then
atdebug("add_tcb_xlink Cannot xlink with same TCB") --atdebug("add_tcb_xlink Cannot xlink with same TCB")
return return
end end
if not tcbs1 or not tcbs2 then if not tcbs1 or not tcbs2 then
atdebug("add_tcb_xlink TCBS doesnt exist") --atdebug("add_tcb_xlink TCBS doesnt exist")
return return
end end
if tcbs1.xlink or tcbs2.xlink then if tcbs1.xlink or tcbs2.xlink then
atdebug("add_tcb_xlink One already linked") --atdebug("add_tcb_xlink One already linked")
return return
end end
-- apply link -- apply link
@ -808,7 +873,7 @@ function ildb.add_tcb_xlink(sigd1, sigd2)
end end
function ildb.remove_tcb_xlink(sigd) function ildb.remove_tcb_xlink(sigd)
atdebug("remove_tcb_xlink",sigd) --atdebug("remove_tcb_xlink",sigd)
-- Validate first. If Xlink is gone already then, nothing to do -- Validate first. If Xlink is gone already then, nothing to do
ildb.validate_tcb_xlink(sigd) ildb.validate_tcb_xlink(sigd)
-- Checking all of these already done by validate -- Checking all of these already done by validate
@ -816,7 +881,7 @@ function ildb.remove_tcb_xlink(sigd)
local osigd = tcbs.xlink local osigd = tcbs.xlink
if not osigd then if not osigd then
-- validate already cleared us -- validate already cleared us
atdebug("remove_tcb_xlink: Already gone by validate") --atdebug("remove_tcb_xlink: Already gone by validate")
return return
end end
local otcbs = ildb.get_tcbs(tcbs.xlink) local otcbs = ildb.get_tcbs(tcbs.xlink)
@ -829,14 +894,14 @@ function ildb.remove_tcb_xlink(sigd)
end end
function ildb.create_ts_from_tcbs(sigd) function ildb.create_ts_from_tcbs(sigd)
atdebug("create_ts_from_tcbs",sigd) --atdebug("create_ts_from_tcbs",sigd)
local all_tcbs = ildb.get_all_tcbs_adjacent(sigd.p, sigd.s) local all_tcbs = ildb.get_all_tcbs_adjacent(sigd.p, sigd.s)
ildb.repair_ts_merge_all(all_tcbs, true) ildb.repair_ts_merge_all(all_tcbs, true)
end end
-- Remove the given track section, leaving its TCBs with no section assigned -- Remove the given track section, leaving its TCBs with no section assigned
function ildb.remove_ts(ts_id) function ildb.remove_ts(ts_id)
atdebug("remove_ts",ts_id) --atdebug("remove_ts",ts_id)
local ts = track_sections[ts_id] local ts = track_sections[ts_id]
if not ts then if not ts then
error("remove_ts: "..ts_id.." doesn't exist") error("remove_ts: "..ts_id.." doesn't exist")
@ -846,10 +911,10 @@ function ildb.remove_ts(ts_id)
local sigd = ts.tc_breaks[i] local sigd = ts.tc_breaks[i]
local tcbs = ildb.get_tcbs(sigd) local tcbs = ildb.get_tcbs(sigd)
if tcbs then if tcbs then
atdebug("cleared TCB",sigd) --atdebug("cleared TCB",sigd)
tcbs.ts_id = nil tcbs.ts_id = nil
else else
atdebug("orphan TCB",sigd) --atdebug("orphan TCB",sigd)
end end
i = i+1 i = i+1
end end

View File

@ -492,6 +492,8 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
local terminal = get_last_route_item(rp.origin, rp.route) local terminal = get_last_route_item(rp.origin, rp.route)
rp.route.terminal = terminal rp.route.terminal = terminal
rp.route.name = fields.name rp.route.name = fields.name
-- new routes now always use the rscache
rp.route.use_rscache = true
table.insert(tcbs.routes, rp.route) table.insert(tcbs.routes, rp.route)

View File

@ -49,7 +49,7 @@ function ilrs.set_route(signal, route, try)
c_tcbs = ildb.get_tcbs(c_sigd) c_tcbs = ildb.get_tcbs(c_sigd)
if not c_tcbs then if not c_tcbs then
if not try then atwarn("Did not find TCBS",c_sigd,"while setting route",rtename,"of",signal) end 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 end
c_ts_id = c_tcbs.ts_id c_ts_id = c_tcbs.ts_id
if not c_ts_id then if not c_ts_id then
@ -69,28 +69,49 @@ function ilrs.set_route(signal, route, try)
return false, "Section '"..c_ts.name.."' is occupied!", c_ts_id, nil return false, "Section '"..c_ts.name.."' is occupied!", c_ts_id, nil
end end
for pts, state in pairs(c_rseg.locks) do -- 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:",lp,"->",lst,"overrides",c_locks[lp] or "none")
c_locks[lp] = lst
end
for lp, state in pairs(c_locks) do
local confl = ilrs.has_route_lock(pts, state) local confl = ilrs.has_route_lock(pts, state)
local pos = minetest.string_to_pos(pts) local pos = advtrains.decode_pos(lp)
if advtrains.is_passive(pos) then if advtrains.is_passive(pos) then
local cstate = advtrains.getstate(pos) local cstate = advtrains.getstate(pos)
if cstate ~= state then if cstate ~= state then
local confl = ilrs.has_route_lock(pts) local confl = ilrs.has_route_lock(lp)
if confl then 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 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 "..pts..", Held locked by:\n"..confl, nil, pts return false, "Lock conflict at "..minetest.pos_to_string(pos)..", Held locked by:\n"..confl, nil, lp
elseif not try then elseif not try then
advtrains.setstate(pos, state) advtrains.setstate(pos, state)
end end
end end
if not try then if not try then
ilrs.add_route_lock(pts, c_ts_id, "Route '"..rtename.."' from signal '"..signalname.."'", signal) ilrs.add_route_lock(lp, c_ts_id, "Route '"..rtename.."' from signal '"..signalname.."'", signal)
c_lckp[#c_lckp+1] = pts c_lckp[#c_lckp+1] = lp
end end
else 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 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
end end
-- reserve ts and write locks -- reserve ts and write locks
@ -126,6 +147,8 @@ function ilrs.set_route(signal, route, try)
return true return true
end 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 -- Checks whether there is a route lock that prohibits setting the component
-- to the wanted state. returns string with reasons on conflict -- to the wanted state. returns string with reasons on conflict
function ilrs.has_route_lock(pts) function ilrs.has_route_lock(pts)
@ -191,7 +214,7 @@ function ilrs.free_route_locks_indiv(pts, ts, nocallbacks)
-- TODO use luaautomation timers? -- TODO use luaautomation timers?
if not nocallbacks then if not nocallbacks then
minetest.after(0, ilrs.update_waiting, "lck", pts) 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
end end
-- frees all route locks, even manual ones set with the tool, at a specific position -- frees all route locks, even manual ones set with the tool, at a specific position

View File

@ -99,10 +99,11 @@ minetest.register_node("advtrains_interlocking:tcb_node", {
end, end,
after_dig_node = function(pos, oldnode, oldmetadata, player) after_dig_node = function(pos, oldnode, oldmetadata, player)
if not oldmetadata or not oldmetadata.fields then return end if not oldmetadata or not oldmetadata.fields then return end
local pname = player:get_player_name()
local tcbpts = oldmetadata.fields.tcb_pos local tcbpts = oldmetadata.fields.tcb_pos
if tcbpts and tcbpts ~= "" then if tcbpts and tcbpts ~= "" then
local tcbpos = minetest.string_to_pos(tcbpts) local tcbpos = minetest.string_to_pos(tcbpts)
ildb.remove_tcb_at(tcbpos) ildb.remove_tcb_at(tcbpos, pname)
end end
end, end,
}) })
@ -160,7 +161,7 @@ minetest.register_on_punchnode(function(pos, node, player, pointed_thing)
if ildb.get_tcb(pos) then if ildb.get_tcb(pos) then
minetest.chat_send_player(pname, "Configuring TCB: Already existed at this position, it is now linked to this TCB marker") minetest.chat_send_player(pname, "Configuring TCB: Already existed at this position, it is now linked to this TCB marker")
else else
ildb.create_tcb_at(pos) ildb.create_tcb_at(pos, pname)
end end
local meta = minetest.get_meta(tcbnpos) local meta = minetest.get_meta(tcbnpos)

View File

@ -6,7 +6,7 @@ local ilrs = advtrains.interlocking.route
local function node_right_click(pos, pname) local function node_right_click(pos, pname)
if advtrains.is_passive(pos) then if advtrains.is_passive(pos) then
local form = "size[7,5]label[0.5,0.5;Route lock inspector]" local form = "size[7,5]label[0.5,0.5;Route lock inspector]"
local pts = minetest.pos_to_string(pos) local pts = advtrains.encode_pos(pos)
local rtl = ilrs.has_route_lock(pts) local rtl = ilrs.has_route_lock(pts)
@ -53,7 +53,7 @@ local function node_left_click(pos, pname)
return return
end end
local ts_id = advtrains.interlocking.db.check_and_repair_ts_at_pos(pos) local ts_id = advtrains.interlocking.db.check_and_repair_ts_at_pos(pos, nil, pname)
if ts_id then if ts_id then
advtrains.interlocking.db.update_rs_cache(ts_id) advtrains.interlocking.db.update_rs_cache(ts_id)
advtrains.interlocking.highlight_track_section(pos) advtrains.interlocking.highlight_track_section(pos)
@ -107,7 +107,7 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
local pos local pos
local pts = string.match(formname, "^at_il_rtool_(.+)$") local pts = string.match(formname, "^at_il_rtool_(.+)$")
if pts then if pts then
pos = minetest.string_to_pos(pts) pos = advtrains.decode_pos(pts)
end end
if pos then if pos then
if advtrains.is_passive(pos) then if advtrains.is_passive(pos) then