summaryrefslogtreecommitdiff
path: root/far/init_code.lua
diff options
context:
space:
mode:
Diffstat (limited to 'far/init_code.lua')
-rw-r--r--far/init_code.lua531
1 files changed, 531 insertions, 0 deletions
diff --git a/far/init_code.lua b/far/init_code.lua
index e6fbcb5..15dfbf8 100644
--- a/far/init_code.lua
+++ b/far/init_code.lua
@@ -56,6 +56,10 @@ F.send_route = function(passive_name, route, show_print)
return return_value
end
+--[[
+F.save_train(POS(26201,24,1417), "east")
+]]
+
F.save_train = function(pos, direction)
if event.train then
if not atc_id then return end
@@ -214,3 +218,530 @@ F.slow_train_down = function(id)
F.print("Train ID " .. id .. " is slowed down to B1")
end
end
+
+-- init code for FAR timetable env
+
+-- stop, scheduled departure every
+-- d_int: Departure every n seconds (epoch modulo)
+-- d_off: Departure time offset
+function F.stop_sd(st_name, doors, departcommand, minstoptime, d_int, d_off)
+ if event.train then
+ local timenow = os.time()
+ local timerdy = timenow + minstoptime
+ local wait = d_int - ((timerdy-d_off) % d_int)
+ local waitcorr = math.floor(wait*0.66)
+ digiline_send("monitor", "Departure scheduled for: | "..os.date("%H:%M:%S", timenow+wait))
+ atc_send("B0 W O"..doors.." D"..waitcorr.." OCD1"..departcommand)
+ else
+ local timenow = os.time()
+ digiline_send("monitor", "Time: "..os.date("%H:%M:%S", timenow))
+ end
+end
+
+function F.stop_sd_sched(st_name, doors, departcommand, minstoptime, d_int, d_off)
+ depart = false
+ if event.train then
+ local time_now = rwt.now()
+ local next_dep_time = rwt.next_rpt(rwt.add(time_now, minstoptime), d_int, d_off)
+ digiline_send("monitor", "Departure scheduled for: | "..rwt.to_string(next_dep_time, true))
+ atc_set_text_inside(st_name.."\nDeparture: "..rwt.to_string(next_dep_time, true))
+ atc_send("B0 W O"..doors)
+ schedule(next_dep_time, "depart")
+ elseif event.schedule then
+ atc_send("OCD1"..departcommand)
+ digiline_send("monitor", "Last Departure: | "..rwt.to_string(rwt.now(), true))
+ atc_set_text_inside("")
+ depart = true
+ end
+end
+
+function F.timedisplay()
+ digiline_send("time", "Time: | "..rwt.to_string(rwt.now(),true).." | "..os.date("%H:%M:%S"))
+ schedule(rwt.next_rpt(rwt.now(),5,0), "")
+end
+
+-- Stat counter and timetaking utilities
+-- Stat from subway
+F.stat=function(line, init)
+ -- statistics
+ -- init
+ if init then
+ reftrain = atc_id
+ a_tbt = 30
+ a_tbtmax = 30
+ a_rtt = 500
+ a_not = 0
+ c_not = 0
+ c_tbtmax = 0
+ time_lt = os.time()
+ time_rt=os.time()
+ end
+ if not a_tbtmax then a_tbtmax = 30 end
+ if not c_tbtmax then c_tbtmax = 0 end
+ --real code
+ if event.train then
+ local time = os.time()
+ c_not = c_not + 1
+ a_tbt = (a_tbt + (time - time_lt)) / 2
+ c_tbtmax = math.max(c_tbtmax, (time - time_lt))
+ if atc_id == reftrain then
+ a_rtt = (a_rtt*0.2 + (time - time_rt)*0.8)
+ a_not = c_not
+ c_not = 0
+ a_tbtmax = (a_tbtmax + c_tbtmax) / 2
+ c_tbtmax = 0
+ end
+ digiline_send("stats", "Stat: "..line..
+ " NoT:"..a_not.."("..c_not..")"..
+ " TbT:"..math.floor(a_tbt).."("..(time-time_lt)..")"..
+ " Tmx:"..math.floor(a_tbtmax).."("..c_tbtmax..")"..
+ " R:"..math.floor(a_rtt).."("..(time - time_rt)..")"
+ )
+ time_lt = time
+ if atc_id == reftrain then
+ time_rt = time
+ end
+ end
+end
+
+S.timetake = {}
+function F.timetake_start(ttname)
+ if not atc_id then return end
+ local nouw = rwt.to_secs(rwt.now())
+ if not S.timetake[ttname] then
+ S.timetake[ttname] = {}
+ end
+ S.timetake[ttname][atc_id] = nouw
+end
+
+--L100
+function F.timetake_end(ttname)
+ if not atc_id then return end
+ if not S.timetake[ttname] or not S.timetake[ttname][atc_id] then
+ digiline_send("timetake", "No start time for "..atc_id)
+ return
+ end
+ local first = S.timetake[ttname][atc_id]
+ local nouw = rwt.to_secs(rwt.now())
+ local tdiff = nouw - first
+ local cavg = S.timetake[ttname].avg
+ local cmax = S.timetake[ttname].max
+ local cmin = S.timetake[ttname].min
+ if cavg and cmax and cmin then
+ S.timetake[ttname].avg = tdiff*0.1 + cavg*0.9
+ S.timetake[ttname].min = math.min(tdiff, cmin)
+ S.timetake[ttname].max = math.max(tdiff, cmax)
+ else
+ S.timetake[ttname].avg = tdiff
+ S.timetake[ttname].min = tdiff
+ S.timetake[ttname].max = tdiff
+ end
+ digiline_send("timetake", ttname..
+ " this:"..tdiff..
+ " min:"..math.floor(S.timetake[ttname].min)..
+ " avg:"..math.floor(S.timetake[ttname].avg)..
+ " max:"..math.floor(S.timetake[ttname].max)
+ )
+end
+
+--== Timetable prototype (TTP) ===
+--[[ table structures:
+F.ttp - static timetable data - see below
+S.ttp[tt_name] = { - dynamic tt data
+ recording_train = <id of the train that is recording travel times, or nil>
+ travel_times = {
+ <station name> = <time in seconds from initial departure at first station of the line to departure at this station>
+ }
+ station_order = { <station 1>, <station 2>...}
+}
+S.ttt[train_id] = { - trains
+ timetable = <timetable ID that the train is currently using>,
+ initial_dep = <departure at first station of the line>,
+ location = <Station where the train was last seen>,
+ desired_dep = <Departure time as in timetable>,
+ planned_dep = <real departure time calculated as the train reaches station>,
+ actual_dep = <actual departure time at last station. is nil while train is stopped>,
+ last_delay = <last known delay of the train - calculated every departure>,
+ }
+}
+]]
+
+local STOP_TIME = 10
+local STOPCMD="B0WO"
+local DEPCMD="A1OCD1SM"
+local RDEPCMD="RA1OCD1SM"
+local DYNAMIC_THR = 10
+local DYNAMIC_EN = false
+
+if not S.ttp then S.ttp = {} end
+if not S.ttt then S.ttt = {} end
+F.ttp={
+ FAR_E = {
+ outside_text = "[FAR] Fareast End\nvia Halfway, Bayonne, Fucking",
+ inside_line_desc = "FAR to Fareast End",
+ stn_display = "FAR Fareast End",
+ },
+ FAR_W = {
+ outside_text = "[FAR] Salt Factory\nvia Fucking, Bayonne, Halfway",
+ inside_line_desc = "FAR to Salt Factory",
+ stn_display = "FAR Salt Factory",
+ },
+}
+
+--[[
+Timetable entry point. The train finalizes its last timetable and
+registers itself on the given timetable instance. It departs at the next time slot
+(given by interval and offset).
+F.ttp_begin({
+ stn = "Warmoneaye", -- station name
+ tt = "CFE_S", -- timetable ID
+ depint = "05;00", --departure slot interval
+ depoff = "00;00", --departure slot offset
+ doorside = "L",
+ reverse = true,
+ only_lines = nil, --if given a table, only trains where only_lines[get_line()] is true are considered
+ force_tt_reset = false, -- force reset of travel times for this timetable
+})
+]]
+-- Make train depart at the next time slot, and save its start time
+function F.ttp_begin(p)
+ __approach_callback_mode = 1
+
+ if not F.ttp[p.tt] then error("No TT instance "..p.tt) end
+ if not atc_id then
+ print(p.stn,"missing train!",event)
+ return
+ end
+ if not atc_arrow then return end
+ if p.only_lines and not p.only_lines[get_line()] then return end
+ if not S.ttp[p.tt] then S.ttp[p.tt] = {} end
+ local tti = S.ttp[p.tt]
+ --L150
+ if event.approach and not event.has_entered then
+ -- make the train stop
+ atc_set_ars_disable(true)
+ atc_set_lzb_tsr(2)
+ atc_set_text_inside("Next stop: "..p.stn.."\nTerminal Station.\nThis train continues as "..F.ttp[p.tt].inside_line_desc)
+ end
+ if event.train then
+ -- train arrived, planning departure
+ atc_send(STOPCMD .. p.doorside)
+
+ local time_now = rwt.now()
+ -- Train might have had another TT before, do the cleanup from ttp_end here.
+ local trno = S.ttt[atc_id]
+ if trno then
+ local ttio = S.ttp[trno.timetable]
+ if ttio.recording_train == atc_id then
+ ttio.travel_times[p.stn] = rwt.diff(trno.initial_dep, time_now)
+ ttio.station_order[#ttio.station_order+1] = p.stn
+ print(atc_id,"for",p.tt,"at",p.stn,"-> travel time",rwt.to_string(ttio.travel_times[p.stn]),"-route end")
+ end
+ end
+
+ local next_dep_time = rwt.next_rpt(rwt.add(time_now, 10), p.depint, p.depoff)
+ schedule(next_dep_time, "departure")
+ S.ttt[atc_id] = {
+ timetable = p.tt,
+ initial_dep = next_dep_time,
+ location = p.stn,
+ desired_dep = next_dep_time,
+ planned_dep = next_dep_time,
+ last_delay = 0,
+ }
+ -- if no travel times are available yet, set this train as recording
+ if not tti.travel_times or p.force_tt_reset or tti.force_tt_reset then
+ tti.travel_times = {}
+ tti.station_order = {p.stn}
+ tti.recording_train = atc_id
+ tti.force_tt_reset = false
+ print(atc_id,"starting TT recording for",p.tt)
+ elseif tti.recording_train == atc_id then
+ tti.recording_train = nil
+ end
+ atc_set_text_outside(F.ttp[p.tt].outside_text)
+ atc_set_text_inside(p.stn.."\nArr: "
+ ..rwt.to_string(time_now, true).." Dep: "
+ ..rwt.to_string(next_dep_time, true))
+ end
+ if event.schedule then
+ -- departure. save actual departure time in tt
+ if S.ttt[atc_id] then -- failsafe: if entry is deleted externally somehow, train just departs and is not tracked by tt (makes resetting S.ttt possible)
+ S.ttt[atc_id].actual_dep = rwt.now()
+ local delay = rwt.diff(S.ttt[atc_id].desired_dep, S.ttt[atc_id].actual_dep)
+ atc_set_text_inside(F.ttp[p.tt].inside_line_desc .. "\nDelay: " .. rwt.to_string(delay, true))
+ S.ttt[atc_id].last_delay = delay
+ end
+ if p.reverse then
+ atc_send(RDEPCMD)
+ else
+ atc_send(DEPCMD)
+ end
+ end
+end
+--[[
+Generic stop on timetable. Any train that has a TT instance registered
+stops here, waits STOP_TIME and continues. Behavior can be altered by options:
+F.ttp_stop({
+ stn = "Personhood West", -- station name
+ doorside = "L",
+ only_lines = nil, --if given a table, only trains where only_lines[get_line()] is true are considered
+ end_of_tt = { TT_ID = true },
+ -- if present and key is true for a TT identifier, this is the last station on this timetable. Trains will stop recording timetable and be deregistered.
+ departure = { TT_ID = RWT relative to initial departure },
+ -- If present, override desired departure time. Defaults to travel time + STOP_TIME if not provided
+ no_disable_ars = nil,
+ -- if true, does not disable ARS on approach (used for example at INTERCAL)
+})
+]]
+function F.ttp_stop(p)
+ -- set my approach callback mode
+ __approach_callback_mode = 1
+ if not atc_id then
+ print(p.stn,"missing train!",event)
+ return
+ end
+ if not S.ttt[atc_id] then return end
+ if p.only_lines and not p.only_lines[get_line()] then return end
+ local trn = S.ttt[atc_id]
+ local tt = trn.timetable
+ if not F.ttp[tt] then
+ S.ttt[atc_id] = nil
+ end
+ local tti = S.ttp[tt]
+ if event.approach and not event.has_entered then
+ -- make the train stop
+ if not p.no_disable_ars then
+ atc_set_ars_disable(true)
+ end
+ atc_set_lzb_tsr(2)
+ atc_set_text_inside("Next stop: "..p.stn)
+ end
+
+--!-- disaster recovery --!--
+-- if event.approach and event.has_entered then
+-- print(atc_id,p.stn,"Disaster Recovery...")
+-- atc_send(DEPCMD)
+-- end
+
+
+ if event.train then
+ -- train arrived, planning departure
+ atc_send(STOPCMD..p.doorside)
+ local time_now = rwt.now()
+ -- update our location and determine desired and planned departure
+--L200
+ local next_dep_time = rwt.add(time_now, STOP_TIME)
+ trn.location = p.stn
+ trn.desired_dep = nil
+ trn.actual_dep = nil
+
+ -- calculate desired departure nouw
+ if p.departure and p.departure[tt] then
+ trn.desired_dep = rwt.add(trn.initial_dep or 0,
+ p.departure[tt])
+ elseif tti.travel_times[p.stn] then
+ trn.desired_dep = rwt.add(trn.initial_dep or 0,
+ tti.travel_times[p.stn] + STOP_TIME)
+ -- dyn travel time
+ if DYNAMIC_EN then
+ local ttpd = rwt.diff(next_dep_time, trn.desired_dep)
+ if ttpd > DYNAMIC_THR then
+ local new_trav = rwt.diff(trn.initial_dep, time_now) + DYNAMIC_THR
+ print(atc_id,tt,"arrived at",p.stn,ttpd,"s early, TT",tti.travel_times[p.stn],"->",new_trav)
+ tti.travel_times[p.stn] = new_trav
+ trn.desired_dep = rwt.add(trn.initial_dep or 0,
+ new_trav + STOP_TIME)
+ end
+ end
+ end
+
+ if trn.desired_dep then
+ -- if we had a source for desired departure, update planned daparture time
+ if rwt.to_secs(next_dep_time) < rwt.to_secs(trn.desired_dep) then
+ -- don't depart before the planned departure time
+ next_dep_time = trn.desired_dep
+ end
+ atc_set_text_inside(p.stn.."\nArr: "
+ ..rwt.to_string(time_now, true).." Plan: "
+ ..rwt.to_string(trn.desired_dep, true).." Dep: "
+ ..rwt.to_string(next_dep_time, true))
+
+ local delay = rwt.diff(trn.desired_dep, next_dep_time)
+ trn.last_delay = delay
+ else
+ atc_set_text_inside(p.stn.."\nAa "
+ ..rwt.to_string(time_now, true).." Dd ? Da"
+ ..rwt.to_string(next_dep_time, true))
+ end
+
+ if tti.recording_train == atc_id then
+ -- we are recording. save travel time
+ tti.travel_times[p.stn] = rwt.diff(trn.initial_dep or 0, time_now)
+ print(atc_id,"for",tt,"at",p.stn,"-> travel time",rwt.to_string(tti.travel_times[p.stn]))
+ tti.station_order[#tti.station_order+1] = p.stn
+ atc_set_text_inside(p.stn.."\nRec TT "
+ ..rwt.to_string(tti.travel_times[p.stn], true).." Da"
+ ..rwt.to_string(next_dep_time, true))
+ end
+
+ trn.planned_dep = next_dep_time
+ schedule(next_dep_time, "departure")
+ end
+ if event.schedule then
+ -- departure. save actual departure time in tt
+ trn.actual_dep = rwt.now()
+ local delay = rwt.diff(trn.desired_dep or trn.actual_dep, trn.actual_dep)
+ atc_set_text_inside(F.ttp[tt].inside_line_desc
+ .."\nDelay: "..rwt.to_string(delay, true))
+ S.ttt[atc_id].last_delay = delay
+ atc_send(DEPCMD)
+ if p.end_of_tt and p.end_of_tt[tt] then
+ -- end of timetable. Deregister train
+ if tti.recording_train == atc_id then
+ tti.recording_train = nil
+ end
+ S.ttt[atc_id] = nil
+ end
+ end
+end
+
+
+function F.ttp_info_times(tt, starttime)
+ --L307
+ local ttf = F.ttp[tt]
+ local tti = S.ttp[tt]
+ local p = {}
+ if tti.recording_train then
+ p[#p+1] = ("recording "..tti.recording_train)
+ end
+ p[#p+1] = ("Di "..rwt.to_string(starttime, false).." "..tti.station_order[1])
+ for i=2,#tti.station_order do
+ local ap = rwt.add(starttime, tti.travel_times[tti.station_order[i]])
+ p[#p+1] = ("Ap "..rwt.to_string(ap, true)..
+ " Dp "..rwt.to_string(rwt.add(ap, STOP_TIME), false)..
+ " "..tti.station_order[i])
+ end
+ return p
+end
+
+function F.ttp_info_trains(tt, starttime)
+ --L307
+ local ttf = F.ttp[tt]
+ local tti = S.ttp[tt]
+ local p = {}
+ for tid,trn in pairs(S.ttt) do
+ if trn.timetable==tt then
+ if trn.actual_dep then
+ p[#p+1] = ("Trn "..tid..
+ " after "..trn.location..
+ " Dd "..rwt.to_string(trn.desired_dep or 0, false)..
+ " Da "..rwt.to_string(trn.actual_dep, false)..
+ " Delay "..rwt.to_string(trn.last_delay or "59;59"))
+ else
+ p[#p+1] = ("Trn "..tid..
+ " at "..trn.location..
+ " Dd "..rwt.to_string(trn.desired_dep or 0, false)..
+ " Delay "..rwt.to_string(trn.last_delay or "59;59"))
+ end
+ end
+ end
+ return p
+end
+
+--[[F.ttp_station_display({
+ lines = {"CFE_S", "NX_S", "E1_S"},
+ departure = {},
+ station = "The Cube",
+ title = "The Cube (Track 2)",
+ interval = 30,
+ display1 = "d1",
+ display2 = "d2",
+ display3 = "d3",
+ show_trainid = false,
+}]]
+function F.ttp_station_display(p)
+ --L425
+ -- { dep, text }
+ local next_trains = {}
+ local function is_past_station(tstn, stnorder)
+ for _,s in ipairs(stnorder) do
+ if s==p.station then return true end
+ if s==tstn then return false end
+ end
+ return true
+ end
+
+ local function add_train(deptime, line, train, tid)
+ local tent = {dep = deptime, text =
+ rwt.to_string(deptime,true)
+ .." "..(p.show_trainid and tid.." " or "")
+ ..F.ttp[line].stn_display
+ .." +"..train.last_delay}
+ for i,ntrn in ipairs(next_trains) do
+ if rwt.diff(ntrn.dep, deptime)<0 then
+ table.insert(next_trains, i, tent)
+ return
+ end
+ end
+ table.insert(next_trains, tent)
+ end
+
+ for _,line in ipairs(p.lines) do
+ local fttp = F.ttp[line]
+ local sttp = S.ttp[line]
+ -- find all trains on this line
+ for id, train in pairs(S.ttt) do
+ if train.timetable == line then
+ if train.location == p.station and not train.actual_dep then
+ -- the train is currently standing at this station
+ add_train(train.planned_dep, line, train, id)
+ elseif not is_past_station(train.location, sttp.station_order) then
+ -- train is still approaching, calculate arrival time
+ local trav_dep = rwt.add(train.initial_dep, (sttp.travel_times[p.station] or 0) + STOP_TIME)
+ local act_dep = rwt.add(trav_dep, train.last_delay)
+ if p.departure and p.departure[line] then
+ local plan_dep = rwt.add(train.initial_dep, p.departure[line])
+ if rwt.to_secs(act_dep) < rwt.to_secs(plan_dep) then
+ act_dep = plan_dep
+ end
+ end
+ add_train(act_dep, line, train, id)
+ end
+ end
+ end
+ end
+
+ -- make output
+ local i
+ local text1 = p.title .. " * "..rwt.to_string(rwt.now(), true).." * "
+ for i=1,3 do
+ if next_trains[i] then
+ text1 = text1 .. "\n".. next_trains[i].text
+ end
+ end
+
+ digiline_send(p.display1, text1)
+ if p.display2 then
+ local text2 = ""
+ for i=4,7 do
+ if next_trains[i] then
+ text2 = text2 .. next_trains[i].text .. "\n"
+ end
+ end
+ digiline_send(p.display2, text2)
+ end
+
+ if p.display3 then
+ local text3 = ""
+ for i=8,11 do
+ if next_trains[i] then
+ text3 = text3 .. next_trains[i].text .. "\n"
+ end
+ end
+ digiline_send(p.display3, text3)
+ end
+ --if not p.notimer then
+ -- schedule_in(p.interval or 30,"foo")
+ --end
+end