-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathEEP_Inventar.html
More file actions
3561 lines (3037 loc) · 132 KB
/
Copy pathEEP_Inventar.html
File metadata and controls
3561 lines (3037 loc) · 132 KB
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
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<!DOCTYPE html>
<html lang="de">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<META http-equiv="Content-Script-Type" content="text/javascript">
<title>Inventar zu einer EEP-Anlage-Datei (.anl3) anzeigen</title>
<meta name="description" content="Dieses Programm nutzt die Javascript-Funktion DOMParser um eine .anl3-Datei von EEP, die aus XML aufgebaut ist, zu interpretieren und in das Document Object Model (DOM) umzuwandeln. Anschließend wird der Inhalt ausgegeben.">
<meta name="author" content="Frank Buchholz">
<meta name="keywords" content="EEP,.anl3,Inventar" />
<meta name="language" content="de" />
<link rel="icon" href="https://www.eepforum.de/images/favicon.ico" type="image/x-icon">
<script type="text/javascript" src="js/EEP_Texts.js"></script> <!-- Load texts and translations -->
<script type="text/javascript" src="js/EEP_Icons.js"></script> <!-- Load icons -->
<link rel="stylesheet" href="css/EEP_Texts.css">
<!--
Table Filter Library
https://www.tablefilter.com/
Filter Operators
https://github.com/koalyptus/TableFilter/wiki/4.-Filter-operators
Data types, column operations
https://www.tablefilter.com/data-types.html
-->
<!-- <script src="https://www.tablefilter.com/tablefilter/tablefilter.js"></script> -->
<script src="node_modules/tablefilter/dist/tablefilter/tablefilter.js"></script>
<!-- It's not neccessary to load the style sheet here because it's loaded automatically -->
<!-- <link href="https://www.tablefilter.com/tablefilter/style/tablefilter.css" rel="stylesheet"> -->
<!-- <link href="node_modules/tablefilter/dist/tablefilter/style/tablefilter.css" rel="stylesheet"> -->
<style> /* mod tablefilter */
/* no select/copy to clipboard. Firefox prevents selection even in case of Ctrl-A, but with Chrome it's not relilaby. */
.rdiv,
.fltrow {
user-select: none; /* no select/copy to clipboard (but it does not work with Ctrl+A) */
}
</style>
<!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
<script type="text/javascript"> // Load file
"use strict";
function loadFile(file) {
let fr; // upvalue for function processFile
if (typeof window.FileReader !== 'function') {
e(null, "p", EEP_Texts.getText("err", "001")); // "The file API isn't supported on this browser yet."
return;
};
if (!file) {
e(null, "p", EEP_Texts.getText("err", "002")); //"Please select a file before clicking 'Load'"
} else {
fr = { FileReader: new FileReader() }; // strict mode does not allow to unset read-only property 'result' directly, therfore we have to put the FileReader into an object
fr.FileReader.onload = processFile;
fr.FileReader.readAsText(file);
}
// process file (local function to get access to local variable fr)
function processFile() {
// Create parser
const parser = new DOMParser();
// Parse xml into DOM
const xmlDoc = parser.parseFromString(fr.FileReader.result, "text/xml");
//fr.FileReader.result = null; // we do not need the file content anymore, but in strict mode we cannot unset the property directly
delete fr.FileReader; // however, we can remove the whole thing (see http://perfectionkills.com/understanding-delete/ )
// Hide fileselector
document.getElementById('fileselector').classList.add('hidden');
// Show main area
document.getElementById('container').classList.remove('hidden');
// Show file name
document.getElementById('filename').textContent = file.name.substring(0, file.name.length -1 -1 -3);
// Process root node (documentElement always represents the root node)
process_node(xmlDoc.documentElement);
}
}
</script>
<!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
<script type="text/javascript" src="js/EEP_Signale_Daten.js"></script> <!-- Lade Zusatzdaten zu Signalen: Name, Signalstellungen -->
<script type="text/javascript" src="js/EEP_MovableAxis_Data.js"></script><!-- Lade Zusatzdaten zu beweglichen Achsen -->
<script type="text/javascript"> // process_node
"use strict";
function process_node(sutrackp) {
//console.log(sutrackp.nodeName);
// EEP: Static texts
const TrackSystemTexts = { // per TrackSystemNumber 1-6 (caution: GleissystemID could show other numbers, too)
1 : _('Eisenbahn'),
2 : _('Strassenbahn'),
3 : _('Strasse'),
4 : _('Wasserwege'),
5 : _('Steuerstrecken'), // nicht in EEP 9
6 : _('GBS'), // nicht in EEP 9
};
function TrackSystemText(GleissystemID) {
return TrackSystemTexts[TrackSystem[GleissystemID]];
}
const GebaeudesammlungText = { // GebaeudesammlungID
1 : _('Eisenbahn'),
2 : _('Strassenbahn'),
3 : _('Strasse'),
4 : _('Immobilien'),
5 : _('Landschaftselemente'),
6 : _('Sonstiges'), // (Wasserwege)
};
const Gleisart = { // clsid
"2E25C8E2-ADCD-469A-942E-7484556FF932" : _('Normal'),
"C889EADB-63B5-44A2-AAB9-457424CFF15F" : _('Weiche'),
"B0818DD8-5DFD-409F-8022-993FD3C90759" : _('3-Weg-Weiche'),
"06D80C90-4E4B-469B-BFE0-509A573EBC99" : _('Prellbock'),
};
/* Gleisstile:
http://up.picr.de/33875489iu.pdf
http://bahn.hersacher.de/splinekatalog/spkat_intro.htm
*/
const unsichtbar = [ // unsichtbare Gleisstile
17, // Wasser, Steuerstrecke
28, 34, 562, 1346, 5100, // Gleis
35, // Strassenbahn
36, // Strasse
5145, 5146, 5147, // Farm track
5602, // Fence
100000, // Kamerafahrweg
];
// GleisData
const GleisDataText = {
1 : _('Oberleitung'), // 1
2 : _('Weichenlaterne verstecken'), // 2
3 : _('Oberleitung') + ', ' + _('Weichenlaterne verstecken'), // 1 + 2
4 : _('Weichenlaterne rechts'), // 4
5 : _('Oberleitung') + ', ' + _('Weichenlaterne rechts'), // 1 + 4
8 : _('Weichenlaterne links'), // 8
9 : _('Oberleitung') + ', ' + _('Weichenlaterne links'), // 1 + 8
16 : _('Weichenlaterne als Immobilie/Straßen-T-Kreuzung'), // special
18 : _('Doppelkreuzungsweiche'), // special
19 : _('Oberleitung') + ', ' + _('Doppelkreuzungsweiche'), // 1 + special
20 : _('Straßen-T-Kreuzung'), // special
// 65 : _('special'), // 1 + 64 ?
// 69 : _('special'), // 1 + 4 + 64 ?
// 73 : _('special'), // 1 + 8 + 64 ?
};
// Weichenstellung
const WeichenstellungText = {
1 : _('Durchfahrt'),
2 : _('Abzweig'),
3 : _('KoAbzweig'),
5 : _('Spezial'),
};
const clsidPropelled = "16095510-0EAB-46AF-B294-CBB6D3516007"; // Angetriebenes Fahrzeug
const clsidNotPropelled = "7B0D0A3A-0882-44D6-B3E7-45D933A70F74"; // Nicht angetriebenes Fahrzeug
// Groesse der Anlage (alle Positionsangaben in m statt cm)
const Schandlaft = sutrackp.getElementsByTagName('Schandlaft')[0];
const Area = {
min : {
x : Schandlaft.getAttribute('posX') / 100,
y : Schandlaft.getAttribute('posY') / 100,
},
width : Math.abs(Schandlaft.getAttribute('posX') * 2 / 100),
height : Math.abs(Schandlaft.getAttribute('posY') * 2 / 100),
};
// Verwendeter Bereich der Anlage
const usedArea = {
min : {x : 0, y : 0, z : 0},
max : {x : 0, y : 0, z : 0},
};
// Variable für die Ausgabe
let div;
div = e(null, 'div' );
div.classList.add('Intro');
let EEPversion;
const Version = sutrackp.getElementsByTagName('Version')[0];
// EEP Version = 15, Anzahl Objekte = 733, Breite = 1007 m, Tiefe = 607 m
if (Version) {
EEPversion = Version.getAttribute('EEP');
e(div, 'p',
`${_('EEP-Version')} = ${Version.getAttribute('EEP')}, `
+ `${_('Anzahl Objekte')} = ${Version.getAttribute('No3DMs')}, `
+ `${_('Breite')} = ${Area.width.toFixed(0)} m, `
+ `${_('Tiefe')} = ${Area.height.toFixed(0)} m`
);
}
// Maps for direct access
const GleisMap = new Map(); // Verwendung: Gleis = GleisMap.get(GleissystemID).get(GleisID))
const KollektorMap = new Map(); // Verwendung: Kollektor = KollektorMap.get(KollektorID)
// Signale und Weichen als Kontaktziele
const KontaktZiele = {};
// Start-Signale zu Ende-Signalen
const EndRouteSignals = {};
// Sortable arrays
const
Weichen = [], // ID (der Weiche), Gleissystem, Gleis
Signale = [], // ID (des Signals), Gleissystem, Gleis, Name, Meldung
Kontakte = [], // SetType, Gleissystem, Gleis, Kontakt
virtualConnections = [], // Gleissystem, Gleis1, Anschluss1, Gleis2, Anschluss
waitingTrains = [], // ID (des Zugverbandes), speed
Zugverbaende = new Map(),//[], // ID (des Zugverbandes), Name, Gleisort, Fahrzeuge
Rollmaterialien = [], // Name (des Rollmaterials), Rollmaterial, Zugverband
Gleisstile = [], // Gleisstil, Datei, Anzahl
Gleisobjekte = [], // ID, [{GleissystemID, GleisID}], ImmoIdx, gsbname
Routen = [],
Sounds = [],
Depots = [], // ID, [{TTrainId, TRouteId, TSpeed, TTime, TDir}]
Kameras = [], // KameraID, Name, Kamera
GBS_table = []; // Gleisbildstellpulte
const KameraNamen = {}; // Verwendung: Name = KameraNamen[index];
const TrackSystem = {}; // Relationship between GleissystemID and TrackSystemNumber
// Schaltverbindungen zwischen Signalen und/oder Weichen
const Couplings = {}; // SignalData, Schaltverbindung zu einem anderen Signal bzw. Weiche
// Extract data from Gleissystem
for (const Gleissystem of sutrackp.getElementsByTagName('Gleissystem')) {
// Use both attributes GleissystemID and TrackSystemNumber if available
let GleissystemID = Gleissystem.getAttribute('GleissystemID');
let TrackSystemNumber = Gleissystem.getAttribute('TrackSystemNumber');
if (GleissystemID && !TrackSystemNumber) {
TrackSystemNumber = GleissystemID;
} else if (!GleissystemID && TrackSystemNumber) {
GleissystemID = TrackSystemNumber;
}
// Store relationship between GleissystemID and TrackSystemNumber
if (!TrackSystem[GleissystemID]) {
TrackSystem[GleissystemID] = TrackSystemNumber;
}
GleisMap.set(GleissystemID, new Map());
for (const Gleis of Gleissystem.getElementsByTagName('Gleis')) {
/* Beispiel bis Version EEP 15:
<Gleis GleisID="1" clsid="2E25C8E2-ADCD-469A-942E-7484556FF932" data="0" scale="1" ElectSideS="0" ElectSideE="0" stil="1353" gsbname="\Gleisstile\Gleise\Beton2_Sch_C_LW1.3dm" LockEd="0">
<Dreibein>
<Vektor x="-12359.88" y="-1854.389" z="30">Pos</Vektor>
<Vektor x="0.699066" y="-0.715057" z="0">Dir</Vektor>
<Vektor x="0.715057" y="0.699066" z="0">Nor</Vektor>
<Vektor x="-0" y="0" z="1">Bin</Vektor>
</Dreibein>
<Anfangsfuehrungsverdrehung Wert="0"/>
<Charakteristik Kruemmung="0" Torsion="0" Fuehrungsverdrehung="-0" Kurve="0" Laenge="2580"/>
</Gleis>
Beispiel ab Version EEP 16:
https://www.trendverlag.com/Schema/EEP.xsd
https://www.trendverlag.com/Schema/Common.xsd
https://www.trendverlag.com/Schema/traxML.xsd
https://www.trendverlag.com/Schema/Train.xsd
<Gleis GleisID="314" clsid="2E25C8E2-ADCD-469A-942E-7484556FF932" data="0" scale="0.6" ElectSideS="0" ElectSideE="0" OneWay="1" stil="2263" gsbname="\Gleisstile\Strassen\Asphaltstrasse_01_RE1.3dm" LockEd="0">
<Frame>...</Frame>
<Interval near="0" far="20.34561"/>
gefolgt von einer der Varianten:
<Curve>
<EEPCurve Kruemmung="0" Torsion="0" Fuehrungsverdrehung="-0" Laenge="1768" Kurve="0" Anfangsfuehrungsverdrehung="0">
<Frame>...</Frame>
</EEPCurve>
</Curve>
<Curve>
<Line>
<VectorBundle>
<Position x="0" y="0" z="0"/>
<Vector dx="1" dy="0" dz="0"/>
</VectorBundle>
<Vector dx="0" dy="0" dz="1"/>
</Line>
</Curve>
<Curve>
<Arc>
<VectorBundle2>
<Position x="0" y="16.40503" z="0"/>
<Vector dx="1" dy="0" dz="0"/>
<Vector dx="0" dy="16.40503" dz="0"/>
</VectorBundle2>
</Arc>
</Curve>
<Curve>
<Helix a="100.4987" b="2.017408">
<VectorBundle2>
<Position x="0" y="0" z="0"/>
<Vector dx="1" dy="0" dz="0"/>
<Vector dx="0" dy="1" dz="0"/>
</VectorBundle2>
</Helix>
</Curve>
<Curve>
<Rotator a="7.933316E-02" b="-0"/>
</Curve>
und abgeschlossen mit
<Twist>...</Twist>
</Gleis>
mit
<Frame>
<Position x="268582" y="-155050" z="29.99999"/>
<Vector dx="1" dy="0" dz="0"/>
<Vector dx="0" dy="1" dz="0"/>
<Vector dx="0" dy="0" dz="1"/>
</Frame>
*/
const GleisID = Gleis.getAttribute('GleisID');
GleisMap.get(GleissystemID).set(GleisID, Gleis);
let Laenge;
if (EEPversion && EEPversion >= 16) { /* EEP 16 */
const Frame = Gleis.getElementsByTagName("Frame")[0];
const FramePos = Frame.getElementsByTagName("Position")[0];
const Interval = Gleis.getElementsByTagName("Interval")[0];
const near = +Interval.getAttribute("near");
const far = +Interval.getAttribute("far");
Laenge = Math.abs( far - near );
Gleis.PosX = +FramePos.getAttribute("x");
Gleis.PosY = +FramePos.getAttribute("y");
Gleis.PosZ = +FramePos.getAttribute("z");
// Identify CurveType
const Curve = Gleis.getElementsByTagName("Curve")[0];
const EEPCurve = Curve.getElementsByTagName("EEPCurve")[0];
const Line = Curve.getElementsByTagName("Line")[0];
const Arc = Curve.getElementsByTagName("Arc")[0];
const Helix = Curve.getElementsByTagName("Helix")[0];
const Clothoid = Curve.getElementsByTagName("Clothoid")[0];
const Cubic = Curve.getElementsByTagName("Cubic")[0];
const Rotator = Curve.getElementsByTagName("Rotator")[0];
const RotatorChain = Curve.getElementsByTagName("RotatorChain")[0];
// Depending on CurveType some adjustments might be required!
// Let's start with a simple case.
if (EEPCurve) { // EEPCurve = Line or Arc
// assumption: Gleis.Frame does not contain relevant data
if (Math.abs(+FramePos.getAttribute("x")) > 1 ) {
console.log(`EEPCurve ${TrackSystemText(GleissystemID)} ${GleisID} has Frame value`);
}
const Frame2 = EEPCurve.getElementsByTagName("Frame")[0];
const Frame2Pos = Frame2.getElementsByTagName("Position")[0];
Gleis.PosX = +Frame2Pos.getAttribute("x") / 100;
Gleis.PosY = +Frame2Pos.getAttribute("y") / 100;
Gleis.PosZ = +Frame2Pos.getAttribute("z") / 100;
} else if (Line) {
const Position = Line.getElementsByTagName("VectorBundle")[0].getElementsByTagName("Position")[0];
const PosX2 = +Position.getAttribute("x"); // dX
const PosY2 = +Position.getAttribute("y"); // dY
const PosZ2 = +Position.getAttribute("z"); // dZ
Gleis.PosX += PosX2;
Gleis.PosY += PosY2;
Gleis.PosZ += PosZ2;
} else if (Arc) {
// relative position of center point of the Arc
const Pos = Arc.getElementsByTagName("VectorBundle2")[0].getElementsByTagName("Position")[0];
const PosX2 = +Pos.getAttribute("x");
const PosY2 = +Pos.getAttribute("y");
const PosZ2 = +Pos.getAttribute("z");
// The distance of the center point = curve radius is the length of the second vector
const Nor = Arc.getElementsByTagName("VectorBundle2")[0].getElementsByTagName("Vector")[1];
const MidX2 = +Nor.getAttribute("dx");
const MidY2 = +Nor.getAttribute("dy");
const MidZ2 = +Nor.getAttribute("dz");
// Translate by distance of mid point of circle
Gleis.PosX += PosX2 - MidX2;
Gleis.PosY += PosY2 - MidY2;
Gleis.PosZ += PosZ2 - MidZ2;
} else if (Helix) {
// ... see more calculations in EEP_Gleisplan.html
} else if (Clothoid) {
// ... see more calculations in EEP_Gleisplan.html
} else if (Cubic) {
// If Frame.Position = 0 and Cubic.Position <> 0 then treat Cubic.Position as the starting point of the curve
const PositionA = Cubic.getElementsByTagName("Position")[0];
const pX = +PositionA.getAttribute("x");
const pY = +PositionA.getAttribute("y");
const pZ = +PositionA.getAttribute("z");
Gleis.PosX += pX;
Gleis.PosY += pY;
Gleis.PosZ += pZ;
} else if (Rotator) {
// A Rotator looks like an Arc if we can ignore the z-axis
} else if (RotatorChain) {
console.log(`RotatorChain not supported yet: ${TrackSystemText(GleissystemID)} ${GleisID}`);
continue;
} else { // curve not supported yet
console.log(`other curve type not supported yet: ${TrackSystemText(GleissystemID)} ${GleisID}`);
continue;
}
} else { /* EEP 15 */
const Charakteristik = Gleis.getElementsByTagName("Charakteristik")[0];
const Dreibein = Gleis.getElementsByTagName("Dreibein")[0];
const FramePos = Dreibein.getElementsByTagName("Vektor")[0];
Laenge = +Charakteristik.getAttribute("Laenge") / 100;
Gleis.PosX = +FramePos.getAttribute("x") / 100;
Gleis.PosY = +FramePos.getAttribute("y") / 100;
Gleis.PosZ = +FramePos.getAttribute("z") / 100;
} /* EEP 15 */
// Verwendeten Bereich der Anlage anpassen (mit zusätzlichen Platz in der Ebene für die Position der Endpunkte)
usedArea.min.x = Math.min( usedArea.min.x, Gleis.PosX - Laenge );
usedArea.min.y = Math.min( usedArea.min.y, Gleis.PosY - Laenge );
usedArea.min.z = Math.min( usedArea.min.z, Gleis.PosZ );
usedArea.max.x = Math.max( usedArea.max.x, Gleis.PosX + Laenge );
usedArea.max.y = Math.max( usedArea.max.y, Gleis.PosY + Laenge );
usedArea.max.z = Math.max( usedArea.max.z, Gleis.PosZ );
// Weichen
const WeicheID = Gleis.getAttribute('Key_Id');
if (WeicheID) { // Ist das Gleis eine Weiche?
Weichen.push({
ID : Number(WeicheID), // sort field
GleissystemID : GleissystemID,
GleisID : GleisID,
});
// Schaltverbindung zu einem anderen Signal bzw. Weiche
collectCouplings( Gleis, +WeicheID );
}
// Weichen mit KontaktZiel
const KontaktZielEntry = Gleis.getElementsByTagName('KontaktZiel')[0];
if (KontaktZielEntry) {
const KontaktZiel = KontaktZielEntry.textContent;
if (KontaktZiel != null && KontaktZiel != 0) {
const WeicheID = Gleis.getAttribute('Key_Id');
KontaktZiele[KontaktZiel] = {
Typ : _('Weiche'),
ID : WeicheID,
GleissystemID : GleissystemID,
GleisID : GleisID,
}
}
}
// Signale
for (const Meldung of Gleis.getElementsByTagName('Meldung')) {
// get potential sort fields
const SignalID = Meldung.getAttribute('Key_Id');
const Name = Meldung.getAttribute('name');
let countTrains = 0;
// Signale mit wartenden Zügen
for (const Wartender of Meldung.getElementsByTagName('Wartender')) {
countTrains += 1;
const ZugID = Wartender.getAttribute('zugverbandID');
const targetSpeed = +Wartender.getAttribute('sollgeschwindigkeit') * 3600 / 1000; // km/h
waitingTrains[ZugID] = {
SignalID : SignalID,
targetSpeed : targetSpeed,
};
}
Signale.push({
ID : Number(SignalID), // sort fields
Name : Name,
GleissystemID : GleissystemID,
GleisID : GleisID,
Meldung : Meldung,
countTrains : countTrains,
});
// Signale mit KontaktZiel
const KontaktZielEntry = Meldung.getElementsByTagName('KontaktZiel')[0];
if (KontaktZielEntry) {
const KontaktZiel = KontaktZielEntry.textContent;
if (KontaktZiel != null && KontaktZiel != 0) {
KontaktZiele[KontaktZiel] = {
Typ : _('Signal'),
ID : SignalID,
GleissystemID : GleissystemID,
GleisID : GleisID,
}
}
}
// Signale für Fahrstraßen: Suche Start-Signale zu Ende-Signalen
const Routes = Meldung.getAttribute('Routes');
const RouteList = Meldung.getElementsByTagName("Route");
let RoutesText = '';
for (const Route of RouteList) {
const Target = Route.getAttribute('Target');
const Color = Route.getAttribute('Color');
if (!EndRouteSignals[Target]) {
EndRouteSignals[Target] = []
}
EndRouteSignals[Target].push({
StartSignal : SignalID,
Color : Color,
})
}
// Schaltverbindung zu einem anderen Signal bzw. Weiche
const Signal = Meldung.getElementsByTagName('Signal')[0];
collectCouplings( Signal, +SignalID );
}
// Kontakte
for (const Kontakt of Gleis.getElementsByTagName('Kontakt')) {
// get potential sort fields
const SetType = Kontakt.getAttribute('SetType');
Kontakte.push({
ID : Number(SetType), // sort fields
GleissystemID : GleissystemID,
GleisID : GleisID,
Kontakt : Kontakt,
KontaktID : Kontakte.length, // arbitrary identfication
});
// Ausfahrt Zug-Depot
if (SetType == 1536) {
const DepotID = Kontakt.getAttribute('SetValue');
const Trains = [];
for (let i = 0; true; i++) {
const TTrainId = Kontakt.getAttribute('TTrainId_'+i); // Verweis auf Zugverband
if (!TTrainId) {
break;
}
const TRouteId = Kontakt.getAttribute('TRouteId_'+i); // Verweis auf Route
const TSpeed = Kontakt.getAttribute('TSpeed_'+i); // in km/h
const TTime = Kontakt.getAttribute('TTime_'+i); // individuelle Abfahrtzeit (Sekunden ab Mitternacht)
const TDir = Kontakt.getAttribute('TDir_'+i); // 0: Richtung eins, 1: Richtung zwei
Trains.push({
TTrainId : TTrainId,
TRouteId : TRouteId,
TSpeed : TSpeed,
TTime : TTime,
TDir : TDir,
})
}
const Wartender = Kontakt.getAttribute('Wartender'); // Auslass-Auto-Zeit
const SignalData = Kontakt.getAttribute('SignalData'); // bzw. Auslass-SignalId
Depots.push({
ID : DepotID,
Wartender : Wartender,
SignalData : SignalData,
GleissystemID : GleissystemID,
GleisID : GleisID,
Trains : Trains,
});
}
}
// Gleisstile
// <Gleis stil="1353" gsbname="\Gleisstile\Gleise\Beton2_Sch_C_LW1.3dm">
const StilID = Number(Gleis.getAttribute('stil'));
const File = Gleis.getAttribute('gsbname');
if (StilID == 0) { // What should we do if there is no value?
}
const index = Gleisstile.findIndex(function (element) {
return (element.ID == StilID && element.Name == File);
});
if (index == -1) {
Gleisstile.push({
ID : StilID,
Name : File,
Anzahl : 1,
})
} else {
Gleisstile[index].Anzahl = Gleisstile[index].Anzahl + 1;
}
}
for (const Gleisverbindung of Gleissystem.getElementsByTagName('Gleisverbindung')) {
// <Gleisverbindung GleisID1="1" Anschluss1="Anfang" GleisID2="2" Anschluss2="Ende" Flags="1"/>
const GleisID1 = Gleisverbindung.getAttribute('GleisID1');
const Anschluss1 = Gleisverbindung.getAttribute('Anschluss1');
const GleisID2 = Gleisverbindung.getAttribute('GleisID2');
const Anschluss2 = Gleisverbindung.getAttribute('Anschluss2');
const Flags = Gleisverbindung.getAttribute('Flags');
if (Flags && Flags == 1) { // Virtual connection
virtualConnections.push({
//ID : ID, // sort fields
//Name : Name,
GleissystemID : GleissystemID,
GleisID1 : GleisID1,
Anschluss1 : Anschluss1,
GleisID2 : GleisID2,
Anschluss2 : Anschluss2,
});
}
}
}
// Verwendeter Bereich der Anlage anpassen (nicht größer als die Anlage selber)
usedArea.min.x = Math.max( usedArea.min.x, Area.min.x );
usedArea.min.y = Math.max( usedArea.min.y, Area.min.y );
usedArea.max.x = Math.min( usedArea.max.x, Area.min.x + Area.width );
usedArea.max.y = Math.min( usedArea.max.y, Area.min.y + Area.height );
// Extract data from GBS
for (const Gebaeudesammlung of sutrackp.getElementsByTagName('Gebaeudesammlung')) {
const GebaudesammlungID = Gebaeudesammlung.getAttribute('GebaudesammlungID');
if ( GebaudesammlungID != 4 ) { continue; }
for (const Immobile of Gebaeudesammlung.getElementsByTagName('Immobile')) {
const GBSObjects = Immobile.getElementsByTagName('GBSObject');
if ( GBSObjects.length == 0 ) { continue; }
const gsbname = Immobile.getAttribute('gsbname');
const ImmoIdx = Immobile.getAttribute('ImmoIdx');
const Dreibein = Immobile.getElementsByTagName('Dreibein')[0];
const Vektor = Dreibein.getElementsByTagName('Vektor')[0];
const PosX = +Vektor.getAttribute('x') / 100;
const PosY = +Vektor.getAttribute('y') / 100;
const PosZ = +Vektor.getAttribute('z') / 100;
const GBSObject = GBSObjects[0];
const ID = GBSObject.getAttribute('id');
const Name = GBSObject.getAttribute('Name');
const GleissystemID = GBSObject.getAttribute('GleissystemID');
const Width = +GBSObject.getAttribute('Width'); // Breite des GBS
const Height = +GBSObject.getAttribute('Height'); // Höhe des GBS
const Tiles = [];
for (const GBSElement of GBSObject.getElementsByTagName('GBSElement')) {
const Tile = {};
Tile.x = +GBSElement.getAttribute('x'); // Position des GBS-Elements
Tile.y = +GBSElement.getAttribute('y'); // Position des GBS-Elements
Tile.ID = ( Tile.y - 1 ) * Width + Tile.x; // ID to sort by position
Tile.Kammera = GBSElement.getAttribute('Kammera'); // Gleissymbol 36 = Kammera
// Get track symbols
const Modell = GBSElement.getElementsByTagName('Modell')[0];
const Table = Modell.getElementsByTagName('Table')[0];
const Count = +Table.getAttribute('Count');
Tile.TrackSymbol = +Table.getAttribute('e0'); // Codenummer des Gleissymbols
Tile.Signal_left = +Table.getAttribute('e1'); // Codenummer für Signalsymbol Links
Tile.Signal_mid = +Table.getAttribute('e2'); // Codenummer für Signalsymbol Mitte
Tile.Signal_right = +Table.getAttribute('e3'); // Codenummer für Signalsymbol Rechts
// Get switch ID
for (const TakeSwitch of GBSElement.getElementsByTagName('TakeSwitch')) { // one optional entry
const Table = TakeSwitch.getElementsByTagName('Table')[0];
const Count = +Table.getAttribute('Count');
Tile.SwitchID = +Table.getAttribute('e0'); // Switch ID
}
// Get signal ID
for (const Signal of GBSElement.getElementsByTagName('Signal')) { // one optional entry
const Table = Signal.getElementsByTagName('Table')[0];
const Count = +Table.getAttribute('Count');
Tile.SignalID_left = +Table.getAttribute('e0'); // Signal-ID Links
Tile.SignalID_mid = +Table.getAttribute('e1'); // Signal-ID Mitte
Tile.SignalID_right = +Table.getAttribute('e2'); // Signal-ID Rechts
}
// Get tracks
Tile.Tracks = [];
for (let i = 1; i <= 4; i++) {
const Tracks = GBSElement.getElementsByTagName('Tracks' + i); // Tracks1 - Tracks4
if ( Tracks.length == 0 ) {
Tile.Tracks.push( 0 );
continue;
}
const Table = Tracks[0].getElementsByTagName('Table')[0];
const Count = +Table.getAttribute('Count');
const GleisID = +Table.getAttribute('e0'); // GleisID für die Besetztmeldung
Tile.Tracks.push( GleisID );
}
// Store GBSElement
Tiles.push( Tile );
}
// Store GBSObject
GBS_table.push( {
ID : ID,
Name : ( Name ? Name : "GBS_" + ID ),
gsbname : gsbname,
ImmoIdx : ImmoIdx,
PosX : PosX,
PosY : PosY,
PosZ : PosZ,
GleissystemID : GleissystemID,
Width : Width,
Height : Height,
Tiles : Tiles,
});
}
}
// Extract data from Fuhrpark
const Fuhrpark = sutrackp.getElementsByTagName('Fuhrpark')[0];
for (const Zugverband of Fuhrpark.getElementsByTagName('Zugverband')) {
const ZugID = Zugverband.getAttribute('ZugID');
const Name = Zugverband.getAttribute('name');
const Gleisort = Zugverband.getElementsByTagName('Gleisort')[0];
const Fahrzeuge = [];
const ZugverbandRollmaterial = Zugverband.getElementsByTagName('Rollmaterial');
let Position = 0;
for (const Rollmaterial of ZugverbandRollmaterial) {
Fahrzeuge.push({
Rollmaterial : Rollmaterial,
});
Position += 1;
Rollmaterialien.push({
Name : Rollmaterial.getAttribute('name'), // sort field
Rollmaterial : Rollmaterial,
Zugverband : Zugverband,
Position : Position,
});
}
Zugverbaende.set(Number(ZugID), { // Zugverbaende.push({
ID : Number(ZugID), // sort fields
Name : Name,
Zugverband : Zugverband,
Gleisort : Gleisort,
Anzahl : ZugverbandRollmaterial.length,
Fahrzeuge : Fahrzeuge,
});
}
// Extract Gleisobjekt data from Kollektor
for (const Kollektor of sutrackp.getElementsByTagName('Kollektor')) {
const KollektorID = Number(Kollektor.getAttribute('id'));
KollektorMap.set(KollektorID, Kollektor);
Gleisobjekte.push({
ID : KollektorID, // sort field
Kollektor : Kollektor,
});
// Cross reference
for (const GleisEntry of Kollektor.getElementsByTagName("Gleis")) {
const GleissystemID = GleisEntry.getAttribute('gleissystemID');
const GleisID = GleisEntry.getAttribute('gleisID');
const Gleis = GleisMap.get(GleissystemID).get(GleisID);
Gleis.Kollektor = Kollektor;
}
}
// Extract Gleisobjekt data from Gebaeudesammlung
for (const Gebaeudesammlung of sutrackp.getElementsByTagName('Gebaeudesammlung')) {
const GebaudesammlungID = Gebaeudesammlung.getAttribute('GebaudesammlungID');
for (const Immobile of Gebaeudesammlung.getElementsByTagName('Immobile')) {
const ImmoIdx = Immobile.getAttribute('ImmoIdx');
const KollektorID = Number(Immobile.getAttribute('kollektorID'));
const gsbname = Immobile.getAttribute('gsbname');
if (KollektorID) {
const Kollektor = KollektorMap.get(KollektorID);
if (Kollektor) {
Kollektor.GebaudesammlungID = GebaudesammlungID;
Kollektor.Immobile = Immobile;
Kollektor.ImmoIdx = ImmoIdx;
Kollektor.gsbname = gsbname;
}
}
}
}
// Extract data from Options
/*
<Options RouteItems="4" SoundItems="0" RouteId_0="1" RouteName_0="Strecke" RouteId_1="2" RouteName_1="Rangieren" RouteId_2="3" RouteName_2="Tausch" RouteId_3="4" RouteName_3="Weiterfahrt" FavTrainCB="0"/>
*/
const Options = sutrackp.getElementsByTagName('Options')[0];
const RouteItems = +Options.getAttribute('RouteItems');
for (let i = 0; i < RouteItems; i++) {
const RouteID = Options.getAttribute('RouteId_' + i);
const RouteName = Options.getAttribute('RouteName_' + i);
Routen[RouteID] = RouteName;
}
const SoundItems = +Options.getAttribute('SoundItems');
for (let i = 0; i < SoundItems; i++) {
const SoundID = Options.getAttribute('SndId_' + i);
const SoundName = Options.getAttribute('SndName_' + i);
Sounds[SoundID] = SoundName;
}
// Extract data from Kamerasammlung
const Kamerasammlung = sutrackp.getElementsByTagName('Kammerasammlung')[0];
const KameraList = Kamerasammlung.getElementsByTagName('Kammera');
let KameraID = 0;
for (const Kamera of KameraList) {
KameraID += 1;
const Name = Kamera.getAttribute('name');
/* Ignore not-used cameras
Should we check the default name or the default position to identify such cameras?
Some observations:
- The default name is language dependent, see file eep.lng entry [OTHER]CAMERA_NAME
- Pos and Dir seem to have quite specific values
- Dir is not a unit vector
- Nor and Bin values might vary
<Kammera name="Leer" DOF="0" Dynamic="0" FocalIdx="193" FStopIdx="13" SubDist="100" DOFSubDist="100" DOFShiftNear="9.65" DOFShiftFar="12.965" FOV="0" TakeSwitch="0">
<Dreibein>
<Vektor x="6000" y="-6000" z="400">Pos</Vektor>
<Vektor x="-6000" y="6000" z="-400">Dir</Vektor>
<Vektor x="-0.7071068" y="-0.7071068" z="0">Nor</Vektor>
<Vektor x="-3.329636E-02" y="3.329636E-02" z="0.9988908">Bin</Vektor>
</Dreibein>
</Kammera>
*/
if (Name === "Leer" || Name === "blank" || Name === "Vide" ) {
continue;
}
Kameras.push({
ID : KameraID, // sort fields
Name : Name,
Kamera : Kamera,
});
KameraNamen[KameraID] = Name;
}
<!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
// Anzeige des verwendeten Bereichs
e(div, 'p',
`${_('Verwendeter Bereich')}: (${(usedArea.min.x).toFixed(0)} m, ${(usedArea.min.y).toFixed(0)} m) .. (${(usedArea.max.x).toFixed(0)} m, ${(usedArea.max.y).toFixed(0)} m)`
);
e(div, 'p',
`${_('Min./max Höhe')}: ${(usedArea.min.z).toFixed(0)} m .. ${(usedArea.max.z).toFixed(0) } m`
);
<!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
div = createSwitchesTable(); // Anzeige der Weichen
/* not needed anymore
if (Weichen.length > 0) {
e(div, 'p', 'SignalData: ' + _('Weiche ist verknüpft mit Signal/Weiche'));
e(div, 'p', 'NextSigFn1: ' + _('Wenn Weiche auf "Durchfahrt" steht dann verknüpfte ID in angegebene Stellung schalten'));
e(div, 'p', 'NextSigFn2: ' + _('Wenn Weiche auf "Abzweig" steht dann verknüpfte ID in angegebene Stellung schalten'));
}
*/
div = createSignalsTable(); // Anzeige der Signale
/* not needed anymore
if (Signale.length > 0) {
e(div, 'p', 'SignalData: ' + _('Signal ist verknüpft mit Signal/Weiche'));
}
*/
div = createContactsTable(); // Anzeige der Kontakte
div = createVirtualConnectionsTable(); // Anzeige der virtuellen Verbindungen
div = createTrainsTable(); // Anzeige der Zugverbände
if (Depots.length > 0) { // Hide empty Depot
div = createDepotsTable(); // Anzeige der Depots
div = createDepotTrainsTable(); // Anzeige der Zugverbände in Depots
}
div = createVehiclesTable(); // Anzeige der Rollmaterialien
div = createTrackTypeTable(); // Anzeige der Gleisstile
div = createTrackObjectsTable(); // Anzeige der Gleisobjekte
div = createCamerasTable(); // Anzeige der Kameras
div = createGBSTable(); // Anzeige der GBS
showGBS( GBS_table, div );
// Allow to sort all tables by clicking on the header
prepareSort();
<!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
// Schaltverbindungen zwischen Signalen und/oder Weichen
function collectCouplings( Object, SourceID ) {
const TargetID = +Object.getAttribute('SignalData'); // Schaltverbindung zu einem anderen Signal bzw. Weiche
if (TargetID) {
const Connections = [];
// gekoppelte Stellungen für Signale
const Count = Object.getAttribute('Count'); // Anzahl der Attribute Fn01, Fn02,...
if (Count) {
for (let i = 1; i <= Count; i++) {
const SourcePos = i;
const TargetPos = Object.getAttribute('Fn' + i.toString().padStart(2, '0')); // gekoppelte Stellung
if (TargetPos && TargetPos != 0) {
Connections.push({ SourcePos : SourcePos, TargetPos : +TargetPos, });
}
}
}
// Gekoppelte Stellungen für Weichen
for (let i = 1; i <= 5; i++) { // Attribute NextSigFn1 (gerade), NextSigFn2 (Abzweig), NextSigFn3, NextSigFn4, NextSigFn5
const SourcePos = i;
const TargetPos = Object.getAttribute('NextSigFn' + i); // gekoppelte Stellung
if (TargetPos && TargetPos != 0) {
Connections.push({ SourcePos : SourcePos, TargetPos : +TargetPos, });
}
}
if (!Couplings[SourceID]) {
Couplings[SourceID] = [];
}
Couplings[SourceID].push({ TargetID : TargetID, Connections : Connections, });
if (!Couplings[TargetID]) {
Couplings[TargetID] = [];
}
Couplings[TargetID].push({ SourceID : SourceID, Connections : Connections, });
}
} // function collectCouplings
function getCouplingText( SourceID ) {