From 6a5540878f334e97b78ef1430698a8bf8b3faa99 Mon Sep 17 00:00:00 2001 From: orwell96 Date: Sun, 19 Mar 2023 15:20:03 +0100 Subject: Auto-Repair Track Sections/TCBs (automatically when adding/removing or triggered by interlocking tool) --- advtrains_interlocking/tool.lua | 90 +++++++++++++++++++++++++++++++---------- 1 file changed, 68 insertions(+), 22 deletions(-) (limited to 'advtrains_interlocking/tool.lua') diff --git a/advtrains_interlocking/tool.lua b/advtrains_interlocking/tool.lua index 5d38b3a..4b701b4 100644 --- a/advtrains_interlocking/tool.lua +++ b/advtrains_interlocking/tool.lua @@ -3,14 +3,64 @@ local ilrs = advtrains.interlocking.route +local function node_right_click(pos, pname) + if advtrains.is_passive(pos) then + local form = "size[7,5]label[0.5,0.5;Route lock inspector]" + local pts = minetest.pos_to_string(pos) + + local rtl = ilrs.has_route_lock(pts) + + if rtl then + form = form.."label[0.5,1;Route locks currently put:\n"..rtl.."]" + form = form.."button_exit[0.5,3.5; 5,1;clear;Clear]" + else + form = form.."label[0.5,1;No route locks set]" + form = form.."button_exit[0.5,3.5; 5,1;emplace;Emplace manual lock]" + end + + minetest.show_formspec(pname, "at_il_rtool_"..pts, form) + return + end + + -- If not a turnout, check the track section and show a form + local node_ok, conns, rail_y=advtrains.get_rail_info_at(pos) + if not node_ok then + minetest.chat_send_player(pname, "Node is not a track!") + return + end + + local ts_id = advtrains.interlocking.db.check_and_repair_ts_at_pos(pos) + if ts_id then + advtrains.interlocking.show_ts_form(ts_id, pname) + else + minetest.chat_send_player(pname, "No track section at this location!") + end +end + +local function node_left_click(pos, pname) + local node_ok, conns, rail_y=advtrains.get_rail_info_at(pos) + if not node_ok then + minetest.chat_send_player(pname, "Node is not a track!") + return + end + + local ts_id = advtrains.interlocking.db.check_and_repair_ts_at_pos(pos) + if ts_id then + advtrains.interlocking.highlight_track_section(pos) + else + minetest.chat_send_player(pname, "No track section at this location!") + end +end + + minetest.register_craftitem("advtrains_interlocking:tool",{ - description = "Interlocking tool\nright-click turnouts to inspect route locks", + description = "Interlocking tool\nPunch: Highlight track section\nPlace: check route locks/show track section info", groups = {cracky=1}, -- key=name, value=rating; rating=1..3. inventory_image = "at_il_tool.png", wield_image = "at_il_tool.png", stack_max = 1, - on_place = function(itemstack, placer, pointed_thing) - local pname = placer:get_player_name() + on_place = function(itemstack, player, pointed_thing) + local pname = player:get_player_name() if not pname then return end @@ -20,27 +70,23 @@ minetest.register_craftitem("advtrains_interlocking:tool",{ end if pointed_thing.type=="node" then local pos=pointed_thing.under - if advtrains.is_passive(pos) then - local form = "size[7,5]label[0.5,0.5;Route lock inspector]" - local pts = minetest.pos_to_string(pos) - - local rtl = ilrs.has_route_lock(pts) - - if rtl then - form = form.."label[0.5,1;Route locks currently put:\n"..rtl.."]" - form = form.."button_exit[0.5,3.5; 5,1;clear;Clear]" - else - form = form.."label[0.5,1;No route locks set]" - form = form.."button_exit[0.5,3.5; 5,1;emplace;Emplace manual lock]" - end - - minetest.show_formspec(pname, "at_il_rtool_"..pts, form) - else - minetest.chat_send_player(pname, "Cannot use this here.") - return - end + node_right_click(pos, pname) end end, + on_use = function(itemstack, player, pointed_thing) + local pname = player:get_player_name() + if not pname then + return + end + if not minetest.check_player_privs(pname, {interlocking=true}) then + minetest.chat_send_player(pname, "Insufficient privileges to use this!") + return + end + if pointed_thing.type=="node" then + local pos=pointed_thing.under + node_left_click(pos, pname) + end + end }) minetest.register_on_player_receive_fields(function(player, formname, fields) -- cgit v1.2.3 From a14eb7fe737ffc4df4077a01f4859d74f62d0af2 Mon Sep 17 00:00:00 2001 From: orwell96 Date: Sun, 19 Mar 2023 17:19:40 +0100 Subject: TCB Xlinking added, to make nonconnected crossings possible --- advtrains/helpers.lua | 2 + advtrains_interlocking/database.lua | 168 ++++++++++++++++++++++++++++++----- advtrains_interlocking/tcb_ts_ui.lua | 95 +++++++++++++------- advtrains_interlocking/tool.lua | 13 ++- 4 files changed, 222 insertions(+), 56 deletions(-) (limited to 'advtrains_interlocking/tool.lua') diff --git a/advtrains/helpers.lua b/advtrains/helpers.lua index 6d22bc5..7e078fb 100644 --- a/advtrains/helpers.lua +++ b/advtrains/helpers.lua @@ -297,6 +297,7 @@ end function advtrains.get_adjacent_rail(this_posnr, this_conns_p, conn_idx, drives_on) local this_pos = advtrains.round_vector_floor_y(this_posnr) local this_conns = this_conns_p + local _ if not this_conns then _, this_conns = advtrains.get_rail_info_at(this_pos) end @@ -583,6 +584,7 @@ function advtrains.get_track_iterator(initial_pos, initial_connid, limit, follow table.insert(ti.branches, {pos = initial_pos, connid = coni, limit=limit}) end end + ti.limit = limit -- safeguard if someone adds a branch before calling anything setmetatable(ti, {__index=trackiter_mt}) return ti end diff --git a/advtrains_interlocking/database.lua b/advtrains_interlocking/database.lua index 38fad77..35afc8d 100644 --- a/advtrains_interlocking/database.lua +++ b/advtrains_interlocking/database.lua @@ -163,6 +163,9 @@ TCB data structure -- This is the "A" side of the TCB [1] = { -- Variant: with adjacent TCs. ts_id = -- ID of the assigned track section + xlink = -- If two sections of track are not physically joined but must function as one TS (e.g. knights move crossing), a bidirectional link can be added with exactly one other TCB. + -- TS search will behave as if these two TCBs were physically connected. + signal = -- optional: when set, routes can be set from this tcb/direction and signal -- aspect will be set accordingly. routeset = -- Route set from this signal. This is the entry that is cleared once @@ -249,17 +252,26 @@ function ildb.get_all_ts() return track_sections end --- Checks the consistency of the track section at the given position --- This method 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: +-- 1: pos is NOT a TCB, tcb_connid MUST be nil +-- 2: pos is a TCB, tcb_connid MUST be given -- @param pos: the position to start from +-- @param tcb_connid: If provided node is a TCB, -- Returns: -- ts_id - the track section that was found -- nil - No track section exists -function ildb.check_and_repair_ts_at_pos(pos) - atdebug("check_and_repair_ts_at_pos", pos) +function ildb.check_and_repair_ts_at_pos(pos, tcb_connid) + atdebug("check_and_repair_ts_at_pos", pos, tcb_connid) + -- check prereqs + 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 + else + if tcb_connid then error("check_and_repair_ts_at_pos: Startpoint is not TCB, must not provide tcb_connid!") end + end -- STEP 1: Ensure that only one section is at this place -- get all TCBs adjacent to this - local all_tcbs = ildb.get_all_tcbs_adjacent(pos, nil) + local all_tcbs = ildb.get_all_tcbs_adjacent(pos, tcb_connid) local first_ts = true local ts_id for _,sigd in ipairs(all_tcbs) do @@ -321,41 +333,63 @@ end -- This function does not trigger a repair. -- @param inipos: the initial position -- @param inidir: the initial direction, or nil to search in all directions +-- @param per_track_callback: A callback function called with signature (pos, connid, bconnid) for every track encountered -- Returns: a list of sigd's describing the TCBs found (sigd's point inward): -- {p=, s=, tcbs=} -function ildb.get_all_tcbs_adjacent(inipos, inidir) +function ildb.get_all_tcbs_adjacent(inipos, inidir, per_track_callback) atdebug("get_all_tcbs_adjacent: inipos",inipos,"inidir",inidir,"") local found_sigd = {} 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 + local inisi = {p=inipos, s=inidir}; + local initcbs = ildb.get_tcbs(inisi) + if initcbs then + ildb.validate_tcb_xlink(inisi, true) + if initcbs.xlink then + -- 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) + ti:add_branch(initcbs.xlink.p, initcbs.xlink.s) + end + end local pos, connid, bconnid, tcb while ti:has_next_branch() do pos, connid = ti:next_branch() --atdebug("get_all_tcbs_adjacent: BRANCH: ",pos, connid) bconnid = nil + is_branch_start = true repeat + -- callback + if per_track_callback then + per_track_callback(pos, connid, bconnid) + end tcb = ildb.get_tcb(pos) if tcb then + local using_connid = bconnid -- found a tcb - if not bconnid then - -- A branch start point cannot be a TCB, as that would imply that it was a turnout/crossing (illegal) - -- Only exception where this can happen is if the start point is a TCB, then we'd need to add the forward side of it to our list - if pos.x==inipos.x and pos.y==inipos.y and pos.z==inipos.z then - -- Note "connid" instead of "bconnid" - atdebug("get_all_tcbs_adjacent: Found Startpoint TCB: ",pos, connid, "ts=", tcb[connid].ts_id) - insert_sigd_if_not_present(found_sigd, {p=pos, s=connid, tcbs=tcb[connid]}) - else - -- this may not happend - error("Found TCB at TrackIterator new branch which is not the start point, this is illegal! pos="..minetest.pos_to_string(pos)) - end - - else - -- add the sigd of this tcb and a reference to the tcb table in it - atdebug("get_all_tcbs_adjacent: Found TCB: ",pos, bconnid, "ts=", tcb[bconnid].ts_id) - insert_sigd_if_not_present(found_sigd, {p=pos, s=bconnid, tcbs=tcb[bconnid]}) + if is_branch_start then + -- 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 + -- Then the correct conn to use is connid (pointing forward) + atdebug("get_all_tcbs_adjacent: Inserting TCB at branch start",pos, connid) + using_connid = connid + end + -- 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) + 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) + ildb.validate_tcb_xlink(si, true) + 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() + atdebug("get_all_tcbs_adjacent: Putting xlink Branch",si.tcbs.xlink) + ti:add_branch(si.tcbs.xlink.p, si.tcbs.xlink.s) + end + insert_sigd_if_not_present(found_sigd, si) + if not is_branch_start then break end end pos, connid, bconnid = ti:next_track() + is_branch_start = false --atdebug("get_all_tcbs_adjacent: TRACK: ",pos, connid, bconnid) until not pos end @@ -464,7 +498,7 @@ function ildb.tcbs_ensure_ts_ref_exists(sigd) local did_insert = insert_sigd_if_not_present(ts.tc_breaks, {p=sigd.p, s=sigd.s}) if did_insert then atdebug("tcbs_ensure_ts_ref_exists(",sigd,"): TCBS was missing reference in TS",tcbs.ts_id) - ildb.update_ts_cache(ts_id) + ildb.update_ts_cache(tcbs.ts_id) end end @@ -515,7 +549,7 @@ function ildb.update_ts_cache(ts_id) end local lntrans = { "A", "B" } -local function sigd_to_string(sigd) +function ildb.sigd_to_string(sigd) return minetest.pos_to_string(sigd.p).." / "..lntrans[sigd.s] end @@ -546,12 +580,98 @@ function ildb.remove_tcb_at(pos) if old_tcb[2].ts_id then ildb.purge_ts_tcb_refs(old_tcb[2].ts_id) end + -- update xlink partners + if old_tcb[1].xlink then + ildb.validate_tcb_xlink(old_tcb[1].xlink) + end + if old_tcb[2].xlink then + ildb.validate_tcb_xlink(old_tcb[2].xlink) + end advtrains.interlocking.remove_tcb_marker(pos) -- If needed, merge the track sections here ildb.check_and_repair_ts_at_pos(pos) return true end +-- Xlink: Connecting not-physically-connected sections handling + +-- Ensures that the xlink of this tcbs is bidirectional +function ildb.validate_tcb_xlink(sigd, suppress_repairs) + local tcbs = sigd.tcbs or ildb.get_tcbs(sigd) + local osigd = tcbs.xlink + if not osigd then return end + local otcbs = ildb.get_tcbs(tcbs.xlink) + if not otcbs then + atdebug("validate_tcb_xlink",sigd,": Link partner ",osigd,"orphaned") + tcbs.xlink = nil + if not suppress_repairs then + ildb.check_and_repair_ts_at_pos(sigd.p, sigd.s) + end + return + end + if otcbs.xlink 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") + tcbs.xlink = nil + if not suppress_repairs then + 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") + ildb.validate_tcb_xlink(osigd) + end + end + else + atdebug("validate_tcb_xlink",sigd,": Link partner ",osigd,"wasn't backreferencing, clearing it") + tcbs.xlink = nil + if not suppress_repairs then + ildb.check_and_repair_ts_at_pos(sigd.p, sigd.s) + end + end +end + +function ildb.add_tcb_xlink(sigd1, sigd2) + atdebug("add_tcb_xlink",sigd1, sigd2) + local tcbs1 = sigd1.tcbs or ildb.get_tcbs(sigd1) + local tcbs2 = sigd2.tcbs or ildb.get_tcbs(sigd2) + if vector.equals(sigd1.p, sigd2.p) then + atdebug("add_tcb_xlink Cannot xlink with same TCB") + return + end + if not tcbs1 or not tcbs2 then + atdebug("add_tcb_xlink TCBS doesnt exist") + return + end + if tcbs1.xlink or tcbs2.xlink then + atdebug("add_tcb_xlink One already linked") + return + end + -- apply link + tcbs1.xlink = {p=sigd2.p, s=sigd2.s} + tcbs2.xlink = {p=sigd1.p, s=sigd1.s} + -- update section. It should be sufficient to call update only once because the TCBs are linked anyway now + ildb.check_and_repair_ts_at_pos(sigd1.p, sigd1.s) +end + +function ildb.remove_tcb_xlink(sigd) + atdebug("remove_tcb_xlink",sigd) + -- Validate first. If Xlink is gone already then, nothing to do + ildb.validate_tcb_xlink(sigd) + -- Checking all of these already done by validate + local tcbs = sigd.tcbs or ildb.get_tcbs(sigd) + local osigd = tcbs.xlink + if not osigd then + -- validate already cleared us + atdebug("remove_tcb_xlink: Already gone by validate") + return + end + local otcbs = ildb.get_tcbs(tcbs.xlink) + -- clear it + otcbs.xlink = nil + tcbs.xlink = nil + -- Update section for ourself and the other one + ildb.check_and_repair_ts_at_pos(sigd.p, sigd.s) + ildb.check_and_repair_ts_at_pos(osigd.p, osigd.s) +end + function ildb.create_ts_from_tcbs(sigd) atdebug("create_ts_from_tcbs",sigd) local all_tcbs = ildb.get_all_tcbs_adjacent(sigd.p, sigd.s) diff --git a/advtrains_interlocking/tcb_ts_ui.lua b/advtrains_interlocking/tcb_ts_ui.lua index 97c28a8..08d1c32 100755 --- a/advtrains_interlocking/tcb_ts_ui.lua +++ b/advtrains_interlocking/tcb_ts_ui.lua @@ -2,6 +2,7 @@ local players_assign_tcb = {} local players_assign_signal = {} +local players_assign_xlink = {} local players_link_ts = {} local ildb = advtrains.interlocking.db @@ -166,6 +167,7 @@ minetest.register_on_punchnode(function(pos, node, player, pointed_thing) meta:set_string("tcb_pos", minetest.pos_to_string(pos)) meta:set_string("infotext", "TCB assigned to "..minetest.pos_to_string(pos)) minetest.chat_send_player(pname, "Configuring TCB: Successfully configured TCB") + advtrains.interlocking.show_tcb_marker(pos) else minetest.chat_send_player(pname, "Configuring TCB: This is not a normal two-connection rail! Aborted.") end @@ -213,9 +215,14 @@ end) -- TCB Form -local function mktcbformspec(tcbs, btnpref, offset, pname) +local function mktcbformspec(pos, side, tcbs, offset, pname) local form = "" + local btnpref = side==1 and "A" or "B" local ts + -- ensure that mapping and xlink are up to date + ildb.tcbs_ensure_ts_ref_exists({p=pos, s=side, tcbs=tcbs}) + ildb.validate_tcb_xlink({p=pos, s=side, tcbs=tcbs}) + -- Note: repair operations may have been triggered by this if tcbs.ts_id then ts = ildb.get_ts(tcbs.ts_id) end @@ -227,6 +234,19 @@ local function mktcbformspec(tcbs, btnpref, offset, pname) form = form.."label[0.5,"..offset..";Side "..btnpref..": ".."End of interlocking]" form = form.."button[0.5,"..(offset+0.5)..";5,1;"..btnpref.."_makeil;Create Interlocked Track Section]" end + -- xlink + if tcbs.xlink then + form = form.."label[0.5,"..(offset+1.5)..";Link:"..ildb.sigd_to_string(tcbs.xlink).."]" + form = form.."button[4.5,"..(offset+1.5)..";1,1;"..btnpref.."_xlinkdel;X]" + else + if players_assign_xlink[pname] then + form = form.."button[0.5,"..(offset+1.5)..";4,1;"..btnpref.."_xlinklink;Link "..ildb.sigd_to_string(players_assign_xlink[pname]).."]" + form = form.."button[4.5,"..(offset+1.5)..";1,1;"..btnpref.."_xlinkabrt;X]" + else + form = form.."label[0.5,"..(offset+1.5)..";No Link]" + form = form.."button[4.5,"..(offset+1.5)..";1,1;"..btnpref.."_xlinkadd;+]" + end + end if tcbs.signal then form = form.."button[0.5,"..(offset+2.5)..";5,1;"..btnpref.."_sigdia;Signalling]" else @@ -245,8 +265,8 @@ function advtrains.interlocking.show_tcb_form(pos, pname) if not tcb then return end local form = "size[6,9] label[0.5,0.5;Track Circuit Break Configuration]" - form = form .. mktcbformspec(tcb[1], "A", 1, pname) - form = form .. mktcbformspec(tcb[2], "B", 5, pname) + form = form .. mktcbformspec(pos, 1, tcb[1], 1, pname) + form = form .. mktcbformspec(pos, 2, tcb[2], 5, pname) minetest.show_formspec(pname, "at_il_tcbconfig_"..minetest.pos_to_string(pos), form) advtrains.interlocking.show_tcb_marker(pos) @@ -276,6 +296,10 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) local f_makeil = {fields.A_makeil, fields.B_makeil} local f_asnsig = {fields.A_asnsig, fields.B_asnsig} local f_sigdia = {fields.A_sigdia, fields.B_sigdia} + local f_xlinkadd = {fields.A_xlinkadd, fields.B_xlinkadd} + local f_xlinkdel = {fields.A_xlinkdel, fields.B_xlinkdel} + local f_xlinklink = {fields.A_xlinklink, fields.B_xlinklink} + local f_xlinkabrt = {fields.A_xlinkabrt, fields.B_xlinkabrt} for connid=1,2 do local tcbs = tcb[connid] @@ -291,6 +315,28 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) end end end + if tcbs.xlink then + if f_xlinkdel[connid] then + ildb.remove_tcb_xlink({p=pos, s=connid}) + end + else + local osigd = players_assign_xlink[pname] + if osigd then + if f_xlinklink[connid] then + ildb.add_tcb_xlink({p=pos, s=connid}, osigd) + players_assign_xlink[pname] = nil + elseif f_xlinkabrt[connid] then + players_assign_xlink[pname] = nil + end + else + if f_xlinkadd[connid] then + players_assign_xlink[pname] = {p=pos, s=connid} + minetest.chat_send_player(pname, "TCB Link: Select linked TCB now!") + minetest.close_formspec(pname, formname) + return -- to not reopen form + end + end + end if f_asnsig[connid] and not tcbs.signal then minetest.chat_send_player(pname, "Configuring TCB: Please punch the signal to assign.") players_assign_signal[pname] = {p=pos, s=connid} @@ -496,36 +542,25 @@ function advtrains.interlocking.remove_tcb_marker(pos) markerent[pts] = nil end +local ts_showparticles_callback = function(pos, connid, bconnid) + minetest.add_particle({ + pos = pos, + velocity = {x=0, y=0, z=0}, + acceleration = {x=0, y=0, z=0}, + expirationtime = 10, + size = 7, + vertical = true, + texture = "at_il_ts_highlight_particle.png", + glow = 6, + }) +end + -- Spawns particles to highlight the clicked track section -- TODO: Adapt behavior to not dumb-walk anymore function advtrains.interlocking.highlight_track_section(pos) - local ti = advtrains.get_track_iterator(pos, nil, 100, true) - local pos, connid, bconnid, tcb - while ti:has_next_branch() do - pos, connid = ti:next_branch() - --atdebug("highlight_track_section: BRANCH: ",pos, connid) - bconnid = nil - repeat - -- spawn particles - minetest.add_particle({ - pos = pos, - velocity = {x=0, y=0, z=0}, - acceleration = {x=0, y=0, z=0}, - expirationtime = 10, - size = 7, - vertical = true, - texture = "at_il_ts_highlight_particle.png", - glow = 6, - }) - -- abort if TCB is found - tcb = ildb.get_tcb(pos) - if tcb then - advtrains.interlocking.show_tcb_marker(pos) - break - end - pos, connid, bconnid = ti:next_track() - --atdebug("highlight_track_section: TRACK: ",pos, connid, bconnid) - until not pos + local all_tcbs = ildb.get_all_tcbs_adjacent(pos, nil, ts_showparticles_callback) + for _,sigd in ipairs(all_tcbs) do + advtrains.interlocking.show_tcb_marker(sigd.p) end end diff --git a/advtrains_interlocking/tool.lua b/advtrains_interlocking/tool.lua index 4b701b4..6723f88 100644 --- a/advtrains_interlocking/tool.lua +++ b/advtrains_interlocking/tool.lua @@ -26,7 +26,11 @@ local function node_right_click(pos, pname) local node_ok, conns, rail_y=advtrains.get_rail_info_at(pos) if not node_ok then minetest.chat_send_player(pname, "Node is not a track!") - return + return + end + if advtrains.interlocking.db.get_tcb(pos) then + advtrains.interlocking.show_tcb_form(pos, pname) + return end local ts_id = advtrains.interlocking.db.check_and_repair_ts_at_pos(pos) @@ -41,7 +45,12 @@ local function node_left_click(pos, pname) local node_ok, conns, rail_y=advtrains.get_rail_info_at(pos) if not node_ok then minetest.chat_send_player(pname, "Node is not a track!") - return + return + end + + if advtrains.interlocking.db.get_tcb(pos) then + advtrains.interlocking.show_tcb_marker(pos) + return end local ts_id = advtrains.interlocking.db.check_and_repair_ts_at_pos(pos) -- cgit v1.2.3 From c890d77d639cb032872c7ddd641927980b2967ac Mon Sep 17 00:00:00 2001 From: orwell Date: Sun, 15 Oct 2023 19:35:39 +0200 Subject: Implement RS cache, per-track-section caching of route locks. This will later be ground truth for routesetting (new-style) --- advtrains_interlocking/database.lua | 144 +++++++++++++++++++++++++++++++++--- advtrains_interlocking/tool.lua | 1 + 2 files changed, 136 insertions(+), 9 deletions(-) (limited to 'advtrains_interlocking/tool.lua') diff --git a/advtrains_interlocking/database.lua b/advtrains_interlocking/database.lua index 551d21a..492326c 100644 --- a/advtrains_interlocking/database.lua +++ b/advtrains_interlocking/database.lua @@ -189,10 +189,10 @@ Track section [id] = { name = "Some human-readable name" tc_breaks = { ,... } -- Bounding TC's (signal specifiers) - rs_cache = { [-] = { [] = "state" } } + rs_cache = { [] = { [] = { [] = "state" } } } -- 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" } - -- Recalculated on every change via update_ts_cache + -- e.g. 1 = { 2 = { "800080008000" = "st" } } + -- Recalculated on every change via update_rs_cache route = { origin = , -- route origin @@ -477,7 +477,7 @@ function ildb.purge_ts_tcb_refs(ts_id) else if has_changed then -- needs to update route cache - ildb.update_ts_cache(ts_id) + ildb.update_rs_cache(ts_id) end return ts end @@ -499,7 +499,7 @@ function ildb.tcbs_ensure_ts_ref_exists(sigd) local did_insert = insert_sigd_if_not_present(ts.tc_breaks, {p=sigd.p, s=sigd.s}) if did_insert then atdebug("tcbs_ensure_ts_ref_exists(",sigd,"): TCBS was missing reference in TS",tcbs.ts_id) - ildb.update_ts_cache(tcbs.ts_id) + ildb.update_rs_cache(tcbs.ts_id) end end @@ -532,23 +532,149 @@ function ildb.create_ts_from_tcb_list(sigd_list) end - ildb.update_ts_cache(id) + ildb.update_rs_cache(id) return id, new_ts end +-- RS CACHE -- + +--[[ +node_from_to_list - cache of from-to connid mappings and their associated state. +Acts like a cache, built on demand by ildb.get_possible_out_connids(nodename) +node_name = { + from_connid = { + to_connid = state + } +} +]] +local node_from_to_state_cache = {} + +function ildb.get_possible_out_connids(node_name, in_connid) + if not node_from_to_state_cache[node_name] then + node_from_to_state_cache[node_name] = {} + end + local nt = node_from_to_state_cache[node_name] + if not nt[in_connid] then + local ta = {} + atdebug("Node From/To State Cache: Caching for ",node_name,"connid",in_connid) + local ndef = minetest.registered_nodes[node_name] + if ndef.advtrains.node_state_map then + for state, tnode in pairs(ndef.advtrains.node_state_map) do + local tndef = minetest.registered_nodes[tnode] + -- 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 + 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!") + end + if #ndef.at_conns ~= #tndef.at_conns then + 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..")") + end + for connid=1,#ndef.at_conns do + if ndef.at_conns[connid].c ~= tndef.at_conns[connid].c then + 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..")") + end + end + -- do the actual caching by looking at the conn_map + local target_connid = tndef.at_conn_map[in_connid] + 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 + 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) + if have_back_conn then + atdebug("Overwriting",in_connid,"-",target_connid,"=",state) + ta[target_connid] = state + end + else + atdebug("Setting",in_connid,"-",target_connid,"=",state) + ta[target_connid] = state + end + end + else + error("Node From/To State Cache: "..node_name.." doesn't have a state map, is not a switchable track! Panic!") + end + nt[in_connid] = ta + end + return nt[in_connid] +end + +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) + local ti = advtrains.get_track_iterator(s_pos, s_connid, scan_limit, false) + local pos, connid, bconnid = ti:next_branch() + pos, connid, bconnid = ti:next_track()-- step once to get ahead of previous turnout + repeat + local node = advtrains.ndb.get_node_or_nil(pos) + atdebug("Walk ",pos, "nodename", node.name, "entering at conn",bconnid) + local ndef = minetest.registered_nodes[node.name] + if ndef.advtrains and ndef.advtrains.node_state_map then + -- 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) + local pts = advtrains.encode_pos(pos) + if locks_found[pts] then + -- we've been here before. Stop + atdebug("Was already seen! returning") + return + end + local out_conns = ildb.get_possible_out_connids(node.name, bconnid) + for oconnid, state in pairs(out_conns) do + atdebug("Going in direction",oconnid,"state",state) + locks_found[pts] = state + recursively_find_routes(pos, oconnid, locks_found, result_table, ti.limit) + locks_found[pts] = nil + end + return + end + --otherwise, this might be a tcb + local tcbs = ildb.get_tcbs({p=pos, s=bconnid}) + if tcbs then + -- we found a tcb, store the current locks in the result_table + local table_key = advtrains.encode_pos(pos).."_"..bconnid + atdebug("Found end TCB", table_key,", returning") + if result_table[table_key] then + atwarn("While caching track section routing, found multiple route paths within same track section. Only first one found will be used") + else + result_table[table_key] = table.copy(locks_found) + end + return + end + -- Go forward + 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 +end -- Updates the turnout cache of the given track section -function ildb.update_ts_cache(ts_id) +function ildb.update_rs_cache(ts_id) local ts = ildb.get_ts(ts_id) if not ts then error("Update TS Cache called with nonexisting ts_id "..(ts_id or "nil")) end local rscache = {} + 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 - -- TODO: Need change in handling of switches - atdebug("update_ts_cache",ts_id,"TODO: implement") + for start_tcbi, start_tcbs in ipairs(ts.tc_breaks) do + rscache[start_tcbi] = {} + atdebug("Starting for ",start_tcbi, start_tcbs) + local locks_found = {} + local result_table = {} + 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 + for end_tcbi, end_tcbs in ipairs(ts.tc_breaks) do + local table_key = advtrains.encode_pos(end_tcbs.p).."_"..end_tcbs.s + if result_table[table_key] then + atdebug("Set RSCache entry",start_tcbi.."-"..end_tcbi,"=",result_table[table_key]) + rscache[start_tcbi][end_tcbi] = result_table[table_key] + end + end + end + ts.rs_cache = rscache + atdebug("== Done update_rs_cache for ",ts_id, "result:",rscache) end + +--- DB API functions + local lntrans = { "A", "B" } function ildb.sigd_to_string(sigd) return minetest.pos_to_string(sigd.p).." / "..lntrans[sigd.s] diff --git a/advtrains_interlocking/tool.lua b/advtrains_interlocking/tool.lua index 6723f88..ef0f06e 100644 --- a/advtrains_interlocking/tool.lua +++ b/advtrains_interlocking/tool.lua @@ -55,6 +55,7 @@ local function node_left_click(pos, pname) local ts_id = advtrains.interlocking.db.check_and_repair_ts_at_pos(pos) if ts_id then + advtrains.interlocking.db.update_rs_cache(ts_id) advtrains.interlocking.highlight_track_section(pos) else minetest.chat_send_player(pname, "No track section at this location!") -- cgit v1.2.3 From 9fa43cb7bfc25ba71d16c8210f0074797a7bca1a Mon Sep 17 00:00:00 2001 From: orwell Date: Sun, 28 Jan 2024 00:42:28 +0100 Subject: Implement routesetting incorporating tscache, other improvements --- advtrains/passive.lua | 2 +- advtrains_interlocking/database.lua | 221 +++++++++++++++++++++----------- advtrains_interlocking/route_prog.lua | 2 + advtrains_interlocking/routesetting.lua | 43 +++++-- advtrains_interlocking/tcb_ts_ui.lua | 5 +- advtrains_interlocking/tool.lua | 6 +- 6 files changed, 185 insertions(+), 94 deletions(-) (limited to 'advtrains_interlocking/tool.lua') diff --git a/advtrains/passive.lua b/advtrains/passive.lua index 741d7df..231da82 100644 --- a/advtrains/passive.lua +++ b/advtrains/passive.lua @@ -57,7 +57,7 @@ function advtrains.setstate(parpos, newstate, pnode) return false, "train_here" 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" end diff --git a/advtrains_interlocking/database.lua b/advtrains_interlocking/database.lua index 5d42309..17a4199 100644 --- a/advtrains_interlocking/database.lua +++ b/advtrains_interlocking/database.lua @@ -84,11 +84,34 @@ function ildb.load(data) if pos then -- that was a pos_to_string local epos = advtrains.encode_pos(pos) + atdebug("ILDB converting TCB position format",pts,"->",epos) track_circuit_breaks[epos] = tcb else -- keep entry, it is already new track_circuit_breaks[pts] = tcb 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 @@ -99,7 +122,23 @@ function ildb.load(data) signal_assignments = data.signalass end 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 if data.rs_callbacks then advtrains.interlocking.route.rte_callbacks = data.rs_callbacks @@ -197,8 +236,8 @@ routes = { -- one table for each track section on the route -- Note that the section ID is implicitly inferred from the TCB 1 = { - locks = { -- component locks for this section of the route. Not used when use_rscache is true. - (-16,9,0) = st + locks = { -- component locks for this section of the route. + 800080008000 = st } next = S[(-23,9,0)/2] -- the start TCB of the next route segment (pointing forward) } @@ -208,7 +247,8 @@ routes = { } name = "" ars = { } - 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: ar_end_sigd = -- the sigd describing the end of the route. Used for merging route options on recalculation } @@ -218,10 +258,12 @@ Track section [id] = { name = "Some human-readable name" tc_breaks = { ,... } -- Bounding TC's (signal specifiers) - rs_cache = { [] = { [] = { [] = "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 - -- 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 + -- Note that the tcb side number is not saved because it is unnecessary route = { origin = , -- route origin @@ -281,17 +323,24 @@ function ildb.get_all_ts() return track_sections 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 -- There are 2 operation modes: -- 1: pos is NOT a TCB, tcb_connid MUST be nil -- 2: pos is a TCB, tcb_connid MUST be given -- @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: -- ts_id - the track section that was found -- nil - No track section exists -function ildb.check_and_repair_ts_at_pos(pos, tcb_connid) - atdebug("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) -- check prereqs 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 @@ -315,15 +364,17 @@ function ildb.check_and_repair_ts_at_pos(pos, tcb_connid) -- these must be the same as the first if ts_id ~= tcbs_ts_id then -- inconsistency is found, repair it - atdebug("check_and_repair_ts_at_pos: Inconsistency is found!") - return ildb.repair_ts_merge_all(all_tcbs) + --atdebug("check_and_repair_ts_at_pos: Inconsistency is found!") + 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 end end end -- 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 + tsrepair_notify(notify_pname, "No track section found here.") return -- All TCBs agreed that there is no section here. 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 -- 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 - atdebug("check_and_repair_ts_at_pos: Partition is found!") - return ildb.repair_ts_merge_all(all_tcbs) + --atdebug("check_and_repair_ts_at_pos: Partition is found!") + tsrepair_notify(notify_pname, "Track section partition found, repairing...") + return ildb.repair_ts_merge_all(all_tcbs, false, notify_pname) end + tsrepair_notify(notify_pname, "Found section", ts.name or ts_id, "here.") return ts_id end @@ -367,7 +420,7 @@ end -- Returns: a list of sigd's describing the TCBs found (sigd's point inward): -- {p=, s=, tcbs=} 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 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 @@ -377,7 +430,7 @@ function ildb.get_all_tcbs_adjacent(inipos, inidir, per_track_callback) ildb.validate_tcb_xlink(inisi, true) if initcbs.xlink then -- 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) 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) -- UNLESS: (a) it is the start point or (b) it was added via xlink -- 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 end -- 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]} -- if xlink exists, add it now (only if we are not in branch start) ildb.validate_tcb_xlink(si, true) 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() - 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) end 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 -- Parameter is the output of ildb.get_all_tcbs_adjacent(pos) -- Returns the ID of the track section that results after the merge -function ildb.repair_ts_merge_all(all_tcbs, force_create) - atdebug("repair_ts_merge_all: Instructed to merge sections of following TCBs:") +function ildb.repair_ts_merge_all(all_tcbs, force_create, notify_pname) + --atdebug("repair_ts_merge_all: Instructed to merge sections of following TCBs:") -- The first loop does the following for each TCBS: -- 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 @@ -439,7 +492,7 @@ function ildb.repair_ts_merge_all(all_tcbs, force_create) local any_ts = false for _,sigd in ipairs(all_tcbs) do local ts_id = sigd.tcbs.ts_id - atdebug(sigd, "ts=", ts_id) + --atdebug(sigd, "ts=", ts_id) if ts_id then local ts = track_sections[ts_id] if ts then @@ -455,7 +508,7 @@ function ildb.repair_ts_merge_all(all_tcbs, force_create) end if not any_ts and not force_create then -- 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 end -- 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 -- 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) + tsrepair_notify(notify_pname, "Created track section",new_ts_id,"from TCBs:", all_tcbs) return new_ts_id end @@ -487,20 +541,20 @@ function ildb.purge_ts_tcb_refs(ts_id) i = i+1 else -- 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) has_changed = true end else -- 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) has_changed = true end end if #ts.tc_breaks == 0 then -- 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 return nil else @@ -520,14 +574,14 @@ function ildb.tcbs_ensure_ts_ref_exists(sigd) if not tcbs or not tcbs.ts_id then return end local ts = ildb.get_ts(tcbs.ts_id) 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 tcbs.ts_id = nil return end local did_insert = insert_sigd_if_not_present(ts.tc_breaks, {p=sigd.p, s=sigd.s}) 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) end end @@ -538,7 +592,7 @@ function ildb.create_ts_from_tcb_list(sigd_list) while track_sections[id] do id = advtrains.random_id(8) 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 = {} -- 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] if not nt[in_connid] then 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] if ndef.advtrains.node_state_map then for state, tnode in pairs(ndef.advtrains.node_state_map) do local tndef = minetest.registered_nodes[tnode] -- 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 - 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!") + --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!") end 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..")") end for connid=1,#ndef.at_conns do 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..")") end end @@ -610,13 +664,13 @@ function ildb.get_possible_out_connids(node_name, in_connid) 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 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 - atdebug("Overwriting",in_connid,"-",target_connid,"=",state) + --atdebug("Overwriting",in_connid,"-",target_connid,"=",state) ta[target_connid] = state end else - atdebug("Setting",in_connid,"-",target_connid,"=",state) + --atdebug("Setting",in_connid,"-",target_connid,"=",state) ta[target_connid] = state end end @@ -629,26 +683,27 @@ function ildb.get_possible_out_connids(node_name, in_connid) end 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 pos, connid, bconnid = ti:next_branch() pos, connid, bconnid = ti:next_track()-- step once to get ahead of previous turnout + local last_pos repeat 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] if ndef.advtrains and ndef.advtrains.node_state_map then -- 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) if locks_found[pts] then -- we've been here before. Stop - atdebug("Was already seen! returning") + --atdebug("Was already seen! returning") return end local out_conns = ildb.get_possible_out_connids(node.name, bconnid) 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 recursively_find_routes(pos, oconnid, locks_found, result_table, ti.limit) locks_found[pts] = nil @@ -656,21 +711,23 @@ local function recursively_find_routes(s_pos, s_connid, locks_found, result_tabl return end --otherwise, this might be a tcb - local tcbs = ildb.get_tcbs({p=pos, s=bconnid}) - if tcbs then + local tcb = ildb.get_tcb(pos) + if tcb then -- we found a tcb, store the current locks in the result_table - local table_key = advtrains.encode_pos(pos).."_"..bconnid - atdebug("Found end TCB", table_key,", returning") - if result_table[table_key] then + local end_pkey = advtrains.encode_pos(pos) + --atdebug("Found end TCB", pos, end_pkey,", returning") + 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") else - result_table[table_key] = table.copy(locks_found) + result_table[end_pkey] = table.copy(locks_found) end return end -- Go forward + last_pos = pos 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 + --atdebug("recursively_find_routes: Reached track end or limit at", last_pos, ". This path is not saved, returning") end -- 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")) end 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 for start_tcbi, start_tcbs in ipairs(ts.tc_breaks) do - rscache[start_tcbi] = {} - atdebug("Starting for ",start_tcbi, start_tcbs) + start_pkey = advtrains.encode_pos(start_tcbs.p) + rscache[start_pkey] = {} + --atdebug("Starting for ",start_tcbi, start_tcbs) local locks_found = {} local result_table = {} 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 for end_tcbi, end_tcbs in ipairs(ts.tc_breaks) do - local table_key = advtrains.encode_pos(end_tcbs.p).."_"..end_tcbs.s - if result_table[table_key] then - atdebug("Set RSCache entry",start_tcbi.."-"..end_tcbi,"=",result_table[table_key]) - rscache[start_tcbi][end_tcbi] = result_table[table_key] + if end_tcbi ~= start_tcbi then + end_pkey = advtrains.encode_pos(end_tcbs.p) + if result_table[end_pkey] then + --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 + -- 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 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 @@ -711,21 +776,21 @@ end -- Create a new TCB at the position and update/repair the adjoining sections function ildb.create_tcb_at(pos) - atdebug("create_tcb_at",pos) + --atdebug("create_tcb_at",pos) local pts = advtrains.encode_pos(pos) track_circuit_breaks[pts] = {[1] = {}, [2] = {}} 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) - atdebug("TCBs on B side",all_tcbs_2) + --atdebug("TCBs on B side",all_tcbs_2) -- perform TS repair - ildb.repair_ts_merge_all(all_tcbs_1) - ildb.repair_ts_merge_all(all_tcbs_2) + ildb.repair_ts_merge_all(all_tcbs_1, false) + ildb.repair_ts_merge_all(all_tcbs_2, false) 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) - atdebug("remove_tcb_at",pos) + --atdebug("remove_tcb_at",pos) local pts = advtrains.encode_pos(pos) local old_tcb = track_circuit_breaks[pts] track_circuit_breaks[pts] = nil @@ -745,7 +810,7 @@ function ildb.remove_tcb_at(pos) end advtrains.interlocking.remove_tcb_marker(pos) -- 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 end @@ -758,7 +823,7 @@ function ildb.validate_tcb_xlink(sigd, suppress_repairs) if not osigd then return end local otcbs = ildb.get_tcbs(tcbs.xlink) 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 if not suppress_repairs then ildb.check_and_repair_ts_at_pos(sigd.p, sigd.s) @@ -767,16 +832,16 @@ function ildb.validate_tcb_xlink(sigd, suppress_repairs) end if otcbs.xlink 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 if not suppress_repairs then 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) end end 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 if not suppress_repairs then ildb.check_and_repair_ts_at_pos(sigd.p, sigd.s) @@ -785,19 +850,19 @@ function ildb.validate_tcb_xlink(sigd, suppress_repairs) end 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 tcbs2 = sigd2.tcbs or ildb.get_tcbs(sigd2) 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 end if not tcbs1 or not tcbs2 then - atdebug("add_tcb_xlink TCBS doesnt exist") + --atdebug("add_tcb_xlink TCBS doesnt exist") return end if tcbs1.xlink or tcbs2.xlink then - atdebug("add_tcb_xlink One already linked") + --atdebug("add_tcb_xlink One already linked") return end -- apply link @@ -808,7 +873,7 @@ function ildb.add_tcb_xlink(sigd1, sigd2) end 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 ildb.validate_tcb_xlink(sigd) -- Checking all of these already done by validate @@ -816,7 +881,7 @@ function ildb.remove_tcb_xlink(sigd) local osigd = tcbs.xlink if not osigd then -- validate already cleared us - atdebug("remove_tcb_xlink: Already gone by validate") + --atdebug("remove_tcb_xlink: Already gone by validate") return end local otcbs = ildb.get_tcbs(tcbs.xlink) @@ -829,14 +894,14 @@ function ildb.remove_tcb_xlink(sigd) end 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) ildb.repair_ts_merge_all(all_tcbs, true) end -- Remove the given track section, leaving its TCBs with no section assigned function ildb.remove_ts(ts_id) - atdebug("remove_ts",ts_id) + --atdebug("remove_ts",ts_id) local ts = track_sections[ts_id] if not ts then 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 tcbs = ildb.get_tcbs(sigd) if tcbs then - atdebug("cleared TCB",sigd) + --atdebug("cleared TCB",sigd) tcbs.ts_id = nil else - atdebug("orphan TCB",sigd) + --atdebug("orphan TCB",sigd) end i = i+1 end diff --git a/advtrains_interlocking/route_prog.lua b/advtrains_interlocking/route_prog.lua index 5fd9363..34807cd 100644 --- a/advtrains_interlocking/route_prog.lua +++ b/advtrains_interlocking/route_prog.lua @@ -492,6 +492,8 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) local terminal = get_last_route_item(rp.origin, rp.route) rp.route.terminal = terminal rp.route.name = fields.name + -- new routes now always use the rscache + rp.route.use_rscache = true table.insert(tcbs.routes, rp.route) diff --git a/advtrains_interlocking/routesetting.lua b/advtrains_interlocking/routesetting.lua index ede3d49..64b8c25 100644 --- a/advtrains_interlocking/routesetting.lua +++ b/advtrains_interlocking/routesetting.lua @@ -49,7 +49,7 @@ function ilrs.set_route(signal, route, try) 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 c_ts_id = c_tcbs.ts_id 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 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 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 -- reserve ts and write locks @@ -126,6 +147,8 @@ function ilrs.set_route(signal, route, try) 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) @@ -191,7 +214,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 diff --git a/advtrains_interlocking/tcb_ts_ui.lua b/advtrains_interlocking/tcb_ts_ui.lua index e365f4f..9e7fcd4 100755 --- a/advtrains_interlocking/tcb_ts_ui.lua +++ b/advtrains_interlocking/tcb_ts_ui.lua @@ -99,10 +99,11 @@ minetest.register_node("advtrains_interlocking:tcb_node", { end, after_dig_node = function(pos, oldnode, oldmetadata, player) if not oldmetadata or not oldmetadata.fields then return end + local pname = player:get_player_name() local tcbpts = oldmetadata.fields.tcb_pos if tcbpts and tcbpts ~= "" then local tcbpos = minetest.string_to_pos(tcbpts) - ildb.remove_tcb_at(tcbpos) + ildb.remove_tcb_at(tcbpos, pname) end end, }) @@ -160,7 +161,7 @@ minetest.register_on_punchnode(function(pos, node, player, pointed_thing) 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") else - ildb.create_tcb_at(pos) + ildb.create_tcb_at(pos, pname) end local meta = minetest.get_meta(tcbnpos) diff --git a/advtrains_interlocking/tool.lua b/advtrains_interlocking/tool.lua index ef0f06e..4ebc56c 100644 --- a/advtrains_interlocking/tool.lua +++ b/advtrains_interlocking/tool.lua @@ -6,7 +6,7 @@ local ilrs = advtrains.interlocking.route local function node_right_click(pos, pname) if advtrains.is_passive(pos) then 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) @@ -53,7 +53,7 @@ local function node_left_click(pos, pname) return 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 advtrains.interlocking.db.update_rs_cache(ts_id) advtrains.interlocking.highlight_track_section(pos) @@ -107,7 +107,7 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) local pos local pts = string.match(formname, "^at_il_rtool_(.+)$") if pts then - pos = minetest.string_to_pos(pts) + pos = advtrains.decode_pos(pts) end if pos then if advtrains.is_passive(pos) then -- cgit v1.2.3 From 7d5f840579b74374698704f256479520bde25091 Mon Sep 17 00:00:00 2001 From: orwell Date: Fri, 13 Dec 2024 00:23:18 +0100 Subject: Repair sections on smartroute, detect start!=end TS in routesetting, create section with IL tool aux1 --- advtrains_interlocking/database.lua | 18 +++++++++++------- advtrains_interlocking/route_ui.lua | 13 ++++++++++++- advtrains_interlocking/routesetting.lua | 10 +++++++++- advtrains_interlocking/smartroute.lua | 23 ++++++++++++++++++----- advtrains_interlocking/tcb_ts_ui.lua | 10 +++++++++- advtrains_interlocking/tool.lua | 16 ++++++++++------ 6 files changed, 69 insertions(+), 21 deletions(-) (limited to 'advtrains_interlocking/tool.lua') diff --git a/advtrains_interlocking/database.lua b/advtrains_interlocking/database.lua index 3104a20..f84f60b 100644 --- a/advtrains_interlocking/database.lua +++ b/advtrains_interlocking/database.lua @@ -351,7 +351,7 @@ end -- Returns: -- ts_id - the track section that was found -- nil - No track section exists -function ildb.check_and_repair_ts_at_pos(pos, tcb_connid, notify_pname) +function ildb.check_and_repair_ts_at_pos(pos, tcb_connid, notify_pname, force_create) --atdebug("check_and_repair_ts_at_pos", pos, tcb_connid) -- check prereqs if ildb.get_tcb(pos) then @@ -378,7 +378,7 @@ function ildb.check_and_repair_ts_at_pos(pos, tcb_connid, notify_pname) -- inconsistency is found, repair it --atdebug("check_and_repair_ts_at_pos: Inconsistency is found!") tsrepair_notify(notify_pname, "Track section inconsistent here, repairing...") - return ildb.repair_ts_merge_all(all_tcbs, false, notify_pname) + return ildb.repair_ts_merge_all(all_tcbs, force_create, notify_pname) -- Step2 check is no longer necessary since we just created that new section end end @@ -386,9 +386,13 @@ function ildb.check_and_repair_ts_at_pos(pos, tcb_connid, notify_pname) -- only one found (it is either nil or a ts id) --atdebug("check_and_repair_ts_at_pos: TS consistent id=",ts_id,"") if not ts_id then - tsrepair_notify(notify_pname, "No track section found here.") - return - -- All TCBs agreed that there is no section here. + if force_create and next(all_tcbs) then --ensure at least one tcb is in list + return ildb.repair_ts_merge_all(all_tcbs, force_create, notify_pname) + else + --tsrepair_notify(notify_pname, "No track section found here.") + return nil + -- All TCBs agreed that there is no section here + end end local ts = ildb.get_ts(ts_id) @@ -403,9 +407,9 @@ function ildb.check_and_repair_ts_at_pos(pos, tcb_connid, notify_pname) if #ts.tc_breaks ~= #all_tcbs then --atdebug("check_and_repair_ts_at_pos: Partition is found!") tsrepair_notify(notify_pname, "Track section partition found, repairing...") - return ildb.repair_ts_merge_all(all_tcbs, false, notify_pname) + return ildb.repair_ts_merge_all(all_tcbs, force_create, notify_pname) end - tsrepair_notify(notify_pname, "Found section", ts.name or ts_id, "here.") + --tsrepair_notify(notify_pname, "Found section", ts.name or ts_id, "here.") return ts_id end diff --git a/advtrains_interlocking/route_ui.lua b/advtrains_interlocking/route_ui.lua index b75f9d7..7dddc6e 100644 --- a/advtrains_interlocking/route_ui.lua +++ b/advtrains_interlocking/route_ui.lua @@ -75,8 +75,17 @@ function atil.show_route_edit_form(pname, sigd, routeid, sel_rpartidx) end 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 then + itab(i, "-!- At "..sigd_to_string(c_sigd)..".Section Start and End do not match!", "err", nil) + break + end + end -- advance - c_sigd = c_rseg.next + c_sigd = nvar i = i + 1 end if c_sigd then @@ -135,6 +144,8 @@ function atil.show_route_edit_form(pname, sigd, routeid, sel_rpartidx) -- checkbox for call-on form = form..string.format("checkbox[4.5,4.0;se_callon;Call-on (section may be occupied);%s]", rseg.call_on) end + elseif sel_rpart and sel_rpart.err then + form = form.."textarea[4.5,2.5;4,4;errorta;Error:;"..tab[sel_rpartidx].."]" else form = form..F.label(4.5, 2, "<< Select a route part to edit options") end diff --git a/advtrains_interlocking/routesetting.lua b/advtrains_interlocking/routesetting.lua index 95ca63c..15c42aa 100644 --- a/advtrains_interlocking/routesetting.lua +++ b/advtrains_interlocking/routesetting.lua @@ -124,9 +124,17 @@ function ilrs.set_route(signal, route, try) 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 then + 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 diff --git a/advtrains_interlocking/smartroute.lua b/advtrains_interlocking/smartroute.lua index 07cdf46..a26f2d1 100644 --- a/advtrains_interlocking/smartroute.lua +++ b/advtrains_interlocking/smartroute.lua @@ -30,7 +30,7 @@ end --route search implementation -- Note this is similar to recursively_find_routes in database.lua, there used for the rscache -local function recursively_find_routes(s_pos, s_connid, searching_shunt, tcbseq, mark_pos, result_table, scan_limit, tscnt_limit) +local function recursively_find_routes(s_pos, s_connid, searching_shunt, tcbseq, mark_pos, result_table, scan_limit, tscnt_limit, cur_ts_id, notify_pname) atdebug("(SmartRoute) Recursively restarting at ",s_pos, s_connid, "limit left", scan_limit,"tscnt",tscnt_limit) local ti = advtrains.get_track_iterator(s_pos, s_connid, scan_limit, false) local pos, connid, bconnid = ti:next_branch() @@ -52,7 +52,7 @@ local function recursively_find_routes(s_pos, s_connid, searching_shunt, tcbseq, --atdebug("Going in direction",oconnid,"state",state) recursively_find_routes(pos, oconnid, searching_shunt, table.copy(tcbseq), table.copy(mark_pos), - result_table, ti.limit, tscnt_limit) + result_table, ti.limit, tscnt_limit, cur_ts_id, notify_pname) end return end @@ -62,6 +62,13 @@ local function recursively_find_routes(s_pos, s_connid, searching_shunt, tcbseq, local fsigd = { p = pos, s = connid } atdebug("(SmartRoute) Encounter TCB ",fsigd) tcbseq[#tcbseq+1] = fsigd + -- TS validity check: ensure that the TS the back connid refers to is the same as the one at the start + local re_ts_id = tcb[bconnid].ts_id + if re_ts_id ~= cur_ts_id then + atwarn("(SmartRoute) Found TS Inconsistency: entered in section",cur_ts_id,"but TCB backref is section",re_ts_id) + ildb.check_and_repair_ts_at_pos(pos, bconnid, notify_pname) + -- nothing needs to be updated on our side + end -- check if this is a possible route endpoint local tcbs = tcb[connid] if tcbs.signal then @@ -93,6 +100,9 @@ local function recursively_find_routes(s_pos, s_connid, searching_shunt, tcbseq, if tscnt_limit <= 0 then break end + -- update the cur_ts_id + cur_ts_id = tcb[connid].ts_id + atdebug("(SmartRoute) Now in section:",cur_ts_id) end -- Go forward last_pos = pos @@ -116,9 +126,9 @@ end -- Maximum scan length for track iterator local TS_MAX_SCAN = 1000 -function sr.rescan(pname, sigd, tscnt_limit, searching_shunt) +function sr.rescan(pname, sigd, tcbs, tscnt_limit, searching_shunt, pname) local result_table = {} - recursively_find_routes(sigd.p, sigd.s, is_startsignal_shunt, {}, {}, result_table, TS_MAX_SCAN, tscnt_limit) + recursively_find_routes(sigd.p, sigd.s, searching_shunt, {}, {}, result_table, TS_MAX_SCAN, tscnt_limit, tcbs.ts_id, pname) return result_table end @@ -128,9 +138,12 @@ function sr.propose_next(pname, sigd, tscnt_limit, searching_shunt) if not tcbs or not tcbs.routes then minetest.chat_send_player(pname, "Smartroute: TCBS or routes don't exist here!") return + elseif not tcbs.ts_id then + minetest.chat_send_player(pname, "Smartroute: No track section directly ahead!") + return end -- Step 1: search for routes using the current settings - local found_routes = sr.rescan(pname, sigd, tscnt_limit, searching_shunt) + local found_routes = sr.rescan(pname, sigd, tcbs, tscnt_limit, searching_shunt, pname) -- Step 2: remove routes for endpoints for which routes already exist local ex_endpts = {} -- key = sigd_to_string for rtid, route in ipairs(tcbs.routes) do diff --git a/advtrains_interlocking/tcb_ts_ui.lua b/advtrains_interlocking/tcb_ts_ui.lua index abcdf61..4f755af 100755 --- a/advtrains_interlocking/tcb_ts_ui.lua +++ b/advtrains_interlocking/tcb_ts_ui.lua @@ -776,8 +776,16 @@ function advtrains.interlocking.check_route_valid(route, sigd) end 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 then + return false, "TCB at "..minetest.pos_to_string(nvar.p).." has different section than previous TCB." + end + end -- advance - c_sigd = c_rseg.next + c_sigd = nvar i = i + 1 end -- check end TCB diff --git a/advtrains_interlocking/tool.lua b/advtrains_interlocking/tool.lua index 4ebc56c..560e129 100644 --- a/advtrains_interlocking/tool.lua +++ b/advtrains_interlocking/tool.lua @@ -3,7 +3,7 @@ local ilrs = advtrains.interlocking.route -local function node_right_click(pos, pname) +local function node_right_click(pos, pname, player) if advtrains.is_passive(pos) then local form = "size[7,5]label[0.5,0.5;Route lock inspector]" local pts = advtrains.encode_pos(pos) @@ -33,7 +33,7 @@ local function node_right_click(pos, pname) return 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 advtrains.interlocking.show_ts_form(ts_id, pname) else @@ -41,7 +41,7 @@ local function node_right_click(pos, pname) end end -local function node_left_click(pos, pname) +local function node_left_click(pos, pname, player) local node_ok, conns, rail_y=advtrains.get_rail_info_at(pos) if not node_ok then minetest.chat_send_player(pname, "Node is not a track!") @@ -52,8 +52,12 @@ local function node_left_click(pos, pname) advtrains.interlocking.show_tcb_marker(pos) return end + + -- create track section if aux1 button down + local pc = player:get_player_control() + local force_create = pc.aux1 - local ts_id = advtrains.interlocking.db.check_and_repair_ts_at_pos(pos, nil, pname) + local ts_id = advtrains.interlocking.db.check_and_repair_ts_at_pos(pos, nil, pname, force_create) if ts_id then advtrains.interlocking.db.update_rs_cache(ts_id) advtrains.interlocking.highlight_track_section(pos) @@ -80,7 +84,7 @@ minetest.register_craftitem("advtrains_interlocking:tool",{ end if pointed_thing.type=="node" then local pos=pointed_thing.under - node_right_click(pos, pname) + node_right_click(pos, pname, player) end end, on_use = function(itemstack, player, pointed_thing) @@ -94,7 +98,7 @@ minetest.register_craftitem("advtrains_interlocking:tool",{ end if pointed_thing.type=="node" then local pos=pointed_thing.under - node_left_click(pos, pname) + node_left_click(pos, pname, player) end end }) -- cgit v1.2.3 From 510734f953f1bf9a4094f67a86a923dc66437f79 Mon Sep 17 00:00:00 2001 From: Maverick2797 Date: Fri, 28 Mar 2025 15:12:12 +0800 Subject: Add craft recipes for Interlocking Tool, Trackmap Tool and PCNaming Tool --- advtrains_interlocking/tool.lua | 6 ++++++ advtrains_luaautomation/pcnaming.lua | 5 +++++ advtrains_trackmap/viewer.lua | 5 +++++ 3 files changed, 16 insertions(+) (limited to 'advtrains_interlocking/tool.lua') diff --git a/advtrains_interlocking/tool.lua b/advtrains_interlocking/tool.lua index 560e129..5ceae04 100644 --- a/advtrains_interlocking/tool.lua +++ b/advtrains_interlocking/tool.lua @@ -124,3 +124,9 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) end end end) + +minetest.register_craft({ + output = "advtrains_interlocking:tool", + type = "shapeless", + recipe = {"dye:green","advtrains:trackworker", "advtrains_interlocking:tcb_node"} +}) \ No newline at end of file diff --git a/advtrains_luaautomation/pcnaming.lua b/advtrains_luaautomation/pcnaming.lua index 0089ae7..ba8048d 100644 --- a/advtrains_luaautomation/pcnaming.lua +++ b/advtrains_luaautomation/pcnaming.lua @@ -86,3 +86,8 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) end end end) +minetest.register_craft({ + output = "advtrains_luaautomation:pcnaming", + type = "shapeless", + recipe = {"dye:red","advtrains:trackworker"} +}) \ No newline at end of file diff --git a/advtrains_trackmap/viewer.lua b/advtrains_trackmap/viewer.lua index b7205c8..2ea54ce 100644 --- a/advtrains_trackmap/viewer.lua +++ b/advtrains_trackmap/viewer.lua @@ -40,4 +40,9 @@ minetest.register_craftitem("advtrains_trackmap:tool",{ node_left_click(pos, pname) end end +}) +minetest.register_craft({ + output = "advtrains_trackmap:tool", + type = "shapeless", + recipe = {"dye:green","advtrains:trackworker", "default:paper"} }) \ No newline at end of file -- cgit v1.2.3