From f8c2ec60d6fe1927b444aae51c3bce075ed05208 Mon Sep 17 00:00:00 2001 From: orwell Date: Sun, 17 Dec 2023 12:20:22 +0100 Subject: Signals can have nil name, documentation on route def --- advtrains_interlocking/route_ui.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'advtrains_interlocking/route_ui.lua') diff --git a/advtrains_interlocking/route_ui.lua b/advtrains_interlocking/route_ui.lua index 1999941..a1a331d 100644 --- a/advtrains_interlocking/route_ui.lua +++ b/advtrains_interlocking/route_ui.lua @@ -33,7 +33,7 @@ function atil.show_route_edit_form(pname, sigd, routeid) local function itab(t) tab[#tab+1] = minetest.formspec_escape(string.gsub(t, ",", " ")) end - itab("TCB "..sigd_to_string(sigd).." ("..tcbs.signal_name..") Route #"..routeid) + itab("TCB "..sigd_to_string(sigd).." ("..(tcbs.signal_name or "")..") Route #"..routeid) -- this code is partially copy-pasted from routesetting.lua -- we start at the tc designated by signal -- cgit v1.2.3 From 6fd845baec0f5aa8b7cdee1adf8d05061a643242 Mon Sep 17 00:00:00 2001 From: orwell Date: Thu, 23 May 2024 00:58:24 +0200 Subject: Connect the ropes, start on making the UI work --- advtrains/passive.lua | 2 +- advtrains_interlocking/database.lua | 2 +- advtrains_interlocking/distant.lua | 200 ----------- advtrains_interlocking/init.lua | 3 - advtrains_interlocking/route_ui.lua | 18 +- advtrains_interlocking/routesetting.lua | 15 +- advtrains_interlocking/signal_api.lua | 220 +++---------- advtrains_interlocking/signal_aspect_accessors.lua | 163 --------- advtrains_interlocking/signal_aspect_ui.lua | 366 ++++++++------------- advtrains_interlocking/tcb_ts_ui.lua | 11 +- advtrains_signals_japan/init.lua | 6 +- advtrains_signals_ks/init.lua | 23 +- 12 files changed, 220 insertions(+), 809 deletions(-) delete mode 100644 advtrains_interlocking/distant.lua delete mode 100644 advtrains_interlocking/signal_aspect_accessors.lua (limited to 'advtrains_interlocking/route_ui.lua') diff --git a/advtrains/passive.lua b/advtrains/passive.lua index 231da82..37b79e4 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.encode_pos(pos)) then + if advtrains.interlocking and advtrains.interlocking.route.has_route_lock(advtrains.encode_pos(pos)) then return false, "route_lock_here" end diff --git a/advtrains_interlocking/database.lua b/advtrains_interlocking/database.lua index 4213c3d..e2df547 100644 --- a/advtrains_interlocking/database.lua +++ b/advtrains_interlocking/database.lua @@ -1006,7 +1006,7 @@ end function ildb.get_ip_signal_asp(pts, connid) local p = ildb.get_ip_signal(pts, connid) if p then - local asp = advtrains.interlocking.signal_get_aspect(p) + local asp = advtrains.interlocking.signal.get_aspect(p) if not asp then atlog("Clearing orphaned signal influence point", pts, "/", connid) ildb.clear_ip_signal(pts, connid) diff --git a/advtrains_interlocking/distant.lua b/advtrains_interlocking/distant.lua deleted file mode 100644 index 32ada82..0000000 --- a/advtrains_interlocking/distant.lua +++ /dev/null @@ -1,200 +0,0 @@ ---- Distant signaling. --- This module implements a database backend for distant signal assignments. --- The actual modifications to signal aspects are still done by signal aspect accessors. --- @module advtrains.interlocking.distant - -local db_distant = {} -local db_distant_of = {} - -local pts = advtrains.encode_pos -local stp = advtrains.decode_pos - ---- Replace the distant signal assignment database. --- @function load --- @param db The new database to load. -local function db_load(x) - if type(x) ~= "table" then - return - end - db_distant = x.distant - db_distant_of = x.distant_of -end - ---- Retrieve the current distant signal assignment database. --- @function save --- @return The current database. -local function db_save() - return { - distant = db_distant, - distant_of = db_distant_of, - } -end - -local update_signal, update_main, update_dst - ---- Unassign a distant signal. --- @function unassign_dst --- @param dst The position of the distant signal. --- @param[opt=false] force Whether to skip callbacks. -local function unassign_dst(dst, force) - local pts_dst = pts(dst) - local main = db_distant_of[pts_dst] - db_distant_of[pts_dst] = nil - if main then - local pts_main = main[1] - local t = db_distant[pts_main] - if t then - t[pts_dst] = nil - end - end - if not force then - update_dst(dst) - end -end - ---- Unassign a main signal. --- @function unassign_main --- @param main The position of the main signal. --- @param[opt=false] force Whether to skip callbacks. -local function unassign_main(main, force) - local pts_main = pts(main) - local t = db_distant[pts_main] - if not t then - return - end - for pts_dst in pairs(t) do - local realmain = db_distant_of[pts_dst] - if realmain and realmain[1] == pts_main then - db_distant_of[pts_dst] = nil - if not force then - local dst = stp(pts_dst) - update_dst(dst) - end - end - end - db_distant[pts_main] = nil -end - ---- Remove all (main and distant) signal assignments from a signal. --- @function unassign_all --- @param pos The position of the signal. --- @param[opt=false] force Whether to skip callbacks. -local function unassign_all(pos, force) - unassign_main(pos) - unassign_dst(pos, force) -end - ---- Check whether a signal is "appropriate" for the distant signal system. --- Currently, a signal is considered appropriate if its signal aspect can be set. --- @function appropriate_signal --- @param pos The position of the signal -local function appropriate_signal(pos) - local node = advtrains.ndb.get_node(pos) - local ndef = minetest.registered_nodes[node.name] or {} - if not ndef then - return false - end - local atdef = ndef.advtrains - if not atdef then - return false - end - return atdef.supported_aspects and atdef.set_aspect and true -end - ---- Assign a distant signal to a main signal. --- @function assign --- @param main The position of the main signal. --- @param dst The position of the distant signal. --- @param[opt="manual"] by The method of assignment. --- @param[opt=false] skip_update Whether to skip callbacks. -local function assign(main, dst, by, skip_update) - if not (appropriate_signal(main) and appropriate_signal(dst)) then - return - end - local pts_main = pts(main) - local pts_dst = pts(dst) - local t = db_distant[pts_main] - if not t then - t = {} - db_distant[pts_main] = t - end - if not by then - by = "manual" - end - unassign_dst(dst, true) - t[pts_dst] = by - db_distant_of[pts_dst] = {pts_main, by} - if not skip_update then - update_dst(dst) - end -end - ---- Get the distant signals assigned to a main signal. --- @function get_distant --- @param main The position of the main signal. --- @treturn {[pos]=by,...} A table of distant signals, with the positions encoded using `advtrains.encode_pos`. -local function get_distant(main) - local pts_main = pts(main) - return db_distant[pts_main] or {} -end - ---- Get the main signal assigned the a distant signal. --- @function get_main --- @param dst The position of the distant signal. --- @return The position of the main signal. --- @return The method of assignment. -local function get_main(dst) - local pts_dst = pts(dst) - local main = db_distant_of[pts_dst] - if not main then - return - end - if main[1] then - return stp(main[1]), unpack(main, 2) - else - return unpack(main) - end -end - ---- Update all distant signals assigned to a main signal. --- @function update_main --- @param main The position of the main signal. -update_main = function(main) - local pts_main = pts(main) - local t = get_distant(main) - for pts_dst in pairs(t) do - local dst = stp(pts_dst) - advtrains.interlocking.signal_readjust_aspect(dst) - end -end - ---- Update the aspect of a distant signal. --- @function update_dst --- @param dst The position of the distant signal. -update_dst = function(dst) - advtrains.interlocking.signal_readjust_aspect(dst) -end - ---- Update the aspect of a combined (main and distant) signal and all distant signals assigned to it. --- @function update_signal --- @param pos The position of the signal. -update_signal = function(pos) - update_main(pos) - update_dst(pos) -end - -advtrains.distant = { - load = db_load, - save = db_save, - assign = assign, - unassign_dst = unassign_dst, - unassign_main = unassign_main, - unassign_all = unassign_all, - get_distant = get_distant, - get_dst = get_distant, - get_main = get_main, - update_main = update_main, - update_dst = update_dst, - update_signal = update_signal, - appropriate_signal = appropriate_signal, -} diff --git a/advtrains_interlocking/init.lua b/advtrains_interlocking/init.lua index dd08b4a..c397aa6 100644 --- a/advtrains_interlocking/init.lua +++ b/advtrains_interlocking/init.lua @@ -15,9 +15,6 @@ local modpath = minetest.get_modpath(minetest.get_current_modname()) .. DIR_DELI --advtrains.interlocking.aspect = dofile(modpath.."aspect.lua") dofile(modpath.."database.lua") -dofile(modpath.."distant.lua") -dofile(modpath.."distant_ui.lua") -dofile(modpath.."signal_aspect_accessors.lua") dofile(modpath.."signal_api.lua") dofile(modpath.."signal_aspect_ui.lua") dofile(modpath.."demosignals.lua") diff --git a/advtrains_interlocking/route_ui.lua b/advtrains_interlocking/route_ui.lua index a1a331d..982c579 100644 --- a/advtrains_interlocking/route_ui.lua +++ b/advtrains_interlocking/route_ui.lua @@ -33,7 +33,7 @@ function atil.show_route_edit_form(pname, sigd, routeid) local function itab(t) tab[#tab+1] = minetest.formspec_escape(string.gsub(t, ",", " ")) end - itab("TCB "..sigd_to_string(sigd).." ("..(tcbs.signal_name or "")..") Route #"..routeid) + itab("("..(tcbs.signal_name or "+")..") Route #"..routeid) -- this code is partially copy-pasted from routesetting.lua -- we start at the tc designated by signal @@ -56,13 +56,14 @@ function atil.show_route_edit_form(pname, sigd, routeid) c_rseg = route[i] c_lckp = {} - itab(""..i.." Entry "..sigd_to_string(c_sigd).." -> Sec. "..(c_ts and c_ts.name or "-").." -> Exit "..(c_rseg.next and sigd_to_string(c_rseg.next) or "END")) + itab(""..i.." "..sigd_to_string(c_sigd)) + itab("= "..(c_ts and c_ts.name or "-").." =") if c_rseg.locks then for pts, state in pairs(c_rseg.locks) do local pos = minetest.string_to_pos(pts) - itab(" Lock: "..pts.." -> "..state) + itab("L "..pts.." -> "..state) if not advtrains.is_passive(pos) then itab("-!- No passive component at "..pts..". Please reconfigure route!") break @@ -75,16 +76,17 @@ function atil.show_route_edit_form(pname, sigd, routeid) end if c_sigd then local e_tcbs = ildb.get_tcbs(c_sigd) - itab("Route end: "..sigd_to_string(c_sigd).." ("..(e_tcbs and e_tcbs.signal_name or "-")..")") + local signame = "-" + if e_tcbs and e_tcbs.signal then signame = e_tcbs.signal_name or "+" end + itab("E "..sigd_to_string(c_sigd).." ("..signame..")") else - itab("Route ends on dead-end") + itab("E (none)") end - form = form.."textlist[0.5,2;7.75,3.9;rtelog;"..table.concat(tab, ",").."]" + form = form.."textlist[0.5,2;3,3.9;rtelog;"..table.concat(tab, ",").."]" form = form.."button[0.5,6;3,1;back;<<< Back to signal]" - form = form.."button[4.5,6;2,1;aspect;Signal Aspect]" - form = form.."button[6.5,6;2,1;delete;Delete Route]" + form = form.."button[5.5,6;3,1;delete;Delete Route]" --atdebug(route.ars) form = form.."style[ars;font=mono]" diff --git a/advtrains_interlocking/routesetting.lua b/advtrains_interlocking/routesetting.lua index 24b3199..a576139 100644 --- a/advtrains_interlocking/routesetting.lua +++ b/advtrains_interlocking/routesetting.lua @@ -138,7 +138,7 @@ function ilrs.set_route(signal, route, try) } if c_tcbs.signal then c_tcbs.route_committed = true - c_tcbs.aspect = route.aspect or advtrains.interlocking.FULL_FREE + c_tcbs.aspect = advtrains.interlocking.signal.MASP_FREE c_tcbs.route_origin = signal signals[#signals+1] = c_tcbs end @@ -166,7 +166,7 @@ function ilrs.set_route(signal, route, try) if (not nodst) and (not assigned_by or assigned_by == "routesetting") then advtrains.distant.assign(lastsig, pos, "routesetting", true) end - advtrains.interlocking.update_signal_aspect(tcbs, i ~= 1) + advtrains.interlocking.signal.update_route_aspect(tcbs, i ~= 1) end end @@ -278,14 +278,7 @@ function ilrs.cancel_route_from(sigd) c_tcbs.route_auto = nil c_tcbs.route_origin = nil - if c_tcbs.signal then - local pos = c_tcbs.signal - local _, assigned_by = advtrains.distant.get_main(pos) - if assigned_by == "routesetting" then - advtrains.distant.unassign_dst(pos, true) - end - end - advtrains.interlocking.update_signal_aspect(c_tcbs) + advtrains.interlocking.signal.update_route_aspect(c_tcbs) c_ts_id = c_tcbs.ts_id if not c_tcbs then @@ -370,7 +363,7 @@ function ilrs.update_route(sigd, tcbs, newrte, cancel) end if has_changed_aspect then -- FIX: prevent an minetest.after() loop caused by update_signal_aspect dispatching path invalidation, which in turn calls ARS again - advtrains.interlocking.update_signal_aspect(tcbs) + advtrains.interlocking.signal.update_route_aspect(tcbs) end advtrains.interlocking.update_player_forms(sigd) end diff --git a/advtrains_interlocking/signal_api.lua b/advtrains_interlocking/signal_api.lua index d27a045..5216594 100644 --- a/advtrains_interlocking/signal_api.lua +++ b/advtrains_interlocking/signal_api.lua @@ -5,9 +5,15 @@ local F = advtrains.formspec local signal = {} signal.MASP_HALT = { - name = "halt", - description = "HALT", - halt = true, + name = nil, + speed = nil, + remote = nil, +} + +signal.MASP_FREE = { + name = "_free", + speed = -1, + remote = nil, } signal.ASPI_HALT = { @@ -50,11 +56,12 @@ Note that once apply_aspect returns, there is no need for advtrains anymore to q When the signal, for any reason, wants to change its aspect by itself *without* going through the signal API then it should update the aspect info cache by calling advtrains.interlocking.signal.update_aspect_info(pos) -Note that the apply_aspect function MUST accept the following main aspect, even if it is not defined in the main_aspects table: -{ name = "halt", halt = true } -It should cause the signal to show its most restrictive aspect. Typically it is a halt aspect, but e.g. for distant-only +Apply_aspect may also receive nil as the main aspect. It usually means that the signal is not assigned to anything particular, +and it should cause the signal to show its most restrictive aspect. Typically it is a halt aspect, but e.g. for distant-only signals this would be "expect stop". +Main aspect names starting with underscore (e.g. "_default") are reserved and must not be used! + == Aspect Info == The actual signal aspect in the already-known format. This is what the trains use to determine halt/proceed and speed. asp = { @@ -152,7 +159,7 @@ end -- - Storing the main aspect and dst pos for this signal permanently (until next change) -- - Assigning the distant signal for this signal -- - Calling apply_aspect() in the signal's node definition to make the signal show the aspect --- - Calling apply_aspect() again whenever the distant signal changes its aspect +-- - Calling apply_aspect() again whenever the remote signal changes its aspect -- - Notifying this signal's distant signals about changes to this signal (unless skip_dst_notify is specified) function signal.set_aspect(pos, main_asp_name, main_asp_speed, rem_pos, skip_dst_notify) local main_pts = advtrains.encode_pos(pos) @@ -252,10 +259,7 @@ function signal.get_aspect(pos) end local function cache_mainaspects(ndefat) - ndefat.main_aspects_lookup = { - -- always define halt aspect - halt = signal.MASP_HALT - } + ndefat.main_aspects_lookup = {} for _,ma in ipairs(ndefat.main_aspects) do ndefat.main_aspects_lookup[ma.name] = ma end @@ -264,7 +268,7 @@ end function signal.get_aspect_internal(pos, aspt) if not aspt then -- oh, no main aspect, nevermind - return nil, aspt.remote, nil + return nil, nil, nil end atdebug("get_aspect_internal",pos,aspt) -- look aspect in nodedef @@ -277,6 +281,10 @@ function signal.get_aspect_internal(pos, aspt) cache_mainaspects(ndefat) end local masp = ndefat.main_aspects_lookup[aspt.name] + -- special handling for the default free aspect ("_free") + if aspt.name == "_free" then + masp = ndefat.main_aspects[1] + end if not masp then atwarn(pos,"invalid main aspect",aspt.name,"valid are",ndefat.main_aspects_lookup) return nil, aspt.remote, node, ndef @@ -355,10 +363,30 @@ end function signal.update_route_aspect(tcbs, skip_dst_notify) if tcbs.signal then local asp = tcbs.aspect or signal.MASP_HALT - signal.set_aspect(tcbs.signal, asp, skip_dst_notify) + signal.set_aspect(tcbs.signal, asp.name, asp.speed, asp.remote, skip_dst_notify) end end +-- Returns how capable the signal is with regards to aspect setting +-- 0: not a signal at all +-- 1: signal has get_aspect_info() but the aspect is not variable (e.g. a sign) +-- 2: signal has apply_aspect() but does not have main aspects (e.g. a pure distant signal) +-- 3: Full capabilities, signal has main aspects and can be used as main/shunt signal (can be start/endpoint of a route) +function signal.get_signal_cap_level(pos) + local node = advtrains.ndb.get_node_or_nil(pos) + local ndef = node and minetest.registered_nodes[node.name] + local ndefat = ndef and ndef.advtrains + if ndefat and ndefat.get_aspect_info then + if ndefat.apply_aspect then + if ndefat.main_aspects then + return 3 + end + return 2 + end + return 1 + end + return 0 +end ---------------- @@ -366,7 +394,7 @@ function signal.can_dig(pos) return not advtrains.interlocking.db.get_sigd_for_signal(pos) end -function advtrains.interlocking.signal_after_dig(pos) +function signal.after_dig(pos) -- TODO clear influence point advtrains.interlocking.signal.clear_aspect(pos) end @@ -374,169 +402,7 @@ end function signal.on_rightclick(pos, node, player, itemstack, pointed_thing) local pname = player:get_player_name() local control = player:get_player_control() - if control.aux1 then - advtrains.interlocking.show_ip_form(pos, pname) - return - end - advtrains.interlocking.show_signal_form(pos, node, pname) -end - -function advtrains.interlocking.show_signal_form(pos, node, pname) - local sigd = advtrains.interlocking.db.get_sigd_for_signal(pos) - if sigd then - advtrains.interlocking.show_signalling_form(sigd, pname) - else - local ndef = minetest.registered_nodes[node.name] - if ndef.advtrains and ndef.advtrains.set_aspect then - -- permit to set aspect manually - local function callback(pname, aspect) - signal.set_aspect(pos, aspect) - end - local isasp = advtrains.interlocking.signal_get_aspect(pos, node) - - advtrains.interlocking.show_signal_aspect_selector( - pname, - ndef.advtrains.supported_aspects, - pos, callback, - isasp) - else - --static signal - only IP - advtrains.interlocking.show_ip_form(pos, pname) - end - end -end - -local players_assign_ip = {} - -local function ipmarker(ipos, connid) - local node_ok, conns, rhe = advtrains.get_rail_info_at(ipos, advtrains.all_tracktypes) - if not node_ok then return end - local yaw = advtrains.dir_to_angle(conns[connid].c) - - -- using tcbmarker here - local obj = minetest.add_entity(vector.add(ipos, {x=0, y=0.2, z=0}), "advtrains_interlocking:tcbmarker") - if not obj then return end - obj:set_yaw(yaw) - obj:set_properties({ - textures = { "at_il_signal_ip.png" }, - }) -end - -function advtrains.interlocking.make_ip_formspec_component(pos, x, y, w) - advtrains.interlocking.db.check_for_duplicate_ip(pos) - local pts, connid = advtrains.interlocking.db.get_ip_by_signalpos(pos) - if pts then - return table.concat { - F.S_label(x, y, "Influence point is set at @1.", string.format("%s/%s", pts, connid)), - F.S_button_exit(x, y+0.5, w/2-0.125, "ip_set", "Modify"), - F.S_button_exit(x+w/2+0.125, y+0.5, w/2-0.125, "ip_clear", "Clear"), - }, pts, connid - else - return table.concat { - F.S_label(x, y, "Influence point is not set."), - F.S_button_exit(x, y+0.5, w, "ip_set", "Set influence point"), - } - end -end - --- shows small info form for signal properties --- This function is named show_ip_form because it was originally only intended --- for assigning/changing the influence point. --- only_notset: show only if it is not set yet (used by signal tcb assignment) -function advtrains.interlocking.show_ip_form(pos, pname, only_notset) - if not minetest.check_player_privs(pname, "interlocking") then - return - end - local ipform, pts, connid = advtrains.interlocking.make_ip_formspec_component(pos, 0.5, 0.5, 7) - local form = { - "formspec_version[4]", - "size[8,2.25]", - ipform, - } - if pts then - local ipos = minetest.string_to_pos(pts) - ipmarker(ipos, connid) - end - if advtrains.distant.appropriate_signal(pos) then - form[#form+1] = advtrains.interlocking.make_dst_formspec_component(pos, 0.5, 2, 7, 4.25) - form[2] = "size[8,6.75]" - end - form = table.concat(form) - if not only_notset or not pts then - minetest.show_formspec(pname, "at_il_propassign_"..minetest.pos_to_string(pos), form) - end + advtrains.interlocking.show_signal_form(pos, node, pname, control.aux1) end -function advtrains.interlocking.handle_ip_formspec_fields(pname, pos, fields) - if not (pos and minetest.check_player_privs(pname, {train_operator=true, interlocking=true})) then - return - end - if fields.ip_set then - advtrains.interlocking.signal_init_ip_assign(pos, pname) - elseif fields.ip_clear then - advtrains.interlocking.db.clear_ip_by_signalpos(pos) - end -end - -minetest.register_on_player_receive_fields(function(player, formname, fields) - local pname = player:get_player_name() - local pts = string.match(formname, "^at_il_propassign_([^_]+)$") - local pos - if pts then - pos = minetest.string_to_pos(pts) - end - if pos then - advtrains.interlocking.handle_ip_formspec_fields(pname, pos, fields) - advtrains.interlocking.handle_dst_formspec_fields(pname, pos, fields) - end -end) - --- inits the signal IP assignment process -function signal.init_ip_assign(pos, pname) - if not minetest.check_player_privs(pname, "interlocking") then - minetest.chat_send_player(pname, "Insufficient privileges to use this!") - return - end - --remove old IP - --advtrains.interlocking.db.clear_ip_by_signalpos(pos) - minetest.chat_send_player(pname, "Configuring Signal: Please look in train's driving direction and punch rail to set influence point.") - - players_assign_ip[pname] = pos -end - -minetest.register_on_punchnode(function(pos, node, player, pointed_thing) - local pname = player:get_player_name() - if not minetest.check_player_privs(pname, "interlocking") then - return - end - -- IP assignment - local signalpos = players_assign_ip[pname] - if signalpos then - if vector.distance(pos, signalpos)<=50 then - local node_ok, conns, rhe = advtrains.get_rail_info_at(pos, advtrains.all_tracktypes) - if node_ok and #conns == 2 then - - local yaw = player:get_look_horizontal() - local plconnid = advtrains.yawToClosestConn(yaw, conns) - - -- add assignment if not already present. - local pts = advtrains.roundfloorpts(pos) - if not advtrains.interlocking.db.get_ip_signal_asp(pts, plconnid) then - advtrains.interlocking.db.set_ip_signal(pts, plconnid, signalpos) - ipmarker(pos, plconnid) - minetest.chat_send_player(pname, "Configuring Signal: Successfully set influence point") - else - minetest.chat_send_player(pname, "Configuring Signal: Influence point of another signal is already present!") - end - else - minetest.chat_send_player(pname, "Configuring Signal: This is not a normal two-connection rail! Aborted.") - end - else - minetest.chat_send_player(pname, "Configuring Signal: Node is too far away. Aborted.") - end - players_assign_ip[pname] = nil - end -end) - - advtrains.interlocking.signal = signal diff --git a/advtrains_interlocking/signal_aspect_accessors.lua b/advtrains_interlocking/signal_aspect_accessors.lua deleted file mode 100644 index d91df31..0000000 --- a/advtrains_interlocking/signal_aspect_accessors.lua +++ /dev/null @@ -1,163 +0,0 @@ ---- Signal aspect accessors --- @module advtrains.interlocking - -local A = advtrains.interlocking.aspect -local D = advtrains.distant -local I = advtrains.interlocking -local N = advtrains.ndb -local pts = advtrains.roundfloorpts - -local get_aspect - -local supposed_aspects = {} - ---- Replace the signal aspect cache. --- @function load_supposed_aspects --- @param db The new database. -function I.load_supposed_aspects(tbl) - if tbl then - supposed_aspects = {} - for k, v in pairs(tbl) do - supposed_aspects[k] = A(v) - end - end -end - ---- Retrieve the signal aspect cache. --- @function save_supposed_aspects --- @return The current database in use. -function I.save_supposed_aspects() - local t = {} - for k, v in pairs(supposed_aspects) do - t[k] = v:plain(true) - end - return t -end - ---- Read the aspect of a signal strictly from cache. --- @param pos The position of the signal. --- @return[1] The aspect of the signal (if present in cache). --- @return[2] The nil constant (otherwise). -local function get_supposed_aspect(pos) - return supposed_aspects[pts(pos)] -end - ---- Update the signal aspect information in cache. --- @param pos The position of the signal. --- @param asp The new signal aspect -local function set_supposed_aspect(pos, asp) - supposed_aspects[pts(pos)] = asp -end - ---- Get the definition of a node. --- @param pos The position of the node. --- @return[1] The definition of the node (if present). --- @return[2] An empty table (otherwise). -local function get_ndef(pos) - local node = N.get_node(pos) - return (minetest.registered_nodes[node.name] or {}), node -end - ---- Get the aspects supported by a signal. --- @function signal_get_supported_aspects --- @param pos The position of the signal. --- @return[1] The table of supported aspects (if present). --- @return[2] The nil constant (otherwise). -local function get_supported_aspects(pos) - local ndef = get_ndef(pos) - if ndef.advtrains and ndef.advtrains.supported_aspects then - return ndef.advtrains.supported_aspects - end - return nil -end - ---- Adjust a new signal aspect to fit a signal. --- @param pos The position of the signal. --- @param asp The new signal aspect. --- @return The adjusted signal aspect. --- @return The information to pass to the `advtrains.set_aspect` field in the node definitions. -local function adjust_aspect(pos, asp) - local asp = A(asp) - - local mainpos = D.get_main(pos) - local nxtasp - if mainpos then - nxtasp = get_aspect(mainpos) - end - local suppasp = get_supported_aspects(pos) - if not suppasp then - return asp - end - return asp:adjust_distant(nxtasp, suppasp.dst_shift):to_group(suppasp.group) -end - ---- Get the aspect of a signal without accessing the cache. --- For most cases, `get_aspect` should be used instead. --- @function signal_get_real_aspect --- @param pos The position of the signal. --- @return[1] The signal aspect adjusted using `adjust_aspect` (if present). --- @return[2] The nil constant (otherwise). -local function get_real_aspect(pos) - local ndef, node = get_ndef(pos) - if ndef.advtrains and ndef.advtrains.get_aspect then - local asp = ndef.advtrains.get_aspect(pos, node) or I.DANGER - return adjust_aspect(pos, asp) - end - return nil -end - ---- Get the aspect of a signal. --- @function signal_get_aspect --- @param pos The position of the signal. --- @return[1] The aspect of the signal (if present). --- @return[2] The nil constant (otherwise). -get_aspect = function(pos) - local asp = get_supposed_aspect(pos) - if not asp then - asp = get_real_aspect(pos) - set_supposed_aspect(pos, asp) - end - return asp -end - ---- Set the aspect of a signal. --- @function signal_set_aspect --- @param pos The position of the signal. --- @param asp The new signal aspect. --- @param[opt=false] skipdst Whether to skip updating distant signals. -local function set_aspect(pos, asp, skipdst) - local node = N.get_node(pos) - local ndef = minetest.registered_nodes[node.name] - if ndef and ndef.advtrains and ndef.advtrains.set_aspect then - local oldasp = I.signal_get_aspect(pos) or DANGER - local newasp = adjust_aspect(pos, asp) - set_supposed_aspect(pos, newasp) - ndef.advtrains.set_aspect(pos, node, newasp) - I.signal_on_aspect_changed(pos) - local aspect_changed = oldasp ~= newasp - if (not skipdst) and aspect_changed then - D.update_main(pos) - end - end -end - ---- Remove a signal from cache. --- @function signal_clear_aspect --- @param pos The position of the signal. -local function clear_aspect(pos) - set_supposed_aspect(pos, nil) -end - ---- Readjust the aspect of a signal. --- @function signal_readjust_aspect --- @param pos The position of the signal. -local function readjust_aspect(pos) - set_aspect(pos, get_aspect(pos)) -end - -I.signal_get_supported_aspects = get_supported_aspects -I.signal_get_real_aspect = get_real_aspect -I.signal_get_aspect = get_aspect -I.signal_set_aspect = set_aspect -I.signal_clear_aspect = clear_aspect -I.signal_readjust_aspect = readjust_aspect diff --git a/advtrains_interlocking/signal_aspect_ui.lua b/advtrains_interlocking/signal_aspect_ui.lua index a81b7fe..e5d2003 100644 --- a/advtrains_interlocking/signal_aspect_ui.lua +++ b/advtrains_interlocking/signal_aspect_ui.lua @@ -1,262 +1,188 @@ local F = advtrains.formspec -local players_aspsel = {} -local function describe_main_aspect(spv) - if spv == 0 then - return attrans("Danger (halt)") - elseif spv == -1 then - return attrans("Continue at maximum speed") - elseif not spv then - return attrans("Continue with current speed limit") +function advtrains.interlocking.show_signal_form(pos, node, pname, aux_key) + local sigd = advtrains.interlocking.db.get_sigd_for_signal(pos) + if sigd and not aux_key then + advtrains.interlocking.show_signalling_form(sigd, pname) else - return attrans("Continue with the speed limit of @1", tostring(spv)) - end -end - -local function describe_shunt_aspect(shunt) - if shunt then - return attrans("Shunting allowed") - else - return attrans("No shunting") + if advtrains.interlocking.signal.get_signal_cap_level(pos) >= 2 then + advtrains.interlocking.show_ip_sa_form(pos, pname) + else + advtrains.interlocking.show_ip_form(pos, pname) + end end end -local function describe_distant_aspect(spv) - if spv == 0 then - return attrans("Expect to stop at the next signal") - elseif spv == -1 then - return attrans("Expect to continue at maximum speed") - elseif not spv then - return attrans("No distant signal information") - else - return attrans("Expect to continue with a speed limit of @1", tostring(spv)) - end +local players_assign_ip = {} +local players_assign_distant = {} + +local function ipmarker(ipos, connid) + local node_ok, conns, rhe = advtrains.get_rail_info_at(ipos, advtrains.all_tracktypes) + if not node_ok then return end + local yaw = advtrains.dir_to_angle(conns[connid].c) + + -- using tcbmarker here + local obj = minetest.add_entity(vector.add(ipos, {x=0, y=0.2, z=0}), "advtrains_interlocking:tcbmarker") + if not obj then return end + obj:set_yaw(yaw) + obj:set_properties({ + textures = { "at_il_signal_ip.png" }, + }) end -advtrains.interlocking.describe_main_aspect = describe_main_aspect -advtrains.interlocking.describe_shunt_aspect = describe_shunt_aspect -advtrains.interlocking.describe_distant_aspect = describe_distant_aspect - -local function dsel(p, q, x, y) - if p == nil then - if q then - return x - else - return y - end - elseif p then - return x +function advtrains.interlocking.make_ip_formspec_component(pos, x, y, w) + advtrains.interlocking.db.check_for_duplicate_ip(pos) + local pts, connid = advtrains.interlocking.db.get_ip_by_signalpos(pos) + if pts then + -- display marker + local ipos = minetest.string_to_pos(pts) + ipmarker(ipos, connid) + return table.concat { + F.S_label(x, y, "Influence point is set at @1.", string.format("%s/%s", pts, connid)), + F.S_button_exit(x, y+0.5, w/2-0.125, "ip_set", "Modify"), + F.S_button_exit(x+w/2+0.125, y+0.5, w/2-0.125, "ip_clear", "Clear"), + } else - return y + return table.concat { + F.S_label(x, y, "Influence point is not set."), + F.S_button_exit(x, y+0.5, w, "ip_set", "Set influence point"), + } end end -local function describe_supported_aspects(suppasp, isasp) - local t = {} - - local entries = {attrans("Use default value")} - local selid = 0 - local mainasps = suppasp.main - if type(mainasps) ~= "table" then - mainasps = {mainasps} - end - for idx, spv in ipairs(mainasps) do - if isasp and spv == rawget(isasp, "main") then - selid = idx - end - entries[idx+1] = describe_main_aspect(spv) - end - t.main = entries - t.main_current = selid+1 - t.main_string = tostring(isasp.main) - if t.main == nil then - t.main_string = "" +-- shows small formspec to set the signal influence point +-- only_notset: show only if it is not set yet (used by signal tcb assignment) +function advtrains.interlocking.show_ip_form(pos, pname, only_notset) + if not minetest.check_player_privs(pname, "interlocking") then + return end - - t.shunt = { - attrans("No shunting"), - attrans("Shunting allowed"), - attrans("Proceed as main"), + local ipform = advtrains.interlocking.make_ip_formspec_component(pos, 0.5, 0.5, 7) + local form = { + "formspec_version[4]", + "size[8,2.25]", + ipform, } - - t.shunt_current = dsel(suppasp.shunt, isasp.shunt, 2, 1) - if dsel(suppasp.proceed_as_main, isasp.proceed_as_main, t.shunt_current == 1) then - t.shunt_current = 3 - end - t.shunt_const = suppasp.shunt ~= nil - - if suppasp.group then - local gdef = advtrains.interlocking.aspect.get_group_definition(suppasp.group) - if gdef then - t.group = suppasp.group - t.groupdef = gdef - local entries = {} - local selid = 1 - for idx, name in ipairs(suppasp.name or {}) do - entries[idx] = gdef.aspects[name].label - if suppasp.group == isasp.group and name == isasp.name then - selid = idx - end - end - t.name = entries - t.name_current = selid - end + if not only_notset or not pts then + minetest.show_formspec(pname, "at_il_ipsaform_"..minetest.pos_to_string(pos), table.concat(form)) end - - return t end -advtrains.interlocking.describe_supported_aspects = describe_supported_aspects - -local function make_signal_aspect_selector(suppasp, purpose, isasp) - local t = describe_supported_aspects(suppasp, isasp) - local formmode = 1 - - local pos - if type(purpose) == "table" then - formmode = 2 - pos = purpose.pos +-- shows larger formspec to set the signal influence point, main aspect and distant signal pos +-- only_notset: show only if it is not set yet (used by signal tcb assignment) +function advtrains.interlocking.show_ip_sa_form(pos, pname) + if not minetest.check_player_privs(pname, "interlocking") then + return end - + local ipform = advtrains.interlocking.make_ip_formspec_component(pos, 0.5, 0.5, 7) + local ma, rpos = advtrains.interlocking.signal.get_aspect(pos) + local saform = F.S_button_exit(0, 2, 4, "sa_dst_assign", rpos and minetest.pos_to_string(rpos) or "") + .. F.S_button_exit(0, 3, 2, "sa_tmp_mainfree", "Main to free") .. F.S_button_exit(2, 3, 2, "sa_tmp_mainhalt", "Main to halt") local form = { "formspec_version[4]", - string.format("size[8,%f]", ({5.75, 10.75})[formmode]), - F.S_label(0.5, 0.5, "Select signal aspect"), + "size[8,4]", + ipform, + saform, } - local h0 = ({0, 1.5})[formmode] - form[#form+1] = F.S_label(0.5, 1.5+h0, "Main aspect") - form[#form+1] = F.S_label(0.5, 3+h0, "Shunt aspect") - form[#form+1] = F.S_button_exit(0.5, 4.5+h0, 7, "asp_save", "Save signal aspect") - if formmode == 1 then - form[#form+1] = F.label(0.5, 1, purpose) - form[#form+1] = F.field(0.5, 2, 7, "asp_mainval", "", t.main_string) - elseif formmode == 2 then - if t.group then - form[#form+1] = F.S_label(0.5, 1.5, "Signal aspect group: @1", t.groupdef.label) - form[#form+1] = F.dropdown(0.5, 2, 7, "asp_namesel", t.name, t.name_current, true) - else - form[#form+1] = F.S_label(0.5, 1.5, "This signal does not belong to a signal aspect group.") - form[#form+1] = F.S_label(0.5, 2, "You can not use a predefined signal aspect.") - end - form[#form+1] = F.S_label(0.5, 1, "Signal at @1", minetest.pos_to_string(pos)) - form[#form+1] = F.dropdown(0.5, 3.5, 7, "asp_mainsel", t.main, t.main_current, true) - form[#form+1] = advtrains.interlocking.make_ip_formspec_component(pos, 0.5, 7, 7) - form[#form+1] = advtrains.interlocking.make_short_dst_formspec_component(pos, 0.5, 8.5, 7) - end + minetest.show_formspec(pname, "at_il_ipsaform_"..minetest.pos_to_string(pos), table.concat(form)) +end - if formmode == 2 and t.shunt_const then - form[#form+1] = F.label(0.5, 3.5+h0, t.shunt[t.shunt_current]) - form[#form+1] = F.S_label(0.5, 4+h0, "The shunt aspect cannot be changed.") - else - form[#form+1] = F.dropdown(0.5, 3.5+h0, 7, "asp_shunt", t.shunt, t.shunt_current, true) +function advtrains.interlocking.handle_ip_sa_formspec_fields(pname, pos, fields) + if not (pos and minetest.check_player_privs(pname, {train_operator=true, interlocking=true})) then + return + end + if fields.ip_set then + advtrains.interlocking.init_ip_assign(pos, pname) + elseif fields.ip_clear then + advtrains.interlocking.db.clear_ip_by_signalpos(pos) + elseif fields.sa_dst_assign then + advtrains.interlocking.init_distant_assign(pos, pname) + elseif fields.sa_tmp_mainfree then + local ma, rpos = advtrains.interlocking.signal.get_aspect(pos) + advtrains.interlocking.signal.set_aspect(pos, "_free", -1, rpos) + elseif fields.sa_tmp_mainhalt then + local ma, rpos = advtrains.interlocking.signal.get_aspect(pos) + advtrains.interlocking.signal.set_aspect(pos, nil, nil, rpos) end - - return table.concat(form) end -function advtrains.interlocking.show_signal_aspect_selector(pname, p_suppasp, p_purpose, callback, isasp) - local suppasp = p_suppasp or { - main = {0, -1}, - dst = {false}, - shunt = false, - info = {}, - } - local purpose = p_purpose or "" +minetest.register_on_player_receive_fields(function(player, formname, fields) + local pname = player:get_player_name() + local pts = string.match(formname, "^at_il_ipsaform_([^_]+)$") local pos - if type(p_purpose) == "table" then - pos = p_purpose - purpose = {pname = pname, pos = pos} + if pts then + pos = minetest.string_to_pos(pts) end - - local form = make_signal_aspect_selector(suppasp, purpose, isasp) - if not form then - return + if pos then + advtrains.interlocking.handle_ip_sa_formspec_fields(pname, pos, fields) end +end) - local token = advtrains.random_id() - minetest.show_formspec(pname, "at_il_sigaspdia_"..token, form) - minetest.after(0, function() - players_aspsel[pname] = { - purpose = purpose, - suppasp = suppasp, - callback = callback, - token = token, - } - end) -end - -local function usebool(sup, val, free) - if sup == nil then - return val == free - else - return sup +-- inits the signal IP assignment process +function advtrains.interlocking.init_ip_assign(pos, pname) + if not minetest.check_player_privs(pname, "interlocking") then + minetest.chat_send_player(pname, "Insufficient privileges to use this!") + return end + --remove old IP + --advtrains.interlocking.db.clear_ip_by_signalpos(pos) + minetest.chat_send_player(pname, "Configuring Signal: Please look in train's driving direction and punch rail to set influence point.") + + players_assign_ip[pname] = pos end -local function get_aspect_from_formspec(suppasp, fields, psl) - local namei, group, name = tonumber(fields.asp_namesel), suppasp.group, nil - local gdef = advtrains.interlocking.aspect.get_group_definition(group) - if gdef then - local names = suppasp.name or {} - name = names[namei] or names[names] - else - group = nil - end - local maini = tonumber(fields.asp_mainsel) - local main = (suppasp.main or {})[(maini or 0)-1] - if not maini then - local mainval = fields.asp_mainval - if mainval == "-1" then - main = -1 - elseif mainval == "x" then - main = false - elseif string.match(mainval, "^%d+$") then - main = tonumber(mainval) - else - main = nil - end - elseif maini <= 1 then - main = nil - end - local shunti = tonumber(fields.asp_shunt) - local shunt = suppasp.shunt - if shunt == nil then - shunt = shunti == 2 - end - local proceed_as_main = suppasp.proceed_as_main - if proceed_as_main == nil then - proceed_as_main = shunti == 3 +-- inits the distant signal assignment process +function advtrains.interlocking.init_distant_assign(pos, pname) + if not minetest.check_player_privs(pname, "interlocking") then + minetest.chat_send_player(pname, "Insufficient privileges to use this!") + return end - return advtrains.interlocking.aspect { - main = main, - shunt = shunt, - proceed_as_main = proceed_as_main, - info = {}, - name = name, - group = group, - } + minetest.chat_send_player(pname, "Set distant signal: Punch the main signal to assign!") + + players_assign_distant[pname] = pos end -minetest.register_on_player_receive_fields(function(player, formname, fields) +minetest.register_on_punchnode(function(pos, node, player, pointed_thing) local pname = player:get_player_name() - local psl = players_aspsel[pname] - if psl then - if formname == "at_il_sigaspdia_"..psl.token then - local suppasp = psl.suppasp - if fields.asp_save then - local asp - asp = get_aspect_from_formspec(suppasp, fields, psl) - if asp then - psl.callback(pname, asp) + if not minetest.check_player_privs(pname, "interlocking") then + return + end + -- IP assignment + local signalpos = players_assign_ip[pname] + if signalpos then + if vector.distance(pos, signalpos)<=50 then + local node_ok, conns, rhe = advtrains.get_rail_info_at(pos, advtrains.all_tracktypes) + if node_ok and #conns == 2 then + + local yaw = player:get_look_horizontal() + local plconnid = advtrains.yawToClosestConn(yaw, conns) + + -- add assignment if not already present. + local pts = advtrains.roundfloorpts(pos) + if not advtrains.interlocking.db.get_ip_signal_asp(pts, plconnid) then + advtrains.interlocking.db.set_ip_signal(pts, plconnid, signalpos) + ipmarker(pos, plconnid) + minetest.chat_send_player(pname, "Configuring Signal: Successfully set influence point") + else + minetest.chat_send_player(pname, "Configuring Signal: Influence point of another signal is already present!") end - end - if type(psl.purpose) == "table" then - local pos = psl.purpose.pos - advtrains.interlocking.handle_ip_formspec_fields(pname, pos, fields) - advtrains.interlocking.handle_dst_formspec_fields(pname, pos, fields) + else + minetest.chat_send_player(pname, "Configuring Signal: This is not a normal two-connection rail! Aborted.") end else - players_aspsel[pname] = nil + minetest.chat_send_player(pname, "Configuring Signal: Node is too far away. Aborted.") end + players_assign_ip[pname] = nil + end + -- DST assignment + signalpos = players_assign_distant[pname] + if signalpos then + -- get current mainaspect + local ma, rpos = advtrains.interlocking.signal.get_aspect(signalpos) + -- if punched pos is valid signal then set it as the new remote, otherwise nil + local nrpos + if advtrains.interlocking.signal.get_signal_cap_level(pos) > 1 then nrpos = pos end + advtrains.interlocking.signal.set_aspect(signalpos, ma.name, ma.speed, nrpos) + players_assign_distant[pname] = nil end end) + diff --git a/advtrains_interlocking/tcb_ts_ui.lua b/advtrains_interlocking/tcb_ts_ui.lua index bfec648..caf22e3 100755 --- a/advtrains_interlocking/tcb_ts_ui.lua +++ b/advtrains_interlocking/tcb_ts_ui.lua @@ -186,7 +186,7 @@ minetest.register_on_punchnode(function(pos, node, player, pointed_thing) local is_signal = minetest.get_item_group(node.name, "advtrains_signal") >= 2 if is_signal then local ndef = minetest.registered_nodes[node.name] - if ndef and ndef.advtrains and ndef.advtrains.set_aspect then + if ndef and ndef.advtrains and ndef.advtrains.apply_aspect then local tcbs = ildb.get_tcbs(sigd) if tcbs then tcbs.signal = pos @@ -464,7 +464,7 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) ts.route = nil for _, sigd in ipairs(ts.tc_breaks) do local tcbs = ildb.get_tcbs(sigd) - advtrains.interlocking.update_signal_aspect(tcbs) + advtrains.interlocking.signal.update_route_aspect(tcbs) end minetest.chat_send_player(pname, "Reset track section "..ts_id.."!") end @@ -642,7 +642,6 @@ function advtrains.interlocking.show_signalling_form(sigd, pname, sel_rte, calle form = form.."button[0.5,8;2.5,1;newroute;New Route]" form = form.."button[ 3,8;2.5,1;unassign;Unassign Signal]" form = form..string.format("checkbox[0.5,8.75;ars;Automatic routesetting;%s]", not tcbs.ars_disabled) - form = form..string.format("checkbox[0.5,9.25;dst;Distant signalling;%s]", not tcbs.nodst) end elseif sigd_equal(tcbs.route_origin, sigd) then -- something has gone wrong: tcbs.routeset should have been set... @@ -660,7 +659,7 @@ function advtrains.interlocking.show_signalling_form(sigd, pname, sel_rte, calle -- always a good idea to update the signal aspect if not called_from_form_update then -- FIX prevent a callback loop - advtrains.interlocking.update_signal_aspect(tcbs) + advtrains.interlocking.signal.update_route_aspect(tcbs) end end @@ -769,10 +768,6 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) if fields.ars then tcbs.ars_disabled = not minetest.is_yes(fields.ars) end - - if fields.dst then - tcbs.nodst = not minetest.is_yes(fields.dst) - end if fields.auto then tcbs.route_auto = true diff --git a/advtrains_signals_japan/init.lua b/advtrains_signals_japan/init.lua index eb39f85..d7cf035 100644 --- a/advtrains_signals_japan/init.lua +++ b/advtrains_signals_japan/init.lua @@ -374,10 +374,6 @@ for sigtype, sigdata in pairs { --sigdefs["rpt_"..sigtype] = process_signal(sigtype, sigdata, true) end -for k in pairs(sigdefs) do - advtrains.trackplacer.register_tracktype("advtrains_signals_japan:"..k) -end - for _, rtab in ipairs { {rot = "0", ici = true}, {rot = "30"}, @@ -455,7 +451,7 @@ for _, rtab in ipairs { can_dig = advtrains.interlocking.signal_can_dig, after_dig_node = advtrains.interlocking.signal_after_dig, }) - advtrains.trackplacer.add_worked("advtrains_signals_japan:"..sigtype, asp, "_"..rot) + --advtrains.trackplacer.add_worked("advtrains_signals_japan:"..sigtype, asp, "_"..rot) end end end diff --git a/advtrains_signals_ks/init.lua b/advtrains_signals_ks/init.lua index 67e0fec..abfb194 100755 --- a/advtrains_signals_ks/init.lua +++ b/advtrains_signals_ks/init.lua @@ -50,15 +50,18 @@ end local applyaspectf_main = function(rot) return function(pos, node, main_aspect, dst_aspect, dst_aspect_info) + if not main_aspect then + -- halt aspect, set red and don't do anything further + advtrains.ndb.swap_node(pos, {name="advtrains_signals_ks:hs_danger_"..rot, param2 = node.param2}) + setzs3v(pos, nil, rot) + return + end -- set zs3 signal to show speed according to main_aspect setzs3(pos, main_aspect.zs3, rot) -- select appropriate lamps based on mainaspect and dst if main_aspect.shunt then advtrains.ndb.swap_node(pos, {name="advtrains_signals_ks:hs_shunt_"..rot, param2 = node.param2}) setzs3v(pos, nil, rot) - elseif main_aspect.halt then - advtrains.ndb.swap_node(pos, {name="advtrains_signals_ks:hs_danger_"..rot, param2 = node.param2}) - setzs3v(pos, nil, rot) else if not dst_aspect_info or not dst_aspect_info.main @@ -128,7 +131,8 @@ local mainaspects_main = { local applyaspectf_ra = function(rot) -- we get here the full main_aspect table return function(pos, node, main_aspect, dst_aspect, dst_aspect_info) - if main_aspect.shunt then + if main_aspect then + -- any main aspect is fine, there's only one anyway advtrains.ndb.swap_node(pos, {name="advtrains_signals_ks:ra_shuntd_"..rot, param2 = node.param2}) else advtrains.ndb.swap_node(pos, {name="advtrains_signals_ks:ra_danger_"..rot, param2 = node.param2}) @@ -144,11 +148,6 @@ local mainaspects_shunt = { description = "Shunt", shunt = true, }, - { - name = "halt", - description = "Halt", - halt = true, - }, } for _, rtab in ipairs({ @@ -225,9 +224,9 @@ for _, rtab in ipairs({ apply_aspect = applyaspectf_main(rot), get_aspect_info = afunc, }, - on_rightclick = advtrains.interlocking.signal_rc_handler, - can_dig = advtrains.interlocking.signal_can_dig, - after_dig_node = advtrains.interlocking.signal_after_dig, + on_rightclick = advtrains.interlocking.signal.on_rightclick, + can_dig = advtrains.interlocking.signal.can_dig, + after_dig_node = advtrains.interlocking.signal.after_dig, }) -- rotatable by trackworker --TODO add rotation using trackworker -- cgit v1.2.3 From c145e5db7473a0baab6438d7c2ed9616948d8387 Mon Sep 17 00:00:00 2001 From: orwell Date: Sat, 22 Jun 2024 21:11:50 +0200 Subject: SmartRoute: Implement auto route search and first prototype --- advtrains/init.lua | 2 +- advtrains_interlocking/init.lua | 1 + advtrains_interlocking/route_ui.lua | 8 ++ advtrains_interlocking/signal_aspect_ui.lua | 1 + advtrains_interlocking/smartroute.lua | 149 ++++++++++++++++++++++++++++ advtrains_interlocking/tcb_ts_ui.lua | 3 + 6 files changed, 163 insertions(+), 1 deletion(-) create mode 100644 advtrains_interlocking/smartroute.lua (limited to 'advtrains_interlocking/route_ui.lua') diff --git a/advtrains/init.lua b/advtrains/init.lua index 3bd8374..bca39df 100644 --- a/advtrains/init.lua +++ b/advtrains/init.lua @@ -33,7 +33,7 @@ advtrains = {trains={}, player_to_train_mapping={}} -- =======================Development/debugging settings===================== -- DO NOT USE FOR NORMAL OPERATION -local DUMP_DEBUG_SAVE = false +local DUMP_DEBUG_SAVE = true -- dump the save files in human-readable format into advtrains_DUMP local GENERATE_ATRICIFIAL_LAG = false diff --git a/advtrains_interlocking/init.lua b/advtrains_interlocking/init.lua index a4ddbad..5883ab3 100644 --- a/advtrains_interlocking/init.lua +++ b/advtrains_interlocking/init.lua @@ -22,6 +22,7 @@ dofile(modpath.."route_prog.lua") dofile(modpath.."routesetting.lua") dofile(modpath.."tcb_ts_ui.lua") dofile(modpath.."route_ui.lua") +dofile(modpath.."smartroute.lua") dofile(modpath.."tool.lua") dofile(modpath.."approach.lua") diff --git a/advtrains_interlocking/route_ui.lua b/advtrains_interlocking/route_ui.lua index 982c579..863fe11 100644 --- a/advtrains_interlocking/route_ui.lua +++ b/advtrains_interlocking/route_ui.lua @@ -86,6 +86,9 @@ function atil.show_route_edit_form(pname, sigd, routeid) form = form.."textlist[0.5,2;3,3.9;rtelog;"..table.concat(tab, ",").."]" form = form.."button[0.5,6;3,1;back;<<< Back to signal]" + if route.smartroute_generated then + form = form.."button[3.5,6;2,1;noautogen;Clr Autogen]" + end form = form.."button[5.5,6;3,1;delete;Delete Route]" --atdebug(route.ars) @@ -135,6 +138,11 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) advtrains.interlocking.show_signal_aspect_selector(pname, suppasp, route.name, callback, route.aspect or advtrains.interlocking.GENERIC_FREE) return end + + if fields.noautogen then + route.smartroute_generated = nil + end + if fields.delete then -- if something set the route in the meantime, make sure this doesn't break. atil.route.update_route(sigd, tcbs, nil, true) diff --git a/advtrains_interlocking/signal_aspect_ui.lua b/advtrains_interlocking/signal_aspect_ui.lua index 892c53b..39aab17 100644 --- a/advtrains_interlocking/signal_aspect_ui.lua +++ b/advtrains_interlocking/signal_aspect_ui.lua @@ -83,6 +83,7 @@ function advtrains.interlocking.show_ip_sa_form(pos, pname) -- Create Signal aspect formspec elements local ndef = advtrains.ndb.get_ndef(pos) if ndef and ndef.advtrains then + form[#form+1] = F.label(0.5, 2, "Signal Aspect:") -- main aspect list if ndef.advtrains.main_aspects then local entries = { "" } diff --git a/advtrains_interlocking/smartroute.lua b/advtrains_interlocking/smartroute.lua new file mode 100644 index 0000000..770c379 --- /dev/null +++ b/advtrains_interlocking/smartroute.lua @@ -0,0 +1,149 @@ +-- smartroute.lua +-- Implementation of the advtrains auto-route search + +local atil = advtrains.interlocking +local ildb = atil.db +local sr = {} + + +local function otherside(s) + if s==1 then return 2 else return 1 end +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) + --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 + -- record position in mark_pos + local pts = advtrains.encode_pos(pos) + mark_pos[pts] = true + + local node = advtrains.ndb.get_node_or_nil(pos) + atdebug("(SmartRoute) 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("(SmartRoute) Found turnout ",pos, "nodename", node.name, "entering at conn",bconnid) + 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) + recursively_find_routes(pos, oconnid, searching_shunt, table.copy(tcbseq), table.copy(mark_pos), result_table, ti.limit) + end + return + end + --otherwise, this might be a tcb + local tcb = ildb.get_tcb(pos) + if tcb then + local fsigd = { p = pos, s = connid } + atdebug("(SmartRoute) Encounter TCB ",fsigd) + tcbseq[#tcbseq+1] = fsigd + -- check if this is a possible route endpoint + local tcbs = tcb[connid] + if tcbs.signal then + local ndef = advtrains.ndb.get_ndef(tcbs.signal) + if ndef and ndef.advtrains then + if ndef.advtrains.route_role == "main" or ndef.advtrains.route_role == "main_distant" + or ndef.advtrains.route_role == "end" or ndef.advtrains.route_role == "shunt" then + -- signal is suitable target + local is_mainsignal = ndef.advtrains.route_role ~= "shunt" + -- record the found route in the results + result_table[#result_table+1] = { + tcbseq = table.copy(tcbseq), + mark_pos = table.copy(mark_pos), + shunt_route = not is_mainsignal, + to_end_of_track = false, + name = tcbs.signal_name or atil.sigd_to_string(fsigd) + } + -- if this is a main signal and/or we are only searching shunt routes, stop the search here + if is_mainsignal or searching_shunt then + atdebug("(SmartRoute) Terminating here because it is main or only shunt routes searched") + return + end + end + end + end + 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 + +local function build_route_from_foundroute(froute, name) + local route = { + name = froute.name, + use_rscache = true, + smartroute_generated = true, + } + for _, sigd in ipairs(froute.tcbseq) do + route[#route+1] = { next = sigd, locks = {} } + end + return route +end + +-- Maximum scan length for track iterator +local TS_MAX_SCAN = 1000 + +function sr.init(pname, sigd) + -- is start signal a shunt signal? + local is_startsignal_shunt = false + local tcbs = ildb.get_tcbs(sigd) + if tcbs.signal then + local ndef = advtrains.ndb.get_ndef(tcbs.signal) + if ndef and ndef.advtrains then + if ndef.advtrains.route_role == "shunt" then + is_startsignal_shunt = true + end + end + end + local result_table = {} + recursively_find_routes(sigd.p, sigd.s, is_startsignal_shunt, {}, {}, result_table, TS_MAX_SCAN) + + atdebug("Smartroute search finished:",result_table) + + -- Short-circuit logic right now for testing + -- go through and delete all routes that are autogenerated + local i = 1 + while i<=#tcbs.routes do + if tcbs.routes[i].smartroute_generated then + table.remove(tcbs.routes, i) + else + i=i+1 + end + end + -- just plainly create routes! + for idx, froute in ipairs(result_table) do + tcbs.routes[#tcbs.routes+1] = build_route_from_foundroute(froute) + end + atwarn("Smartroute done!") +end + + + +--[[ + player1 = { + origin = + found_routes = { + { tcbseq = {, , }, mark_pos = { table with keys being encoded_pos of rails constituting route }, to_end_of_track = false, shunt_route = false } + } + } +]]-- +local player_smartroute = {} + +minetest.register_on_punchnode(function(pos, node, player, pointed_thing) + local pname = player:get_player_name() + if not minetest.check_player_privs(pname, "interlocking") then + return + end + -- TODO +end) + + +advtrains.interlocking.smartroute = sr diff --git a/advtrains_interlocking/tcb_ts_ui.lua b/advtrains_interlocking/tcb_ts_ui.lua index bdb0a18..7f75bb9 100755 --- a/advtrains_interlocking/tcb_ts_ui.lua +++ b/advtrains_interlocking/tcb_ts_ui.lua @@ -616,6 +616,9 @@ function advtrains.interlocking.show_signalling_form(sigd, pname, sel_rte, calle local strtab = {} for idx, route in ipairs(tcbs.routes) do local clr = "" + if route.smartroute_generated then + clr = "#FFFF55" + end if route.ars then clr = "#FF5555" if route.ars.default then -- cgit v1.2.3 From 69e4b8f99aac08c404a733c2338f9de9cddab829 Mon Sep 17 00:00:00 2001 From: orwell Date: Sat, 20 Jul 2024 21:04:17 +0200 Subject: Support signal aspect selection for routes again --- advtrains_interlocking/database.lua | 7 ++++ advtrains_interlocking/route_ui.lua | 58 ++++++++++++++++++++++++++------- advtrains_interlocking/routesetting.lua | 16 ++++++--- advtrains_interlocking/signal_api.lua | 3 +- 4 files changed, 68 insertions(+), 16 deletions(-) (limited to 'advtrains_interlocking/route_ui.lua') diff --git a/advtrains_interlocking/database.lua b/advtrains_interlocking/database.lua index 49ca13d..a8d9584 100644 --- a/advtrains_interlocking/database.lua +++ b/advtrains_interlocking/database.lua @@ -254,6 +254,13 @@ routes = { ars = { } 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 to specify the signal aspect of the signal + main_aspect = "_free" -- The main aspect that the route start signal is to show + assign_dst = false -- Whether to assign distant signal (affects only the signal at the start of the route) + -- false: start signal does not set distant signal (the default), for long blocks + -- it is assumed that the next main signal will have its own distant sig + -- true: start signal sets distant signal to the next signal on the route with route_role "main" (typically the end signal) + -- for short blocks where end signal doesn't have its own distant sig -- Fields used by the autorouter: ar_end_sigd = -- the sigd describing the end of the route. Used for merging route options on recalculation } diff --git a/advtrains_interlocking/route_ui.lua b/advtrains_interlocking/route_ui.lua index 863fe11..b01d449 100644 --- a/advtrains_interlocking/route_ui.lua +++ b/advtrains_interlocking/route_ui.lua @@ -3,6 +3,7 @@ local atil = advtrains.interlocking local ildb = atil.db +local F = advtrains.formspec -- TODO duplicate local lntrans = { "A", "B" } @@ -83,9 +84,32 @@ function atil.show_route_edit_form(pname, sigd, routeid) itab("E (none)") end - form = form.."textlist[0.5,2;3,3.9;rtelog;"..table.concat(tab, ",").."]" + form = form.."textlist[0.5,2;3.5,3.9;rtelog;"..table.concat(tab, ",").."]" + + -- to the right of rtelog a signal aspect selection for the start signal + form = form..F.label(4.5, 2, "Signal Aspect:") + -- main aspect list + local signalpos = tcbs.signal + local ndef = signalpos and advtrains.ndb.get_ndef(signalpos) + if ndef and ndef.advtrains and ndef.advtrains.main_aspects then + local entries = { "" } + local sel = 1 + for i, mae in ipairs(ndef.advtrains.main_aspects) do + entries[i+1] = mae.description + if mae.name == route.main_aspect then + sel = i+1 + end + end + form = form..F.dropdown(4.5, 3.0, 4, "sa_main_aspect", entries, sel, true) + -- checkbox for assign distant signal + form = form..string.format("checkbox[4.5,4.0;sa_distant;Announce distant signal;%s]", route.assign_dst) + end + + form = form.."button[0.5,6;1,1;prev;<<<]" + form = form.."button[1.5,6;1,1;back;Back]" + form = form.."button[2.5,6;1,1;next;>>>]" + - form = form.."button[0.5,6;3,1;back;<<< Back to signal]" if route.smartroute_generated then form = form.."button[3.5,6;2,1;noautogen;Clr Autogen]" end @@ -123,20 +147,32 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) local route = tcbs.routes[routeid] if not route then return end + if fields.prev then + atil.show_route_edit_form(pname, sigd, routeid - 1) + return + end + if fields.next then + atil.show_route_edit_form(pname, sigd, routeid + 1) + return + end + if fields.setname and fields.name then route.name = fields.name end - if fields.aspect then - local suppasp = advtrains.interlocking.signal_get_supported_aspects(tcbs.signal) - - local callback = function(pname, asp) - route.aspect = asp - advtrains.interlocking.show_route_edit_form(pname, sigd, routeid) + if fields.sa_main_aspect then + local idx = tonumber(fields.sa_main_aspect) + route.main_aspect = nil + if idx > 1 then + local signalpos = tcbs.signal + local ndef = signalpos and advtrains.ndb.get_ndef(signalpos) + if ndef and ndef.advtrains and ndef.advtrains.main_aspects then + route.main_aspect = ndef.advtrains.main_aspects[idx - 1].name + end end - - advtrains.interlocking.show_signal_aspect_selector(pname, suppasp, route.name, callback, route.aspect or advtrains.interlocking.GENERIC_FREE) - return + end + if fields.sa_distant then + route.assign_dst = minetest.is_yes(fields.sa_distant) end if fields.noautogen then diff --git a/advtrains_interlocking/routesetting.lua b/advtrains_interlocking/routesetting.lua index 34a273a..0207519 100644 --- a/advtrains_interlocking/routesetting.lua +++ b/advtrains_interlocking/routesetting.lua @@ -147,7 +147,7 @@ function ilrs.set_route(signal, route, try) tcbs_ref = c_tcbs, role = ndef and ndef.advtrains and ndef.advtrains.route_role, masp_override = c_rseg.masp_override, --TODO implement masp_override on UI side - dst_type = "next_main", --TODO allow user differentiate + assign_dst = (i~=1) or route.assign_dst -- Behavior: for first signal assign depending on route.assign_dst, all others always assign } signals[#signals+1] = sig_table end @@ -171,10 +171,18 @@ function ilrs.set_route(signal, route, try) -- note the signals are iterated backwards. Switch depending on the role local sig = signals[i] -- apply mainaspect - sig.tcbs_ref.route_aspect = sig.masp_override or "_default" + sig.tcbs_ref.route_aspect = sig.masp_override or route.main_aspect or "_default" if sig.role == "distant" or sig.role == "distant_repeater" or sig.role == "main_distant" then - -- assign the remote as the last mainsig - sig.tcbs_ref.route_remote = last_mainsig + if last_mainsig then + -- assign the remote as the last mainsig if desired + if sig.assign_dst then + sig.tcbs_ref.route_remote = last_mainsig + end + -- if it wasn't a distant_repeater clear the mainsig + if sig.role ~= "distant_repeater" then + last_mainsig = false + end + end end if sig.role == "main" or sig.role == "main_distant" or sig.role == "end" then -- record this as the new last mainsig diff --git a/advtrains_interlocking/signal_api.lua b/advtrains_interlocking/signal_api.lua index cede405..bf14247 100644 --- a/advtrains_interlocking/signal_api.lua +++ b/advtrains_interlocking/signal_api.lua @@ -100,7 +100,8 @@ ndef.advtrains = { -- Determines how the signal behaves when routes are set. Only in effect when signal is assigned to a TCB. -- main: The signal is a possible endpoint for a train move route. Distant signals before it refer to it. -- shunt: The signal is a possible endpoint for a shunt move route. Ignored for distant signals. - -- distant, distant_repeater: When route is set, signal is always assigned its first main aspect. The next signal with role="main" is set as the remote signal. (currently no further distinction) + -- distant, distant_repeater: The next signal with role="main" is set as the remote signal. main_aspects may be undefined, the main aspect passed to apply_aspect is a dummy one in this case. + -- distant: if more than one distant signal is before a main signal, only the last one is assigned (but any number of distant_repeater signals are allowed) -- main_distant: Combination of main and distant - like "main", but additionally gets assigned to the next main like a "distant" -- end: like main, but signifies that it marks an end of track and trains cannot continue further. (currently no practical implications above main) } -- cgit v1.2.3 From d83d06ecdd8a23e5c36826f150c72d0ff8b644f9 Mon Sep 17 00:00:00 2001 From: 1F616EMO Date: Mon, 30 Sep 2024 19:37:18 +0800 Subject: Add "New From Route" function into route editing form This patch allows creating new routes based on what was done on another route. This can be useful to fix minor mistakes on a route, or to create similar routes. Note that the route buffer created "steps back" one section, but with turnouts kept. The new button is placed above the "Save ARS" button - I know it is ugly, so please suggest a better place for it. --- advtrains_interlocking/route_prog.lua | 27 ++++++++++++++++++++------- advtrains_interlocking/route_ui.lua | 15 ++++++++++++--- 2 files changed, 32 insertions(+), 10 deletions(-) (limited to 'advtrains_interlocking/route_ui.lua') diff --git a/advtrains_interlocking/route_prog.lua b/advtrains_interlocking/route_prog.lua index 6abe431..e8f5e8e 100644 --- a/advtrains_interlocking/route_prog.lua +++ b/advtrains_interlocking/route_prog.lua @@ -214,19 +214,32 @@ end local player_rte_prog = {} -function advtrains.interlocking.init_route_prog(pname, sigd) +function advtrains.interlocking.init_route_prog(pname, sigd, default_route) if not minetest.check_player_privs(pname, "interlocking") then minetest.chat_send_player(pname, "Insufficient privileges to use this!") return end - player_rte_prog[pname] = { + local rp = { origin = sigd, - route = { - name = "PROG["..pname.."]", - }, - tmp_lcks = {}, } - advtrains.interlocking.visualize_route(sigd, player_rte_prog[pname].route, "prog_"..pname, player_rte_prog[pname].tmp_lcks, pname) + if default_route then + rp.route = table.copy(default_route) + + -- "Step back one section", but keeping turnouts + local last_route = rp.route[#rp.route] + if last_route then + rp.tmp_lcks = last_route.locks + rp.route[#rp.route] = nil + end + rp.route.name = "PROG["..pname.."]" + else + rp.route = { + name = "PROG["..pname.."]" + } + rp.tmp_lcks = {} + end + player_rte_prog[pname] = rp + advtrains.interlocking.visualize_route(sigd, rp.route, "prog_"..pname, rp.tmp_lcks, pname) minetest.chat_send_player(pname, "Route programming mode active. Punch TCBs to add route segments, punch turnouts to lock them.") end diff --git a/advtrains_interlocking/route_ui.lua b/advtrains_interlocking/route_ui.lua index 1999941..a8fee83 100644 --- a/advtrains_interlocking/route_ui.lua +++ b/advtrains_interlocking/route_ui.lua @@ -24,7 +24,7 @@ function atil.show_route_edit_form(pname, sigd, routeid) local route = tcbs.routes[routeid] if not route then return end - local form = "size[9,10]label[0.5,0.2;Route overview]" + local form = "size[9,11]label[0.5,0.2;Route overview]" form = form.."field[0.8,1.2;6.5,1;name;Route name;"..minetest.formspec_escape(route.name).."]" form = form.."button[7.0,0.9;1.5,1;setname;Set]" @@ -85,11 +85,13 @@ function atil.show_route_edit_form(pname, sigd, routeid) form = form.."button[0.5,6;3,1;back;<<< Back to signal]" form = form.."button[4.5,6;2,1;aspect;Signal Aspect]" form = form.."button[6.5,6;2,1;delete;Delete Route]" + + form = form.."button[5.5,7;3,1;newfrom;New From Route]" --atdebug(route.ars) form = form.."style[ars;font=mono]" - form = form.."textarea[0.8,7.3;5,3;ars;ARS Rule List;"..atil.ars_to_text(route.ars).."]" - form = form.."button[5.5,7.23;3,1;savears;Save ARS List]" + form = form.."textarea[0.8,8.3;5,3;ars;ARS Rule List;"..atil.ars_to_text(route.ars).."]" + form = form.."button[5.5,8.23;3,1;savears;Save ARS List]" minetest.show_formspec(pname, "at_il_routeedit_"..minetest.pos_to_string(sigd.p).."_"..sigd.s.."_"..routeid, form) @@ -139,6 +141,13 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) table.remove(tcbs.routes, routeid) advtrains.interlocking.show_signalling_form(sigd, pname) end + + if fields.newfrom then + advtrains.interlocking.init_route_prog(pname, sigd, route) + minetest.close_formspec(pname, formname) + tcbs.ars_ignore_next = nil + return + end if fields.ars and fields.savears then route.ars = atil.text_to_ars(fields.ars) -- cgit v1.2.3 From d42f1bcf1a7ec18d392671be54636e7cad8b788b Mon Sep 17 00:00:00 2001 From: orwell Date: Wed, 13 Nov 2024 20:51:04 +0100 Subject: Route edit UI: option to copy route in place --- advtrains_interlocking/route_ui.lua | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) (limited to 'advtrains_interlocking/route_ui.lua') diff --git a/advtrains_interlocking/route_ui.lua b/advtrains_interlocking/route_ui.lua index 478e8dc..89580a8 100644 --- a/advtrains_interlocking/route_ui.lua +++ b/advtrains_interlocking/route_ui.lua @@ -106,7 +106,7 @@ function atil.show_route_edit_form(pname, sigd, routeid) end form = form.."button[0.5,6;1,1;prev;<<<]" - form = form.."button[1.5,6;1,1;back;Back]" + form = form.."button[1.5,6;1,1;back;"..routeid.."/"..#tcbs.routes.."]" form = form.."button[2.5,6;1,1;next;>>>]" @@ -114,6 +114,8 @@ function atil.show_route_edit_form(pname, sigd, routeid) form = form.."button[3.5,6;2,1;noautogen;Clr Autogen]" end form = form.."button[5.5,6;3,1;delete;Delete Route]" + form = form.."button[0.5,7;3,1;back;Back to signal]" + form = form.."button[3.5,7;2,1;clone;Clone Route]" form = form.."button[5.5,7;3,1;newfrom;New From Route]" --atdebug(route.ars) @@ -186,6 +188,16 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) table.remove(tcbs.routes, routeid) advtrains.interlocking.show_signalling_form(sigd, pname) end + + if fields.clone then + -- if something set the route in the meantime, make sure this doesn't break. + atil.route.update_route(sigd, tcbs, nil, true) + local rcopy = table.copy(route) + rcopy.name = route.name.."_copy" + rcopy.smartroute_generated = nil + table.insert(tcbs.routes, routeid+1, rcopy) + advtrains.interlocking.show_signalling_form(sigd, pname) + end if fields.newfrom then advtrains.interlocking.init_route_prog(pname, sigd, route) -- cgit v1.2.3 From fe57e7dd089653e2361a4ebb0b34137a3261e198 Mon Sep 17 00:00:00 2001 From: orwell Date: Thu, 14 Nov 2024 00:03:38 +0100 Subject: Add Blocksignal mode for signals to autocreate simple block route --- advtrains_interlocking/database.lua | 7 +- advtrains_interlocking/route_prog.lua | 10 +- advtrains_interlocking/route_ui.lua | 3 +- advtrains_interlocking/routesetting.lua | 11 +- advtrains_interlocking/signal_api.lua | 2 +- advtrains_interlocking/tcb_ts_ui.lua | 177 ++++++++++++++++++++++---------- advtrains_signals_ks/init.lua | 8 +- 7 files changed, 143 insertions(+), 75 deletions(-) (limited to 'advtrains_interlocking/route_ui.lua') diff --git a/advtrains_interlocking/database.lua b/advtrains_interlocking/database.lua index a8d9584..e77d073 100644 --- a/advtrains_interlocking/database.lua +++ b/advtrains_interlocking/database.lua @@ -261,8 +261,9 @@ routes = { -- it is assumed that the next main signal will have its own distant sig -- true: start signal sets distant signal to the next signal on the route with route_role "main" (typically the end signal) -- for short blocks where end signal doesn't have its own distant sig - -- Fields used by the autorouter: - ar_end_sigd = -- the sigd describing the end of the route. Used for merging route options on recalculation + terminal = -- the sigd describing the end of the route (e.g. the "next" entry in the final route segment). + -- Might be missing or wrong. Routesetting currently does not care about this value being present. + default_autoworking = false -- if true, when route is set autoworking will be by default on. Used for Blocksignal mode } } @@ -771,7 +772,7 @@ function ildb.update_rs_cache(ts_id) 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) + atwarn("In update_rs_cache for section",ts_id,"found superfluous endpoint",sup_end_pkey,"->",sup_entry) end end ts.rs_cache = rscache diff --git a/advtrains_interlocking/route_prog.lua b/advtrains_interlocking/route_prog.lua index 3ab5686..2f0f8ee 100644 --- a/advtrains_interlocking/route_prog.lua +++ b/advtrains_interlocking/route_prog.lua @@ -111,15 +111,7 @@ end --[[ Route definition: -route = { - name = - [n] = { - next = , -- of the next (note: next) TCB on the route - locks = { = "state"} -- route locks of this route segment - } - terminal = , - aspect = ,--note, might change in future -} +=== See database.lua L238 The first item in the TCB path (namely i=0) is always the start signal of this route, so this is left out. All subsequent entries, starting from 1, contain: diff --git a/advtrains_interlocking/route_ui.lua b/advtrains_interlocking/route_ui.lua index 89580a8..2b79f68 100644 --- a/advtrains_interlocking/route_ui.lua +++ b/advtrains_interlocking/route_ui.lua @@ -110,7 +110,7 @@ function atil.show_route_edit_form(pname, sigd, routeid) form = form.."button[2.5,6;1,1;next;>>>]" - if route.smartroute_generated then + if route.smartroute_generated or route.default_autoworking then form = form.."button[3.5,6;2,1;noautogen;Clr Autogen]" end form = form.."button[5.5,6;3,1;delete;Delete Route]" @@ -180,6 +180,7 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) if fields.noautogen then route.smartroute_generated = nil + route.default_autoworking = nil end if fields.delete then diff --git a/advtrains_interlocking/routesetting.lua b/advtrains_interlocking/routesetting.lua index a72f644..f2a00cd 100644 --- a/advtrains_interlocking/routesetting.lua +++ b/advtrains_interlocking/routesetting.lua @@ -171,7 +171,7 @@ function ilrs.set_route(signal, route, try) -- note the signals are iterated backwards. Switch depending on the role local sig = signals[i] -- apply mainaspect - sig.tcbs_ref.route_aspect = sig.masp_override or route.main_aspect or "_default" + sig.tcbs_ref.route_aspect = sig.masp_override or "_default" -- or route.main_aspect : TODO this does not work if a distant signal is on the path! Implement per-sig aspects! if sig.role == "distant" or sig.role == "distant_repeater" or sig.role == "main_distant" then if last_mainsig then -- assign the remote as the last mainsig if desired @@ -192,6 +192,8 @@ function ilrs.set_route(signal, route, try) -- update the signal aspect on map advtrains.interlocking.signal.update_route_aspect(sig.tcbs_ref, i ~= 1) end + -- Only for the first signal on the route, set route aspect. TODO: remove when masp_overrides are implemented + signal.route_aspect = route.main_aspect or "_default" return true end @@ -366,8 +368,9 @@ function ilrs.update_route(sigd, tcbs, newrte, cancel) if newrte then tcbs.routeset = newrte end --atdebug("Setting:",tcbs.routeset) local succ, rsn, cbts, cblk - if tcbs.routes[tcbs.routeset] then - succ, rsn, cbts, cblk = ilrs.set_route(sigd, tcbs.routes[tcbs.routeset]) + local route = tcbs.routes[tcbs.routeset] + if route then + succ, rsn, cbts, cblk = ilrs.set_route(sigd, route) else succ = false rsn = attrans("Route state changed.") @@ -390,6 +393,8 @@ function ilrs.update_route(sigd, tcbs, newrte, cancel) --atdebug("Committed Route:",tcbs.routeset) -- set_route now sets the signal aspects --has_changed_aspect = true + -- route success. apply default_autoworking flag if requested + tcbs.route_auto = route.default_autoworking end end if has_changed_aspect then diff --git a/advtrains_interlocking/signal_api.lua b/advtrains_interlocking/signal_api.lua index bf14247..9b0479f 100644 --- a/advtrains_interlocking/signal_api.lua +++ b/advtrains_interlocking/signal_api.lua @@ -312,7 +312,7 @@ function signal.get_aspect_internal(pos, aspt) -- case is fine, distant only signal masp = signal.MASP_DEFAULT else - assert(ndefat.main_aspects, "With named aspects, node needs advtrains.main_aspects table!") + assert(ndefat.main_aspects, "With named aspects, node "..node.name.." needs advtrains.main_aspects table!") -- resolve the main aspect from the mainaspects table if not ndefat.main_aspects_lookup then cache_mainaspects(ndefat) diff --git a/advtrains_interlocking/tcb_ts_ui.lua b/advtrains_interlocking/tcb_ts_ui.lua index e7ff685..1cdbb29 100755 --- a/advtrains_interlocking/tcb_ts_ui.lua +++ b/advtrains_interlocking/tcb_ts_ui.lua @@ -618,48 +618,78 @@ function advtrains.interlocking.show_signalling_form(sigd, pname, sel_rte, calle form = form.."button[0.5,6; 5,1;cancelroute;Cancel Route]" else if not tcbs.route_origin then - local strtab = {} - for idx, route in ipairs(tcbs.routes) do - local clr = "" - if route.smartroute_generated then - clr = "#FFFF55" - end - if route.ars then - clr = "#FF5555" - if route.ars.default then - clr = "#55FF55" + if #tcbs.routes > 0 then + -- at least one route is defined, show normal dialog + local strtab = {} + for idx, route in ipairs(tcbs.routes) do + local clr = "" + if route.smartroute_generated then + clr = "#FFFF55" + end + if route.ars then + clr = "#FF5555" + if route.ars.default then + clr = "#55FF55" + end end + strtab[#strtab+1] = clr .. minetest.formspec_escape(route.name) end - strtab[#strtab+1] = clr .. minetest.formspec_escape(route.name) - end - form = form.."label[0.5,2.5;Routes:]" - form = form.."textlist[0.5,3;5,3;rtelist;"..table.concat(strtab, ",") - if sel_rte then - form = form .. ";" .. sel_rte .."]" - form = form.."button[0.5,6; 5,1;setroute;Set Route]" - form = form.."button[0.5,7;2,1;dsproute;Show]" - if hasprivs then - form = form.."button[3.5,7;2,1;editroute;Edit]" - if sel_rte > 1 then - form = form .. "button[5.5,4;0.5,0.3;moveup;↑]" + form = form.."label[0.5,2.5;Routes:]" + form = form.."textlist[0.5,3;5,3;rtelist;"..table.concat(strtab, ",") + if sel_rte then + form = form .. ";" .. sel_rte .."]" + form = form.."button[0.5,6; 5,1;setroute;Set Route]" + form = form.."button[0.5,7;2,1;dsproute;Show]" + if hasprivs then + form = form.."button[3.5,7;2,1;editroute;Edit]" + if sel_rte > 1 then + form = form .. "button[5.5,4;0.5,0.3;moveup;↑]" + end + if sel_rte < #strtab then + form = form .. "button[5.5,4.7;0.5,0.3;movedown;↓]" + end end - if sel_rte < #strtab then - form = form .. "button[5.5,4.7;0.5,0.3;movedown;↓]" + else + form = form .. "]" + if tcbs.ars_disabled then + form = form.."label[0.5,6 ;NOTE: ARS is disabled.]" + form = form.."label[0.5,6.5;Routes are not automatically set.]" end end + if hasprivs then + form = form.."button[0.5,8;2.5,1;smartroute;Smart Route]" + form = form.."button[ 3,8;2.5,1;newroute;New (Manual)]" + form = form..string.format("checkbox[0.5,8.75;ars;Automatic routesetting;%s]", not tcbs.ars_disabled) + form = form..string.format("checkbox[0.5,9.25;dstarstrig;Distant signal triggers ARS;%s]", not tcbs.no_dst_ars_trig) + end else - form = form .. "]" - if tcbs.ars_disabled then - form = form.."label[0.5,6 ;NOTE: ARS is disabled.]" - form = form.."label[0.5,6.5;Routes are not automatically set.]" + -- no route is active, and no route is so far defined + if not tcbs.signal then atwarn("signalling form missing signal?!", pos) return end -- safeguard, nothing else in this function checks tcbs.signal + local caps = advtrains.interlocking.signal.get_signal_cap_level(tcbs.signal) + if caps >= 3 then + -- offer user the "block signal mode" + form = form.."label[0.5,2.5;No routes are yet defined.]" + if hasprivs then + form = form.."button[0.5,4;2.5,1;smartroute;Smart Route]" + form = form.."button[ 3,4;2.5,1;newroute;New (Manual)]" + + form = form.."label[0.5,5.5;Setup block signal route (up to following signal):]" + form = form.."button[0.5,6;2.5,1;setupblocklong;Long (No Dst)]" + form = form.."tooltip[setupblocklong;Following track section must have no turnouts and end at another signal.\n" + .."Sets a route into the section ahead with auto-working set on\n" + .."Long block: This signal does not become distant signal.]" + form = form.."button[ 3,6;2.5,1;setupblockshort;Short (With Dst)]" + form = form.."tooltip[setupblockshort;Following track section must have no turnouts and end at another signal.\n" + .."Sets a route into the section ahead with auto-working set on\n" + .."Short block: This signal becomes distant signal for next signal.]" + end + else + -- signal caps say it cannot be route start/end + form = form.."label[0.5,2.5;This is a Non-Halt signal (e.g. pure distant signal)\n" + .."No route is currently set through.]" end end - if hasprivs then - form = form.."button[0.5,8;2.5,1;smartroute;Smart Route]" - form = form.."button[ 3,8;2.5,1;newroute;New (Manual)]" - form = form..string.format("checkbox[0.5,8.75;ars;Automatic routesetting;%s]", not tcbs.ars_disabled) - form = form..string.format("checkbox[0.5,9.25;dstarstrig;Distant signal triggers ARS;%s]", not tcbs.no_dst_ars_trig) - end + elseif sigd_equal(tcbs.route_origin, sigd) then -- something has gone wrong: tcbs.routeset should have been set... form = form.."label[0.5,2.5;Inconsistent state: route_origin is same TCBS but no route set. Try again.]" @@ -753,6 +783,63 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) tcbs.ars_ignore_next = nil return end + if (fields.setupblocklong or fields.setupblockshort) and hasprivs then + -- check adjacent section + if not tcbs.ts_id then + minetest.chat_send_player(pname, "Block route not possible: No track section ahead") + return + end + local ts = ildb.get_ts(tcbs.ts_id) + if #ts.tc_breaks ~= 2 then + minetest.chat_send_player(pname, "Block route not possible: Section "..(ts.name or "-").." ("..tcbs.ts_id..") has "..#ts.tc_breaks.." ends, must be 2") + return + end + local e_sigd + if vector.equals(ts.tc_breaks[1].p, pos) then + e_sigd = { p = ts.tc_breaks[2].p, + s = ts.tc_breaks[2].s==1 and 2 or 1} + elseif vector.equals(ts.tc_breaks[2].p, pos) then + e_sigd = { p = ts.tc_breaks[1].p, + s = ts.tc_breaks[1].s==1 and 2 or 1} + else + minetest.chat_send_player(pname, "Block route not possible: Section "..(ts.name or "-").." ("..tcbs.ts_id..") TCBs are inconsistent, check section!") + return + end + local e_tcbs = ildb.get_tcbs(e_sigd) + if not e_tcbs then + minetest.chat_send_player(pname, "Block route not possible: Adjacent TCB not found, check section!") + return + end + -- now we have the TCB at the end of the following section. check that signal is set + if not e_tcbs.signal then + minetest.chat_send_player(pname, "Block route not possible: Adjacent TCB has no signal assigned!") + return + end + local caps = advtrains.interlocking.signal.get_signal_cap_level(e_tcbs.signal) + if caps < 3 then + minetest.chat_send_player(pname, "Block route not possible: Following signal is not capable of displaying a Halt aspect (caplevel "..caps..")") + return + end + -- all preconditions checked! go ahead and create route + local route = { + name = "BS", + [1] = { + next = e_sigd, -- of the next (note: next) TCB on the route + locks = {} -- route locks of this route segment + }, + terminal = e_sigd, + use_rscache = true, + -- main_aspect = + assign_dst = fields.setupblockshort and true, -- assign dst, if short block was selected + default_autoworking = true, + } + local rid = #tcbs.routes + 1 -- typically 1 + tcbs.routes[rid] = route + -- directly set our newly created route + ilrs.update_route(sigd, tcbs, rid) + advtrains.interlocking.show_signalling_form(sigd, pname, nil, true) + return + end if sel_rte and tcbs.routes[sel_rte] then if fields.setroute then ilrs.update_route(sigd, tcbs, sel_rte) @@ -764,8 +851,6 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) end if fields.editroute and hasprivs then advtrains.interlocking.show_route_edit_form(pname, sigd, sel_rte) - --local rte = tcbs.routes[sel_rte] - --minetest.show_formspec(pname, formname.."_renroute_"..sel_rte, "field[name;Enter new route name;"..rte.name.."]") return end end @@ -803,24 +888,4 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) advtrains.interlocking.show_signalling_form(sigd, pname, sel_rte, true) return end - - - if not hasprivs then return end - -- rename route - local rind, rte_id - pts, connids, rind = string.match(formname, "^at_il_signalling_([^_]+)_(%d)_renroute_(%d+)$") - if pts then - pos = minetest.string_to_pos(pts) - connid = tonumber(connids) - rte_id = tonumber(rind) - if not connid or connid<1 or connid>2 then return end - end - if pos and connid and rind and fields.name then - local sigd = {p=pos, s=connid} - local tcbs = ildb.get_tcbs(sigd) - if tcbs.routes[rte_id] then - tcbs.routes[rte_id].name = fields.name - advtrains.interlocking.show_signalling_form(sigd, pname) - end - end end) diff --git a/advtrains_signals_ks/init.lua b/advtrains_signals_ks/init.lua index c0e74ea..a85bec9 100755 --- a/advtrains_signals_ks/init.lua +++ b/advtrains_signals_ks/init.lua @@ -161,7 +161,7 @@ end -- Main aspects shunt signal -- Shunt signals have only two states, distant doesn't matter -local mainaspects_shunt = { +local mainaspects_ra = { { name = "shunt", description = "Shunt", @@ -335,6 +335,10 @@ for _, rtab in ipairs({ }) do local sbox = table.copy(rtab.sbox) sbox[5] = 0 + local afunc = prts.asp + if type(afunc) == "table" then + afunc = function() return prts.asp end + end minetest.register_node("advtrains_signals_ks:ra_"..typ.."_"..rot, { description = "Ks Shunting Signal", drawtype = "mesh", @@ -366,7 +370,7 @@ for _, rtab in ipairs({ advtrains = { main_aspects = mainaspects_ra, apply_aspect = applyaspectf_ra(rot), - get_aspect_info = prts.asp, + get_aspect_info = afunc, route_role = "shunt", trackworker_next_rot = "advtrains_signals_ks:ra_"..typ.."_"..rtab.nextrot, trackworker_rot_incr_param2 = (rot=="60") -- cgit v1.2.3 From ced8d8998de6b2cb6a758224641c125d02efea41 Mon Sep 17 00:00:00 2001 From: orwell Date: Thu, 5 Dec 2024 23:31:11 +0100 Subject: Route Edit: Support aspect selection per signal, support call-on routes --- advtrains_interlocking/route_ui.lua | 148 +++++++++++++++++++++++--------- advtrains_interlocking/routesetting.lua | 23 +++-- advtrains_interlocking/tcb_ts_ui.lua | 4 +- 3 files changed, 125 insertions(+), 50 deletions(-) (limited to 'advtrains_interlocking/route_ui.lua') diff --git a/advtrains_interlocking/route_ui.lua b/advtrains_interlocking/route_ui.lua index 2b79f68..2b7e2e5 100644 --- a/advtrains_interlocking/route_ui.lua +++ b/advtrains_interlocking/route_ui.lua @@ -11,9 +11,10 @@ local function sigd_to_string(sigd) return minetest.pos_to_string(sigd.p).." / "..lntrans[sigd.s] end +-- indexed by pname +local sel_rpartcache = {} - -function atil.show_route_edit_form(pname, sigd, routeid) +function atil.show_route_edit_form(pname, sigd, routeid, sel_rpartidx) if not minetest.check_player_privs(pname, {train_operator=true, interlocking=true}) then minetest.chat_send_player(pname, "Insufficient privileges to use this!") @@ -31,10 +32,12 @@ function atil.show_route_edit_form(pname, sigd, routeid) -- construct textlist for route information local tab = {} - local function itab(t) + local tabref = {} + local function itab(rseg, t, rty, rpara) tab[#tab+1] = minetest.formspec_escape(string.gsub(t, ",", " ")) + tabref[#tab] = { [rty] = true, param = rpara, seg = rseg, idx = #tab } end - itab("("..(tcbs.signal_name or "+")..") Route #"..routeid) + itab(1, "("..(tcbs.signal_name or "+")..") Route #"..routeid, "signal", sigd) -- this code is partially copy-pasted from routesetting.lua -- we start at the tc designated by signal @@ -44,12 +47,12 @@ function atil.show_route_edit_form(pname, sigd, routeid) while c_sigd and i<=#route do c_tcbs = ildb.get_tcbs(c_sigd) if not c_tcbs then - itab("-!- No TCBS at "..sigd_to_string(c_sigd)..". Please reconfigure route!") + itab(i, "-!- No TCBS at "..sigd_to_string(c_sigd)..". Please reconfigure route!", "err", nil) break end c_ts_id = c_tcbs.ts_id if not c_ts_id then - itab("-!- No track section adjacent to "..sigd_to_string(c_sigd)..". Please reconfigure route!") + itab(i, "-!- No track section adjacent to "..sigd_to_string(c_sigd)..". Please reconfigure route!", "err", nil) break end c_ts = ildb.get_ts(c_ts_id) @@ -57,16 +60,18 @@ function atil.show_route_edit_form(pname, sigd, routeid) c_rseg = route[i] c_lckp = {} - itab(""..i.." "..sigd_to_string(c_sigd)) - itab("= "..(c_ts and c_ts.name or "-").." =") + local signame = "-" + if c_tcbs and c_tcbs.signal then signame = c_tcbs.signal_name or "o" end + itab(i, ""..i.." "..sigd_to_string(c_sigd).." ("..signame..")", "signal", c_sigd) + itab(i, "= "..(c_ts and c_ts.name or c_ts_id).." ="..(c_rseg.call_on and " [CO]" or ""), "section", c_ts_id) if c_rseg.locks then for pts, state in pairs(c_rseg.locks) do local pos = minetest.string_to_pos(pts) - itab("L "..pts.." -> "..state) + itab(i, "L "..pts.." -> "..state, "lock", pos) if not advtrains.is_passive(pos) then - itab("-!- No passive component at "..pts..". Please reconfigure route!") + itab(i, "-!- No passive component at "..pts..". Please reconfigure route!", "err", nil) break end end @@ -78,31 +83,61 @@ function atil.show_route_edit_form(pname, sigd, routeid) if c_sigd then local e_tcbs = ildb.get_tcbs(c_sigd) local signame = "-" - if e_tcbs and e_tcbs.signal then signame = e_tcbs.signal_name or "+" end - itab("E "..sigd_to_string(c_sigd).." ("..signame..")") + if e_tcbs and e_tcbs.signal then signame = e_tcbs.signal_name or "o" end + itab(i, "E "..sigd_to_string(c_sigd).." ("..signame..")", "end", c_sigd) else - itab("E (none)") + itab(i, "E (none)", "end", nil) end - form = form.."textlist[0.5,2;3.5,3.9;rtelog;"..table.concat(tab, ",").."]" + if not sel_rpartidx then sel_rpartidx = 1 end + form = form.."textlist[0.5,2;3.5,3.9;routelog;"..table.concat(tab, ",")..";"..(sel_rpartidx or 1)..";false]" + + -- to the right of rtelog, controls are displayed for the thing in focus + -- What is in focus is determined by the parameter sel_rpartidx + + local sel_rpart = tabref[sel_rpartidx] + atdebug("sel rpart",sel_rpart) - -- to the right of rtelog a signal aspect selection for the start signal - form = form..F.label(4.5, 2, "Signal Aspect:") - -- main aspect list - local signalpos = tcbs.signal - local ndef = signalpos and advtrains.ndb.get_ndef(signalpos) - if ndef and ndef.advtrains and ndef.advtrains.main_aspects then - local entries = { "" } - local sel = 1 - for i, mae in ipairs(ndef.advtrains.main_aspects) do - entries[i+1] = mae.description - if mae.name == route.main_aspect then - sel = i+1 + if sel_rpart and sel_rpart.signal then + -- get TCBS here and rseg selected + local s_tcbs = ildb.get_tcbs(sel_rpart.param) + local rseg = route[sel_rpart.seg] + -- main aspect list + local signalpos = s_tcbs and s_tcbs.signal + if signalpos and rseg then + form = form..F.label(4.5, 2, "Signal Aspect:") + local ndef = signalpos and advtrains.ndb.get_ndef(signalpos) + if ndef and ndef.advtrains and ndef.advtrains.main_aspects then + local entries = { "" } + local sel = 1 + for i, mae in ipairs(ndef.advtrains.main_aspects) do + entries[i+1] = mae.description + if mae.name == rseg.main_aspect then + sel = i+1 + end + end + form = form..F.dropdown(4.5, 3.0, 4, "sa_main_aspect", entries, sel, true) + end + -- checkbox for assign distant signal + local assign_dst = rseg.assign_dst + if assign_dst == nil then + assign_dst = (sel_rpart.seg~=1) -- special behavior when assign_dst is nil (and not false): + -- defaults to false for the very first signal and true for all others (= minimal user configuration overhead) + -- Note: on save, the value will be fixed at either false or true end + form = form..string.format("checkbox[4.5,4.0;sa_distant;Announce distant signal;%s]", assign_dst) + else + form = form..F.label(4.5, 2, "No Signal at this TCB") end - form = form..F.dropdown(4.5, 3.0, 4, "sa_main_aspect", entries, sel, true) - -- checkbox for assign distant signal - form = form..string.format("checkbox[4.5,4.0;sa_distant;Announce distant signal;%s]", route.assign_dst) + elseif sel_rpart and sel_rpart.section then + local rseg = route[sel_rpart.seg] + if rseg then + form = form..F.label(4.5, 2, "Section Options:") + -- 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 + else + form = form..F.label(4.5, 2, "<< Select a route part to edit options") end form = form.."button[0.5,6;1,1;prev;<<<]" @@ -123,13 +158,18 @@ function atil.show_route_edit_form(pname, sigd, routeid) form = form.."textarea[0.8,8.3;5,3;ars;ARS Rule List;"..atil.ars_to_text(route.ars).."]" form = form.."button[5.5,8.23;3,1;savears;Save ARS List]" - minetest.show_formspec(pname, "at_il_routeedit_"..minetest.pos_to_string(sigd.p).."_"..sigd.s.."_"..routeid, form) - + local formname = "at_il_routeedit_"..minetest.pos_to_string(sigd.p).."_"..sigd.s.."_"..routeid + minetest.show_formspec(pname, formname, form) + -- record selected entry from routelog for receive fields callback + sel_rpartcache[pname] = sel_rpart end minetest.register_on_player_receive_fields(function(player, formname, fields) local pname = player:get_player_name() + -- retreive sel_rpart from the cache in any case and clear it out + local sel_rpart = sel_rpartcache[pname] + sel_rpartcache[pname] = nil if not minetest.check_player_privs(pname, {train_operator=true, interlocking=true}) then return end @@ -163,19 +203,34 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) route.name = fields.name end - if fields.sa_main_aspect then + if fields.sa_main_aspect and sel_rpart and sel_rpart.signal then local idx = tonumber(fields.sa_main_aspect) - route.main_aspect = nil - if idx > 1 then - local signalpos = tcbs.signal - local ndef = signalpos and advtrains.ndb.get_ndef(signalpos) - if ndef and ndef.advtrains and ndef.advtrains.main_aspects then - route.main_aspect = ndef.advtrains.main_aspects[idx - 1].name + -- get TCBS here and rseg selected + local s_tcbs = ildb.get_tcbs(sel_rpart.param) + local rseg = route[sel_rpart.seg] + -- main aspect list + local signalpos = s_tcbs and s_tcbs.signal + if rseg then + rseg.main_aspect = nil + if idx > 1 then + local ndef = signalpos and advtrains.ndb.get_ndef(signalpos) + if ndef and ndef.advtrains and ndef.advtrains.main_aspects then + rseg.main_aspect = ndef.advtrains.main_aspects[idx - 1].name + end end end end - if fields.sa_distant then - route.assign_dst = minetest.is_yes(fields.sa_distant) + if fields.sa_distant and sel_rpart and sel_rpart.signal then + local rseg = route[sel_rpart.seg] + if rseg then + rseg.assign_dst = minetest.is_yes(fields.sa_distant) + end + end + if fields.se_callon and sel_rpart and sel_rpart.section then + local rseg = route[sel_rpart.seg] + if rseg then + rseg.call_on = minetest.is_yes(fields.se_callon) + end end if fields.noautogen then @@ -214,6 +269,19 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) if fields.back then advtrains.interlocking.show_signalling_form(sigd, pname) + return + end + + -- if an entry was selected in the textlist (and its not the current one) update the form + if fields.routelog then + local prev_idx = sel_rpart and sel_rpart.idx or 1 + local tev = minetest.explode_textlist_event(fields.routelog) + local new_idx = tev and tev.index + atdebug("routelog sel",prev_idx,new_idx) + if new_idx and new_idx ~= prev_idx then + atil.show_route_edit_form(pname, sigd, routeid, new_idx) + return + end end end diff --git a/advtrains_interlocking/routesetting.lua b/advtrains_interlocking/routesetting.lua index 669a604..95ca63c 100644 --- a/advtrains_interlocking/routesetting.lua +++ b/advtrains_interlocking/routesetting.lua @@ -45,7 +45,7 @@ function ilrs.set_route(signal, route, try) local rtename = route.name local signalname = (ildb.get_tcbs(signal).signal_name or "") .. sigd_to_string(signal) local c_tcbs, c_ts_id, c_ts, c_rseg, c_lckp - -- signals = { { pos = ., tcbs_ref = , role = "main_distant", masp_override = nil, dst_type = "next_main" or "none" } + -- signals = { { pos = ., tcbs_ref = , role = "main_distant", main_aspect = nil, dst_type = "next_main" or "none" } local signals = {} local nodst while c_sigd and i<=#route do @@ -71,8 +71,12 @@ function ilrs.set_route(signal, route, try) return false, "Section '"..(c_ts.name or c_ts_id).."' already has route set from "..sigd_to_string(c_ts.route.origin)..":\n"..c_ts.route.rsn, c_ts_id, nil end if c_ts.trains and #c_ts.trains>0 then - if not try then atwarn("Encountered ts occupied during a real run of routesetting routine, at ts=",c_ts_id,"while setting route",rtename,"of",signal) end - return false, "Section '"..(c_ts.name or c_ts_id).."' is occupied!", c_ts_id, nil + if c_rseg.call_on then + --atdebug("Routesetting: Call-on situation in", c_ts_id) + else + if not try then atwarn("Encountered ts occupied during a real run of routesetting routine, at ts=",c_ts_id,"while setting route",rtename,"of",signal) end + return false, "Section '"..(c_ts.name or c_ts_id).."' is occupied!", c_ts_id, nil + end end -- collect locks from rs cache and from route def @@ -142,12 +146,17 @@ function ilrs.set_route(signal, route, try) c_tcbs.route_origin = signal -- determine route role local ndef = advtrains.ndb.get_ndef(c_tcbs.signal) + local assign_dst = c_rseg.assign_dst + if assign_dst == nil then + assign_dst = (i~=1) -- special behavior when assign_dst is nil (and not false): + -- defaults to false for the very first signal and true for all others (= minimal user configuration overhead) + end local sig_table = { pos = c_tcbs.signal, tcbs_ref = c_tcbs, role = ndef and ndef.advtrains and ndef.advtrains.route_role, - masp_override = c_rseg.masp_override, --TODO implement masp_override on UI side - assign_dst = (i~=1) or route.assign_dst -- Behavior: for first signal assign depending on route.assign_dst, all others always assign + main_aspect = c_rseg.main_aspect, + assign_dst = assign_dst } signals[#signals+1] = sig_table end @@ -171,7 +180,7 @@ function ilrs.set_route(signal, route, try) -- note the signals are iterated backwards. Switch depending on the role local sig = signals[i] -- apply mainaspect - sig.tcbs_ref.route_aspect = sig.masp_override or "_default" -- or route.main_aspect : TODO this does not work if a distant signal is on the path! Implement per-sig aspects! + sig.tcbs_ref.route_aspect = sig.main_aspect or "_default" -- or route.main_aspect : TODO this does not work if a distant signal is on the path! Implement per-sig aspects! if sig.role == "distant" or sig.role == "distant_repeater" or sig.role == "main_distant" then if last_mainsig then -- assign the remote as the last mainsig if desired @@ -192,8 +201,6 @@ function ilrs.set_route(signal, route, try) -- update the signal aspect on map advtrains.interlocking.signal.update_route_aspect(sig.tcbs_ref, i ~= 1) end - -- Only for the first signal on the route, set route aspect. TODO: remove when masp_overrides are implemented - signal.route_aspect = route.main_aspect or "_default" return true end diff --git a/advtrains_interlocking/tcb_ts_ui.lua b/advtrains_interlocking/tcb_ts_ui.lua index bc52339..dfb2714 100755 --- a/advtrains_interlocking/tcb_ts_ui.lua +++ b/advtrains_interlocking/tcb_ts_ui.lua @@ -1008,12 +1008,12 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) name = "BS", [1] = { next = e_sigd, -- of the next (note: next) TCB on the route - locks = {} -- route locks of this route segment + locks = {}, -- route locks of this route segment + assign_dst = fields.setupblockshort and true, -- assign dst, if short block was selected }, terminal = e_sigd, use_rscache = true, -- main_aspect = - assign_dst = fields.setupblockshort and true, -- assign dst, if short block was selected default_autoworking = true, } local rid = #tcbs.routes + 1 -- typically 1 -- cgit v1.2.3 From 361970cdaf734828397cf22fa1db6ffc885eba10 Mon Sep 17 00:00:00 2001 From: orwell Date: Thu, 5 Dec 2024 23:40:01 +0100 Subject: Route Edit: Do not clear sel_rpart too eagerly, reshow form when necessary --- advtrains_interlocking/route_ui.lua | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) (limited to 'advtrains_interlocking/route_ui.lua') diff --git a/advtrains_interlocking/route_ui.lua b/advtrains_interlocking/route_ui.lua index 2b7e2e5..fa707d9 100644 --- a/advtrains_interlocking/route_ui.lua +++ b/advtrains_interlocking/route_ui.lua @@ -96,7 +96,7 @@ function atil.show_route_edit_form(pname, sigd, routeid, sel_rpartidx) -- What is in focus is determined by the parameter sel_rpartidx local sel_rpart = tabref[sel_rpartidx] - atdebug("sel rpart",sel_rpart) + --atdebug("sel rpart",sel_rpart) if sel_rpart and sel_rpart.signal then -- get TCBS here and rseg selected @@ -169,7 +169,6 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) local pname = player:get_player_name() -- retreive sel_rpart from the cache in any case and clear it out local sel_rpart = sel_rpartcache[pname] - sel_rpartcache[pname] = nil if not minetest.check_player_privs(pname, {train_operator=true, interlocking=true}) then return end @@ -230,12 +229,18 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) local rseg = route[sel_rpart.seg] if rseg then rseg.call_on = minetest.is_yes(fields.se_callon) + -- reshow form to update CO marker + atil.show_route_edit_form(pname, sigd, routeid, sel_rpart.idx) + return end end if fields.noautogen then route.smartroute_generated = nil route.default_autoworking = nil + -- reshow form for the button to disappear + atil.show_route_edit_form(pname, sigd, routeid, sel_rpart and sel_rpart.idx) + return end if fields.delete then @@ -243,6 +248,9 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) atil.route.update_route(sigd, tcbs, nil, true) table.remove(tcbs.routes, routeid) advtrains.interlocking.show_signalling_form(sigd, pname) + -- cleanup + sel_rpartcache[pname] = nil + return end if fields.clone then @@ -253,12 +261,17 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) rcopy.smartroute_generated = nil table.insert(tcbs.routes, routeid+1, rcopy) advtrains.interlocking.show_signalling_form(sigd, pname) + -- cleanup + sel_rpartcache[pname] = nil + return end if fields.newfrom then advtrains.interlocking.init_route_prog(pname, sigd, route) minetest.close_formspec(pname, formname) tcbs.ars_ignore_next = nil + -- cleanup + sel_rpartcache[pname] = nil return end @@ -269,6 +282,8 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) if fields.back then advtrains.interlocking.show_signalling_form(sigd, pname) + -- cleanup + sel_rpartcache[pname] = nil return end @@ -277,12 +292,16 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) local prev_idx = sel_rpart and sel_rpart.idx or 1 local tev = minetest.explode_textlist_event(fields.routelog) local new_idx = tev and tev.index - atdebug("routelog sel",prev_idx,new_idx) if new_idx and new_idx ~= prev_idx then atil.show_route_edit_form(pname, sigd, routeid, new_idx) return end end + if field.quit then + -- cleanup + sel_rpartcache[pname] = nil + end + end end) -- cgit v1.2.3 From 67ace4bde0edd0d40c63168758ac4b4878135946 Mon Sep 17 00:00:00 2001 From: orwell Date: Tue, 10 Dec 2024 23:07:06 +0100 Subject: Smartroute: Never replace already existing intact routes, change colorcoding, other fixes --- advtrains_interlocking/database.lua | 17 +++-- advtrains_interlocking/route_ui.lua | 25 ++++--- advtrains_interlocking/smartroute.lua | 118 ++++++++++++++++++++-------------- advtrains_interlocking/tcb_ts_ui.lua | 52 +++++++++++++-- 4 files changed, 134 insertions(+), 78 deletions(-) (limited to 'advtrains_interlocking/route_ui.lua') diff --git a/advtrains_interlocking/database.lua b/advtrains_interlocking/database.lua index 09b1c72..3104a20 100644 --- a/advtrains_interlocking/database.lua +++ b/advtrains_interlocking/database.lua @@ -245,6 +245,14 @@ routes = { 800080008000 = st } next = S[(-23,9,0)/2] -- the start TCB of the next route segment (pointing forward) + -- Signal info: relates to the signal at the start of this section: + main_aspect = "_free" -- The main aspect that the route start signal is to show + assign_dst = false -- Whether to assign distant signal (affects only the signal at the start of the route) + -- false: start signal does not set distant signal (the default), for long blocks + -- it is assumed that the next main signal will have its own distant sig + -- true: start signal sets distant signal to the next signal on the route with route_role "main" (typically the end signal) + -- for short blocks where end signal doesn't have its own distant sig + call_on = false -- if true, when this route is set, section is allowed to be occupied by a train (but it must not have a route set in) } 2 = { locks = {} @@ -254,15 +262,6 @@ routes = { ars = { } 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 to specify the signal aspect of the signal - main_aspect = "_free" -- The main aspect that the route start signal is to show - assign_dst = false -- Whether to assign distant signal (affects only the signal at the start of the route) - -- false: start signal does not set distant signal (the default), for long blocks - -- it is assumed that the next main signal will have its own distant sig - -- true: start signal sets distant signal to the next signal on the route with route_role "main" (typically the end signal) - -- for short blocks where end signal doesn't have its own distant sig - terminal = -- the sigd describing the end of the route (e.g. the "next" entry in the final route segment). - -- Might be missing or wrong. Routesetting currently does not care about this value being present. default_autoworking = false -- if true, when route is set autoworking will be by default on. Used for Blocksignal mode } } diff --git a/advtrains_interlocking/route_ui.lua b/advtrains_interlocking/route_ui.lua index fa707d9..b75f9d7 100644 --- a/advtrains_interlocking/route_ui.lua +++ b/advtrains_interlocking/route_ui.lua @@ -43,7 +43,7 @@ function atil.show_route_edit_form(pname, sigd, routeid, sel_rpartidx) -- we start at the tc designated by signal local c_sigd = sigd local i = 1 - local c_tcbs, c_ts_id, c_ts, c_rseg, c_lckp + local c_tcbs, c_ts_id, c_ts, c_rseg while c_sigd and i<=#route do c_tcbs = ildb.get_tcbs(c_sigd) if not c_tcbs then @@ -58,7 +58,6 @@ function atil.show_route_edit_form(pname, sigd, routeid, sel_rpartidx) c_ts = ildb.get_ts(c_ts_id) c_rseg = route[i] - c_lckp = {} local signame = "-" if c_tcbs and c_tcbs.signal then signame = c_tcbs.signal_name or "o" end @@ -145,9 +144,9 @@ function atil.show_route_edit_form(pname, sigd, routeid, sel_rpartidx) form = form.."button[2.5,6;1,1;next;>>>]" - if route.smartroute_generated or route.default_autoworking then - form = form.."button[3.5,6;2,1;noautogen;Clr Autogen]" - end + --if route.smartroute_generated or route.default_autoworking then + -- form = form.."button[3.5,6;2,1;noautogen;Clr Autogen]" + --end form = form.."button[5.5,6;3,1;delete;Delete Route]" form = form.."button[0.5,7;3,1;back;Back to signal]" form = form.."button[3.5,7;2,1;clone;Clone Route]" @@ -235,13 +234,13 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) end end - if fields.noautogen then - route.smartroute_generated = nil - route.default_autoworking = nil - -- reshow form for the button to disappear - atil.show_route_edit_form(pname, sigd, routeid, sel_rpart and sel_rpart.idx) - return - end + --if fields.noautogen then + -- route.smartroute_generated = nil + -- route.default_autoworking = nil + -- -- reshow form for the button to disappear + -- atil.show_route_edit_form(pname, sigd, routeid, sel_rpart and sel_rpart.idx) + -- return + --end if fields.delete then -- if something set the route in the meantime, make sure this doesn't break. @@ -298,7 +297,7 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) end end - if field.quit then + if fields.quit then -- cleanup sel_rpartcache[pname] = nil end diff --git a/advtrains_interlocking/smartroute.lua b/advtrains_interlocking/smartroute.lua index 770c379..07cdf46 100644 --- a/advtrains_interlocking/smartroute.lua +++ b/advtrains_interlocking/smartroute.lua @@ -6,6 +6,23 @@ local ildb = atil.db local sr = {} +-- Start the SmartRoute process. This searches for routes and tries to match them with existing routes, showing them in a form +function sr.start(pname, sigd) + -- is start signal a shunt signal? This becomes default setting for searching_shunt + local is_startsignal_shunt = false + local tcbs = ildb.get_tcbs(sigd) + if tcbs.signal then + local ndef = advtrains.ndb.get_ndef(tcbs.signal) + if ndef and ndef.advtrains then + if ndef.advtrains.route_role == "shunt" then + is_startsignal_shunt = true + end + end + end + sr.propose_next(pname, sigd, 10, is_startsignal_shunt) -- TODO set tscnt_limit to 2 initially and then increase it. Do this when form is implemented +end + + local function otherside(s) if s==1 then return 2 else return 1 end end @@ -13,8 +30,8 @@ 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) - --atdebug("Recursively restarting at ",s_pos, s_connid, "limit left", scan_limit) +local function recursively_find_routes(s_pos, s_connid, searching_shunt, tcbseq, mark_pos, result_table, scan_limit, tscnt_limit) + 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() pos, connid, bconnid = ti:next_track()-- step once to get ahead of previous turnout @@ -25,7 +42,7 @@ local function recursively_find_routes(s_pos, s_connid, searching_shunt, tcbseq, mark_pos[pts] = true local node = advtrains.ndb.get_node_or_nil(pos) - atdebug("(SmartRoute) Walk ",pos, "nodename", node.name, "entering at conn",bconnid) + --atdebug("(SmartRoute) 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 @@ -33,7 +50,9 @@ local function recursively_find_routes(s_pos, s_connid, searching_shunt, tcbseq, 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) - recursively_find_routes(pos, oconnid, searching_shunt, table.copy(tcbseq), table.copy(mark_pos), result_table, ti.limit) + recursively_find_routes(pos, oconnid, searching_shunt, + table.copy(tcbseq), table.copy(mark_pos), + result_table, ti.limit, tscnt_limit) end return end @@ -68,12 +87,18 @@ local function recursively_find_routes(s_pos, s_connid, searching_shunt, tcbseq, end end end + -- decrease tscnt + tscnt_limit = tscnt_limit - 1 + atdebug("(SmartRoute) Remaining TS Count:",tscnt_limit) + if tscnt_limit <= 0 then + break + end 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") + atdebug("(SmartRoute) Reached track end or limit at", last_pos, ". This path is not saved, returning") end local function build_route_from_foundroute(froute, name) @@ -91,59 +116,54 @@ end -- Maximum scan length for track iterator local TS_MAX_SCAN = 1000 -function sr.init(pname, sigd) - -- is start signal a shunt signal? - local is_startsignal_shunt = false +function sr.rescan(pname, sigd, tscnt_limit, searching_shunt) + local result_table = {} + recursively_find_routes(sigd.p, sigd.s, is_startsignal_shunt, {}, {}, result_table, TS_MAX_SCAN, tscnt_limit) + return result_table +end + +-- Propose to pname the smartroute actions in a form, with the current settings as passed to this function +function sr.propose_next(pname, sigd, tscnt_limit, searching_shunt) local tcbs = ildb.get_tcbs(sigd) - if tcbs.signal then - local ndef = advtrains.ndb.get_ndef(tcbs.signal) - if ndef and ndef.advtrains then - if ndef.advtrains.route_role == "shunt" then - is_startsignal_shunt = true - end + if not tcbs or not tcbs.routes then + minetest.chat_send_player(pname, "Smartroute: TCBS or routes don't exist here!") + return + end + -- Step 1: search for routes using the current settings + local found_routes = sr.rescan(pname, sigd, tscnt_limit, searching_shunt) + -- 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 + local valid = advtrains.interlocking.check_route_valid(route, sigd) + local endpoint = route[#route].next -- 'next' field of the last route segment (the segment with index==len) + if valid and endpoint then + local endstr = advtrains.interlocking.sigd_to_string(endpoint) + atdebug("(Smartroute) Find existing endpoint:",route.name,"ends at",endstr) + ex_endpts[endstr] = route.name + else + atdebug("(Smartroute) Find existing endpoint:",route.name," not considered, endpoint",endpoint,"valid",valid) end end - local result_table = {} - recursively_find_routes(sigd.p, sigd.s, is_startsignal_shunt, {}, {}, result_table, TS_MAX_SCAN) - - atdebug("Smartroute search finished:",result_table) - - -- Short-circuit logic right now for testing - -- go through and delete all routes that are autogenerated - local i = 1 - while i<=#tcbs.routes do - if tcbs.routes[i].smartroute_generated then - table.remove(tcbs.routes, i) + local new_frte = {} + for _,froute in ipairs(found_routes) do + local endpoint = froute.tcbseq[#froute.tcbseq] + local endstr = advtrains.interlocking.sigd_to_string(endpoint) + if not ex_endpts[endstr] then + new_frte[#new_frte+1] = froute else - i=i+1 + atdebug("(Smartroute) Throwing away",froute.name,"because endpoint",endstr,"already reached by route",ex_endpts[endstr]) end end - -- just plainly create routes! - for idx, froute in ipairs(result_table) do + + -- All remaining routes will be shown to user now. + -- TODO: show a form. Right now still shortcircuit + local sel_rte = #tcbs.routes+1 + for idx, froute in ipairs(new_frte) do tcbs.routes[#tcbs.routes+1] = build_route_from_foundroute(froute) end - atwarn("Smartroute done!") + atdebug("Smartroute done!") + advtrains.interlocking.show_signalling_form(sigd, pname, sel_rte) end - ---[[ - player1 = { - origin = - found_routes = { - { tcbseq = {, , }, mark_pos = { table with keys being encoded_pos of rails constituting route }, to_end_of_track = false, shunt_route = false } - } - } -]]-- -local player_smartroute = {} - -minetest.register_on_punchnode(function(pos, node, player, pointed_thing) - local pname = player:get_player_name() - if not minetest.check_player_privs(pname, "interlocking") then - return - end - -- TODO -end) - - advtrains.interlocking.smartroute = sr diff --git a/advtrains_interlocking/tcb_ts_ui.lua b/advtrains_interlocking/tcb_ts_ui.lua index dfb2714..5bd6c08 100755 --- a/advtrains_interlocking/tcb_ts_ui.lua +++ b/advtrains_interlocking/tcb_ts_ui.lua @@ -6,6 +6,7 @@ local players_assign_xlink = {} local players_link_ts = {} local players_assign_fixedlocks = {} +local atil = advtrains.interlocking local ildb = advtrains.interlocking.db local ilrs = advtrains.interlocking.route @@ -746,6 +747,42 @@ function advtrains.interlocking.highlight_track_section(pos) end end +-- checks that the given route is still valid (i.e. all its TCBs, sections and locks exist) +-- returns true (ok) or false, reason (on issue) +function advtrains.interlocking.check_route_valid(route, sigd) + -- this code is partially copy-pasted from routesetting.lua + -- we start at the tc designated by signal + local c_sigd = sigd + local i = 1 + local c_tcbs, c_ts_id, c_ts, c_rseg + while c_sigd and i<=#route do + c_tcbs = ildb.get_tcbs(c_sigd) + if not c_tcbs then + return false, "No TCBS at "..sigd_to_string(c_sigd) + end + c_ts_id = c_tcbs.ts_id + if not c_ts_id then + return false, "No track section adjacent to "..sigd_to_string(c_sigd) + end + c_ts = ildb.get_ts(c_ts_id) + + c_rseg = route[i] + + if c_rseg.locks then + for pts, state in pairs(c_rseg.locks) do + local pos = minetest.string_to_pos(pts) + if not advtrains.is_passive(pos) then + return false, "No passive component for lock at "..pts + end + end + end + -- advance + c_sigd = c_rseg.next + i = i + 1 + end + return true, nil, c_sigd +end + -- Signalling formspec - set routes a.s.o -- textlist selection temporary storage @@ -800,17 +837,19 @@ function advtrains.interlocking.show_signalling_form(sigd, pname, sel_rte, calle -- at least one route is defined, show normal dialog local strtab = {} for idx, route in ipairs(tcbs.routes) do + local rname = route.name + local valid = atil.check_route_valid(route, sigd) local clr = "" - if route.smartroute_generated then - clr = "#FFFF55" - end - if route.ars then + if not valid then clr = "#FF5555" + rname = rname.." (invalid)" + elseif route.ars then + clr = "#FFFF55" if route.ars.default then clr = "#55FF55" end end - strtab[#strtab+1] = clr .. minetest.formspec_escape(route.name) + strtab[#strtab+1] = clr .. minetest.formspec_escape(rname) end form = form.."label[0.5,2.5;Routes:]" form = form.."textlist[0.5,3;5,3;rtelist;"..table.concat(strtab, ",") @@ -961,8 +1000,7 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) return end if fields.smartroute and hasprivs then - advtrains.interlocking.smartroute.init(pname, sigd) - minetest.close_formspec(pname, formname) + advtrains.interlocking.smartroute.start(pname, sigd) tcbs.ars_ignore_next = nil return end -- 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/route_ui.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 8c91ce1ec178d6d49f3ddefc57d99eecd0caef3c Mon Sep 17 00:00:00 2001 From: orwell Date: Thu, 9 Jan 2025 00:57:32 +0100 Subject: Various bugfixes found in lunixforks debug --- advtrains/occupation.lua | 4 ++-- advtrains/trainlogic.lua | 4 ++-- advtrains/wagons.lua | 2 +- advtrains_interlocking/database.lua | 9 +++++---- advtrains_interlocking/route_ui.lua | 2 +- advtrains_interlocking/routesetting.lua | 9 ++++++--- advtrains_interlocking/signal_api.lua | 1 + advtrains_interlocking/tcb_ts_ui.lua | 5 ++++- advtrains_luaautomation/environment.lua | 10 +++++++--- 9 files changed, 29 insertions(+), 17 deletions(-) (limited to 'advtrains_interlocking/route_ui.lua') diff --git a/advtrains/occupation.lua b/advtrains/occupation.lua index 26e1f79..20a986e 100644 --- a/advtrains/occupation.lua +++ b/advtrains/occupation.lua @@ -89,8 +89,8 @@ function o.set_item(train_id, pos, idx) assert(idx) local i = 1 while t[i] do - if t[i]==train_id and t[i+1]==index then - break + if t[i]==train_id and t[i+1]==idx then + return end i = i + 2 end diff --git a/advtrains/trainlogic.lua b/advtrains/trainlogic.lua index e4939df..0e588c7 100644 --- a/advtrains/trainlogic.lua +++ b/advtrains/trainlogic.lua @@ -630,7 +630,7 @@ function advtrains.train_step_b(id, train, dtime) local ocn = otrn.path_cn[ob_idx] local ocp = otrn.path_cp[ob_idx] - local target_is_inside, ref_index, facing + local target_is_inside, ref_index, facing, same_dir if base_cn == ocn then -- same direction @@ -1049,7 +1049,7 @@ function advtrains.update_trainpart_properties(train_id, invert_flipstate) if not wagon then local ent = advtrains.wagon_objects[w_id] local pdesc - if ent then + if ent and ent:get_pos() then pdesc = "at " .. minetest.pos_to_string(ent:get_pos()) elseif train.last_pos then pdesc = "near " .. minetest.pos_to_string(train.last_pos) diff --git a/advtrains/wagons.lua b/advtrains/wagons.lua index ef057e5..01c60ec 100644 --- a/advtrains/wagons.lua +++ b/advtrains/wagons.lua @@ -1333,7 +1333,7 @@ function advtrains.get_wagon_prototype(data) end local rt, proto = advtrains.resolve_wagon_alias(wt) if not rt then - atwarn("Unable to load wagon type",wt,", using placeholder") + --atwarn("Unable to load wagon type",wt,", using placeholder") rt = "advtrains:wagon_placeholder" proto = advtrains.wagon_prototypes[rt] end diff --git a/advtrains_interlocking/database.lua b/advtrains_interlocking/database.lua index 9c72a72..d80fb76 100644 --- a/advtrains_interlocking/database.lua +++ b/advtrains_interlocking/database.lua @@ -84,7 +84,7 @@ 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) + atdebug("ILDB converting TCB position format",pts,"->",epos) track_circuit_breaks[epos] = tcb else -- keep entry, it is already new @@ -100,7 +100,7 @@ function ildb.load(data) 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) + atdebug("ILDB converting tcb",pts,"side",t_side,"route",t_route,"lock position format",lpts,"->",epos) locks_n[epos] = state else -- already correct format @@ -131,7 +131,7 @@ function ildb.load(data) if pos then -- that was a pos_to_string local epos = advtrains.encode_pos(pos) - --atdebug("ILDB converting Route Lock position format",pts,"->",epos) + atdebug("ILDB converting Route Lock position format",pts,"->",epos) advtrains.interlocking.route.rte_locks[epos] = lta else -- keep entry, it is already new @@ -412,6 +412,7 @@ function ildb.check_and_repair_ts_at_pos(pos, tcb_connid, notify_pname, force_cr 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.") + ildb.update_rs_cache(ts_id) return ts_id end @@ -457,7 +458,7 @@ function ildb.get_all_tcbs_adjacent(inipos, inidir, per_track_callback) pos, connid = ti:next_branch() --atdebug("get_all_tcbs_adjacent: BRANCH: ",pos, connid) bconnid = nil - is_branch_start = true + local is_branch_start = true repeat -- callback if per_track_callback then diff --git a/advtrains_interlocking/route_ui.lua b/advtrains_interlocking/route_ui.lua index 7dddc6e..3c7bd64 100644 --- a/advtrains_interlocking/route_ui.lua +++ b/advtrains_interlocking/route_ui.lua @@ -67,7 +67,7 @@ function atil.show_route_edit_form(pname, sigd, routeid, sel_rpartidx) if c_rseg.locks then for pts, state in pairs(c_rseg.locks) do - local pos = minetest.string_to_pos(pts) + local pos = advtrains.decode_pos(pts) itab(i, "L "..pts.." -> "..state, "lock", pos) if not advtrains.is_passive(pos) then itab(i, "-!- No passive component at "..pts..". Please reconfigure route!", "err", nil) diff --git a/advtrains_interlocking/routesetting.lua b/advtrains_interlocking/routesetting.lua index 28c8c3c..0668e62 100644 --- a/advtrains_interlocking/routesetting.lua +++ b/advtrains_interlocking/routesetting.lua @@ -104,7 +104,7 @@ function ilrs.set_route(signal, route, try) end for lp, state in pairs(c_locks) do - local confl = ilrs.has_route_lock(pts, state) + local confl = ilrs.has_route_lock(lp, state) local pos = advtrains.decode_pos(lp) if advtrains.is_passive(pos) then @@ -131,7 +131,8 @@ function ilrs.set_route(signal, route, try) 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 re_tcbs or not re_tcbs.ts_id or re_tcbs.ts_id~=c_ts_id) + and route[i+1] then --FIX 2025-01-08: in old worlds the final TCB may be wrong (it didn't matter back then), don't error out here (route still shown invalid in UI) if not try then atwarn("Encountered inconsistent ts (front~=back) while a real run of routesetting routine, at position",pts,"while setting route",rtename,"of",signal) end return false, "TCB at "..minetest.pos_to_string(nvar.p).." has different section than previous TCB. Please update track section or reconfigure route!" end @@ -413,7 +414,9 @@ function ilrs.update_route(sigd, tcbs, newrte, cancel) -- set_route now sets the signal aspects --has_changed_aspect = true -- route success. apply default_autoworking flag if requested - tcbs.route_auto = route.default_autoworking + if route.default_autoworking then + tcbs.route_auto = true --FIX 2025-01-08: never set it to false if it was true! + end end end if has_changed_aspect then diff --git a/advtrains_interlocking/signal_api.lua b/advtrains_interlocking/signal_api.lua index b607750..e92658d 100644 --- a/advtrains_interlocking/signal_api.lua +++ b/advtrains_interlocking/signal_api.lua @@ -375,6 +375,7 @@ function signal.reapply_aspect(pts) local rem_aspt = signal.aspects[remote] --atdebug("resolving remote",advtrains.decode_pos(remote),"aspt",rem_aspt) local rem_pos = advtrains.decode_pos(remote) + local _,rem_ndef rem_masp, _, _, rem_ndef = signal.get_aspect_internal(rem_pos, rem_aspt) if rem_masp then if rem_ndef.advtrains and rem_ndef.advtrains.get_aspect_info then diff --git a/advtrains_interlocking/tcb_ts_ui.lua b/advtrains_interlocking/tcb_ts_ui.lua index 59d3be4..814a11a 100755 --- a/advtrains_interlocking/tcb_ts_ui.lua +++ b/advtrains_interlocking/tcb_ts_ui.lua @@ -776,7 +776,7 @@ function advtrains.interlocking.check_route_valid(route, sigd) if c_rseg.locks then for pts, state in pairs(c_rseg.locks) do - local pos = minetest.string_to_pos(pts) + local pos = advtrains.decode_pos(pts) if not advtrains.is_passive(pos) then return false, "No passive component for lock at "..pts end @@ -795,6 +795,9 @@ function advtrains.interlocking.check_route_valid(route, sigd) i = i + 1 end -- check end TCB + if not c_sigd then + return false, "Final TCBS unset (legacy-style buffer route)" + end c_tcbs = ildb.get_tcbs(c_sigd) if not c_tcbs then return false, "Final TCBS missing at "..sigd_to_string(c_sigd) diff --git a/advtrains_luaautomation/environment.lua b/advtrains_luaautomation/environment.lua index b54d45c..a6ed2c7 100644 --- a/advtrains_luaautomation/environment.lua +++ b/advtrains_luaautomation/environment.lua @@ -226,11 +226,15 @@ if advtrains.interlocking then end static_env.get_aspect = function(signal) local pos = atlatc.pcnaming.resolve_pos(signal) - return advtrains.interlocking.signal_get_aspect(pos) + return advtrains.interlocking.signal.get_aspect_info(pos) end - static_env.set_aspect = function(signal, asp) + static_env.set_aspect = function(signal, main_asp, rem_signal) + if type(main_asp) == "table" then + error("set_aspect: Parameters of this method have changed to (signal, main_asp, rem_signal) with introduction of distant signalling: parameter 2 is now the main aspect name (a string)") + end local pos = atlatc.pcnaming.resolve_pos(signal) - return advtrains.interlocking.signal_set_aspect(pos,asp) + local rem_pos = rem_signal and atlatc.pcnaming.resolve_pos(rem_signal) + return advtrains.interlocking.signal_set_aspect(pos, main_asp, rem_pos) end --section_occupancy() -- cgit v1.2.3