Skip to content

Commit 65aefb4

Browse files
bedaHovorkaclaude
andcommitted
test(xml): add Praha topology improvement tests with civil-engineer validation (#347)
- Exact element counts (12 InOuts, 41 switches, 56 signals, 111 blocks) - N-Bypass and S-CarTrain InOut presence and orientation - Bypass route navigability (N-Bypass → S-Bypass) - Car train terminal reachability via bypass corridor - Switch orientation domain correctness at (6,20) and (52,20) - Fix XML: add RailSemaphore at (52,22) as required node cell for track block endpoint - Fix XML: resolve duplicate segment-A conflict at (16,20) via segment B for diagonal approach Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent a88424d commit 65aefb4

File tree

3 files changed

+238
-5
lines changed

3 files changed

+238
-5
lines changed

core-test/src/commonMain/resources/cz/vutbr/fit/interlockSim/xml/fixtures/praha-hlavni-nadrazi.xml

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
- Car train terminal siding (Y=22)
1515
1616
Grid: 70x25
17-
Total elements: 41 switches, 55 signals, 12 InOuts, 111 track blocks
17+
Total elements: 41 switches, 56 signals, 12 InOuts, 111 track blocks
1818
-->
1919
<net X="70" Y="25">
2020
<!-- North entry points -->
@@ -142,6 +142,9 @@
142142
<!-- SIMPLE_RIGHT_TRUE (not FALSE): bypass row needs segment D for the Y=22 car-train terminal siding -->
143143
<RailSwitch X="52" Y="20" SpatialType="HORIZONTAL" Type="SIMPLE_RIGHT_TRUE"/>
144144

145+
<!-- Car train terminal approach signal (node cell required for track block endpoint at Y=22) -->
146+
<RailSemaphore X="52" Y="22" SpatialType="HORIZONTAL" orientation="true"/>
147+
145148
<!-- South Layer 4 signals (between merge and diverge switches) -->
146149
<RailSemaphore X="54" Y="4" SpatialType="HORIZONTAL" orientation="true"/>
147150
<RailSemaphore X="54" Y="12" SpatialType="HORIZONTAL" orientation="true"/>
@@ -228,7 +231,7 @@
228231
<SimpleTrackBlock fromX="14" fromY="14" toX="16" toY="14" fromSegment="F" toSegment="A" length="20.0" maxSpeedfrom="25.0" maxSpeedto="20.0"/>
229232
<SimpleTrackBlock fromX="14" fromY="16" toX="16" toY="16" fromSegment="F" toSegment="A" length="20.0" maxSpeedfrom="25.0" maxSpeedto="20.0"/>
230233
<SimpleTrackBlock fromX="14" fromY="18" toX="16" toY="18" fromSegment="F" toSegment="A" length="20.0" maxSpeedfrom="25.0" maxSpeedto="20.0"/>
231-
<SimpleTrackBlock fromX="14" fromY="18" toX="16" toY="20" fromSegment="D" toSegment="A" length="28.0" maxSpeedfrom="25.0" maxSpeedto="20.0"/>
234+
<SimpleTrackBlock fromX="14" fromY="18" toX="16" toY="20" fromSegment="D" toSegment="B" length="28.0" maxSpeedfrom="25.0" maxSpeedto="20.0"/>
232235

233236
<!-- Platform zone (main tracks) -->
234237
<SimpleTrackBlock fromX="16" fromY="4" toX="42" toY="4" fromSegment="F" toSegment="A" length="260.0" maxSpeedfrom="20.0" maxSpeedto="20.0"/>

desktop-ui/src/test/kotlin/cz/vutbr/fit/interlockSim/xml/XMLContextFactoryTest.kt

Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1088,6 +1088,216 @@ class XMLContextFactoryTest : KoinTestBase() {
10881088
}
10891089
}
10901090

