diff options
Diffstat (limited to 'advtrains_line_automation/line_functions.lua')
-rw-r--r-- | advtrains_line_automation/line_functions.lua | 1377 |
1 files changed, 1377 insertions, 0 deletions
diff --git a/advtrains_line_automation/line_functions.lua b/advtrains_line_automation/line_functions.lua new file mode 100644 index 0000000..ee8f238 --- /dev/null +++ b/advtrains_line_automation/line_functions.lua @@ -0,0 +1,1377 @@ +local al = advtrains.lines +local rwt = assert(advtrains.lines.rwt) + +local MODE_NORMAL = 0 -- normální zastávka (výchozí nebo mezilehlá) +local MODE_REQUEST_STOP = 1 -- zastávka na znamení (mezilehlá) +local MODE_HIDDEN = 2 -- skrytá zastávka (výchozí nebo mezilehlá) +local MODE_DISABLED = 3 -- vypnutá zastávka (mezilehlá nebo koncová) +local MODE_FINAL = 4 -- koncová zastávka (linka zde jízdu končí) +local MODE_FINAL_HIDDEN = 5 -- koncová zastávka skrytá +local MODE_FINAL_CONTINUE = 6 -- koncová zastávka (vlak pokračuje jako jiná linka) + +local simple_modes = { + [MODE_NORMAL] = MODE_NORMAL, + [MODE_REQUEST_STOP] = MODE_NORMAL, + [MODE_HIDDEN] = MODE_NORMAL, + [MODE_DISABLED] = MODE_DISABLED, + [MODE_FINAL] = MODE_FINAL, + [MODE_FINAL_HIDDEN] = MODE_FINAL, + [MODE_FINAL_CONTINUE] = MODE_FINAL, +} + +local current_passages = {--[[ + [train_id] = { + [1] = rwtime, + ..., + [n] = rwtime (časy *odjezdu*, kromě koncových zastávek, kde jde o čas příjezdu) + wait = int or nil (původně naplánovaná doba čekání na výchozí zastávce) + } +]]} + +local last_passages = {--[[ + [linevar] = { + [1..10] = {[1] = rwtime, ..., wait} -- jízdy seřazeny od nejstarší (1) po nejnovější (až 10) podle odjezdu z výchozí zastávky + } +]]} + +local diakritika_na_velka = { + ["á"] = "Á", ["ä"] = "Ä", ["č"] = "Č", ["ď"] = "Ď", ["é"] = "É", ["ě"] = "Ě", ["í"] = "Í", ["ĺ"] = "Ĺ", ["ľ"] = "Ľ", + ["ň"] = "Ň", ["ó"] = "Ó", ["ô"] = "Ô", ["ŕ"] = "Ŕ", ["ř"] = "Ř", ["š"] = "Š", ["ť"] = "Ť", ["ú"] = "Ú", ["ů"] = "Ů", + ["ý"] = "Ý", ["ž"] = "Ž", +} + +local debug_print_i = 0 + +-- LOCAL funkce: +-- ========================================================================= +local function debug_print(s) + debug_print_i = debug_print_i + 1 + -- core.chat_send_all("["..debug_print_i.."] "..tostring(s)) + return s +end + +-- Je-li stn kód stanice, vrací její jméno, jinak vrací "???". stn může být i nil. +local function get_station_name(stn) + local station = advtrains.lines.stations[stn or ""] + if station ~= nil and station.name ~= nil then + return station.name + else + return "???" + end +end +al.get_station_name = get_station_name + +local function na_velka_pismena(s) + local l = #s + local i = 1 + local res = "" + local c + while i <= l do + c = diakritika_na_velka[s:sub(i, i + 1)] + if c then + res = res .. c + i = i + 2 + else + res = res .. s:sub(i, i) + i = i + 1 + end + end + return string.upper(res) +end + +--[[ + -- Vrací index následujícího výskytu 'stn' v seznamu zastávek podle linevar_def. + -- Vrací i skryté zastávky, ale ne vypnuté. + -- Je-li linevar_def == nil nebo stn == nil nebo stn == "", vrací nil. + - line_status = table + - linevar_def = table or nil + - stn = string + return: int or nil +]] +local function find_next_index(line_status, linevar_def, stn) + assert(line_status) + if linevar_def ~= nil and stn ~= nil and stn ~= "" then + local stops = linevar_def.stops + if line_status.linevar_index < #stops then + for i = line_status.linevar_index + 1, #stops do + local stop = stops[i] + if stop.stn == stn and (stop.mode or MODE_NORMAL) ~= MODE_DISABLED then + return i + end + end + end + end +end + +-- Vrací kódy výchozí a cílové stanice k zobrazení cestujícím: stn_first (string), stn_terminus (string) +-- Předané linevar_def musí obsahovat platný seznam 'stops', ale nemusí obsahovat nic jiného (nemusí to být platná definice). +-- V případě chyby vrací nil, nil. +local function get_first_last_stations(linevar_def) + local a, b = linevar_def.index_vychozi, linevar_def.index_cil + if a ~= nil and b ~= nil then + local stops = linevar_def.stops + return stops[a].stn, stops[b].stn + else + return nil, nil + end +end + +--[[ + Používá se na *nelinkový* vlak stojící na *neanonymní* zastávce. + Zahájí jízdu na lince nalezené podle kombinace line/stn/rc, nebo vrátí false. +]] +local function line_start(train, stn, departure_rwtime) + assert(train) + assert(stn and stn ~= "") + assert(departure_rwtime) + local ls = al.get_line_status(train) + if ls.linevar ~= nil then + error("line_start() used on a train that is already on a line: "..dump2({train = train})) + end + local linevar, linevar_def = al.try_get_linevar(train.line, stn, train.routingcode) + if linevar == nil or linevar_def.disabled then + return false + end + ls.linevar = linevar + ls.linevar_station = stn + ls.linevar_index = 1 + ls.linevar_dep = departure_rwtime + ls.linevar_last_dep = departure_rwtime + ls.linevar_last_stn = stn + ls.linevar_past = nil + train.text_outside = al.get_line_description(linevar_def, { + line_number = true, + last_stop = true, + last_stop_prefix = "", + last_stop_uppercase = true, + train_name = true, + }) + return true +end + +--[[ + Vrací: + - "true", pokud má vlak zastavit + - nil, pokud zastavit nemá + - "on_request", pokud má zastavit na znamení, ale znamení zatím nebylo dáno +]] +local function should_stop(pos, stdata, train) + if stdata == nil or stdata.stn == nil then + return nil -- neplatná data + end + local n_trainparts = #assert(train.trainparts) + -- vyhovuje počet vagonů? + if not ((stdata.minparts or 0) <= n_trainparts and n_trainparts <= (stdata.maxparts or 128)) then + return nil + end + local stn = assert(stdata.stn) -- zastávka stále může být anonymní + local ls, linevar_def = al.get_line_status(train) + local next_index + if stn ~= "" then + next_index = find_next_index(ls, linevar_def, stn) + end + -- jde o linkový vlak? + if linevar_def ~= nil then + if next_index == nil then + return nil + end + local stop = assert(linevar_def.stops[next_index]) + if stop.pos ~= nil and stop.pos ~= string.format("%d,%d,%d", pos.x, pos.y, pos.z) then + return nil + end + if stop.mode ~= nil and stop.mode == MODE_REQUEST_STOP then + if ls.stop_request ~= nil then + debug_print("Vlak "..train.id.." zastaví na zastávce na znamení.") + return "true" + else + debug_print("Vlak "..train.id.." možná zastaví na zastávce na znamení.") + return "on_request" + end + end + return "true" + -- TODO: zastávky na znamení + else + local ars = stdata.ars + -- vyhovuje vlak ARS pravidlům? + local result = ars and (ars.default or advtrains.interlocking.ars_check_rule_match(ars, train)) + if result then + return "true" + else + return nil + end + end +end + +local function update_value(train, key, setting) + if setting == "-" then + train[key] = nil + elseif setting ~= "" then + train[key] = setting + end + return train[key] +end + +--[[ + Pokud vlak jede na lince, odebere ho z této linky a vrátí true; jinak vrátí false. +]] +function al.cancel_linevar(train) + local ls = train.line_status + if ls == nil or ls.linevar == nil then return false end + ls.linevar = nil + ls.linevar_station = nil + ls.linevar_index = nil + ls.linevar_dep = nil + ls.linevar_last_dep = nil + ls.linevar_last_stn = nil + train.text_outside = "" + return true +end + +-- Vrací seznam názvů neskrytých zastávek, počínaje od uvedeného indexu. +-- Pokud by vrátila prázdný seznam, vrátí místo něj {"???"}. +function al.get_stop_names(linevar_def, start_index) + local stations = advtrains.lines.stations + local result = {} + if linevar_def ~= nil then + local stops = linevar_def.stops + if start_index == nil then + start_index = 1 + end + for i = start_index, #stops do + local stop = stops[i] + if stop.mode == nil or (stop.mode ~= MODE_HIDDEN and stop.mode ~= MODE_FINAL_HIDDEN and stop.mode ~= MODE_DISABLED) then + local station = stations[stop.stn] + if station ~= nil and station.name ~= nil and station.name ~= "" then + table.insert(result, station.name) + else + table.insert(result, "???") + end + end + end + end + if result[1] == nil then + result[1] = "???" + end + return result +end + +--[[ + Vrací: + a) index, stop_data -- pokud byla vyhovující předchozí zastávka nalezena + b) nil, nil -- pokud nalezena nebyla +]] +function al.get_first_stop(linevar_def, allow_hidden_stops) + if allow_hidden_stops then + return 1, linevar_def.stops[1] + else + local result = linevar_def.index_vychozi + if result ~= nil then + return result, linevar_def.stops[result] + else + return nil, nil + end + end +end + +--[[ + Vrací: + a) index, stop_data -- pokud byla vyhovující předchozí zastávka nalezena + b) nil, nil -- pokud nalezena nebyla +]] +function al.get_last_stop(linevar_def, allow_hidden_stops) + if allow_hidden_stops then + local stops = linevar_def.stops[result] + for i = linevar_def.index_cil, #stops do + local stop = stops[i] + local mode = stop.mode + if mode == MODE_FINAL or mode == MODE_FINAL_CONTINUE or mode == MODE_FINAL_HIDDEN then + return i, stop + end + end + else + local result = linevar_def.index_cil + if result ~= nil then + return result, linevar_def.stops[result] + end + end + return nil, nil +end + +--[[ + Vrací: + a) index, stop_data -- pokud byla vyhovující předchozí zastávka nalezena + b) nil, nil -- pokud nalezena nebyla +]] +function al.get_prev_stop(linevar_def, current_index, allow_hidden_stops) + local stops = assert(linevar_def.stops) + assert(current_index) + if current_index > 1 then + for i = current_index - 1, 1, -1 do + local mode = stops[i].mode + if mode == nil or (mode ~= MODE_DISABLED and ((mode ~= MODE_HIDDEN and mode ~= MODE_FINAL_HIDDEN) or allow_hidden_stops)) then + return i, stops[i] + end + end + end + return nil, nil +end + +--[[ + Vrací: + a) index, stop_data -- pokud byla vyhovující další zastávka nalezena + b) nil, nil -- pokud nalezena nebyla +]] +function al.get_next_stop(linevar_def, current_index, allow_hidden_stops) + local stops = assert(linevar_def.stops) + assert(current_index) + if current_index < #stops then + for i = current_index + 1, #stops do + local mode = stops[i].mode + if mode == nil or (mode ~= MODE_DISABLED and ((mode ~= MODE_HIDDEN and mode ~= MODE_FINAL_HIDDEN) or allow_hidden_stops)) then + return i, stops[i] + end + end + end + return nil, nil +end + +--[[ + Vrací: + a) index, stop_data -- pokud byla vyhovující koncová zastávka nalezena + b) nil, nil -- pokud nalezena nebyla +]] +function al.get_terminus(linevar_def, current_index, allow_hidden_stops) + if linevar_def.index_cil ~= nil then + return linevar_def.index_cil, linevar_def.stops[linevar_def.index_cil] + end + local stops = assert(linevar_def.stops) + local r_i, r_stop + if current_index < #stops then + for i = current_index + 1, #stops do + local mode = stops[i].mode or MODE_NORMAL + if mode ~= MODE_DISABLED and ((mode ~= MODE_HIDDEN and mode ~= MODE_FINAL_HIDDEN) or allow_hidden_stops) then + r_i, r_stop = i, stops[i] + end + if mode == MODE_FINAL or mode == MODE_FINAL_CONTINUE or mode == MODE_FINAL_HIDDEN then + break + end + end + end + return r_i, r_stop +end + +--[[ + options = { + line_number = bool or nil, -- zahrnout do popisu číslo linky? nil => false + first_stop = bool or nil, -- zahrnout do popisu název výchozí zastávky? nil => false + last_stop = bool or nil, -- zahrnout do popisu název cílové zastávky? nil => true + last_stop_prefix = string or nil, -- text před název cílové zastávky; nil => "⇒ " + last_stop_uppercase = bool or nil, -- je-li true, název cílové zastávky se před uvedením převede na velká písmena + train_name = bool or nil, -- zahrnout do popisu jméno vlaku, je-li k dispozici; nil => false + train_name_prefix = string or nil, -- text před jméno vlaku; nil => "\n" + } +]] +function al.get_line_description(linevar_def, options) + local line, stn = al.linevar_decompose(linevar_def.name) + local first_stn, last_stn = get_first_last_stations(linevar_def) + if line == nil or first_stn == nil or last_stn == nil then + return "???" + end + if options == nil then options = {} end + local s1, s2, s3, s4 + if options.line_number then + s1 = "["..line.."] " + else + s1 = "" + end + if first_stn == last_stn and options.first_stop and (options.last_stop == nil or options.last_stop) then + s2 = get_station_name(last_stn) + if options.last_stop_uppercase then + s2 = na_velka_pismena(s2) + end + s3 = " (okružní)" + else + if options.first_stop then + s2 = get_station_name(first_stn).." " + else + s2 = "" + end + if options.last_stop == nil or options.last_stop then + s3 = get_station_name(last_stn) + if options.last_stop_uppercase then + s3 = na_velka_pismena(s3) + end + s3 = (options.last_stop_prefix or "⇒ ")..s3 + else + s3 = "" + end + end + if options.train_name and linevar_def.train_name ~= nil then + s4 = (options.train_name_prefix or "\n")..linevar_def.train_name + else + s4 = "" + end + return s1..s2..s3..s4 +end + +function al.get_stop_description(stop_data, next_stop_data) + local s1, s2 = "", "" + if stop_data ~= nil then + local mode = stop_data.mode + if mode ~= MODE_DISABLED and mode ~= MODE_HIDDEN and mode ~= MODE_FINAL_HIDDEN then + s1 = get_station_name(stop_data.stn) + --[[ + if + mode == MODE_FINAL or mode == MODE_FINAL_CONTINUE or + (next_stop_data ~= nil and next_stop_data.mode == MODE_FINAL_HIDDEN) + then + s2 = "Koncová zastávka" + end + ]] + end + end + if next_stop_data ~= nil then + local mode = next_stop_data.mode + if mode ~= MODE_DISABLED and mode ~= MODE_HIDDEN and mode ~= MODE_FINAL_HIDDEN then + s2 = "Příští zastávka/stanice: "..get_station_name(next_stop_data.stn) + if mode == MODE_REQUEST_STOP then + s2 = s2.." (na znamení)" + end + end + else + s2 = "Koncová zastávka" + end + if s1 ~= "" and s2 ~= "" then + return s1.."\n"..s2 + else + return s1..s2 + end +end + +--[[ + Není-li zpoždění k dispozici, vrací {text = ""}, jinak vrací: + { + has_delay = bool, + delay = int, + text = string, + } +]] +function al.get_delay_description(line_status, linevar_def, rwtime) + assert(line_status) + if linevar_def == nil then + return {text = ""} + end + local stops = linevar_def.stops + local expected_departure = line_status.linevar_dep + assert(stops[line_status.linevar_index]).dep + local real_departure = line_status.linevar_last_dep + local delay = real_departure - expected_departure + + if rwtime ~= nil then + local expected_departure_next + for i = line_status.linevar_index + 1, #stops do + if stops[i] == nil then + break + else + local mode = stops[i].mode + if mode == nil or mode ~= MODE_DISABLED then + expected_departure_next = line_status.linevar_dep + stops[i].dep + break + end + end + end + if expected_departure_next ~= nil then + local delay2 = rwtime - expected_departure_next + if delay2 > delay then + delay = delay2 + end + end + end + + if delay < -1 or delay > 10 then + return { + has_delay = true, + delay = delay, + text = "zpoždění "..delay.." sekund", + } + else + return { + has_delay = false, + delay = delay, + text = "bez zpoždění", + } + end +end + +-- Test na módy MODE_FINAL* +function al.is_final_mode(stop_mode) + return stop_mode ~= nil and simple_modes[stop_mode] == MODE_FINAL +end +local is_final_mode = al.is_final_mode + +-- Test na skrytou zastávku. Vrací true, pokud zadaný mód neodpovídá skryté zastávce. +function al.is_visible_mode(stop_mode) + return stop_mode == nil or (stop_mode ~= MODE_HIDDEN and stop_mode ~= MODE_FINAL_HIDDEN) +end +local is_visible_mode = al.is_visible_mode + +-- Pokud zadaná varianta linky existuje, vrátí: +-- linevar_def, linevar_station +-- Jinak vrací: +-- nil, nil +-- Je-li linevar_station == nil, doplní se z linevar. Je-li linevar == nil, vrátí nil, nil. +function al.try_get_linevar_def(linevar, linevar_station) + if linevar == nil then + return nil, nil + end + if linevar_station == nil then + local line + line, linevar_station = al.linevar_decompose(linevar) + if line == nil then + return nil, nil + end + end + local t = advtrains.lines.stations[linevar_station] + if t ~= nil then + t = t.linevars + if t ~= nil then + return t[linevar], linevar_station + end + end + return nil, nil +end + +--[[ + Vrací: + a) nil, nil, pokud daná kombinace line/stn/rc nemá definovanou variantu linky + b) linevar, linevar_def, pokud má +]] +function al.try_get_linevar(line, stn, rc) + if line ~= nil and line ~= "" and stn ~= nil and stn ~= "" then + local linevar = line.."/"..stn.."/"..(rc or "") + local result = al.try_get_linevar_def(linevar, stn) + if result ~= nil then + return linevar, result + end + end + return nil, nil +end + +--[[ + Vrací 2 hodnoty: + - line_status = table -- tabulka train.line_status (může být prázdná) + - linevar_def = table or nil -- jde-li o linkový vlak, vrací definici linevar, jinak nil + Parametry: + - train = table (train) +]] +function al.get_line_status(train) + assert(train) + if train.line_status == nil then + train.line_status = {} + return train.line_status, nil + end + local ls = train.line_status + if ls.linevar == nil then + -- nelinkový vlak + local linevar_past = ls.linevar_past + if linevar_past ~= nil then + local rwtime = rwt.to_secs(rwt.get_time()) + if train.line ~= linevar_past.line or rwtime - linevar_past.arrival >= 86400 then + -- smazat linevar_past, pokud se změnilo označení linky nebo uplynulo 24 hodin + ls.linevar_past = nil + end + end + return ls, nil + end + local rwtime = rwt.to_secs(rwt.get_time()) + if rwtime - ls.linevar_dep >= 86400 then + core.log("warning", "Train "..train.id.." put out of linevar '"..ls.linevar.."', because it was riding more then 24 hours.") + al.cancel_linevar(train) + return ls, nil + end + local linevar_def = al.try_get_linevar_def(ls.linevar, ls.linevar_station) + if linevar_def == nil then + core.log("warning", "Train "..train.id.." was riding a non-existent (undefined) line '"..tostring(ls.linevar).."'!") + al.cancel_linevar(train) + elseif linevar_def.stops[ls.linevar_index] == nil then + core.log("warning", "Train "..train.id.." put out of linevar '"..ls.linevar.."', because its index "..ls.linevar_index.." became invalid.") + al.cancel_linevar(train) + linevar_def = nil + else + local train_line_prefix = (train.line or "").."/" + if train_line_prefix ~= ls.linevar:sub(1, train_line_prefix:len()) then + core.log("warning", "Train "..train.id.." put out of linevar '"..ls.linevar.."', because its line changed to '"..tostring(train.line).."'.") + al.cancel_linevar(train) + linevar_def = nil + end + end + return ls, linevar_def +end + +function al.on_train_approach(pos, train_id, train, index, has_entered) + if has_entered then return end -- do not stop again! + if train.path_cn[index] ~= 1 then return end -- špatný směr + local pe = advtrains.encode_pos(pos) + local stdata = advtrains.lines.stops[pe] + if should_stop(pos, stdata, train) ~= nil then + advtrains.lzb_add_checkpoint(train, index, 2, nil) + if train.line_status.linevar == nil then + -- nelinkový vlak: + local stn = advtrains.lines.stations[stdata.stn] + local stnname = stn and stn.name or attrans("Unknown Station") + train.text_inside = attrans("Next Stop:") .. "\n"..stnname + end + advtrains.interlocking.ars_set_disable(train, true) + end +end + +local function record_skipped_stops(train_id, linevar_def, linevar_index, next_index) + assert(linevar_def) + if next_index > linevar_index + 1 then + local skipped_stops = {} + for i = linevar_index + 1, next_index - 1 do + local mode = linevar_def.stops[i].mode or MODE_NORMAL + if mode ~= MODE_DISABLED then + table.insert(skipped_stops, linevar_def.stops[i].stn) + end + end + if #skipped_stops > 0 then + core.log("warning", "Train "..train_id.." of line '"..linevar_def.name.."' skipped "..#skipped_stops.." stops: ".. + table.concat(skipped_stops, ", ")) + end + return #skipped_stops + else + return 0 + end +end + +function al.on_train_enter(pos, train_id, train, index) + if train.path_cn[index] ~= 1 then return end -- špatný směr + local pe = advtrains.encode_pos(pos) + local stdata = advtrains.lines.stops[pe] + local stn = stdata.stn or "" + local rwtime = assert(rwt.to_secs(rwt.get_time())) + local ls, linevar_def = al.get_line_status(train) + local next_index + if stn ~= "" then + if linevar_def ~= nil then + next_index = find_next_index(ls, linevar_def, stn) + end + ls.last_enter = {stn = stn, encpos = pe, rwtime = rwtime} + debug_print("Vlak "..train_id.." zaznamenán: "..stn.." "..core.pos_to_string(pos).." @ "..rwtime) + end + local should_stop_result = should_stop(pos, stdata, train) + if should_stop_result == nil then + debug_print("Vlak "..train_id.." projel zastávkou "..stn) + return + elseif should_stop_result == "on_request" then + -- projetí zastávky na znamení + debug_print("Vlak "..train_id.." projel zastávkou na znamení "..stn) + if linevar_def ~= nil and next_index ~= nil then + local stop_def = assert(linevar_def.stops[next_index]) + if stop_def.mode == nil or stop_def.mode ~= MODE_REQUEST_STOP then + error("Internal error: mode "..MODE_REQUEST_STOP.." expected, but the real mode is "..tostring(stop_def.mode).."!") + end + assert(stn ~= "") + record_skipped_stops(train_id, linevar_def, ls.linevar_index, next_index) + ls.linevar_index = next_index + ls.linevar_last_dep = rwtime -- u zastávky na znamení se průjezd počítá jako odjezd + ls.linevar_last_stn = stn + local next_stop_index, next_stop_data = al.get_next_stop(linevar_def, next_index) + train.text_inside = al.get_stop_description(nil, linevar_def.stops[next_stop_index or 0]) + -- ATC command: + local atc_command = "A1 S" ..(stdata.speed or "M") + advtrains.atc.train_set_command(train, atc_command, true) + end + return + end + + -- naplánovat čas odjezdu + local wait = tonumber(stdata.wait) or 0 + local interval = stdata.interval + local last_dep = stdata.last_dep -- posl. odjezd z této zastávkové koleje + + if interval ~= nil and last_dep ~= nil then + if last_dep > rwtime then + last_dep = rwtime + end + local ioffset = stdata.ioffset or 0 + local normal_dep = rwtime + wait + local next_dep = last_dep + (interval - (last_dep + (interval - ioffset)) % interval) + if normal_dep < next_dep then + -- core.log("action", "[INFO] The train "..train_id.." will wait for "..(next_dep - normal_dep).." additional seconds due to interval at "..core.pos_to_string(pos)..".") + wait = wait + (next_dep - normal_dep) + -- else -- will wait normal time + end + end + local planned_departure = rwtime + wait + debug_print("Vlak "..train_id.." zastavil na "..stn.." a odjede za "..wait.." sekund ("..planned_departure..").") + stdata.last_dep = planned_departure -- naplánovaný čas odjezdu + stdata.last_wait = wait -- naplánovaná doba čekání + ls.standing_at = pe + if linevar_def == nil or next_index == nil or (linevar_def.stops[next_index].mode or MODE_NORMAL) ~= MODE_HIDDEN then + -- zrušit stop_request, pokud jsme nezastavili na skryté zastávce: + ls.stop_request = nil + end + + local can_start_line + local had_linevar = linevar_def ~= nil + if linevar_def == nil then + -- nelinkový vlak + can_start_line = true + elseif next_index ~= nil then + -- linkový vlak zastavil na své řádné zastávce + assert(stn ~= "") -- dopravna musí mít kód + local stop_def = assert(linevar_def.stops[next_index]) + debug_print("Vlak "..train_id.." je linkový vlak ("..ls.linevar..") a zastavil na své pravidelné zastávce "..stn.." (index = "..next_index..")") + record_skipped_stops(train_id, linevar_def, ls.linevar_index, next_index) + local stop_smode = simple_modes[stop_def.mode or 0] + if stop_smode == MODE_NORMAL then + -- mezilehlá zastávka + can_start_line = false + ls.linevar_index = next_index + ls.linevar_last_dep = planned_departure + ls.linevar_last_stn = stn + debug_print("Jde o mezilehlou zastávku.") + local next_stop_index, next_stop_data = al.get_next_stop(linevar_def, next_index) + train.text_inside = al.get_stop_description(linevar_def.stops[next_index], linevar_def.stops[next_stop_index or 0]) + else + -- koncová zastávka + can_start_line = stop_def.mode == MODE_FINAL_CONTINUE + core.log("action", "Train "..train_id.." arrived at the final station "..stop_def.stn.." of linevar "..ls.linevar.." after "..(rwtime - ls.linevar_dep).." seconds.") + debug_print("Vlak "..train_id.." skončil jízdu na lince "..ls.linevar..", může pokračovat na jinou linku: "..(can_start_line and "ihned" or "na příští zastávce")) + train.text_inside = al.get_stop_description(linevar_def.stops[next_index]) + local current_passage = current_passages[train_id] + if current_passage ~= nil then + current_passage[next_index] = rwtime + current_passages[train_id] = nil + end + al.cancel_linevar(train) + -- vyplnit linevar_past: + if train.line ~= nil and train.line ~= "" then + local past_linevar = assert(linevar_def.name) + local past_line = al.linevar_decompose(past_linevar) + ls.linevar_past = { + line = assert(past_line), + linevar = past_linevar, + station = stn, + arrival = rwtime, + } + end + end + else + -- linkový vlak zastavil na neznámé zastávce (nemělo by nastat) + core.log("warning", "Train "..train_id.." of linevar '"..ls.linevar.."' stopped at unknown station '"..stn.."' at "..core.pos_to_string(pos).."!") + debug_print("Vlak "..train_id.." je linkový vlak ("..ls.linevar.."), ale zastavil na sobě neznámé zastávce "..stn..", což by se nemělo stát.") + can_start_line = false + end + + -- ATC příkaz + local atc_command = "B0 W O"..stdata.doors..(stdata.kick and "K" or "").." D"..wait.. + (stdata.keepopen and " " or " OC ")..(stdata.reverse and "R" or "").." D"..(stdata.ddelay or 1).. + " A1 S" ..(stdata.speed or "M") + advtrains.atc.train_set_command(train, atc_command, true) + + -- provést změny vlaku + local new_line = stdata.line or "" + local new_routingcode = stdata.routingcode or "" + update_value(train, "line", new_line) + update_value(train, "routingcode", new_routingcode) + + -- začít novou linku? + if can_start_line and stn ~= nil and stn ~= "" and line_start(train, stn, planned_departure) then + debug_print("Vlak "..train_id.." zahájil jízdu na nové lince ("..ls.linevar..") ze stanice "..stn..".") + core.log("action", "Train "..train_id.." started a route with linevar '"..ls.linevar.."' at station '"..stn.."'.") + train.text_inside = get_station_name(stn) + assert(ls.linevar) + linevar_def = assert(al.try_get_linevar_def(ls.linevar)) + local next_stop_index, next_stop_data = al.get_next_stop(linevar_def, 1) + if next_stop_index ~= nil then + train.text_inside = train.text_inside.."\nPříští zastávka/stanice: "..get_station_name(next_stop_data.stn) + if next_stop_data.mode ~= nil and next_stop_data.mode == MODE_REQUEST_STOP then + train.text_inside = train.text_inside.." (na znamení)" + end + end + elseif not had_linevar and ls.linevar == nil then + -- vlak, který nebyl a stále není linkový: + train.text_inside = get_station_name(stn) + -- core.after(wait, function() train.text_inside = "" end) + end +end + +function al.on_train_leave(pos, train_id, train, index) + local pe = advtrains.encode_pos(pos) + local stdata = advtrains.lines.stops[pe] + if stdata == nil then + return -- neplatná zastávka + end + local stn = stdata.stn or "" + local ls, linevar_def = al.get_line_status(train) + local rwtime = assert(rwt.to_secs(rwt.get_time())) + + if stn ~= "" then + ls.last_leave = {stn = stn, encpos = pe, rwtime = rwtime} + debug_print("Vlak "..train_id.." zaznamenán na odjezdu: "..stn.." "..core.pos_to_string(pos).." @ "..rwtime) + end + + if ls.standing_at == pe then + -- vlak stál v této dopravně + ls.standing_at = nil + if + linevar_def == nil or ls.linevar_index == nil or + linevar_def.stops[ls.linevar_index] == nil or + (linevar_def.stops[ls.linevar_index].mode or MODE_NORMAL) ~= MODE_HIDDEN + then + ls.stop_request = nil -- zrušit stop_request při odjezdu ze zastávky, pokud není nekoncová skrytá + end + if stn ~= "" then + debug_print("Vlak "..train_id.." odjel ze zastávky "..stn) + if ls.linevar_last_dep ~= nil and ls.linevar_last_dep > rwtime then + debug_print("Vlak "..train_id.." předčasně odjel z dopravny "..stn.." (zbývalo "..(ls.linevar_last_dep - rwtime).." sekund)") + ls.linevar_last_dep = rwtime -- předčasný odjezd + if ls.linevar_index == 1 then + ls.linevar_dep = rwtime + end + end + else + debug_print("Vlak "..train_id.." odjel z anonymní zastávky "..core.pos_to_string(pos)..".") + end + train.text_inside = "" + if linevar_def ~= nil then + -- linkový vlak: + debug_print("Linkový vlak "..train_id.." odjel ze zastávky s indexem "..ls.linevar_index) + if ls.linevar_index == 1 then + -- odjezd z výchozí zastávky: + local new_passage = {rwtime} + if stdata.last_wait ~= nil then + new_passage.wait = stdata.last_wait + end + current_passages[train_id] = new_passage + local passages_by_linevar = last_passages[ls.linevar] + if passages_by_linevar == nil then + passages_by_linevar = {new_passage} + last_passages[ls.linevar] = passages_by_linevar + else + while #passages_by_linevar >= 10 do + table.remove(passages_by_linevar, 1) + end + table.insert(passages_by_linevar, new_passage) + end + else + -- odjezd z nácestné zastávky + local current_passage = current_passages[train_id] + if current_passage ~= nil then + current_passage[ls.linevar_index] = rwtime + end + end + local next_stop_index, next_stop_data = al.get_next_stop(linevar_def, ls.linevar_index) + if next_stop_index ~= nil then + train.text_inside = al.get_stop_description(nil, next_stop_data) + end + end + -- else + --[[ průjezd? + local stn = (stdata and stdata.stn) or "" + if stn ~= "" then + debug_print("Vlak "..train_id.." projel (odjezd) zastávkou "..stn..".") + else + debug_print("Vlak "..train_id.." projel (odjezd) anonymní zastávkou "..core.pos_to_string(pos)..".") + end + ]] + end +end + +function al.linevar_decompose(linevar) + if linevar == nil then + return nil, nil, nil + end + local parts = string.split(linevar, "/", true, 3) + return parts[1], parts[2] or "", parts[3] or "" +end + +--[[ + Vrací: + a) pokud linevar existuje a má průjezdy: + passages, stops: + {{[1] = rwtime, ..., wait = int or nil}...}, {"kód", "název"}...} + b) jinak: + nil, nil +]] +function al.get_last_passages(linevar_def) + local lp = last_passages[linevar_def.name] + if linevar_def ~= nil and lp ~= nil and lp[1] ~= nil then + local passages, stops = {}, {} + for i, stop in ipairs(linevar_def.stops) do + stops[i] = {stop.stn, get_station_name(stop.stn)} + end + for i = 1, #lp do + passages[i] = table.copy(lp[i]) + end + return passages, stops + end +end + +local function get_last_pos(line_status) + assert(line_status) + local last_enter, last_leave, standing_at = line_status.last_enter, line_status.last_leave, line_status.standing_at + local last_pos, last_pos_type + if last_enter ~= nil then + last_pos, last_pos_type = last_enter, "enter" + if standing_at ~= nil and standing_at == last_enter.encpos then + last_pos_type = "standing" + elseif last_leave ~= nil and last_leave.rwtime > last_enter.rwtime then + last_pos, last_pos_type = last_leave, "leave" + end + elseif last_leave ~= nil then + last_pos, last_pos_type = last_leave, "leave" + else + result.type = "none" + return {type = "none"} + end + local result = {type = last_pos_type, last_pos = last_pos} + if last_enter ~= nil then + result.last_enter = last_enter + end + if last_leave ~= nil then + result.last_leave = last_leave + end + return result +end + +function al.get_last_pos_station_name(line_status) + local result = get_last_pos(line_status) + if result.type ~= "none" then + return get_station_name(result.last_pos.stn) + else + return nil + end +end + +local function get_train_position(line_status, linevar_def, rwtime) + if line_status ~= nil then + local last_pos_info = get_last_pos(line_status) + local last_pos = last_pos_info.last_pos + if last_pos ~= nil then + local result = "„"..get_station_name(last_pos.stn).."“" + local delay_info = al.get_delay_description(line_status, linevar_def, rwtime) + if last_pos_info.type ~= "standing" then + result = result.." (před "..(rwtime - last_pos.rwtime).." sekundami)" + end + if delay_info.has_delay ~= nil then + result = result.." ("..delay_info.text..")" + end + return result + end + end + return "???" +end + +--[[ +local function prepare_prediction_for_dump(result) + local x = {} + for i, record in ipairs(result) do + local y = table.copy(record) + y.stdata = record.stdata.name + if y.arr_linevar_def ~= nil then + y.arr_linevar_def = y.arr_linevar_def.name + end + if y.dep_linevar_def ~= nil then + y.dep_linevar_def = y.dep_linevar_def.name + end + x[i] = y + end + return x +end +]] + +local function predict_train_continue(line, stn, rc, departure, result) + if line == nil or line == "" then + return + end + local linevar_def = al.try_get_linevar_def(line.."/"..stn.."/"..rc, stn) + if linevar_def == nil then + return + end + if #result == 0 then + return + end + local stops = assert(linevar_def.stops) + local last_record = result[#result] + last_record.dep = departure + last_record.dep_linevar_def = linevar_def + last_record.dep_index = 1 + local index = 2 + while stops[index] ~= nil do + local stop = stops[index] + local stdata = advtrains.lines.stations[stop.stn] + if stop.mode == MODE_FINAL or stop.mode == MODE_FINAL_CONTINUE or stop.mode == MODE_FINAL_HIDDEN then + -- koncová zastávka + local arr = departure + stop.dep + local record = { + stn = assert(stop.stn), + track = stop.track or "", + stdata = stdata, + arr = arr, + arr_linevar_def = linevar_def, + arr_index = index, + delay = 0, + hidden = stop.mode == MODE_FINAL_HIDDEN, + } + table.insert(result, record) + return + elseif stop.mode ~= MODE_DISABLED then + -- mezilehlá zastávka + local dep = departure + stop.dep + table.insert(result, { + stn = assert(stop.stn), + track = stop.track or "", + stdata = stdata, + arr = dep - (stop.wait or 10), + arr_linevar_def = linevar_def, + arr_index = index, + dep = dep, + dep_linevar_def = linevar_def, + dep_index = index, + delay = 0, + hidden = stop.mode == MODE_HIDDEN, + }) + end + index = index + 1 + end +end + +--[[ + Zadaný vlak musí být linkový. + Parametry: + line_status = table, + linevar_def = table, + rwtime = int, + allow_continue = bool, + Vrací *pole* následujících záznamů: + { + stn = string, + track = string, + delay = int, + stdata = table or nil, + dep = int or nil, dep_linevar_def = table or nil, dep_index = int or nil, + arr = int or nil, arr_linevar_def = table or nil, arr_index = int or nil, + } +]] +function al.predict_train(line_status, linevar_def, rwtime, allow_continue) + assert(line_status) + assert(linevar_def) + local stops = linevar_def.stops + local result = {} + local index = assert(line_status.linevar_index) + if rwtime == nil then + rwtime = rwt.to_secs(rwt.get_time()) + end + local delay_desc = al.get_delay_description(line_status, linevar_def, rwtime) + local delay + if delay_desc.has_delay then + delay = delay_desc.delay + else + delay = 0 + end + local departure = line_status.linevar_dep + if line_status.standing_at ~= nil then + -- vlak stojí na zastávce + local stop = assert(stops[index]) + local stdata = advtrains.lines.stations[stop.stn] + table.insert(result, { + stn = line_status.linevar_last_stn, + track = stop.track or "", + stdata = stdata, + dep = assert(line_status.linevar_last_dep), + dep_linevar_def = assert(linevar_def), + dep_index = index, + -- arr = nil, arr_index = nil, arr_linevar_def = nil, + delay = delay, + hidden = stop.mode == MODE_HIDDEN + }) + end + index = index + 1 + while stops[index] ~= nil do + local stop = stops[index] + local stdata = advtrains.lines.stations[stop.stn] + if stop.mode == MODE_FINAL or stop.mode == MODE_FINAL_CONTINUE or stop.mode == MODE_FINAL_HIDDEN then + -- koncová zastávka + local arr = departure + stop.dep + delay + local record = { + stn = assert(stop.stn), + track = stop.track or "", + stdata = stdata, + arr = arr, + arr_linevar_def = linevar_def, + arr_index = index, + delay = delay, + hidden = stop.mode == MODE_FINAL_HIDDEN, + final = true, + } + table.insert(result, record) + if allow_continue and (linevar_def.continue_line or "") ~= "" and stop.mode == MODE_FINAL_CONTINUE then + predict_train_continue(linevar_def.continue_line, stop.stn, linevar_def.continue_rc, arr + (stop.wait or 10), result) + end + break + elseif stop.mode ~= MODE_DISABLED then + -- mezilehlá zastávka + local dep = departure + stop.dep + delay + table.insert(result, { + stn = assert(stop.stn), + track = stop.track or "", + stdata = stdata, + arr = dep - (stop.wait or 10), + arr_linevar_def = linevar_def, + arr_index = index, + dep = dep, + dep_linevar_def = assert(linevar_def), + dep_index = index, + delay = delay, + hidden = stop.mode == MODE_HIDDEN, + }) + end + index = index + 1 + end + -- print("DEBUG: "..dump2({prediction = prepare_prediction_for_dump(result)})) + return result +end + +--[[ + Parametry: + linevar_def = table, -- definice linevar, z něhož se má analýza provádět + linevar_index = int, + rwtime = int or nil, -- (aktuální žel. čas; nepovinné) + trains = {train_table...} or nil, -- je-li zadáno, bude zkoumat pouze vlaky v tomto seznamu + Vrací *pole* záznamů (stejných jako al.predict_train) vztahujících se k odjezdu + z požadované zastávky, seřazené od nejbližšího odjezdu po nejvzdálenější. +]] +function al.predict_station_departures(linevar_def, linevar_index, rwtime, trains) + assert(linevar_def) + assert(linevar_index) + local linevar = linevar_def.name + local stop = assert(linevar_def.stops[linevar_index]) + if trains == nil then + trains = al.get_trains_by_linevar()[linevar] or {} + end + if rwtime == nil then + rwtime = rwt.to_secs(rwt.get_time()) + end + local result = {} + for _, train in ipairs(trains) do + local ls, lvdef = al.get_line_status(train) + if ls.linevar == linevar and ls.linevar_index <= linevar_index then + local prediction = al.predict_train(ls, linevar_def, rwtime, true) + for _, pr in ipairs(prediction) do + if + pr.dep ~= nil and pr.dep_linevar_def ~= nil and pr.dep_index ~= nil and + pr.dep_linevar_def.name == linevar and + pr.dep_index == linevar_index and + pr.dep > rwtime + then + table.insert(result, pr) + break + end + end + end + end + table.sort(result, function(a, b) return a.dep < b.dep end) + return result +end + +--[[ + => {{ + linevar = string, + indices = {int,...}, + linevar_def = linevar_def, + }...} +]] +function al.get_linevars_by_station(stn, track, options) + if options == nil then + options = {} + end + local include_hidden_stops = options.include_hidden_stops + local ignore_first_stop = options.ignore_first_stop + local ignore_last_stop = options.ignore_last_stop + local result = {} + assert(stn) + for lvstn, stdata in pairs(advtrains.lines.stations) do + if stdata.linevars ~= nil then + for linevar, linevar_def in pairs(stdata.linevars) do + local first_stop_index = al.get_first_stop(linevar_def, include_hidden_stops) + local last_stop_index = al.get_last_stop(linevar_def, include_hidden_stops) + if not (ignore_first_stop or ignore_last_stop) or (first_stop_index ~= nil and last_stop_index ~= nil) then + local ld + for i, stop in ipairs(linevar_def.stops) do + if + stop.stn == stn and + (track == nil or tostring(stop.track) == track) and + (include_hidden_stops or is_visible_mode(stop.mode)) and + ((not ignore_first_stop) or i ~= 1) and + ((not ignore_last_stop) or (not is_final_mode(stop.mode))) + then + if ld == nil then + ld = {linevar = linevar, linevar_def = linevar_def, indices = {i}} + table.insert(result, ld) + else + table.insert(ld.indices, i) + end + end + end + end + end + end + end + if #result > 1 then + table.sort(result, function(a, b) return a.linevar < b.linevar end) + end + return result +end + +--[[ + => { + [linevar] = {train...}, -- generuje jen neprázdné seznamy + } +]] +function al.get_trains_by_linevar() + local result = {} + for train_id, train in pairs(advtrains.trains) do + local ls, linevar_def = al.get_line_status(train) + if linevar_def ~= nil then + local linevar = linevar_def.name + local list = result[linevar] + if list ~= nil then + table.insert(list, train) + else + list = {train} + result[linevar] = list + end + end + end + for linevar, list in pairs(result) do + if list[2] ~= nil then + table.sort(list, function(a, b) return a.id < b.id end) + end + end + return result +end + +local function vlaky(param, past_trains_too) + local result = {} + if param:match("/") then + return result -- parametr nesmí obsahovat '/' + end + local train_line_prefix + if param ~= "" then + train_line_prefix = param.."/" + end + local rwtime = rwt.to_secs(rwt.get_time()) + local players_per_train = {} + local results = {} + for player_name, train_id in pairs(advtrains.player_to_train_mapping) do + results[train_id] = (results[train_id] or 0) + 1 + end + for train_id, train in pairs(advtrains.trains) do + local ls, linevar_def = al.get_line_status(train) + if linevar_def ~= nil then + if train_line_prefix == nil or train_line_prefix == ls.linevar:sub(1, #train_line_prefix) then + local direction_index, direction_stop = al.get_terminus(linevar_def, ls.linevar_index, false) + local direction = "???" + if direction_index ~= nil then + direction = get_station_name(direction_stop.stn) + end + local line = al.linevar_decompose(linevar_def.name) + local s = "("..train_id..") ["..line.."] směr „"..direction.."“, poloha: ".. + get_train_position(ls, linevar_def, rwtime) + if results[train_id] ~= nil then + s = s.." ["..results[train_id].." cestující/ch]" + end + table.insert(results, {key = linevar_def.name.."/"..ls.linevar_index, value = s}) + end + elseif past_trains_too and ls.linevar_past ~= nil and (train_line_prefix == nil or ls.linevar_past.line == param) then + local age = rwtime - ls.linevar_past.arrival + local s = "("..train_id..") ["..ls.linevar_past.line.."] služební, poloha: ".. + get_station_name(ls.linevar_past.station).." (před "..age.." sekundami)" + if results[train_id] ~= nil then + s = s.." ["..results[train_id].." cestující/ch]" + end + table.insert(results, { + key = string.format("%s/~/%s/%05d", ls.linevar_past.line, ls.linevar_past.station, age), + value = s, + }) + end + end + table.sort(results, function(a, b) return a.key < b.key end) + for i, v in ipairs(results) do + result[i] = v.value + end + return result +end + +-- příkaz /vlaky +local def = { + params = "[linka]", + description = "Vypíše všechny linkové vlaky na zadané lince (resp. na všech linkách)", + privs = {}, + func = function(player_name, param) + local result = vlaky(param, false) + if #result == 0 then + return false, "Nenalezen žádný odpovídající vlak." + end + return true, "Nalezeno "..#result.." vlaků:\n- "..table.concat(result, "\n- ") + end, +} +core.register_chatcommand("vlaky", def) +def = { + params = "[linka]", + description = "Vypíše všechny linkové vlaky na zadané lince (resp. na všech linkách) a ty, které nedávno jízdu na lince ukončily", + privs = {}, + func = function(player_name, param) + local result = vlaky(param, true) + if #result == 0 then + return false, "Nenalezen žádný odpovídající vlak." + end + return true, "Nalezeno "..#result.." vlaků:\n- "..table.concat(result, "\n- ") + end, +} +core.register_chatcommand("vlaky+", def) + +def = { + -- params = "", + description = "(pro ladění)", + privs = {server = true}, + func = function(player_name, param) + local train = advtrains.trains[param] + if train == nil then + return false, "Vlak "..param.." nenalezen!" + end + local line_status, linevar_def = al.get_line_status(train) + if linevar_def == nil then + return false, "Vlak "..param.." není linkový!" + end + -- function al.predict_train(line_status, linevar_def, rwtime, allow_continue) + local rwtime = rwt.to_secs(rwt.get_time()) + local predictions = al.predict_train(line_status, linevar_def, rwtime, true) + local result = {"----"} + local s + for i, record in ipairs(predictions) do + local arr, dep + if record.arr ~= nil then + arr = "("..(record.arr - rwtime)..", lv="..record.arr_linevar_def.name..", i="..record.arr_index..")" + else + arr = "nil" + end + if record.dep ~= nil then + dep = "("..(record.dep - rwtime)..", lv="..record.dep_linevar_def.name..", i="..record.dep_index..")" + else + dep = "nil" + end + result[i + 1] = "- "..record.stn.." ["..record.track.."] arr="..arr.." dep="..dep.." delay="..record.delay + end + table.insert(result, "----") + s = table.concat(result, "\n") + print(s) + core.chat_send_player(player_name, s) + return true + end, +} +core.register_chatcommand("jřád", def) +core.register_chatcommand("jrad", def) |