From 33c839b40d48e154f5b03619a9bdce1bed1fc602 Mon Sep 17 00:00:00 2001 From: orwell96 Date: Wed, 10 Oct 2018 21:49:52 +0200 Subject: Add signal safety control override, restructure control system --- advtrains/atc.lua | 30 ++--- advtrains/init.lua | 1 + advtrains/path.lua | 2 +- advtrains/trainhud.lua | 37 ++++-- advtrains/trainlogic.lua | 95 +++++++++++---- advtrains_interlocking/database.lua | 2 +- advtrains_interlocking/init.lua | 5 +- advtrains_interlocking/lzb.lua | 194 ++++++++++++++++++++++++++++++ advtrains_interlocking/train_related.lua | 183 ---------------------------- advtrains_interlocking/train_sections.lua | 183 ++++++++++++++++++++++++++++ advtrains_luaautomation/atc_rail.lua | 6 +- 11 files changed, 498 insertions(+), 240 deletions(-) create mode 100644 advtrains_interlocking/lzb.lua delete mode 100644 advtrains_interlocking/train_related.lua create mode 100644 advtrains_interlocking/train_sections.lua diff --git a/advtrains/atc.lua b/advtrains/atc.lua index 22de4ca..b00849d 100644 --- a/advtrains/atc.lua +++ b/advtrains/atc.lua @@ -20,10 +20,11 @@ end --contents: {command="...", arrowconn=0-15 where arrow points} --general -function atc.train_set_command(train_id, command, arrow) - atc.train_reset_command(train_id) - advtrains.trains[train_id].atc_arrow = arrow - advtrains.trains[train_id].atc_command = command +function atc.train_set_command(train, command, arrow) + atc.train_reset_command(train) + train.atc_delay = 0 + train.atc_arrow = arrow + train.atc_command = command end function atc.send_command(pos, par_tid) @@ -49,7 +50,7 @@ function atc.send_command(pos, par_tid) atwarn("ATC rail at", pos, ": Rail not on train's path! Can't determine arrow direction. Assuming +!") end - atc.train_set_command(train_id, atc.controllers[pts].command, iconnid==1) + atc.train_set_command(train, atc.controllers[pts].command, iconnid==1) atprint("Sending ATC Command to", train_id, ":", atc.controllers[pts].command, "iconnid=",iconnid) return true @@ -66,12 +67,13 @@ function atc.send_command(pos, par_tid) return false end -function atc.train_reset_command(train_id) - advtrains.trains[train_id].atc_command=nil - advtrains.trains[train_id].atc_delay=0 - advtrains.trains[train_id].atc_brake_target=nil - advtrains.trains[train_id].atc_wait_finish=nil - advtrains.trains[train_id].atc_arrow=nil +function atc.train_reset_command(train) + train.atc_command=nil + train.atc_delay=nil + train.atc_brake_target=nil + train.atc_wait_finish=nil + train.atc_arrow=nil + train.tarvelocity=nil end --nodes @@ -179,7 +181,7 @@ local matchptn={ train.tarvelocity = 0 elseif train.velocity>tonumber(match) then train.atc_brake_target=tonumber(match) - if train.tarvelocity>train.atc_brake_target then + if not train.tarvelocity or train.tarvelocity>train.atc_brake_target then train.tarvelocity=train.atc_brake_target end end @@ -258,7 +260,7 @@ function atc.execute_atc_command(id, train) while nest>=0 do if pos>#rest then atwarn(sid(id), attrans("ATC command syntax error: I statement not closed: @1",command)) - atc.train_reset_command(id) + atc.train_reset_command(train) return end local char=string.sub(rest, pos, pos) @@ -301,7 +303,7 @@ function atc.execute_atc_command(id, train) end end atwarn(sid(id), attrans("ATC command parse error: Unknown command: @1", command)) - atc.train_reset_command(id) + atc.train_reset_command(train) end diff --git a/advtrains/init.lua b/advtrains/init.lua index 46a73d4..c1f280d 100644 --- a/advtrains/init.lua +++ b/advtrains/init.lua @@ -45,6 +45,7 @@ function advtrains.pcall(fun) atwarn(debug.traceback()) if advtrains.atprint_context_tid then advtrains.path_print(advtrains.trains[advtrains.atprint_context_tid], atdebug) + atwarn(advtrains.trains[advtrains.atprint_context_tid].debug) end end) if not succ then diff --git a/advtrains/path.lua b/advtrains/path.lua index 19b4e78..5c3db94 100644 --- a/advtrains/path.lua +++ b/advtrains/path.lua @@ -260,7 +260,7 @@ function advtrains.path_get_index_by_offset(train, index, offset) --atdebug("pibo: 2 off=",off,"idx=",idx) -- then walk the path forward until we would overshoot while off - train.path_dist[idx] >= 0 do - idx = idx - 1 + idx = idx + 1 advtrains.path_get_adjacent(train, idx) if not train.path_dist[idx] then for i=-5,5 do diff --git a/advtrains/trainhud.lua b/advtrains/trainhud.lua index 60ef5d1..f326427 100644 --- a/advtrains/trainhud.lua +++ b/advtrains/trainhud.lua @@ -44,19 +44,19 @@ function advtrains.on_control_change(pc, train, flip) else local act=false if pc.up then - train.lever=4 + train.ctrl.user=4 act=true end if pc.jump then - train.lever = 1 + train.ctrl.user = 1 act=true end if pc.down then if train.velocity>0 then if pc.jump then - train.lever = 0 + train.ctrl.user = 0 else - train.lever = 2 + train.ctrl.user = 2 end act=true else @@ -77,7 +77,9 @@ function advtrains.on_control_change(pc, train, flip) train.door_open = 1 end end - train.active_control = act + if not act then + train.ctrl.user = nil + end if pc.aux1 then --horn end @@ -161,9 +163,7 @@ function advtrains.hud_train_format(train, flip) local max=train.max_speed or 10 local vel=advtrains.abs_ceil(train.velocity) - local tvel=advtrains.abs_ceil(train.tarvelocity) local vel_kmh=advtrains.abs_ceil(advtrains.ms_to_kmh(train.velocity)) - local tvel_kmh=advtrains.abs_ceil(advtrains.ms_to_kmh(train.tarvelocity)) local levers = "B - o +" local tlev=train.lever @@ -174,11 +174,28 @@ function advtrains.hud_train_format(train, flip) if tlev == 3 then levers = "B - >o< +" end if tlev == 4 then levers = "B - o >+<" end - local topLine, firstLine, secondLine + local topLine, firstLine + + local secondLine + if train.tarvelocity then + local b=" " + local tvel=advtrains.abs_ceil(train.tarvelocity) + local tvel_kmh=advtrains.abs_ceil(advtrains.ms_to_kmh(train.tarvelocity)) + if train.atc_brake_target then + b="-B-" + end + secondLine="ATC"..b..": |"..string.rep("+", tvel)..string.rep("_", max-tvel).."> "..tvel_kmh.." km/h" + elseif train.atc_delay then + secondLine = "ATC waiting "..advtrains.abs_ceil(train.atc_delay).."s" + else + secondLine = "Manual operation" + end + if train.ctrl.lzb then + secondLine = "-!- Safety override -!-" + end topLine=" ["..mletter[fct].."] {"..levers.."} "..doorstr[(train.door_open or 0) * fct] firstLine=attrans("Speed:").." |"..string.rep("+", vel)..string.rep("_", max-vel).."> "..vel_kmh.." km/h" - secondLine=attrans("Target:").." |"..string.rep("+", tvel)..string.rep("_", max-tvel).."> "..tvel_kmh.." km/h" - return topLine.."\n"..firstLine.."\n"..secondLine + return (train.debug or "").."\n"..topLine.."\n"..firstLine.."\n"..secondLine end diff --git a/advtrains/trainlogic.lua b/advtrains/trainlogic.lua index 861c042..f26f7da 100644 --- a/advtrains/trainlogic.lua +++ b/advtrains/trainlogic.lua @@ -181,6 +181,13 @@ local function assertdef(tbl, var, def) end end +function advtrains.get_acceleration(train, lever) + local acc_all = t_accel_all[lever] + local acc_eng = t_accel_eng[lever] + local nwagons = #train.trainparts + local acc = acc_all + (acc_eng*train.locomotives_in_train)/nwagons + return acc +end -- Small local util function to recalculate train's end index local function recalc_end_index(train) @@ -219,9 +226,10 @@ function advtrains.train_ensure_init(id, train) if train.no_step then return end assertdef(train, "velocity", 0) - assertdef(train, "tarvelocity", 0) + --assertdef(train, "tarvelocity", 0) assertdef(train, "acceleration", 0) assertdef(train, "id", id) + assertdef(train, "ctrl", {}) if not train.drives_on or not train.max_speed then @@ -275,11 +283,10 @@ function advtrains.train_step_b(id, train, dtime) --- 3. handle velocity influences --- local train_moves=(train.velocity~=0) - local tarvel_cap + local tarvel_cap = train.speed_restriction if train.recently_collided_with_env then tarvel_cap=0 - train.active_control=false if not train_moves then train.recently_collided_with_env=nil--reset status when stopped end @@ -307,11 +314,24 @@ function advtrains.train_step_b(id, train, dtime) tarvel_cap=1 end + -- Driving control rework: + --[[ + Items are only defined when something is controlling them. + In order of precedence. + train.ctrl = { + lzb = restrictive override from LZB + user = User input from driverstand + atc = ATC command override (determined here) + } + The code here determines the precedence and writes the final control into train.lever + ]] + --interpret ATC command and apply auto-lever control when not actively controlled local trainvelocity = train.velocity - if not train.lever then train.lever=3 end - if train.active_control then - advtrains.atc.train_reset_command(id) + + + if train.ctrl.user then + advtrains.atc.train_reset_command(train) else local braketar = train.atc_brake_target local emerg = false -- atc_brake_target==-1 means emergency brake (BB command) @@ -323,8 +343,11 @@ function advtrains.train_step_b(id, train, dtime) train.atc_brake_target=nil braketar = nil end + if train.tarvelocity and train.velocity==train.tarvelocity then + train.tarvelocity = nil + end if train.atc_wait_finish then - if not train.atc_brake_target and train.velocity==train.tarvelocity then + if not train.atc_brake_target and not train.tarvelocity then train.atc_wait_finish=nil end end @@ -334,42 +357,62 @@ function advtrains.train_step_b(id, train, dtime) else train.atc_delay=train.atc_delay-dtime end + elseif train.atc_delay then + train.atc_delay = nil end - train.lever = 3 - if train.tarvelocity>trainvelocity then train.lever=4 end - if train.tarvelocitytrainvelocity then + train.ctrl.atc=4 + end + if train.tarvelocity and train.tarvelocitytarvel_cap then tmp_lever = 0 end + train.lever = tmp_lever + --- 3a. actually calculate new velocity --- if tmp_lever~=3 then - local acc_all = t_accel_all[tmp_lever] - local acc_eng = t_accel_eng[tmp_lever] - local nwagons = #train.trainparts - local accel = acc_all + (acc_eng*train.locomotives_in_train)/nwagons + local accel = advtrains.get_acceleration(train, tmp_lever) local vdiff = accel*dtime - if not train.active_control then + + -- ATC control exception: don't cross tarvelocity if + -- atc provided a target_vel + if train.tarvelocity then local tvdiff = train.tarvelocity - trainvelocity - if math.abs(vdiff) > math.abs(tvdiff) then + if tvdiff~=0 and math.abs(vdiff) > math.abs(tvdiff) then --applying this change would cross tarvelocity + --atdebug("In Tvdiff condition, clipping",vdiff,"to",tvdiff) + --atdebug("vel=",trainvelocity,"tvel=",train.tarvelocity) vdiff=tvdiff end end @@ -385,9 +428,9 @@ function advtrains.train_step_b(id, train, dtime) end train.acceleration=vdiff train.velocity=train.velocity+vdiff - if train.active_control then - train.tarvelocity = train.velocity - end + --if train.ctrl.user then + -- train.tarvelocity = train.velocity + --end else train.acceleration = 0 end @@ -443,7 +486,7 @@ if train.no_step or train.wait_for_path then return end if not collided and advtrains.occ.check_collision(testpos, id) then --collides train.velocity = 0 - train.tarvelocity = 0 + advtrains.atc.train_reset_command(train) collided = true end --- 8b damage players --- @@ -622,7 +665,7 @@ function advtrains.create_new_train_at(pos, connid, ioff, trainparts) t.last_connid=connid t.last_frac=ioff - t.tarvelocity=0 + --t.tarvelocity=0 t.velocity=0 t.trainparts=trainparts diff --git a/advtrains_interlocking/database.lua b/advtrains_interlocking/database.lua index 030a5e0..af90880 100644 --- a/advtrains_interlocking/database.lua +++ b/advtrains_interlocking/database.lua @@ -485,7 +485,7 @@ function ildb.get_ip_signal_asp(pts, connid) ildb.clear_ip_signal(pts, connid) return nil end - return asp + return asp, p end return nil end diff --git a/advtrains_interlocking/init.lua b/advtrains_interlocking/init.lua index 19501b6..d6625eb 100644 --- a/advtrains_interlocking/init.lua +++ b/advtrains_interlocking/init.lua @@ -8,9 +8,12 @@ local modpath = minetest.get_modpath(minetest.get_current_modname()) .. DIR_DELI dofile(modpath.."database.lua") dofile(modpath.."signal_api.lua") dofile(modpath.."demosignals.lua") -dofile(modpath.."train_related.lua") +dofile(modpath.."train_sections.lua") dofile(modpath.."route_prog.lua") dofile(modpath.."routesetting.lua") dofile(modpath.."tcb_ts_ui.lua") +dofile(modpath.."lzb.lua") + + minetest.register_privilege("interlocking", {description = "Can set up track sections, routes and signals.", give_to_singleplayer = true}) diff --git a/advtrains_interlocking/lzb.lua b/advtrains_interlocking/lzb.lua new file mode 100644 index 0000000..c9e40ab --- /dev/null +++ b/advtrains_interlocking/lzb.lua @@ -0,0 +1,194 @@ +-- lzb.lua +-- Enforced and/or automatic train override control, obeying signals + +--[[ +Documentation of train.lzb table +train.lzb = { + trav = Current index that the traverser has advanced so far + travsht = boolean indicating whether the train will be a shunt move at "trav" + travspd = speed restriction at end of traverser + travwspd = warning speed res. + oncoming = table containing oncoming signals, in order of appearance on the path + { + pos = position of the signal (not the IP!) + idx = where this is on the path + spd = speed allowed to pass (determined dynamically) + } +} +each step, for every item in "oncoming", we need to determine the location to start braking (+ some safety margin) +and, if we passed this point for at least one of the items, initiate brake. +When speed has dropped below, say 3, decrease the margin to zero, so that trains actually stop at the signal IP. +The spd variable and travsht need to be updated on every aspect change. it's probably best to reset everything when any aspect changes + +The traverser stops at signals that result in spd==0, because changes beyond there are likely. +]] + +local il = advtrains.interlocking + +local BRAKE_SPACE = 10 +local AWARE_ZONE = 50 + +local ADD_STAND = 2 +local ADD_SLOW = 1 +local ADD_FAST = 10 + +local SHUNT_SPEED_MAX = 4 + +local function look_ahead(id, train) + + local acc = advtrains.get_acceleration(train, 1) + local vel = train.velocity + local brakedst = -(vel*vel) / (2*acc) + + local brake_i = advtrains.path_get_index_by_offset(train, train.index, brakedst + BRAKE_SPACE) + --local aware_i = advtrains.path_get_index_by_offset(train, brake_i, AWARE_ZONE) + + local lzb = train.lzb + local trav = lzb.trav + local travspd = lzb.travspd + local travwspd = lzb.travwspd + local lspd + + train.debug = lspd + + while trav <= brake_i and (not lspd or lspd>0) do + trav = trav + 1 + local pos = advtrains.path_get(train, trav) + local pts = advtrains.roundfloorpts(pos) + local cn = train.path_cn[trav] + -- check offtrack + if trav > train.path_trk_f then + lspd = 0 + table.insert(lzb.oncoming, { + idx = trav-1, + spd = 0, + }) + else + -- check for signal + local asp, spos = il.db.get_ip_signal_asp(pts, cn) + --atdebug("trav: ",pos, cn, asp, spos, "travsht=", lzb.travsht) + if asp then + local nspd = 0 + --interpreting aspect and determining speed to proceed + if lzb.travsht then + --shunt move + if asp.shunt.free then + nspd = SHUNT_SPEED_MAX + elseif asp.shunt.proceed_as_main and asp.main.free then + nspd = asp.main.speed + lzb.travsht = false + end + else + --train move + if asp.main.free then + nspd = asp.main.speed + elseif asp.shunt.free then + nspd = SHUNT_SPEED_MAX + lzb.travsht = true + end + end + -- nspd can now be: 1. !=0: new speed restriction, 2. =0: stop here or 3. nil: keep travspd + if nspd then + if nspd == -1 then + travspd = nil + else + travspd = nspd + end + end + + local nwspd = asp.info.w_speed + if nwspd then + if nwspd == -1 then + travwspd = nil + else + travwspd = nwspd + end + end + --atdebug("ns,wns,ts,wts", nspd, nwspd, travspd, travwspd) + lspd = travspd + if travwspd and (not lspd or lspd>travwspd) then + lspd = travwspd + end + + table.insert(lzb.oncoming, { + pos = spos, + idx = trav, + spd = lspd, + }) + -- TODO register aspect change callback! + end + end + end + + lzb.trav = trav + lzb.travspd = travspd + lzb.travwspd = travwspd + +end + +--[[ +Distance needed to accelerate from v0 to v1 with constant acceleration a: + + v1 - v0 a / v1 - v0 \ 2 +s = v0 * ------- + - * | ------- | + a 2 \ a / +]] + +local function apply_control(id, train) + local lzb = train.lzb + + local i = 1 + while i<#lzb.oncoming do + if lzb.oncoming[i].idx < train.index then + train.speed_restriction = lzb.oncoming[i].spd + table.remove(lzb.oncoming, i) + else + i = i + 1 + end + end + + for i, it in ipairs(lzb.oncoming) do + local a = advtrains.get_acceleration(train, 1) --should be negative + local v0 = train.velocity + local v1 = it.spd + if v1 and v1 <= v0 then + local f = (v1-v0) / a + local s = v0*f + a*f*f/2 + + local st = s + ADD_SLOW + if v0 > 3 then + st = s + ADD_FAST + end + if v0<=0 then + st = s + ADD_STAND + end + + local i = advtrains.path_get_index_by_offset(train, it.idx, -st) + + --train.debug = dump({v0f=v0*f, aff=a*f*f,v0=v0, v1=v1, f=f, a=a, s=s, st=st, i=i, idx=train.index}) + if i <= train.index then + -- Gotcha! Braking... + train.ctrl.lzb = 1 + --train.debug = train.debug .. "BRAKE!!!" + return + end + end + end + train.ctrl.lzb = nil +end + + +advtrains.te_register_on_new_path(function(id, train) + train.lzb = { + trav = atfloor(train.index), + travsht = train.is_shunt, + oncoming = {} + } + train.ctrl.lzb = nil + look_ahead(id, train) +end) + +advtrains.te_register_on_update(function(id, train) + look_ahead(id, train) + apply_control(id, train) +end) diff --git a/advtrains_interlocking/train_related.lua b/advtrains_interlocking/train_related.lua deleted file mode 100644 index 8bc9716..0000000 --- a/advtrains_interlocking/train_related.lua +++ /dev/null @@ -1,183 +0,0 @@ --- train_related.lua --- Occupation of track sections - mainly implementation of train callbacks - ---[[ -Track section occupation is saved as follows - -In train: -train.il_sections = { - [n] = {ts_id = <...> (origin = )} -} --- "origin" is the TCB (signal describer) the train initially entered this section - -In track section -ts.trains = { - [n] = -} - -When any inconsistency is detected, we will assume the most restrictive setup. -It will be possible to indicate a section "free" via the GUI. -]] - -local ildb = advtrains.interlocking.db - - -local function itexist(tbl, com) - for _,item in ipairs(tbl) do - if (item==com) then - return true - end - end - return false -end -local function itkexist(tbl, ikey, com) - for _,item in ipairs(tbl) do - if item[ikey] == com then - return true - end - end - return false -end - -local function itremove(tbl, com) - local i=1 - while i <= #tbl do - if tbl[i] == com then - table.remove(tbl, i) - else - i = i + 1 - end - end -end -local function itkremove(tbl, ikey, com) - local i=1 - while i <= #tbl do - if tbl[i][ikey] == com then - table.remove(tbl, i) - else - i = i + 1 - end - end -end - -local function setsection(tid, train, ts_id, ts, origin) - -- train - if not train.il_sections then train.il_sections = {} end - if not itkexist(train.il_sections, "ts_id", ts_id) then - table.insert(train.il_sections, {ts_id = ts_id, origin = origin}) - end - - -- ts - if not ts.trains then ts.trains = {} end - if not itexist(ts.trains, tid) then - table.insert(ts.trains, tid) - end - - -- route setting - clear route state - if ts.route then - if ts.route.first then - -- this is the first route section. clear route status from origin sigd - local tcbs = advtrains.interlocking.db.get_tcbs(ts.route.origin) - if tcbs then - tcbs.route_committed = nil - tcbs.aspect = nil - advtrains.interlocking.update_signal_aspect(tcbs) - if tcbs.route_auto then - advtrains.interlocking.route.update_route(ts.route.origin, tcbs) - else - tcbs.routeset = nil - end - end - end - ts.route = nil - end - -end - -local function freesection(tid, train, ts_id, ts) - -- train - if not train.il_sections then train.il_sections = {} end - itkremove(train.il_sections, "ts_id", ts_id) - - -- ts - if not ts.trains then ts.trains = {} end - itremove(ts.trains, tid) - - if ts.route_post then - advtrains.interlocking.route.free_route_locks(ts_id, ts.route_post.locks) - if ts.route_post.next then - --this does nothing when the train went the right way, because - -- "route" info is already cleared. - advtrains.interlocking.route.cancel_route_from(ts.route_post.next) - end - ts.route_post = nil - end - -- This must be delayed, because this code is executed in-between a train step - -- TODO use luaautomation timers? - minetest.after(0, advtrains.interlocking.route.update_waiting, "ts", ts_id) -end - - --- This is regular operation --- The train is on a track and drives back and forth - --- This sets the section for both directions, to be failsafe -advtrains.tnc_register_on_enter(function(pos, id, train, index) - local tcb = ildb.get_tcb(pos) - if tcb then - for connid=1,2 do - local ts = tcb[connid].ts_id and ildb.get_ts(tcb[connid].ts_id) - if ts then - setsection(id, train, tcb[connid].ts_id, ts, {p=pos, s=connid}) - end - end - end -end) - - --- this time, of course, only clear the backside (cp connid) -advtrains.tnc_register_on_leave(function(pos, id, train, index) - local tcb = ildb.get_tcb(pos) - if tcb and train.path_cp[index] then - local connid = train.path_cp[index] - local ts = tcb[connid].ts_id and ildb.get_ts(tcb[connid].ts_id) - if ts then - freesection(id, train, tcb[connid].ts_id, ts) - end - end -end) - --- those callbacks are needed to account for created and removed trains (also regarding coupling) - -advtrains.te_register_on_create(function(id, train) - -- let's see what track sections we find here - local index = atround(train.index) - local pos = advtrains.path_get(train, index) - local ts_id, origin = ildb.get_ts_at_pos(pos) - if ts_id then - local ts = ildb.get_ts(ts_id) - if ts then - setsection(id, train, ts_id, ts, origin) - else - atwarn("ILDB corruption: TCB",origin," has invalid TS reference") - end - elseif ts_id==nil then - atwarn("Train",id,": Unable to determine whether to block a track section!") - else - --atdebug("Train",id,": Outside of interlocked area!") - end -end) - -advtrains.te_register_on_remove(function(id, train) - if train.il_sections then - for idx, item in ipairs(train.il_sections) do - - local ts = item.ts_id and ildb.get_ts(item.ts_id) - - if ts and ts.trains then - itremove(ts.trains, id) - end - end - train.il_sections = nil - end -end) diff --git a/advtrains_interlocking/train_sections.lua b/advtrains_interlocking/train_sections.lua new file mode 100644 index 0000000..8bc9716 --- /dev/null +++ b/advtrains_interlocking/train_sections.lua @@ -0,0 +1,183 @@ +-- train_related.lua +-- Occupation of track sections - mainly implementation of train callbacks + +--[[ +Track section occupation is saved as follows + +In train: +train.il_sections = { + [n] = {ts_id = <...> (origin = )} +} +-- "origin" is the TCB (signal describer) the train initially entered this section + +In track section +ts.trains = { + [n] = +} + +When any inconsistency is detected, we will assume the most restrictive setup. +It will be possible to indicate a section "free" via the GUI. +]] + +local ildb = advtrains.interlocking.db + + +local function itexist(tbl, com) + for _,item in ipairs(tbl) do + if (item==com) then + return true + end + end + return false +end +local function itkexist(tbl, ikey, com) + for _,item in ipairs(tbl) do + if item[ikey] == com then + return true + end + end + return false +end + +local function itremove(tbl, com) + local i=1 + while i <= #tbl do + if tbl[i] == com then + table.remove(tbl, i) + else + i = i + 1 + end + end +end +local function itkremove(tbl, ikey, com) + local i=1 + while i <= #tbl do + if tbl[i][ikey] == com then + table.remove(tbl, i) + else + i = i + 1 + end + end +end + +local function setsection(tid, train, ts_id, ts, origin) + -- train + if not train.il_sections then train.il_sections = {} end + if not itkexist(train.il_sections, "ts_id", ts_id) then + table.insert(train.il_sections, {ts_id = ts_id, origin = origin}) + end + + -- ts + if not ts.trains then ts.trains = {} end + if not itexist(ts.trains, tid) then + table.insert(ts.trains, tid) + end + + -- route setting - clear route state + if ts.route then + if ts.route.first then + -- this is the first route section. clear route status from origin sigd + local tcbs = advtrains.interlocking.db.get_tcbs(ts.route.origin) + if tcbs then + tcbs.route_committed = nil + tcbs.aspect = nil + advtrains.interlocking.update_signal_aspect(tcbs) + if tcbs.route_auto then + advtrains.interlocking.route.update_route(ts.route.origin, tcbs) + else + tcbs.routeset = nil + end + end + end + ts.route = nil + end + +end + +local function freesection(tid, train, ts_id, ts) + -- train + if not train.il_sections then train.il_sections = {} end + itkremove(train.il_sections, "ts_id", ts_id) + + -- ts + if not ts.trains then ts.trains = {} end + itremove(ts.trains, tid) + + if ts.route_post then + advtrains.interlocking.route.free_route_locks(ts_id, ts.route_post.locks) + if ts.route_post.next then + --this does nothing when the train went the right way, because + -- "route" info is already cleared. + advtrains.interlocking.route.cancel_route_from(ts.route_post.next) + end + ts.route_post = nil + end + -- This must be delayed, because this code is executed in-between a train step + -- TODO use luaautomation timers? + minetest.after(0, advtrains.interlocking.route.update_waiting, "ts", ts_id) +end + + +-- This is regular operation +-- The train is on a track and drives back and forth + +-- This sets the section for both directions, to be failsafe +advtrains.tnc_register_on_enter(function(pos, id, train, index) + local tcb = ildb.get_tcb(pos) + if tcb then + for connid=1,2 do + local ts = tcb[connid].ts_id and ildb.get_ts(tcb[connid].ts_id) + if ts then + setsection(id, train, tcb[connid].ts_id, ts, {p=pos, s=connid}) + end + end + end +end) + + +-- this time, of course, only clear the backside (cp connid) +advtrains.tnc_register_on_leave(function(pos, id, train, index) + local tcb = ildb.get_tcb(pos) + if tcb and train.path_cp[index] then + local connid = train.path_cp[index] + local ts = tcb[connid].ts_id and ildb.get_ts(tcb[connid].ts_id) + if ts then + freesection(id, train, tcb[connid].ts_id, ts) + end + end +end) + +-- those callbacks are needed to account for created and removed trains (also regarding coupling) + +advtrains.te_register_on_create(function(id, train) + -- let's see what track sections we find here + local index = atround(train.index) + local pos = advtrains.path_get(train, index) + local ts_id, origin = ildb.get_ts_at_pos(pos) + if ts_id then + local ts = ildb.get_ts(ts_id) + if ts then + setsection(id, train, ts_id, ts, origin) + else + atwarn("ILDB corruption: TCB",origin," has invalid TS reference") + end + elseif ts_id==nil then + atwarn("Train",id,": Unable to determine whether to block a track section!") + else + --atdebug("Train",id,": Outside of interlocked area!") + end +end) + +advtrains.te_register_on_remove(function(id, train) + if train.il_sections then + for idx, item in ipairs(train.il_sections) do + + local ts = item.ts_id and ildb.get_ts(item.ts_id) + + if ts and ts.trains then + itremove(ts.trains, id) + end + end + train.il_sections = nil + end +end) diff --git a/advtrains_luaautomation/atc_rail.lua b/advtrains_luaautomation/atc_rail.lua index d001d88..1fab620 100644 --- a/advtrains_luaautomation/atc_rail.lua +++ b/advtrains_luaautomation/atc_rail.lua @@ -50,9 +50,7 @@ function r.fire_event(pos, evtdata) atc_send = function(cmd) if not train_id then return false end assertt(cmd, "string") - advtrains.atc.train_reset_command(train_id) - train.atc_command=cmd - train.atc_arrow=atc_arrow + advtrains.atc.train_set_command(train, cmd, atc_arrow) return true end, set_line = function(line) @@ -62,7 +60,7 @@ function r.fire_event(pos, evtdata) atc_reset = function(cmd) if not train_id then return false end assertt(cmd, "string") - advtrains.atc.train_reset_command(train_id) + advtrains.atc.train_reset_command(train) return true end, atc_arrow = atc_arrow, -- cgit v1.2.3