aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--advtrains_line_automation/line_editor.lua2
-rw-r--r--advtrains_line_automation/line_functions.lua76
-rw-r--r--advtrains_line_automation/railwaytime.lua382
-rw-r--r--advtrains_line_automation/station_editor.lua4
4 files changed, 292 insertions, 172 deletions
diff --git a/advtrains_line_automation/line_editor.lua b/advtrains_line_automation/line_editor.lua
index 0428035..bd286c7 100644
--- a/advtrains_line_automation/line_editor.lua
+++ b/advtrains_line_automation/line_editor.lua
@@ -827,7 +827,7 @@ show_last_passages_formspec = function(player, linevar_def, selected_linevar)
for j = 1, 10 do
local time = passages[j][1]
if time ~= nil then
- table.insert(formspec, ",("..time..")")
+ table.insert(formspec, ",("..rwt.t_str(time)..")")
if max_time[j] < time then
max_time[j] = time
end
diff --git a/advtrains_line_automation/line_functions.lua b/advtrains_line_automation/line_functions.lua
index ecf7616..f53c9d6 100644
--- a/advtrains_line_automation/line_functions.lua
+++ b/advtrains_line_automation/line_functions.lua
@@ -467,9 +467,9 @@ function al.get_delay_description(line_status, linevar_def, rwtime)
return {text = ""}
end
local stops = linevar_def.stops
- local expected_departure = line_status.linevar_dep + assert(stops[line_status.linevar_index]).dep
+ local expected_departure = rwt.add(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
+ local delay = rwt.diff(expected_departure, real_departure)
if rwtime ~= nil then
local expected_departure_next
@@ -479,13 +479,13 @@ function al.get_delay_description(line_status, linevar_def, rwtime)
else
local mode = stops[i].mode
if mode == nil or mode ~= MODE_DISABLED then
- expected_departure_next = line_status.linevar_dep + stops[i].dep
+ expected_departure_next = rwt.add(line_status.linevar_dep, stops[i].dep)
break
end
end
end
if expected_departure_next ~= nil then
- local delay2 = rwtime - expected_departure_next
+ local delay2 = rwt.diff(expected_departure_next, rwtime)
if delay2 > delay then
delay = delay2
end
@@ -563,16 +563,14 @@ function al.get_line_status(train)
-- 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
+ if train.line ~= linevar_past.line or rwt.diff(linevar_past.arrival, rwt.now()) >= 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
+ if rwt.diff(ls.linevar_dep, rwt.now()) >= 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
@@ -655,7 +653,7 @@ function al.on_train_enter(pos, train_id, train, index)
end
local stn = stdata.stn or ""
- local rwtime = assert(rwt.to_secs(rwt.get_time()))
+ local rwtime = assert(rwt.now())
local ls, linevar_def = al.get_line_status(train)
local next_index
if stn ~= "" then
@@ -663,7 +661,7 @@ function al.on_train_enter(pos, train_id, train, index)
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)
+ debug_print("Vlak "..train_id.." zaznamenán: "..stn.." "..core.pos_to_string(pos).." @ "..rwt.t_str(rwtime))
end
local should_stop_result = should_stop(pos, stdata, train)
if should_stop_result == nil then
@@ -700,20 +698,20 @@ function al.on_train_enter(pos, train_id, train, index)
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
+ if rwt.is_after(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
+ local normal_dep = rwt.add(rwtime, wait)
+ local next_dep = rwt.next_rpt(last_dep, interval, ioffset)
+ if rwt.is_before(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)
+ wait = wait + rwt.diff(normal_dep, next_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..").")
+ local planned_departure = rwt.add(rwtime, wait)
+ debug_print("Vlak "..train_id.." zastavil na "..stn.." a odjede za "..wait.." sekund ("..rwt.t_str(planned_departure)..").")
stdata.last_dep = planned_departure -- naplánovaný čas odjezdu
stdata.last_wait = wait -- naplánovaná doba čekání
ls.standing_at = pe
@@ -746,7 +744,7 @@ function al.on_train_enter(pos, train_id, train, index)
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.")
+ core.log("action", "Train "..train_id.." arrived at the final station "..stop_def.stn.." of linevar "..ls.linevar.." after "..rwt.diff(ls.linevar_dep, rwtime).." 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]
@@ -819,11 +817,11 @@ function al.on_train_leave(pos, train_id, train, index)
end
local stn = stdata.stn or ""
local ls, linevar_def = al.get_line_status(train)
- local rwtime = assert(rwt.to_secs(rwt.get_time()))
+ local rwtime = rwt.now()
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)
+ debug_print("Vlak "..train_id.." zaznamenán na odjezdu: "..stn.." "..core.pos_to_string(pos).." @ "..rwt.t_str(rwtime))
end
if ls.standing_at == pe then
@@ -838,8 +836,8 @@ function al.on_train_leave(pos, train_id, train, index)
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)")
+ if ls.linevar_last_dep ~= nil and rwt.is_after(ls.linevar_last_dep, rwtime) then
+ debug_print("Vlak "..train_id.." předčasně odjel z dopravny "..stn.." (zbývalo "..rwt.diff(rwtime, ls.linevar_last_dep).." sekund)")
ls.linevar_last_dep = rwtime -- předčasný odjezd
if ls.linevar_index == 1 then
ls.linevar_dep = rwtime
@@ -931,7 +929,7 @@ local function get_last_pos(line_status)
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
+ elseif last_leave ~= nil and rwt.is_after(last_leave.rwtime, last_enter.rwtime) then
last_pos, last_pos_type = last_leave, "leave"
end
elseif last_leave ~= nil then
@@ -967,7 +965,7 @@ local function get_train_position(line_status, linevar_def, rwtime)
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.." ("..(rwtime - last_pos.rwtime).." seconds ago)"
+ result = result.." ("..rwt.diff(last_pos.rwtime, rwtime).." seconds ago)"
end
if delay_info.has_delay ~= nil then
result = result.." ("..delay_info.text..")"
@@ -1018,7 +1016,7 @@ local function predict_train_continue(line, stn, rc, departure, result)
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 arr = rwt.add(departure + stop.dep)
local record = {
stn = assert(stop.stn),
track = stop.track or "",
@@ -1033,7 +1031,7 @@ local function predict_train_continue(line, stn, rc, departure, result)
return
elseif stop.mode ~= MODE_DISABLED then
-- mezilehlá zastávka
- local dep = departure + stop.dep
+ local dep = rwt.add(departure + stop.dep)
table.insert(result, {
stn = assert(stop.stn),
track = stop.track or "",
@@ -1076,7 +1074,7 @@ function al.predict_train(line_status, linevar_def, rwtime, allow_continue)
local result = {}
local index = assert(line_status.linevar_index)
if rwtime == nil then
- rwtime = rwt.to_secs(rwt.get_time())
+ rwtime = rwt.now()
end
local delay_desc = al.get_delay_description(line_status, linevar_def, rwtime)
local delay
@@ -1108,7 +1106,7 @@ function al.predict_train(line_status, linevar_def, rwtime, allow_continue)
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 arr = rwt.add(rwt.add(departure, stop.dep), delay)
local record = {
stn = assert(stop.stn),
track = stop.track or "",
@@ -1122,17 +1120,17 @@ function al.predict_train(line_status, linevar_def, rwtime, allow_continue)
}
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)
+ predict_train_continue(linevar_def.continue_line, stop.stn, linevar_def.continue_rc, rwt.add(arr, (stop.wait or 10)), result)
end
break
elseif stop.mode ~= MODE_DISABLED then
-- mezilehlá zastávka
- local dep = departure + stop.dep + delay
+ local dep = rwt.add(rwt.add(departure, stop.dep), delay)
table.insert(result, {
stn = assert(stop.stn),
track = stop.track or "",
stdata = stdata,
- arr = dep - (stop.wait or 10),
+ arr = rwt.sub(dep, (stop.wait or 10)),
arr_linevar_def = linevar_def,
arr_index = index,
dep = dep,
@@ -1166,7 +1164,7 @@ function al.predict_station_departures(linevar_def, linevar_index, rwtime, train
trains = al.get_trains_by_linevar()[linevar] or {}
end
if rwtime == nil then
- rwtime = rwt.to_secs(rwt.get_time())
+ rwtime = rwt.now()
end
local result = {}
for _, train in ipairs(trains) do
@@ -1178,7 +1176,7 @@ function al.predict_station_departures(linevar_def, linevar_index, rwtime, train
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
+ rwt.is_after(pr.dep, rwtime)
then
table.insert(result, pr)
break
@@ -1186,7 +1184,7 @@ function al.predict_station_departures(linevar_def, linevar_index, rwtime, train
end
end
end
- table.sort(result, function(a, b) return a.dep < b.dep end)
+ table.sort(result, function(a, b) return rwt.is_before(a.dep, b.dep) end)
return result
end
@@ -1276,7 +1274,7 @@ local function vlaky(param, past_trains_too)
if param ~= "" then
train_line_prefix = param.."/"
end
- local rwtime = rwt.to_secs(rwt.get_time())
+ local rwtime = rwt.now()
local players_per_train = {}
local results = {}
for player_name, train_id in pairs(advtrains.player_to_train_mapping) do
@@ -1336,7 +1334,7 @@ core.register_chatcommand("list_trains", def)
def = {
params = "[line]",
description = "List all trains on the given line (or all lines) + trains that have recently completed the line",
- privs = {ch_registered_player = true},
+ privs = {},
func = function(player_name, param)
local result = vlaky(param, true)
if #result == 0 then
@@ -1361,19 +1359,19 @@ def = {
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 rwtime = rwt.now()
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..")"
+ arr = "("..rwt.diff(rwtime, record.arr)..", 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..")"
+ dep = "("..rwt.diff(rwtime, record.dep)..", lv="..record.dep_linevar_def.name..", i="..record.dep_index..")"
else
dep = "nil"
end
diff --git a/advtrains_line_automation/railwaytime.lua b/advtrains_line_automation/railwaytime.lua
index 29ef110..ca14ba0 100644
--- a/advtrains_line_automation/railwaytime.lua
+++ b/advtrains_line_automation/railwaytime.lua
@@ -5,38 +5,66 @@
-- - Server stops/restarts
-- However, this means that implementing a "timetable" system using the "real time" is not practical. Therefore,
-- we introduce a custom time system, the RWT(Railway Time), which has nothing to do with RLT(Real-Life Time)
--- RWT has a time cycle of 1 hour. This should be sufficient for most train lines that will ever be built in Minetest.
--- A RWT looks like this: 37;25
--- The ; is to distinguish it from a normal RLT (which has colons e.g. 12:34:56). Left number is minutes, right number is seconds.
--- The minimum RWT is 00;00, the maximum is 59;59.
--- It is OK to leave one places out at either end, esp. when writing relative times, such as:
--- 43;3 22;0 2;30 0;10 ;10
--- Those places are then filled with zeroes. Indeed, ";" would be valid for 00;00 .
-
+-- RWT has a time cycle of 1 day. This should be sufficient for most train lines that will ever be built in Minetest.
+-- A RWT looks like this: 14;37;25
+-- The ; is to distinguish it from a normal RLT (which has colons e.g. 12:34:56). Left number is hours, middle number is minutes, right number is seconds.
+-- The minimum RWT is 00;00;00, the maximum is 23;59;59.
-- There is an "adapt mode", which was proposed by gpcf, and results in RWT automatically adapting itself to real-world time.
--- It works by shifting the minute/second after the realtime minute/second, adjusting the cycle value as needed.
+-- It works by shifting the hour/minute/second after the realtime hour/minute/second, adjusting the cycle value as needed.
+--[[
+There is an important distinction between:
--- Using negative times is discouraged. If you need a negative time, you may insert a minus (-) ONLY in the "c" place
+## Time (absolute time) ##
+Represents a time instant.
+It is identified by the 4 values cycle (day), hour, minute and second. These values are never negative.
---[[
-1;23;45 = {
- s=45,
- m=23,
- c=1, -- Cycle(~hour), not displayed most time
-}
+In string form, it is written as C;HH;MM;SS.
+The following short forms are allowed to display a RWT (but they do not uniquely identify a RWT):
+HH;MM;SS
+HH;MM
+It is discouraged to use the string form to store RWTs, the table form or seconds form is better.
+
+In table form:
+{ s=45, m=23, h=12, c=1 }
+
+In seconds form:
+c*24*60*60 + h*60*60 + m*60 + s
+
+Absolute time wraps around at a boundary of 7 cycles (valid cycle values are 0 to 6). If the difference between instants A and B
+is < 3.5 days, B is considered in the future, otherwise B is considered in the past.
+The RWT functions take this wrapping into account.
+
+## Interval (relative time) ##
+Represents a difference between times.
+
+It is commonly represented in seconds form as a number of seconds.
+It can be formatted as a string e.g.
+ 90 seconds = "+01;30"
+-90 seconds = "-01;30"
+When converting to a string, a + or - must be prepended.
+When parsing a string it is permissible to leave out the + if it is clear from the context that
+an interval is parsed.
+The meaning of string short forms differ from the time instant case:
+[+-]MM;SS (meaning when interval is parsed FROM a string, and the default when converting to string and HH==0 and C==0)
+[+-]HH;MM;SS (when HH!=0 and C==0)
+[+-]C;HH;MM;SS (when all !=0)
+[+-]HH;MM (permissible only when converting TO a string, to be used when it is displayed alongside an instant e.g. 05;30+00;04)
-Railway times can exist in 3 forms:
-- as table (see above)
-- as string (like "12;34")
-- as number (of seconds)
-Forms are automagically converted as needed by the converter functions to_*
-To be sure a rwt is in the required form, explicitly use a converter.
+]]--
-]]
local rwt = {}
+local CYCLE = 24*60*60
+local MODULUS = CYCLE*7
+local MODULUS_H = MODULUS / 2
+
+-- adjust so that it is in range [-MODULUS/2, MODULUS/2)
+local function i_adjust(sec)
+ return ((sec + MODULUS_H) % MODULUS) - MODULUS_H
+end
+
--Time Stamp (Seconds since start of world)
local e_time = 0
local e_has_loaded = false
@@ -62,7 +90,7 @@ local function adapt_real_time()
atlog("[lines][rwt] Skipping",secsfwd,"seconds forward to sync rwt (",rwt.to_string(rwttab),") to real time (",os.date("%H:%M:%S"),")")
- e_time = e_time + secsfwd
+ e_time = (e_time + secsfwd) % MODULUS
end
function rwt.set_time(t)
@@ -87,14 +115,14 @@ function rwt.step(dt)
if setting_rwt_real=="independent" then
-- Regular stepping with dtime
- e_time = e_time + dt
+ e_time = (e_time + dt) % MODULUS
else
-- advance with real-world time
local diff = os.time() - e_last_epoch
e_last_epoch = os.time()
if diff>0 then
- e_time = e_time + diff
+ e_time = (e_time + diff) % MODULUS
end
end
end
@@ -103,17 +131,35 @@ function rwt.now()
return rwt.to_table(e_time)
end
-function rwt.new(c, m, s)
+function rwt.new_t(c, h, m, s)
+ assert(c>=0 and c<7 and c==math.floor(c))
+ assert(h>=0 and h<24 and h==math.floor(h))
+ assert(m>=0 and m<60 and m==math.floor(m))
+ assert(s>=0 and s<60)
return {
c = c or 0,
+ h = h or 0,
m = m or 0,
s = s or 0
}
end
+
+function rwt.new_i(sign, c, h, m, s)
+ assert(math.abs(sign)==1)
+ assert(c>=0 and c<7 and c==math.floor(c))
+ assert(h>=0 and h<24 and h==math.floor(h))
+ assert(m>=0 and m<60 and m==math.floor(m))
+ assert(s>=0 and s<60)
+
+ return sign * (c*24*60*60 + h*60*60 + m*60 + s)
+end
+
+
function rwt.copy(rwtime)
local rwtimet = rwt.to_table(rwtime)
return {
c = rwtimet.c or 0,
+ h = rwtimet.h or 0,
m = rwtimet.m or 0,
s = rwtimet.s or 0
}
@@ -123,185 +169,261 @@ function rwt.to_table(rwtime)
if type(rwtime) == "table" then
return rwtime
elseif type(rwtime) == "string" then
- return rwt.parse(rwtime)
+ return rwt.parse_t(rwtime)
elseif type(rwtime) == "number" then
local res = {}
local seconds = atfloor(rwtime)
res.s = seconds % 60
local minutes = atfloor(seconds/60)
res.m = minutes % 60
- res.c = atfloor(minutes/60)
+ local hours = atfloor(minutes/60)
+ res.h = hours % 24
+ res.c = atfloor(hours/24)
return res
end
end
-function rwt.to_secs(rwtime, c_over)
+local function intv_to_table(rwintp)
+ if type(rwintp) == "number" then
+ local res = {}
+ local rwint = math.abs(rwintp)
+ if rwint~=0 then
+ res.sign = rwintp/rwint -- 1 or -1
+ else
+ res.sign = 1
+ end
+ local seconds = atfloor(rwint)
+ res.s = seconds % 60
+ local minutes = atfloor(seconds/60)
+ res.m = minutes % 60
+ local hours = atfloor(minutes/60)
+ res.h = hours % 24
+ res.c = atfloor(hours/24)
+ return res
+ else
+ error("intv_to_table needs number")
+ end
+end
+
+-- time instant to seconds
+function rwt.t_sec(rwtime)
local res = rwtime
if type(rwtime) == "string" then
- res = rwt.parse(rwtime)
+ res = rwt.parse_t(rwtime)
elseif type(rwtime) == "number" then
return rwtime
end
if type(res)=="table" then
- return (c_over or res.c)*60*60 + res.m*60 + res.s
+ return res.c*60*60*24 + res.h*60*60 + res.m*60 + res.s
+ end
+end
+
+-- interval to seconds
+function rwt.i_sec(rwint)
+ if type(rwint) == "string" then
+ return rwt.parse_i(rwint)
+ elseif type(rwint) == "number" then
+ return rwint
+ elseif type(rwint)=="table" then
+ return res.sign * (res.c*60*60*24 + res.h*60*60 + res.m*60 + res.s)
end
end
-function rwt.to_string(rwtime_p, no_cycle)
+-- time to string
+-- fmt:
+-- 0, nil or false: full format C;HH;MM;SS
+-- 1 or true: format HH;MM;SS
+-- 2: format MM;SS
+-- 3: format HH;MM
+function rwt.t_str(rwtime_p, fmt)
local rwtime = rwt.to_table(rwtime_p)
- if rwtime.c~=0 and not no_cycle then
- return string.format("%d;%02d;%02d", rwtime.c, rwtime.m, rwtime.s)
- else
+ if fmt==0 or not fmt then
+ return string.format("%d;%02d;%02d;%02d", rwtime.c, rwtime.h, rwtime.m, rwtime.s)
+ elseif fmt==1 or fmt==true then
+ return string.format("%02d;%02d;%02d", rwtime.h, rwtime.m, rwtime.s)
+ elseif fmt==2 then
return string.format("%02d;%02d", rwtime.m, rwtime.s)
+ elseif fmt==3 then
+ return string.format("%02d;%02d", rwtime.h, rwtime.m)
+ end
+end
+-- compatibility alias
+rwt.to_string = rwt.t_str
+
+-- interval to string
+function rwt.i_str(rwint_p, omit_plus, omit_second)
+ local rwint = intv_to_table(rwint_p)
+ -- sign
+ local sic = rwint.sign==1 and (omit_plus and "" or "+") or "-"
+ -- find out formatting
+ if omit_second then
+ if rwint.c~=0 then
+ return string.format("%s%d;%02d;%02d", sic, rwint.c, rwint.h, rwint.m)
+ else
+ return string.format("%s%02d;%02d", sic, rwint.h, rwint.m)
+ end
+ else
+ if rwint.c~=0 then
+ return string.format("%s%d;%02d;%02d;%02d", sic, rwint.c, rwint.h, rwint.m, rwint.s)
+ elseif rwint.h~=0 then
+ return string.format("%s%02d;%02d;%02d", sic, rwint.h, rwint.m, rwint.s)
+ else
+ return string.format("%s%02d;%02d", sic, rwint.m, rwint.s)
+ end
end
end
---
-local function v_n(str, cpl)
- if not str then return nil end
+local function v_n(str)
if str == "" then
return 0
end
- local n = tonumber(str)
- if not cpl and (n<0 or n>59) then
- return nil
- end
- return n
+ return tonumber(str)
end
+-- parse an interval (the default case - parsing an instant is rare)
function rwt.parse(str)
--atdebug("parse",str)
+ --4-value form
+ local sic, str_c, str_h, str_m, str_s = string.match(str, "^([%-%+]?)(%d?%d?);(%d%d);(%d%d);(%d?%d?)$")
+ if sic and str_c and str_h and str_m and str_s then
+ --atdebug("4v", sic, str_c, str_h, str_m, str_s)
+ local c, h, m, s = v_n(str_c), v_n(str_h), v_n(str_m), v_n(str_s)
+ if c and h and m and s then
+ return rwt.new_i(sic=="-" and -1 or 1, c,h,m,s)
+ end
+ end
--3-value form
- local str_c, str_m, str_s = string.match(str, "^(%-?%d?%d?);(%d%d);(%d?%d?)$")
- if str_c and str_m and str_s then
- --atdebug("3v",str_c, str_m, str_s)
- local c, m, s = v_n(str_c, true), v_n(str_m), v_n(str_s)
- if c and m and s then
- return rwt.new(c,m,s)
+ local sic, str_h, str_m, str_s = string.match(str, "^([%-%+]?)(%d?%d?);(%d%d);(%d?%d?)$")
+ if sic and str_h and str_m and str_s then
+ --atdebug("3v", sic, str_h, str_m, str_s)
+ local c, h, m, s = 0, v_n(str_h), v_n(str_m), v_n(str_s)
+ if c and h and m and s then
+ return rwt.new_i(sic=="-" and -1 or 1, c,h,m,s)
end
end
--2-value form
- local str_m, str_s = string.match(str, "^(%d?%d?);(%d?%d?)$")
- if str_m and str_s then
- --atdebug("2v",str_m, str_s)
- local m, s = v_n(str_m), v_n(str_s)
- if m and s then
- return rwt.new(0,m,s)
+ local sic, str_m, str_s = string.match(str, "^([%-%+]?)(%d?%d?);(%d?%d?)$")
+ if sic and str_m and str_s then
+ --atdebug("2v", sic, str_m, str_s)
+ local c, h, m, s = 0, 0, v_n(str_m), v_n(str_s)
+ if c and h and m and s then
+ return rwt.new_i(sic=="-" and -1 or 1, c,h,m,s)
end
end
end
+rwt.parse_i = rwt.parse
----
-
-function rwt.add(t1, t2)
- local t1s = rwt.to_secs(t1)
- local t2s = rwt.to_secs(t2)
- return rwt.to_table(t1s + t2s)
+-- parse a time instant - only the 4-value form is allowed!
+function rwt.parse_t(str)
+ --atdebug("parse",str)
+ --4-value form
+ local str_c, str_h, str_m, str_s = string.match(str, "^(%d?%d?);(%d%d);(%d%d);(%d?%d?)$")
+ if str_c and str_h and str_m and str_s then
+ --atdebug("4v", str_c, str_h, str_m, str_s)
+ local c, h, m, s = v_n(str_c), v_n(str_h), v_n(str_m), v_n(str_s)
+ if c and h and m and s then
+ return rwt.new_t(c,h,m,s)
+ end
+ end
end
--- How many seconds FROM t1 TO t2
-function rwt.diff(t1, t2)
- local t1s = rwt.to_secs(t1)
- local t2s = rwt.to_secs(t2)
- return t2s - t1s
-end
+---
--- Subtract t2 from t1 (inverted argument order compared to diff())
-function rwt.sub(t1, t2)
- return rwt.to_table(rwt.diff(t2, t1))
+-- add a time and an interval. The result is a time (table form)
+function rwt.add(t1, i)
+ local t1s = rwt.t_sec(t1)
+ local t2s = rwt.i_sec(i)
+ return rwt.to_table( (t1s + t2s) % MODULUS )
end
--- Adjusts t2 by thresh and then returns time from t1 to t2
-function rwt.adj_diff(t1, t2, thresh)
- local newc = rwt.adjust_cycle(t2, thresh, t1)
- local t1s = rwt.to_secs(t1)
- local t2s = rwt.to_secs(t2, newc)
- return t1s - t2s
+-- subtract a time and an interval. The result is a time (table form)
+function rwt.sub(t1, i)
+ local t1s = rwt.t_sec(t1)
+ local t2s = rwt.i_sec(i)
+ return rwt.to_table( (t1s - t2s) % MODULUS )
end
-
--- Threshold values
--- "reftime" is the time to which this is made relative and defaults to now.
-rwt.CA_FUTURE = 60*60 - 1 -- Selected so that time lies at or in the future of reftime (at nearest point in time)
-rwt.CA_FUTURES = 60*60 -- Same, except when times are equal, advances one full cycle
-rwt.CA_PAST = 0 -- Selected so that time lies at or in the past of reftime
-rwt.CA_PASTS = -1 -- Same, except when times are equal, goes back one full cycle
-rwt.CA_CENTER = 30*60 -- If time is within past 30 minutes of reftime, selected as past, else selected as future.
-
--- Adjusts the "cycle" value of a railway time to be in some relation to reftime.
--- Returns new cycle
-function rwt.adjust_cycle(rwtime, reftime_p, thresh)
- local reftime = reftime_p or rwt.now()
-
- local reftimes = rwt.to_secs(reftime)
-
- local rwtimes = rwt.to_secs(rwtime, 0)
- local timeres = reftimes + thresh - rwtimes
- local cycles = atfloor(timeres / (60*60))
-
- return cycles
+-- How many seconds FROM t1 TO t2 (two time instants, result is an interval)
+function rwt.diff(t1, t2)
+ local t1s = rwt.t_sec(t1)
+ local t2s = rwt.t_sec(t2)
+ return i_adjust(t2s - t1s)
end
-function rwt.adjust(rwtime, reftime, thresh)
- local cp = rwt.copy(rwtime)
- cp.c = rwt.adjust_cycle(rwtime, reftime, thresh)
- return cp
+-- Whether t1 is (strictly) after t2
+function rwt.is_after(t1, t2)
+ return rwt.diff(t2,t1) > 0
end
--- Useful for departure times: returns time (in seconds)
--- until the next (adjusted FUTURE) occurence of deptime is reached
--- in this case, rwtime is used as reftime and deptime should lie in the future of rwtime
--- rwtime defaults to NOW
-function rwt.get_time_until(deptime, rwtime_p)
- local rwtime = rwtime_p or rwt.now()
- return rwt.adj_diff(rwtime, deptime, rwt.CA_FUTURE)
+-- Whether t1 is (strictly) before t2
+function rwt.is_before(t1, t2)
+ return rwt.diff(t1,t2) > 0
end
-
-- Helper functions for handling "repeating times" (rpt)
-- Those are generic declarations for time intervals like "every 5 minutes", with an optional offset
--- ( /02;00-00;45 in timetable syntax
+-- Note the following:
+-- - rwtime is a time instant, rpt_interval and rpt_offset are intervals
+-- - repeating times are hard-capped at cycle (24-hour) boundary
+-- - if rpt_interval is given as nil, there is exactly 1 match per cycle and it is at X;00;00;00 + offset ( equals a time-of-day)
+-- - the offset determines where the "reference cycle" starts e.g. if the off=05;00;00 and int=03;45;00, starting from 0;05;00 the times would be
+-- 05;00, 08;45, ..., 20;00, 23;45, 1;03;30, then next cycle starts 1;05;00
+-- - if the cycle is not a multiple of the interval like in the previous example, the very last instance is not considered when it is closer than interval/2 to the cycle boundary
+-- in the example above the distance between 1;03;30 and 1;05;00 is only 90s, which is smaller than interval/2
+-- It is preferred to therefore have a longer interval
+-- and the actual sequence will be ..., 20;00, 23;45, 1;05;00
-- Get the time (in seconds) until the next time this rpt occurs
function rwt.time_to_next_rpt(rwtime, rpt_interval, rpt_offset)
- local rpti_s = rwt.to_secs(rpt_interval)
+ if not rpt_interval then rpt_interval = CYCLE end
+
+ local rwtime_s = rwt.t_sec(rwtime)
+ local rpti_s = rwt.i_sec(rpt_interval)
+ local rpto_s = rwt.i_sec(rpt_offset)
+ local otimcyc = (rwtime_s - rpto_s) % CYCLE -- time into current cycle, offset by the rpt offset
+ -- otimcyc is the time into a cycle which is assumed to start at rpto_s
+ local lasttime = otimcyc - (otimcyc % rpti_s)
+ local nexttime = lasttime + rpti_s -- next occurence is last occurence plus interval
+
+ if nexttime > (CYCLE - rpti_s/2) then -- last time cutoff rule, and overflow of cycle
+ nexttime = CYCLE
+ end
- return (rpti_s - rwt.time_from_last_rpt(rwtime, rpti_s, rpt_offset)) % rpti_s
- -- Modulo is just there to clip a false value of rpti_s to 0
+ return nexttime - otimcyc
end
-- Get the time (in seconds) since the last time this rpt occured
function rwt.time_from_last_rpt(rwtime, rpt_interval, rpt_offset)
- local rwtime_s = rwt.to_secs(rwtime)
- local rpti_s = rwt.to_secs(rpt_interval)
- local rpto_s = rwt.to_secs(rpt_offset)
+ if not rpt_interval then rpt_interval = CYCLE end
- return ((rwtime_s - rpto_s) % rpti_s)
+ local rwtime_s = rwt.t_sec(rwtime)
+ local rpti_s = rwt.i_sec(rpt_interval)
+ local rpto_s = rwt.i_sec(rpt_offset)
+ local otimcyc = (rwtime_s - rpto_s) % CYCLE -- time into current cycle, offset by the rpt offset
+ -- otimcyc is the time into a cycle which is assumed to start at rpto_s
+ local lasttime = otimcyc - (otimcyc % rpti_s)
+
+ if lasttime > (CYCLE - rpti_s/2) then -- last time cutoff rule
+ lasttime = lasttime - rpti_s
+ end
+
+ return otimcyc - lasttime
end
-- From rwtime, get the next time that is divisible by rpt_interval offset by rpt_offset
function rwt.next_rpt(rwtime, rpt_interval, rpt_offset)
- local rwtime_s = rwt.to_secs(rwtime)
- local rpti_s = rwt.to_secs(rpt_interval)
- local time_from_last = rwt.time_from_last_rpt(rwtime_s, rpti_s, rpt_offset)
-
- local res_s = rwtime_s - time_from_last + rpti_s
-
- return rwt.to_table(res_s)
+ local time_to_next = rwt.time_to_next_rpt(rwtime, rpt_interval, rpt_offset)
+ return rwt.add(rwtime, time_to_next)
end
-- from rwtime, get the last time that this rpt matched (which is actually just next_rpt - rpt_offset
function rwt.last_rpt(rwtime, rpt_interval, rpt_offset)
- local rwtime_s = rwt.to_sec(rwtime)
- local rpti_s = rwt.to_sec(rpt_interval)
local time_from_last = rwt.time_from_last_rpt(rwtime, rpt_interval, rpt_offset)
-
- local res_s = rwtime_s - time_from_last
-
- return rwt.to_table(res_s)
+ return rwt.sub(rwtime, time_from_last)
end
function rwt.to_os_time(rwtime)
diff --git a/advtrains_line_automation/station_editor.lua b/advtrains_line_automation/station_editor.lua
index e9baaf1..4c247c5 100644
--- a/advtrains_line_automation/station_editor.lua
+++ b/advtrains_line_automation/station_editor.lua
@@ -816,7 +816,7 @@ local function jr_refresh_departure(custom_state)
custom_state.message = ""
return
end
- local rwtime = rwt.to_secs(rwt.get_time())
+ local rwtime = rwt.now()
local prediction = advtrains.lines.predict_station_departures(linevar_def, assert(stop_info.linevar_index), rwtime)
if #prediction == 0 then
custom_state.message = S("No departures for the selected line found in the near future")
@@ -824,7 +824,7 @@ local function jr_refresh_departure(custom_state)
end
local deps = {}
for i, pred in ipairs(prediction) do
- deps[i] = tostring(pred.dep - rwtime)
+ deps[i] = tostring(rwt.diff(rwtime, pred.dep))
end
custom_state.message = S("next departures of the selected line in: ")..table.concat(deps, ", ").." s"
end