From e25b1c744dad7daa561ee0b23b006bc616f88f23 Mon Sep 17 00:00:00 2001 From: "Y. Wang" Date: Sun, 26 Mar 2023 11:53:00 +0200 Subject: Cancel type 2 signals; introduce signal groups for all signals --- advtrains_interlocking/aspect.lua | 290 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 290 insertions(+) create mode 100644 advtrains_interlocking/aspect.lua (limited to 'advtrains_interlocking/aspect.lua') diff --git a/advtrains_interlocking/aspect.lua b/advtrains_interlocking/aspect.lua new file mode 100644 index 0000000..1575fb1 --- /dev/null +++ b/advtrains_interlocking/aspect.lua @@ -0,0 +1,290 @@ +--- Signal aspect handling. +-- @module advtrains.interlocking.aspect + +local registered_groups = {} + +local named_aspect_aspfields = {main = true, shunt = true, proceed_as_main = true} + +local signal_aspect = {} + +local signal_aspect_metatable = { + __eq = function(asp1, asp2) + for _, k in pairs {"main", "dst", "shunt", "proceed_as_main"} do + local v1, v2 = (asp1[k] or false), (asp2[k] or false) + if v1 ~= v2 then + return false + end + end + if asp1.group and asp1.group == asp2.group then + return asp1.name == asp2.name + end + return true + end, + __index = function(asp, field) + local method = signal_aspect[field] + if method then + return method + end + if not named_aspect_aspfields[field] then + return nil + end + local group = registered_groups[rawget(asp, "group")] + if not group then + return false + end + local aspdef = group.aspects[rawget(asp, "name")] + if not aspdef then + return false + end + return aspdef[field] or false + end, + __tostring = function(asp) + local st = {} + if asp.group and asp.name then + table.insert(st, ("%q in %q"):format(asp.name, asp.group)) + end + if asp.main then + table.insert(st, ("current %d"):format(asp.main)) + end + if asp.main ~= 0 then + if asp.dst then + table.insert(st, string.format("next %d", asp.dst)) + end + end + if asp.main ~= 0 and asp.proceed_as_main then + table.insert(st, "proceed as main") + end + return ("[%s]"):format(table.concat(st, ", ")) + end, +} + +local function quicknew(t) + return setmetatable(t, signal_aspect_metatable) +end + +--- Signal aspect class. +-- @type signal_aspect + +--- Return a plain version of the signal aspect. +-- @param[opt=false] raw Bypass metamethods when fetching signal aspects +-- @return A plain copy of the signal aspect object. +function signal_aspect:plain(raw) + local t = {} + for _, k in pairs {"main", "dst", "shunt", "proceed_as_main", "group", "name"} do + local v + if raw then + v = rawget(self, k) + else + v = self[k] + end + t[k] = v + end + return t +end + +--- Create (or copy) a signal aspect object. +-- Note that signal aspect objects can also be created by calling the `advtrains.interlocking.aspect` table. +-- @return The newly created signal aspect object. +function signal_aspect:new() + if type(self) ~= "table" then + return quicknew{} + end + local newasp = {} + for _, k in pairs {"main", "dst"} do + if type(self[k]) == "table" then + if self[k].free then + newasp[k] = self[k].speed + else + newasp[k] = 0 + end + else + newasp[k] = self[k] + end + end + if type(self.shunt) == "table" then + newasp.shunt = self.shunt.free + newasp.proceed_as_main = self.shunt.proceed_as_main + else + newasp.shunt = self.shunt + end + for _, k in pairs {"group", "name"} do + newasp[k] = self[k] + end + return quicknew(newasp) +end + +--- Modify the signal aspect in-place to fit in the specific signal group. +-- @param group The signal group. The `nil` indicates a generic group. +-- @return The (now modified) signal aspect itself. +function signal_aspect:to_group(group) + local cg = self.group + local gdef = registered_groups[group] + if type(self.name) ~= "string" then + self.name = nil + end + if not gdef then + for k in pairs(named_aspect_aspfields) do + rawset(self, k, self[k]) + end + self.group = nil + self.name = nil + return self + elseif cg == group and gdef.aspects[self.name] then + return self + end + local newidx = 1 + if self.main == 0 then + newidx = #gdef.aspects + end + local cgdef = registered_groups[cg] + if cgdef then + local idx = (cgdef.aspects[self.name] or {}).index + if idx then + if idx >= #cgdef.aspects then + idx = #gdef.aspects + elseif idx >= #gdef.aspects then + idx = #gdef.aspects-1 + end + newidx = idx + end + end + self.group = group + self.name = group.aspects[newidx][1] + return self +end + +--- Modify the signal aspect in-place to indicate a specific distant aspect. +-- @param dst The distant aspect +-- @param[opt=1] shift The phase shift of the current signal. +-- @return The (now modified) signal aspect itself. +function signal_aspect:adjust_distant(dst, shift) + if (shift or -1) < 0 then + shift = 1 + end + if not dst then + self.dst = nil + return self + end + if self.main ~= 0 then + self.dst = dst.main + else + self.dst = nil + end + local dgdef = registered_groups[dst.group] + if dgdef then + if self.group == dst.group and shift == 0 then + self.name = dst.name + else + local idx = (dgdef.aspects[dst.name] or {}).index + if idx then + idx = math.max(idx-shift, 1) + self.group = dst.group + self.name = dgdef.aspects[idx][1] + end + end + end + return self +end + +--- Signal groups. +-- @section signal_group + +--- Register a signal group. +-- @function register_group +-- @param def The definition table. +local function register_group(def) + local t = {} + local name = def.name + if type(name) ~= "string" then + return error("Expected signal group name to be a string, got " .. type(name)) + elseif registered_groups[name] then + return error(string.format("Attempt to redefine signal group %q, previously defined in %s", name, registered_groups[name].defined)) + end + t.name = name + + t.defined = debug.getinfo(2, "S").short_src or "[?]" + + local label = def.label or name + if type(label) ~= "string" then + return error("Label is not a string") + end + t.label = label + + local mainasps = {} + for idx, asp in pairs(def.aspects) do + local idxtp = type(idx) + if idxtp == "string" then + local t = {} + t.name = idx + + local label = asp.label or idx + if type(label) ~= "string" then + return error("Aspect label is not a string") + end + t.label = label + + for k in pairs(named_aspect_aspfields) do + t[k] = asp[k] + end + + mainasps[idx] = t + end + end + if #def.aspects < 2 then + return error("Insufficient entries in signal aspect list") + end + for idx, asplist in ipairs(def.aspects) do + if type(asplist) ~= "table" then + asplist = {asplist} + else + asplist = table.copy(asplist) + end + if #asplist < 1 then + error("Invalid entry in signal aspect list") + end + for _, k in ipairs(asplist) do + if type(k) ~= "string" then + return error("Invalid signal aspect ID") + end + local asp = mainasps[k] + if not asp then + return error("Invalid signal aspect ID") + end + if asp.index ~= nil then + return error("Attempt to assign a signal aspect to multiple numeric indices") + end + asp.index = idx + end + mainasps[idx] = asplist + end + t.aspects = mainasps + + registered_groups[name] = t +end + +--- Get the definition of a signal group. +-- @function get_group_definition +-- @param name The name of the signal group. +-- @return[1] The definition for the signal group (if present). +-- @return[2] The nil constant (otherwise). +local function get_group_definition(name) + local t = registered_groups[name] + if t then + return table.copy(t) + else + return nil + end +end + +local lib = { + register_group = register_group, + get_group_definition = get_group_definition, +} + +local libmt = { + __call = function(_, ...) + return signal_aspect.new(...) + end, +} + +return setmetatable(lib, libmt) -- cgit v1.2.3 From 2afe8027986b2a8172d2cb47236e53e8f24b30f5 Mon Sep 17 00:00:00 2001 From: "Y. Wang" Date: Sun, 26 Mar 2023 16:25:45 +0200 Subject: Allow selecting named aspect --- advtrains_interlocking/aspect.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'advtrains_interlocking/aspect.lua') diff --git a/advtrains_interlocking/aspect.lua b/advtrains_interlocking/aspect.lua index 1575fb1..6d6cb93 100644 --- a/advtrains_interlocking/aspect.lua +++ b/advtrains_interlocking/aspect.lua @@ -149,7 +149,7 @@ function signal_aspect:to_group(group) end end self.group = group - self.name = group.aspects[newidx][1] + self.name = gdef.aspects[newidx][1] return self end @@ -169,6 +169,7 @@ function signal_aspect:adjust_distant(dst, shift) self.dst = dst.main else self.dst = nil + return self end local dgdef = registered_groups[dst.group] if dgdef then -- cgit v1.2.3 From 5695a3e9ee457d8b0aa9ba03e7d73803d972a156 Mon Sep 17 00:00:00 2001 From: "Y. Wang" Date: Mon, 10 Apr 2023 19:01:03 +0200 Subject: Adjust default aspect to allow shunting --- advtrains_interlocking/aspect.lua | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-) (limited to 'advtrains_interlocking/aspect.lua') diff --git a/advtrains_interlocking/aspect.lua b/advtrains_interlocking/aspect.lua index 6d6cb93..c7d5c81 100644 --- a/advtrains_interlocking/aspect.lua +++ b/advtrains_interlocking/aspect.lua @@ -3,7 +3,12 @@ local registered_groups = {} -local named_aspect_aspfields = {main = true, shunt = true, proceed_as_main = true} +local default_aspect = { + main = false, + dst = false, + shunt = true, + proceed_as_main = false, +} local signal_aspect = {} @@ -21,22 +26,22 @@ local signal_aspect_metatable = { return true end, __index = function(asp, field) - local method = signal_aspect[field] - if method then - return method + local val = signal_aspect[field] + if val then + return val end - if not named_aspect_aspfields[field] then + val = default_aspect[field] + if val == nil then return nil end local group = registered_groups[rawget(asp, "group")] - if not group then - return false - end - local aspdef = group.aspects[rawget(asp, "name")] - if not aspdef then - return false + if group then + local aspdef = group.aspects[rawget(asp, "name")] + if aspdef[field] ~= nil then + val = aspdef[field] + end end - return aspdef[field] or false + return val end, __tostring = function(asp) local st = {} @@ -123,7 +128,7 @@ function signal_aspect:to_group(group) self.name = nil end if not gdef then - for k in pairs(named_aspect_aspfields) do + for k in pairs(default_aspect) do rawset(self, k, self[k]) end self.group = nil @@ -224,7 +229,7 @@ local function register_group(def) end t.label = label - for k in pairs(named_aspect_aspfields) do + for _, k in pairs{"main", "dst", "shunt"} do t[k] = asp[k] end -- cgit v1.2.3