1091+
@Nested
1092+
@DisplayName("Praha Topology Improvements (PR #347)")
1093+
inner class PragueTopologyImprovementsTests {
1094+
1095+
@Test
1096+
@DisplayName("Praha XML loads with exact element counts after PR #347 additions")
1097+
fun testPragueExactElementCounts() {
1098+
val xml = getFixtureStream("praha-hlavni-nadrazi.xml")
1099+
val context = editingContextFactory.createContext(xml) as EditingContext
1100+
1101+
@Suppress("UNCHECKED_CAST")
1102+
val cellGrid = context.getRailWayNetGrid() as RailwayNetGrid<Cell>
1103+
1104+
var inOutCount = 0
1105+
var switchCount = 0
1106+
var semaphoreCount = 0
1107+
for (entry in cellGrid) {
1108+
when (entry.value) {
1109+
is InOut -> inOutCount++
1110+
is RailSwitch -> switchCount++
1111+
is RailSemaphore -> semaphoreCount++
1112+
}
1113+
}
1114+
1115+
// Count unique track blocks via graph (SimpleTrackBlocks are graph edges, not grid cells)
1116+
val seenBlocks = java.util.IdentityHashMap<TrackSection, Unit>()
1117+
val graph = (context as DefaultEditingContext).getGraph()
1118+
for (node in graph.nodeSet()) {
1119+
for (entry in graph.assignedEdges(node).entries) {
1120+
val edge = entry.value
1121+
if (edge is TrackSection) {
1122+
seenBlocks[edge] = Unit
1123+
}
1124+
}
1125+
}
1126+
val trackBlockCount = seenBlocks.size
1127+
1128+
assertThat(inOutCount)
1129+
.withMessage("Praha should have exactly 12 InOut elements after PR #347")
1130+
.isEqualTo(12)
1131+
assertThat(switchCount)
1132+
.withMessage("Praha should have exactly 41 switches after PR #347")
1133+
.isEqualTo(41)
1134+
assertThat(semaphoreCount)
1135+
.withMessage("Praha should have exactly 56 signals after PR #347 (55 original + 1 car train terminal approach)")
1136+
.isEqualTo(56)
1137+
assertThat(trackBlockCount)
1138+
.withMessage("Praha should have exactly 111 track blocks after PR #347")
1139+
.isEqualTo(111)
1140+
}
1141+
1142+
@Test
1143+
@DisplayName("N-Bypass InOut is present at grid (2,20) with entry orientation")
1144+
fun testPragueNorthBypassInOutPresent() {
1145+
val xml = getFixtureStream("praha-hlavni-nadrazi.xml")
1146+
val context = editingContextFactory.createContext(xml)
1147+
1148+
val cell = context.getRailWayNetGrid().getCellAt(2, 20)
1149+
assertThat(cell).isNotNull().isInstanceOf(InOut::class)
1150+
1151+
val inOut = cell as InOut
1152+
assertThat(inOut.getName())
1153+
.withMessage("N-Bypass InOut should have correct name")
1154+
.isEqualTo("N-Bypass")
1155+
assertThat(inOut.getOrientation())
1156+
.withMessage("N-Bypass InOut should be an entry point (orientation=false)")
1157+
.isFalse()
1158+
}
1159+
1160+
@Test
1161+
@DisplayName("S-CarTrain InOut is present at grid (60,22) with exit orientation")
1162+
fun testPragueCarTrainTerminalInOutPresent() {
1163+
val xml = getFixtureStream("praha-hlavni-nadrazi.xml")
1164+
val context = editingContextFactory.createContext(xml)
1165+
1166+
val cell = context.getRailWayNetGrid().getCellAt(60, 22)
1167+
assertThat(cell).isNotNull().isInstanceOf(InOut::class)
1168+
1169+
val inOut = cell as InOut
1170+
assertThat(inOut.getName())
1171+
.withMessage("S-CarTrain InOut should have correct name")
1172+
.isEqualTo("S-CarTrain")
1173+
assertThat(inOut.getOrientation())
1174+
.withMessage("S-CarTrain InOut should be an exit point (orientation=true)")
1175+
.isTrue()
1176+
}
1177+
1178+
@Test
1179+
@DisplayName("Bypass route N-Bypass to S-Bypass is navigable")
1180+
fun testPragueBypassRouteNavigable() {
1181+
val xml = getFixtureStream("praha-hlavni-nadrazi.xml")
1182+
val context = editingContextFactory.createContext(xml) as EditingContext
1183+
1184+
var nBypass: InOut? = null
1185+
var sBypass: InOut? = null
1186+
for (entry in context.getRailWayNetGrid()) {
1187+
val cell = entry.value
1188+
if (cell is InOut) {
1189+
when (cell.getName()) {
1190+
"N-Bypass" -> nBypass = cell
1191+
"S-Bypass" -> sBypass = cell
1192+
}
1193+
}
1194+
}
1195+
1196+
assertThat(nBypass)
1197+
.withMessage("N-Bypass InOut should exist in Praha XML")
1198+
.isNotNull()
1199+
assertThat(sBypass)
1200+
.withMessage("S-Bypass InOut should exist in Praha XML")
1201+
.isNotNull()
1202+
1203+
assertThat(existPath(nBypass!!, sBypass!!, context as DefaultEditingContext))
1204+
.withMessage("Path should exist from N-Bypass to S-Bypass")
1205+
.isTrue()
1206+
}
1207+
1208+
@Test
1209+
@DisplayName("S-CarTrain terminal is reachable from the N-Bypass entry")
1210+
fun testPragueCarTrainTerminalAccessible() {
1211+
val xml = getFixtureStream("praha-hlavni-nadrazi.xml")
1212+
val context = editingContextFactory.createContext(xml) as EditingContext
1213+
1214+
// S-CarTrain is accessible from N-Bypass via the Y=20/Y=22 bypass corridor
1215+
var nBypass: InOut? = null
1216+
var sCarTrain: InOut? = null
1217+
for (entry in context.getRailWayNetGrid()) {
1218+
val cell = entry.value
1219+
if (cell is InOut) {
1220+
when (cell.getName()) {
1221+
"N-Bypass" -> nBypass = cell
1222+
"S-CarTrain" -> sCarTrain = cell
1223+
}
1224+
}
1225+
}
1226+
1227+
assertThat(nBypass)
1228+
.withMessage("N-Bypass InOut should exist in Praha XML")
1229+
.isNotNull()
1230+
assertThat(sCarTrain)
1231+
.withMessage("S-CarTrain InOut should exist in Praha XML")
1232+
.isNotNull()
1233+
1234+
assertThat(existPath(nBypass!!, sCarTrain!!, context as DefaultEditingContext))
1235+
.withMessage("Path should exist from N-Bypass to S-CarTrain via bypass corridor (Y=20→Y=22)")
1236+
.isTrue()
1237+
}
1238+
1239+
@Test
1240+
@DisplayName("Switch orientations at new bypass infrastructure are railway-domain correct")
1241+
fun testPragueSwitchOrientationsAtNewInfrastructure() {
1242+
val xml = getFixtureStream("praha-hlavni-nadrazi.xml")
1243+
val context = editingContextFactory.createContext(xml)
1244+
1245+
val switchAt6x20 = context.getRailWayNetGrid().getCellAt(6, 20)
1246+
assertThat(switchAt6x20).isNotNull().isInstanceOf(RailSwitch::class)
1247+
assertThat((switchAt6x20 as RailSwitch).type)
1248+
.withMessage("Bypass entry switch at (6,20) must be SIMPLE_LEFT_TRUE for correct bypass diverge")
1249+
.isEqualTo(RailSwitch.Type.SIMPLE_LEFT_TRUE)
1250+
1251+
val switchAt52x20 = context.getRailWayNetGrid().getCellAt(52, 20)
1252+
assertThat(switchAt52x20).isNotNull().isInstanceOf(RailSwitch::class)
1253+
assertThat((switchAt52x20 as RailSwitch).type)
1254+
.withMessage("Bypass exit switch at (52,20) must be SIMPLE_RIGHT_TRUE to allow diverge toward Y=22")
1255+
.isEqualTo(RailSwitch.Type.SIMPLE_RIGHT_TRUE)
1256+
}
1257+
1258+
/**
1259+
* Path existence check for Praha topology improvement tests.
1260+
* Reuses the BFS approach from ComplexStationConfigurationTests.
1261+
*/
1262+
private fun existPath(
1263+
from: InOut,
1264+
to: InOut,
1265+
context: DefaultEditingContext
1266+
): Boolean {
1267+
val fromLoc = context.getRailWayNetGrid().getLocation(from) ?: return false
1268+
val toLoc = context.getRailWayNetGrid().getLocation(to) ?: return false
1269+
if (fromLoc == toLoc) return true
1270+
1271+
val graph = context.getGraph()
1272+
val visited = mutableSetOf<Point>()
1273+
val queue = mutableListOf(fromLoc)
1274+
1275+
while (queue.isNotEmpty()) {
1276+
val current = queue.removeFirst()
1277+
if (current in visited) continue
1278+
visited.add(current)
1279+
if (current == toLoc) return true
1280+
1281+
val edges = graph.assignedEdges(current)
1282+
for (entry in edges.entries) {
1283+
val trackBlock = entry.value
1284+
if (trackBlock !is TrackSection) continue
1285+
1286+
val ends = trackBlock.ends()
1287+
for (pathSeparator in ends) {
1288+
if (pathSeparator !is cz.vutbr.fit.interlockSim.objects.cells.NodeCell) continue
1289+
1290+
val endLocation = context.getRailWayNetGrid().getLocation(pathSeparator) ?: continue
1291+
if (endLocation != current && endLocation !in visited) {
1292+
queue.add(endLocation)
1293+
}
1294+
}
1295+
}
1296+
}
1297+
return false
1298+
}
1299+
}
1300+
10911301
@Nested
10921302
@DisplayName("Name attribute persistence and validation (Issue #306)")
10931303
inner class NameAttributeTests {

desktop-ui/src/test/resources/cz/vutbr/fit/interlockSim/xml/fixtures/praha-hlavni-nadrazi.xml

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,31 +4,39 @@
44
55
Configuration for test XMLContextFactoryTest with:
66
- 4 north entry points (Libeň, Vysočany lines)
7+
- 1 north bypass entry point (N-Bypass)
78
- 6 south exit points (Vinohrady, Vršovice)
9+
- 1 south bypass exit point (S-Bypass)
10+
- 1 car train terminal (S-CarTrain)
811
- 3-layer switching in north and south throats
912
- 9 platform tracks through central zone
13+
- Direct bypass routing (N-Bypass → S-Bypass)
14+
- Car train terminal siding (Y=22)
1015
1116
Grid: 70x25
12-
Total elements: 40+ switches, 40+ signals, 90+ track blocks
17+
Total elements: 41 switches, 56 signals, 12 InOuts, 111 track blocks
1318
-->
1419
<net X="70" Y="25">
1520
<!-- North entry points -->
1621
<InOut X="2" Y="4" SpatialType="HORIZONTAL" orientation="false" name="N-Lib-1"/>
1722
<InOut X="2" Y="8" SpatialType="HORIZONTAL" orientation="false" name="N-Lib-2"/>
1823
<InOut X="2" Y="12" SpatialType="HORIZONTAL" orientation="false" name="N-Vys-1"/>
1924
<InOut X="2" Y="16" SpatialType="HORIZONTAL" orientation="false" name="N-Vys-2"/>
25+
<InOut X="2" Y="20" SpatialType="HORIZONTAL" orientation="false" name="N-Bypass"/>
2026

2127
<!-- North signals -->
2228
<RailSemaphore X="4" Y="4" SpatialType="HORIZONTAL" orientation="false"/>
2329
<RailSemaphore X="4" Y="8" SpatialType="HORIZONTAL" orientation="false"/>
2430
<RailSemaphore X="4" Y="12" SpatialType="HORIZONTAL" orientation="false"/>
2531
<RailSemaphore X="4" Y="16" SpatialType="HORIZONTAL" orientation="false"/>
32+
<RailSemaphore X="4" Y="20" SpatialType="HORIZONTAL" orientation="false"/>
2633

2734
<!-- North Layer 1 switches -->
2835
<RailSwitch X="6" Y="4" SpatialType="HORIZONTAL" Type="SIMPLE_LEFT_TRUE"/>
2936
<RailSwitch X="6" Y="8" SpatialType="HORIZONTAL" Type="SIMPLE_LEFT_TRUE"/>
3037
<RailSwitch X="6" Y="12" SpatialType="HORIZONTAL" Type="SIMPLE_LEFT_TRUE"/>
3138
<RailSwitch X="6" Y="16" SpatialType="HORIZONTAL" Type="SIMPLE_LEFT_TRUE"/>
39+
<RailSwitch X="6" Y="20" SpatialType="HORIZONTAL" Type="SIMPLE_LEFT_TRUE"/>
3240

3341
<!-- North L1 signals -->
3442
<RailSemaphore X="8" Y="4" SpatialType="HORIZONTAL" orientation="false"/>
@@ -131,7 +139,11 @@
131139
<!-- South Layer 3 switches - MERGE switches (tracks converge here) -->
132140
<RailSwitch X="52" Y="4" SpatialType="HORIZONTAL" Type="SIMPLE_RIGHT_FALSE"/>
133141
<RailSwitch X="52" Y="12" SpatialType="HORIZONTAL" Type="SIMPLE_RIGHT_FALSE"/>
134-
<RailSwitch X="52" Y="20" SpatialType="HORIZONTAL" Type="SIMPLE_RIGHT_FALSE"/>
142+
<!-- SIMPLE_RIGHT_TRUE (not FALSE): bypass row needs segment D for the Y=22 car-train terminal siding -->
143+
<RailSwitch X="52" Y="20" SpatialType="HORIZONTAL" Type="SIMPLE_RIGHT_TRUE"/>
144+
145+
<!-- Car train terminal approach signal (node cell required for track block endpoint at Y=22) -->
146+
<RailSemaphore X="52" Y="22" SpatialType="HORIZONTAL" orientation="true"/>
135147

136148
<!-- South Layer 4 signals (between merge and diverge switches) -->
137149
<RailSemaphore X="54" Y="4" SpatialType="HORIZONTAL" orientation="true"/>
@@ -151,24 +163,28 @@
151163
<InOut X="60" Y="14" SpatialType="HORIZONTAL" orientation="true" name="S-Vrs-2"/>
152164
<InOut X="60" Y="16" SpatialType="HORIZONTAL" orientation="true" name="S-Vrs-3"/>
153165
<InOut X="60" Y="20" SpatialType="HORIZONTAL" orientation="true" name="S-Bypass"/>
166+
<InOut X="60" Y="22" SpatialType="HORIZONTAL" orientation="true" name="S-CarTrain"/>
154167

155168
<!-- Track blocks: North approach -->
156169
<SimpleTrackBlock fromX="2" fromY="4" toX="4" toY="4" fromSegment="F" toSegment="A" length="150.0" maxSpeedfrom="40.0" maxSpeedto="40.0"/>
157170
<SimpleTrackBlock fromX="2" fromY="8" toX="4" toY="8" fromSegment="F" toSegment="A" length="150.0" maxSpeedfrom="40.0" maxSpeedto="40.0"/>
158171
<SimpleTrackBlock fromX="2" fromY="12" toX="4" toY="12" fromSegment="F" toSegment="A" length="150.0" maxSpeedfrom="40.0" maxSpeedto="40.0"/>
159172
<SimpleTrackBlock fromX="2" fromY="16" toX="4" toY="16" fromSegment="F" toSegment="A" length="150.0" maxSpeedfrom="40.0" maxSpeedto="40.0"/>
173+
<SimpleTrackBlock fromX="2" fromY="20" toX="4" toY="20" fromSegment="F" toSegment="A" length="150.0" maxSpeedfrom="40.0" maxSpeedto="40.0"/>
160174

161175
<!-- North signals to L1 switches -->
162176
<SimpleTrackBlock fromX="4" fromY="4" toX="6" toY="4" fromSegment="F" toSegment="A" length="30.0" maxSpeedfrom="40.0" maxSpeedto="35.0"/>
163177
<SimpleTrackBlock fromX="4" fromY="8" toX="6" toY="8" fromSegment="F" toSegment="A" length="30.0" maxSpeedfrom="40.0" maxSpeedto="35.0"/>
164178
<SimpleTrackBlock fromX="4" fromY="12" toX="6" toY="12" fromSegment="F" toSegment="A" length="30.0" maxSpeedfrom="40.0" maxSpeedto="35.0"/>
165179
<SimpleTrackBlock fromX="4" fromY="16" toX="6" toY="16" fromSegment="F" toSegment="A" length="30.0" maxSpeedfrom="40.0" maxSpeedto="35.0"/>
180+
<SimpleTrackBlock fromX="4" fromY="20" toX="6" toY="20" fromSegment="F" toSegment="A" length="30.0" maxSpeedfrom="40.0" maxSpeedto="35.0"/>
166181

167182
<!-- L1 switches straight -->
168183
<SimpleTrackBlock fromX="6" fromY="4" toX="8" toY="4" fromSegment="F" toSegment="A" length="20.0" maxSpeedfrom="35.0" maxSpeedto="35.0"/>
169184
<SimpleTrackBlock fromX="6" fromY="8" toX="8" toY="8" fromSegment="F" toSegment="A" length="20.0" maxSpeedfrom="35.0" maxSpeedto="35.0"/>
170185
<SimpleTrackBlock fromX="6" fromY="12" toX="8" toY="12" fromSegment="F" toSegment="A" length="20.0" maxSpeedfrom="35.0" maxSpeedto="35.0"/>
171186
<SimpleTrackBlock fromX="6" fromY="16" toX="8" toY="16" fromSegment="F" toSegment="A" length="20.0" maxSpeedfrom="35.0" maxSpeedto="35.0"/>
187+
<SimpleTrackBlock fromX="6" fromY="20" toX="16" toY="20" fromSegment="F" toSegment="A" length="100.0" maxSpeedfrom="35.0" maxSpeedto="30.0"/>
172188

173189
<!-- L1 switches diverge -->
174190
<SimpleTrackBlock fromX="6" fromY="4" toX="8" toY="6" fromSegment="D" toSegment="A" length="28.0" maxSpeedfrom="30.0" maxSpeedto="30.0"/>
@@ -215,7 +231,7 @@
215231
<SimpleTrackBlock fromX="14" fromY="14" toX="16" toY="14" fromSegment="F" toSegment="A" length="20.0" maxSpeedfrom="25.0" maxSpeedto="20.0"/>
216232
<SimpleTrackBlock fromX="14" fromY="16" toX="16" toY="16" fromSegment="F" toSegment="A" length="20.0" maxSpeedfrom="25.0" maxSpeedto="20.0"/>
217233
<SimpleTrackBlock fromX="14" fromY="18" toX="16" toY="18" fromSegment="F" toSegment="A" length="20.0" maxSpeedfrom="25.0" maxSpeedto="20.0"/>
218-
<SimpleTrackBlock fromX="14" fromY="18" toX="16" toY="20" fromSegment="D" toSegment="A" length="28.0" maxSpeedfrom="25.0" maxSpeedto="20.0"/>
234+
<SimpleTrackBlock fromX="14" fromY="18" toX="16" toY="20" fromSegment="D" toSegment="B" length="28.0" maxSpeedfrom="25.0" maxSpeedto="20.0"/>
219235

220236
<!-- Platform zone (main tracks) -->
221237
<SimpleTrackBlock fromX="16" fromY="4" toX="42" toY="4" fromSegment="F" toSegment="A" length="260.0" maxSpeedfrom="20.0" maxSpeedto="20.0"/>
@@ -279,6 +295,7 @@
279295
<SimpleTrackBlock fromX="52" fromY="4" toX="54" toY="4" fromSegment="F" toSegment="A" length="20.0" maxSpeedfrom="40.0" maxSpeedto="40.0"/>
280296
<SimpleTrackBlock fromX="52" fromY="12" toX="54" toY="12" fromSegment="F" toSegment="A" length="20.0" maxSpeedfrom="40.0" maxSpeedto="40.0"/>
281297
<SimpleTrackBlock fromX="52" fromY="20" toX="60" toY="20" fromSegment="F" toSegment="A" length="80.0" maxSpeedfrom="30.0" maxSpeedto="30.0"/>
298+
<SimpleTrackBlock fromX="52" fromY="20" toX="52" toY="22" fromSegment="D" toSegment="A" length="20.0" maxSpeedfrom="25.0" maxSpeedto="25.0"/>
282299

283300
<!-- L4 signals to L5 DIVERGE switches -->
284301
<SimpleTrackBlock fromX="54" fromY="4" toX="56" toY="4" fromSegment="F" toSegment="A" length="20.0" maxSpeedfrom="40.0" maxSpeedto="40.0"/>
@@ -296,4 +313,7 @@
296313
<SimpleTrackBlock fromX="58" fromY="14" toX="60" toY="14" fromSegment="F" toSegment="A" length="20.0" maxSpeedfrom="35.0" maxSpeedto="40.0"/>
297314
<SimpleTrackBlock fromX="58" fromY="14" toX="60" toY="16" fromSegment="D" toSegment="A" length="28.0" maxSpeedfrom="30.0" maxSpeedto="40.0"/>
298315

316+
<!-- Car train terminal (Y=22) -->
317+
<SimpleTrackBlock fromX="52" fromY="22" toX="60" toY="22" fromSegment="F" toSegment="A" length="80.0" maxSpeedfrom="25.0" maxSpeedto="25.0"/>
318+
299319
</net>

0 commit comments

Comments
 (0)