Skip to content

Commit 30457e4

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 17f5bfe commit 30457e4

3 files changed

Lines changed: 238 additions & 5 deletions

File tree

core/src/jvmTest/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
@@ -1087,6 +1087,216 @@ class XMLContextFactoryTest : KoinTestBase() {
10871087
}
10881088
}
10891089

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