diff options
Diffstat (limited to 'far/init_code.lua')
-rw-r--r-- | far/init_code.lua | 531 |
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 |