1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
|
--- Signal aspect handling.
-- @module advtrains.interlocking.aspects
local type2defs = {}
--- Register a type 2 signal group.
-- @function register_type2
-- @param def The definition table.
local function register_type2(def)
local t = {type = 2}
local name = def.name
if type(name) ~= "string" then
return error("Name is not a string")
elseif type2defs[name] then
return error(string.format("Attempt to redefine type 2 signal aspect group %q, previously defined in %s", name, type2defs[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 ipairs(def.main) do
local t = {}
local name = asp.name
if type(name) ~= "string" then
return error("Aspect name is not a string")
end
t.name = name
local label = asp.label or name
if type(label) ~= "string" then
return error("Aspect label is not a string")
end
t.label = label
t.main = asp.main
t.shunt = asp.shunt
t.proceed_as_main = asp.proceed_as_main
mainasps[idx] = t
mainasps[name] = idx
end
t.main = mainasps
type2defs[name] = t
end
--- Get the definition of a type 2 signal group.
-- @function get_type2_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_type2_definition(name)
local t = type2defs[name]
if t then
return table.copy(t)
else
return nil
end
end
--- Get the name of the distant aspect before the current aspect.
-- @function get_type2_dst
-- @param group The name of the group.
-- @param name The name of the current aspect.
-- @return[1] The name of the distant aspect (if present).
-- @return[2] The nil constant (otherwise).
local function get_type2_dst(group, name)
local def = type2defs[group]
if not def then
return nil
end
local aspidx = name
if type(name) ~= "number" then
aspidx = def.main[name] or 1
end
return def.main[math.max(1, aspidx-1)].name
end
--- Convert a type 2 signal aspect to a type 1 signal aspect.
-- @function type2_to_type1
-- @param suppasp The table of supported aspects for the signal.
-- @param asp The name of the signal aspect.
-- @return[1] The type 1 signal aspect table (if present).
-- @return[2] The nil constant (otherwise).
local function type2_to_type1(suppasp, asp)
local name = suppasp.group
local shift = suppasp.dst_shift
local def = type2defs[name]
if not def then
return nil
end
local aspidx
if type(asp) == "number" then
aspidx = asp
else
aspidx = def.main[asp] or 2
end
local realidx = math.min(#def.main, aspidx+(shift or 0))
local asptbl = def.main[realidx]
if not asptbl then
return nil
end
if type(asp) == "number" then
asp = asptbl.name
end
local main, shunt, dst
if shift then
dst = asptbl.main
else
main = asptbl.main
shunt = asptbl.shunt
dst = def.main[math.min(#def.main, aspidx+1)].main
end
local t = {
main = main,
shunt = shunt,
proceed_as_main = asptbl.proceed_as_main,
type2name = asp,
type2group = name,
dst = dst,
}
if aspidx > 1 and aspidx < #asptbl then
t.dst = asptbl[aspidx+1].main
end
return t
end
--- Convert a type 1 signal aspect table to a type 2 signal aspect.
-- @function type1_to_type2main
-- @param asp The type 1 signal aspect table
-- @param group The signal aspect group
-- @param[opt=0] shift The shift for the signal aspect.
-- @return[1] The name of the signal aspect (if present).
-- @return[2] The nil constant (otherwise).
local function type1_to_type2main(asp, group, shift)
local def = type2defs[group]
if not def then
return nil
end
local t_main = def.main
local idx
if group == asp.type2group and t_main[asp.type2name] then
idx = t_main[asp.type2name]
elseif not asp.main or asp.main == -1 then
idx = 1
elseif asp.main == 0 then
idx = #t_main
else
idx = #t_main-1
end
return t_main[math.max(1, idx-(shift or 0))].name
end
--- Compare two type 1 signal aspect tables.
-- @function equalp
-- @param asp1 The first signal aspect table.
-- @param asp2 The second signal aspect table.
-- @return Whether the two signal aspect tables give the same (type 1 aspect) information.
local function equalp(asp1, asp2)
if asp1 == asp2 then -- same reference
return true
else
for _, k in pairs {"main", "shunt", "dst"} do
if asp1[k] ~= asp2[k] then
return false
end
end
end
if asp1.type2group and asp1.type2group == asp2.type2group then
return asp1.type2name == asp2.type2name
end
return true
end
--- Compare two signal aspect tables.
-- @function not_equalp
-- @param asp1 The first signal aspect table.
-- @param asp2 The second signal aspect table.
-- @return The negation of `equalp``(asp1, asp2)`.
local function not_equalp(asp1, asp2)
return not equalp(asp1, asp2)
end
return {
register_type2 = register_type2,
get_type2_definition = get_type2_definition,
get_type2_dst = get_type2_dst,
type2_to_type1 = type2_to_type1,
type1_to_type2main = type1_to_type2main,
equalp = equalp,
not_equalp = not_equalp,
}
|