-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathEEP2Lua.lua
1236 lines (984 loc) · 38.3 KB
/
EEP2Lua.lua
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
--[[
Datei: EEP2Lua.lua
Das Modul EEP2Lua liest und konvertiert eine EEP-Anlage-Datei in Lua-Tabellen.
Achtung: Wenn man dieses Modul in EEP fuer die aktuellen Anlage nutzen will, z.B. weil man die Zugliste ermitteln will, dann muss man aufpassen:
Es kann nicht der aktuelle Zustand der aktuellen EEP-Anlage gelesen werden sondern nur der zuletzt gespeicherte Zustand!
Man muss also selber sicherstellen, dass die EEP-Anlage vor Aufruf dieses Moduls gespeichert wurde!
Aufruf:
--local Anlage = require("EEP2Lua"){ debug = true } -- mit erweiterter Protokollausgabe
local Anlage = require("EEP2Lua"){}
local sutrackp = Anlage.loadFile(input_file)
Der optionale Rueckgabewert sutrackp enthaelt die Tabellenstruktur in der Form wie sie xml2lua liefert.
Nach dem Aufruf von loadFile koennen aufbereitete Informationen zur EEP-Anlage abgefragt werden:
Anlage.Beschreibung
Anlage.GleissystemText
Anlage.Zugverbaende
Anlage.Rollmaterialien
usw.
Die Feldlisten entnimmt man am Besten den unten markierten Programmstellen oder dem Beispiel-Skript EEP_Inventar.lua .
(Die Feldlisten hier aufzufuehren waere mir bei Änderungen zu fehlertraechtig.)
Die Lua-Datei EEP_Inventar.lua sollte im LUA-Ordner der EEP-Programminstallation angelegt werden und kann sowohl
aus EEP heraus mit require('EEP_Inventar') wie auch standalone z.B. in SciTE in gestartet werden.
Das Modul benoetigt Teile des Paketes xml2lua
Quelle:
https://github.com/manoelcampos/xml2lua
Benoetigte Dateien im LUA-Ordner:
xml2lua.lua
XmlParser.lua
xmlhandler/tree.lua
--
Eine EEP-Anlagedatei und die daraus abgeleitetet sutrackp-Tabellenstruktur hat folgenden Aufbau:
sutrackp
Version
Gleissystem (1..6)
Dreibein
Vektor ([1]=Pos=Position, [2]=Dir=Richtung, [3]=Nor=Normale, [4]=Bin=Binormale)
Gleis (multiple)
Dreibein
Vektor ([1]=Pos=Position, [2]=Dir=Richtung, [3]=Nor=Normale, [4]=Bin=Binormale)
Anfangsfuehrungsverdrehung
Charakteristik
Kontakt
Gleisverbindung
Kollektor
Dreibein
Vektor (s.o.)
Gleis (mehrfach)
Fuhrpark
Zugverband (0..n)
Gleisort
Rollmaterial (0..n)
Zugmaschine
Motor
Getriebe
Modell
Achse (mehrfach)
Achse
Gebaeudesammlung (1..6)
Immobile
BeweglichesTeil (mehrfach)
Dreibein
Vektor (s.o.)
Modell
Achse (mehrfach)
Gleisbildstellpultsammlung
...
AnimRoll
Goods
CrowdSammlung
...
Kammerasammlung
Kammera (mehrfach)
Settings (enthaelt u.a. die Tastatur-Hotkeys)
Options (enthaelt SoundItems und die Routenbezeichnungen)
Weather
TimeTable
EEPLua (LUAS=Pruefsumme fuer Lua-Datei, LUAPath=Dateiname)
Kamera3D
Schandlaft
Beschreibung
Die Mitte der Anlage hat die Koordinaten x = 0 und y = 0.
--]]
-- Extend path to search files in folder xml2lua, too
if string.find(package.path, "xml2lua" , 1 , true) ~= true then
package.path = package.path .. ";.\\lua\\xml2lua\\?.lua"
end
-- Lokale Daten und Funktionen ----------------------------------------
-- Ausfuehrliche Protokollierung zur Fehleranalyse (true/false)
local debug = false
-- Maximale Anzahl der gespeicherten Positionen von Dateien
-- (Das wird begrenzt, da dies bei Landschaftselementen eine sehr grosse Zahl sein koennte)
local MaxPositions = 10
-- Statische Texte
local GleissystemText = { -- GleissystemID
[1] = 'Eisenbahn',
[2] = 'Strassenbahn',
[3] = 'Strasse',
[4] = 'Wasserwege',
[5] = 'Steuerstrecken', -- nicht in EEP 9
[6] = 'GBS' -- nicht in EEP 9
}
local Gleisart = { -- clsid
['2E25C8E2-ADCD-469A-942E-7484556FF932'] = 'Normales Gleis',
['C889EADB-63B5-44A2-AAB9-457424CFF15F'] = '2-Weg-Weiche',
['B0818DD8-5DFD-409F-8022-993FD3C90759'] = '3-Weg-Weiche',
['06D80C90-4E4B-469B-BFE0-509A573EBC99'] = 'Prellbock-Gleis'
}
local KontaktTyp = { -- SetType ist ein Bit-Feld (noch nicht vollstaendig analysiert)
[0] = { Typ = 'Signal', Farbe = 'rot' }, -- $0000000000000000
[1] = { Typ = 'Fahrzeug', Farbe = 'lila' }, -- $0000000000000001
[3] = { Typ = '(3)?', Farbe = '(3)?' }, -- $0000000000000011
[7] = { Typ = '(7)?', Farbe = '(7)?' }, -- $0000000000000111
[256] = { Typ = 'Sound', Farbe = 'gelb' }, -- $0000000100000000
[512] = { Typ = 'Kamera', Farbe = 'gruen' }, -- $0000001000000000
[1280] = { Typ = 'Einfahrt Zug-Depot', Farbe = 'grau, Quadrat' }, -- $0000010100000000
[1536] = { Typ = 'Ausfahrt Zug-Depot', Farbe = 'grau, Dreieck' }, -- $0000011000000000
[32768] = { Typ = 'Gruppenkontakt', Farbe = 'blau' }, -- $1000000000000000
}
local GebaeudesammlungText = { -- GebaudesammlungID
[1] = 'Gleisobjekte Eisenbahn', -- u.a. Weichen, Bahnhoefe, Bruecken
[2] = 'Gleisobjekte Straßenbahn',
[3] = 'Gleisobjekte Straßen',
[4] = 'Immobilien', -- u.a. Gebaeude, Mauern, Tunnel, stehende Fahrzeuge
[5] = 'Landschaftselemente', -- u.a. Personen, Flora, Terra
[6] = 'Gleisobjekte Sonstiges (Wasserwege)'
}
local Statistics = {
-- Dateigroesse in Bytes
FileSize = 0,
-- Laufzeit in Sekunden
LoadFileTime = 0, ParserTime = 0, ProcessTime = 0, TotalTime = 0
}
-- xml_tabelle
local sutrackp = nil
-- Konvertierte Daten
local Gleise = {}
local Gleisverbindungen = {}
local Fuhrparks = {}
local Zugverbaende = {}
local Rollmaterialien = {}
local Kontakte = {}
local Signale = {}
local KontaktZiele = {}
local Routen = {}
local Sounds = {}
local LuaFunktionen = {}
local Immobilien = {}
local Dateien = {
Gleissystem = {},
Signal = {},
Rollmaterial = {},
Immobile = {},
Sound = {},
}
-- Vorabdeklaration der Variablen der oeffentlichen Schnittstelle
local EEP2Lua = {}
-- Hilfsfunktion zur Pruefung ob eine Tabelle einen bestimmten Eintrag enthaelt
local function tableHasKey(table,key)
return table[key] ~= nil
end
-- Speichere Informationen zu Dateien
local function storeDatei(Gruppe, Dateiname, Position)
-- neue Datei
if not Dateien[Gruppe][Dateiname] then
Dateien[Gruppe][Dateiname] = { Anzahl = 0, Positionen = {} }
end
-- Nur zaehlen wenn auf der Anlage verbaut (evt. wichtig fuer Sounds)
if Position then
Dateien[Gruppe][Dateiname].Anzahl = Dateien[Gruppe][Dateiname].Anzahl + 1
end
-- Speichere nur bis zu MaxPositions Positionen
if Position and #Dateien[Gruppe][Dateiname].Positionen < MaxPositions then
table.insert(Dateien[Gruppe][Dateiname].Positionen, Position)
end
end
-- Speichere Lua-Funktion
local function storeLuaFunktion(LuaFn, KontaktIDTab, Position)
-- Wie sollen Lua-Funktionen mit Parametern verwaltet werden?
-- In der aktuellen Version werden alle Strings als unterschiedlich betrachtet
-- Besser waere es, den Funktionsnamen von den Parameters zu trennen
-- Achtung: es kann auch inline-Lua ohne Funktion angegeben sein
-- neue Lua-Funktion
if not LuaFunktionen[LuaFn] then
LuaFunktionen[LuaFn] = { Anzahl = 0, Kontakte = {}, Positionen = {} }
end
LuaFunktionen[LuaFn].Anzahl = LuaFunktionen[LuaFn].Anzahl + 1
table.insert(LuaFunktionen[LuaFn].Kontakte, KontaktIDTab)
table.insert(LuaFunktionen[LuaFn].Positionen, Position)
end
-- Funktionen zur Verarbeitung einzelner Tokens
local function processSutrackp(xmlsutrackp, Parameters)
if debug then print('processSutrackp') end
-- hier gibt es nichts zu tun
return Parameters
end
local function processGleissystem(xmlGleissystem, Parameters)
if debug then print('processGleissystem') end
local GleissystemID = tonumber(xmlGleissystem._attr.GleissystemID)
-- Feldliste: Kontakte
Kontakte[GleissystemID] = {
Anzahl = 0
}
-- Feldliste: Signale
Signale[GleissystemID] = {
Anzahl = 0
}
-- neues Gleissystem anlegen
Gleise[GleissystemID] = {
--.. -- kein Elternknoten
GleissystemID = GleissystemID, -- Schluessel des aktuellen Eintrages
Laenge = 0, -- Gesamtlaenge aller Gleise
Anzahl = {
Gleise = 0,
Kontakte = 0,
Signale = 0,
Zugverbaende = 0,
Rollmaterialien = 0,
}
--.. hier weitere Felder zu einem Gleissystem eintragen
}
local Gleissystem = Gleise[GleissystemID]
return Gleissystem -- Elternknoten fuer die naechste Ebene
end
local function processGleis(xmlGleis, Gleissystem)
if debug then print('processGleis') end
--[[
Wo befindet dich das Gleis, Der Kontakt oder das Signal?
Wo befindet sich das 'heisse' Ende einer Weiche?
Auch wenn eine grobe Naeherungsantwort recht leicht anzugeben ist - man waehlt dazu einfach die
Anfangskoordinaten des Gleises - ist eine exakte Angabe ueberraschend schwer genau zu ermitteln.
Zuerst muessen die entspechenden Attribute identifiziert werden und dann gilt es, entweder in
eine 3D-Matrix-Berechnung einzusteigen oder vereinfacht auf 2D einige trigonometrische Formeln
aufzustellen.
Parameter eines Gleises
alle Gleise:
Position x,y,z des Gleisanfangs [m]: Vektor[1]{x, y, z} / 100
Laenge [m]: Charakteristik.Laenge / 100
Kruemmung [°] des Gleises: math.deg( Charakteristik.Kruemmung * Charakteristik.Laenge )
Skalierung: _attr.scale
Gleisart: clsid
['2E25C8E2-ADCD-469A-942E-7484556FF932'] = 'Normales Gleis',
['C889EADB-63B5-44A2-AAB9-457424CFF15F'] = '2-Weg-Weiche',
['B0818DD8-5DFD-409F-8022-993FD3C90759'] = '3-Weg-Weiche',
['06D80C90-4E4B-469B-BFE0-509A573EBC99'] = 'Prellbock-Gleis'
weitere Felder (die jetzt noch nicht beruecksichtigt werden):
Anfangsfuehrungsverdrehung: Anfangsfuehrungsverdrehung.Wert
Fuehrungsverdrehung: Charakteristik.Fuehrungsverdrehung
Biegung: Charakteristik.Korve
Torsion: Charakteristik.Torsion
zusaetzlich bei 2-Weg-Weiche / 3-Weg-Weiche:
WeicheID: Key_Id
Stellung der Weiche: weichenstellung
Die Kruemmung eines Gleises wird im Bogenmass bezogen auf die Laenge normalisiert gespeichert:
Charakteristik.Kruemmung = math.rad( WinkelGrad ) / Laenge
bzw.
Winkel [°]: deg( Charakteristik.Kruemmung * Charakteristik.Laenge )
Der normalisiert Winkel (des Gleisanfangs) auf der xy-Ebene sowie die normalisierte Steigung
wird in den weiteren Dreibein-Vektoren gespeichert, die damit eine 3x3 Matrix darstellen.
Mit solch einer Matrix lassen sich die Berechnungen fuer eine oder mehrerer Translationen (Verschiebungen), Skalierungen
oder Rotationen beschreiben und effizient ueber Matrixmultiplikationen berechnen.
http://www.mttcs.org/Skripte/Pra/Material/vorlesung3.pdf
https://www.tu-ilmenau.de/fileadmin/media/gdv/Lehre/Computergrafik_I/Uebung/gdv1gt.pdf
a) Translation (Verschiebung)
1 0 Dx
0 1 Dy
0 0 1
Die Verschiebung wird in EEP nicht genutzt.
b) Skalierung
Sx 0 0
0 Sy 0
0 0 1
Die Skalierung wird in EEP ueber ein separates Attribut dargestellt.
c) Rotation
Winkel des Gleises (= Drehung um die z-Achse)
Die Drehung eines Gleises auf der Ebene gegen den Uhrzeigersinn um den Winkel w wird mit einer Matrixmultiplikation berechnet:
Dies entspricht dem 2D-Fall (die drei Zeilen der Matrix entsprechen den Werten von Dir, Nor und Bin):
Drehung eines normalisierten Vektors r (mit der Laenge 1) um die z-Achse gegen den Uhrzeigersinn
um den Winkel w
cos w -sin w 0
sin w cos w 0
0 0 1
Damit ergibt sich folgende Formel fuer den Endpunkt eines Gleises dessen Anfang auf x0, y0 liegt:
x1 = x0 + l * cos( w )
y1 = y0 + l * sin( w )
Steigung des Gleises (= Drehung um die y-Achse)
Drehung eines Vektors r um die y-Achse gegen den Uhrzeigersinn um den Winkel w
cos w 0 sin w
0 1 0
-sin w 0 cos w
Verdrehung des Gleises
Drehung eines Vektors r um die x-Achse gegen den Uhrzeigersinn um den Winkel w
1 0 0
0 cos w -sin w
0 sin w cos w
Wenn mehrere dieser Rotationen vorgenommen werden, dann werden die jeweiligen Matixen multipliziert.
Umrechnung der Rotationsmatrix in Winkel:
https://www.andre-gaschler.com/rotationconverter/
http://www.euclideanspace.com/maths/geometry/rotations/conversions/angleToMatrix/
http://web.cs.iastate.edu/~cs577/handouts/homogeneous-transform.pdf
Da in EEP anscheinend nur die Steigung und die Drehung in der Matrix abgebildet werden verwendet (und somit
genuegend 0-en und 1-en bei einer Matrixmultiplikation beteiligt sind) werden lassen sich die Werte fuer den
Winkel und die Steigung zumeist vereinfacht ablesen:
Winkel [rad]: acos( Vektor[3].y ) * sign( Vektor[2].y )
Steigung [rad]: acos( Vektor[4].z ) * sign( Vektor[2].z )
Eine genauere Rechnug wuerde z.B. ueber die Diagonale der Roationsmatrix laufen (Vorzeichen wie oben korrigieren):
Winkel [rad]: acos( ( Vektor[2].x + Vektor[3].y + Vektor[z].z - 1) / 2 ) * sign( Vektor[2].z )
Fuer die Umrechnung zwischen Radius eines gebobenen Gleisen und dem Winkel gilt:
r = l / rad( a )
a = deg( l / r )
Fuer die Umrechnung der Steigung gilt:
s = asin( sm / l )
sm = l * sin( s )
--]]
-- Gleisattribute, linke Seite (in Klammern die Einheit)
local x, y, z, zr, wc, w, f, L, al, a, r, sc, s, sm, dx, dy, d, b, xe, ye, we
if xmlGleis.Dreibein then -- EEP up to version 15
-- Position des Gleisanfangs auf der Ebene [m]
x = tonumber( xmlGleis.Dreibein.Vektor[1]._attr.x ) / 100
y = tonumber( xmlGleis.Dreibein.Vektor[1]._attr.y ) / 100
-- absolute Hoehe des Gleisanfangs [m]
z = tonumber( xmlGleis.Dreibein.Vektor[1]._attr.z ) / 100
-- relative Hoehe [m]
zr = nil
-- Winkel der Drehung des Gleisanfangs um die Z-Achse gegen den Uhrzeigersinn [rad] (vereinfacht!!!)
wc = tonumber( xmlGleis.Dreibein.Vektor[3]._attr.y ) -- cos(w)
w = math.acos( wc ) -- [rad]
* ( tonumber( xmlGleis.Dreibein.Vektor[2]._attr.y ) < 0 and -1 or 1 )
-- Skalierung, Faktor bezueglich der Laenge [dimensionslos]
f = tonumber( xmlGleis._attr.scale )
-- Gleisattribute, rechte Seite
-- Laenge des Gleises [m]
L = tonumber( xmlGleis.Charakteristik._attr.Laenge ) / 100
-- Winkel der Kruemmung auf der Ebene gegen den Uhrzeigersinn [rad]
al = tonumber( xmlGleis.Charakteristik._attr.Kruemmung ) * 100 -- normalisiert
a = L * al -- [rad] -- a = 0 fuer gerade Gleise
-- Radius der Kruemmung auf der Ebene [m]
r = (a ~= 0 and L / a or 0) -- oder r = 1 / al -- r = 0 fuer gerade Gleise
-- Steigung [rad] (vereinfacht!!!)
sc = tonumber( xmlGleis.Dreibein.Vektor[4]._attr.z ) -- cos(s)
s = math.acos( sc ) -- [rad]
* ( tonumber( xmlGleis.Dreibein.Vektor[2]._attr.z ) < 0 and -1 or 1 )
-- Steigung [m]
sm = L * math.asin( s )
-- vereinfachte 2D-Berechnung fuer das Gleisende
-- Abstand zwischen Anfangs- und Endpunkt eines gekruemmtes Gleises [m]
dx = (a ~= 0 and r * math.sin( a ) or 0 )
dy = (a ~= 0 and r * ( 1 - math.cos( a )) or 0 )
-- effektive Laenge eines Gleises
d = (a ~= 0 and math.sqrt( dx * dx + dy * dy ) or L) -- d = L fuer gerade Gleise
-- Winkel zwischen Anfangs- und Endpunkt [m] -- a = 0 fuer gerade Gleise
b = a / 2 -- bzw. asin( dy / d ) oder acos( dx / d )
-- Position des Gleisendes auf der Ebene unter Beruecksichtigung von Drehung und Kruemmung [m]
xe = x + d * math.cos( w + b )
ye = y + d * math.sin( w + b )
-- Winkel der Drehung des Gleisendes um die Z-Achse gegen den Uhrzeigersinn [rad]
we = w + a
else -- EEP as of version 16
x = 0
y = 0
z = 0
zr = 0
wc = 0
w = 0
f = 0
L = 0
al = 0
a = 0
r = 0
sc = 0
sm = 0
dx = 0
dy = 0
d = 0
b = 0
xe = 0
ye = 0
we = 0
end -- if xmlGleis.Dreibein
local GleisID = tonumber(xmlGleis._attr.GleisID)
local GleissystemID = Gleissystem.GleissystemID
-- Felder von Gleissystem aktualisieren
Gleissystem.Laenge = Gleissystem.Laenge + L
Gleissystem.Anzahl.Gleise = Gleissystem.Anzahl.Gleise + 1
if debug then
print(
'Gleis = ', GleisID, ' ',
'x = ', x, ' ',
'y = ', y, ' ',
'L = ', L, ' ',
'w = ', w, ' rad ', math.deg(w), ' grad ',
'a = ', a, ' rad ', math.deg(a), ' grad ',
'r = ', r, ' ',
'dx = ', dx, ' ',
'dy = ', dy, ' ',
'd = ', d, ' ',
'b = ', b, ' rad ', math.deg(b), ' grad ',
'xe = ', xe, ' ',
'ye = ', ye, ' ',
'we = ', we, ' rad ', math.deg(we), ' grad '
)
end
-- neues Gleis anlegen
Gleise[GleissystemID][GleisID] = {
Gleissystem = Gleissystem, -- Elternknoten
GleisID = GleisID, -- Schluessel des aktuellen Eintrages
Gleisart = xmlGleis._attr.clsid,
Laenge = L, -- [m]
Position = { x = x, y = y, z = z }, -- [m]
PositionEnde = { x = xe, y = ye, z = z }, -- [m] (z wird noch nicht berechnet)
WinkelAnfang = w, -- [Bogenmass]
WinkelEnde = we, -- [Bogenmass]
Kruemmung = a, -- [Bogenmass]
Radius = r, -- [m] (fuer gerades Gleis: 0)
Steigung = s, -- [Bogenmass]
WeicheID = tonumber(xmlGleis._attr.Key_Id), -- nil fuer normale Gleise
Weichenstellung = tonumber(xmlGleis._attr.weichenstellung),
KontaktZiel = tonumber(xmlGleis.KontaktZiel), -- Weichen sind potentielle Kontakt-Ziele
Verbindung = {}, -- Verbindungen zu anderen Gleisen werden spaeter eingetragen
-- siehe Funktion verbindeGleise
-- es werden ggf. mehrere der folgenden Tabellen eingetragen:
-- { Anfang = Gleis, AnfangAnschluss = ... }
-- { Ende = Gleis, EndeAnschluss = ... }
-- { EndeAbzweig = Gleis, EndeAbzweigAnschluss = ... }
-- { EndeKoAbzweig = Gleis, EndeKoAbzweigAnschluss = ... }
TipTxt = xmlGleis._attr.TipTxt, -- Tipp-Text (kann auch nil sein)
--.. hier weitere Felder zu einem Gleis eintragen
}
local Gleis = Gleise[GleissystemID][GleisID]
-- Kontakt-Ziel fuer Gleis/Weiche eintagen
if Gleis.KontaktZiel then
-- Neues Kontakt-Ziel?
if not KontaktZiele[Gleis.KontaktZiel] then
KontaktZiele[Gleis.KontaktZiel] = {}
end
-- Gleis/Weiche eintragen
KontaktZiele[Gleis.KontaktZiel].Gleis = Gleis
end
-- Speichere Datei
storeDatei('Gleissystem',
xmlGleis._attr.gsbname,
Gleis.Position
)
return Gleis -- Elternknoten fuer die naechste Ebene
end
local function processKontakt(xmlKontakt, Gleis)
if debug then print('processKontakt') end
local KontaktID = 0 -- keine KontaktID vorhanden (wird spaeter bestimmt)
local GleisID = Gleis.GleisID
local GleissystemID = Gleis.Gleissystem.GleissystemID
local LuaFn = xmlKontakt._attr.LuaFn
-- Felder von Gleissystem aktualisieren
Gleise[GleissystemID].Anzahl.Kontakte = Gleise[GleissystemID].Anzahl.Kontakte + 1
-- Die Position kann in grober Naeherung gleich der Anfangsposition des Gleises angenommen werden
local Position = Gleis.Position
-- Ein verbesserte Position beruecksichtigt den Winkel des Gleises und die relative Position auf dem Gleis
local relPosition = tonumber(xmlKontakt._attr.Position) / 100 -- Position relativ zum Gleisursprung [m]
Position = {
x = Gleis.Position.x + relPosition * math.cos( Gleis.WinkelAnfang ),
y = Gleis.Position.y + relPosition * math.sin( Gleis.WinkelAnfang ),
z = Gleis.Position.z,
}
-- erster Kontakt des Gleises
if not Kontakte[GleissystemID][GleisID] then
Kontakte[GleissystemID][GleisID] = {}
end
-- neuen Kontakt anlegen
table.insert(
Kontakte[GleissystemID][GleisID],
{
Gleis = Gleis, -- Elternknoten
KontaktID = KontaktID, -- Schluessel des aktuellen Eintrages (vorlaeufig)
Position = Position, -- (berechnet/geschaetzt) [m]
relPosition = relPosition, -- Position relativ zum Gleisursprung [m]
SetType = tonumber(xmlKontakt._attr.SetType), -- KontaktTyp (Bit-Vektor)
SetValue = xmlKontakt._attr.SetValue, -- Interpretation je nach Kontakttyp
Delay = xmlKontakt._attr.Delay, -- Startverzoegerung
Group = xmlKontakt._attr.Group, -- Kontaktgruppe
KontaktZiel = tonumber(xmlKontakt._attr.KontaktZiel), -- Kontakt-Ziel des Kontaktes (kann auch nil sein)
LuaFn = ( LuaFn == '' and nil or LuaFn ), -- Lua-Funktion (nur wenn nicht leer)
clsid = xmlKontakt._attr.clsid, -- konstanter Wert
TipTxt = xmlKontakt._attr.TipTxt, -- Tipp-Text (kann auch nil sein)
--.. hier weitere Felder zu einem Kontakt eintragen
}
)
KontaktID = #Kontakte[GleissystemID][GleisID] -- nun kann die KontaktID bestimmt werden
local Kontakt = Kontakte[GleissystemID][GleisID][KontaktID]
Kontakt.KontaktID = KontaktID -- Schluessel des aktuellen Eintrages eintragen
-- Kontakt-Ziel fuer Kontakt eintragen
if Kontakt.KontaktZiel then
-- Neues Kontakt-Ziel?
if not KontaktZiele[Kontakt.KontaktZiel] then
KontaktZiele[Kontakt.KontaktZiel] = {}
end
-- neuer Kontakt fuer Kontakt-Ziel?
if not KontaktZiele[Kontakt.KontaktZiel].Kontakte then
KontaktZiele[Kontakt.KontaktZiel].Kontakte = {}
end
-- Kontakt eintragen
table.insert(KontaktZiele[Kontakt.KontaktZiel].Kontakte, Kontakt)
end
-- Speichere Lua-Funktion
if LuaFn and LuaFn ~='' then
storeLuaFunktion(
LuaFn,
{GleissystemID = GleissystemID, GleisID = GleisID, KontaktID = KontaktID}, -- Identifikation des Kontaks
Gleise[GleissystemID][GleisID].Position
)
end
return Kontakt -- Elternknoten fuer die naechste Ebene
end
local function processMeldung(xmlMeldung, Gleis)
if debug then print('processMeldung') end
local SignalID = tonumber(xmlMeldung._attr.Key_Id)
local GleisID = Gleis.GleisID
local GleissystemID = Gleis.Gleissystem.GleissystemID
-- Felder von Gleissystem aktualisieren
Gleise[GleissystemID].Anzahl.Signale = Gleise[GleissystemID].Anzahl.Signale + 1
-- Die Position kann in grober Naeherung gleich der Anfangsposition des Gleises angenommen werden
local Position = Gleis.Position
-- Ein verbesserte Position beruecksichtigt den Winkel des Gleises und die relative Position auf dem Gleis
local relPosition = tonumber(xmlMeldung._attr.Position) / 100 -- Position relativ zum Gleisursprung [m]
Position = {
x = Gleis.Position.x + relPosition * math.cos( Gleis.WinkelAnfang ),
y = Gleis.Position.y + relPosition * math.sin( Gleis.WinkelAnfang ),
z = Gleis.Position.z,
}
-- neues Signal anlegen
Signale[GleissystemID][SignalID] = {
Gleis = Gleis, -- Elternknoten
SignalID = SignalID, -- Schluessel des aktuellen Eintrages
Position = Position, -- (berechnet/geschaetzt) [m]
relPosition = relPosition, -- Position relativ zum Gleisursprung
name = xmlMeldung._attr.name,
Stellung = xmlMeldung.Signal._attr.stellung,
KontaktZiel = tonumber(xmlMeldung.KontaktZiel), -- Signale sind potentielle Kontakt-Ziele
TipTxt = xmlMeldung._attr.TipTxt, -- Tipp-Text (kann auch nil sein)
--.. hier weitere Felder zu einem Signal eintragen
}
local Signal = Signale[GleissystemID][SignalID]
-- Kontakt-Ziel fuer Signal eintragen
if Signal.KontaktZiel then
-- Neues Kontakt-Ziel?
if not KontaktZiele[SignalID] then
KontaktZiele[SignalID] = {}
end
-- Signal eintragen
KontaktZiele[SignalID].Signal = Signal
end
-- Speichere Datei
storeDatei('Signal',
xmlMeldung._attr.name,
Gleise[GleissystemID][GleisID].Position
)
return Signal -- Elternknoten fuer die naechste Ebene
end
local function processFuhrpark(xmlFuhrpark, Parameters)
if debug then print('processFuhrpark') end
local FuhrparkID = tonumber(xmlFuhrpark._attr.FuhrparkID)
-- neuen Fuhrpark anlegen
Fuhrparks[FuhrparkID] = {
--.. -- kein Elternknoten
FuhrparkID = FuhrparkID, -- Schluessel des aktuellen Eintrages
Anzahl = {
Zugverbaende = 0,
Rollmaterialien = 0,
},
--.. hier weitere Felder zu einem Fuhrpark eintragen
}
local Fuhrpark = Fuhrparks[FuhrparkID]
return Fuhrpark -- Elternknoten fuer die naechste Ebene
end
local function processZugverband(xmlZugverband, Fuhrpark)
if debug then print('processZugverband') end
local ZugID = tonumber(xmlZugverband._attr.ZugID)
local GleissystemID = tonumber(xmlZugverband.Gleisort._attr.gleissystemID)
local GleisID = tonumber(xmlZugverband.Gleisort._attr.gleisID)
-- Felder des uebergeordneten Fuhrparks aktualisieren
Fuhrpark.Anzahl.Zugverbaende = Fuhrpark.Anzahl.Zugverbaende + 1
-- Felder des Gleissystems aktualisieren (das geht nur wenn das Gleissystem vor dem Fuhrpark verarbeitet wurde)
Gleise[GleissystemID].Anzahl.Zugverbaende = Gleise[GleissystemID].Anzahl.Zugverbaende + 1
-- neuen Zugverband anlegen
Zugverbaende[ZugID] = {
Fuhrpark = Fuhrpark, -- Elternknoten
ZugID = ZugID, -- Schluessel des aktuellen Eintrages
Gleis = Gleise[GleissystemID][GleisID], -- Querverweis auf das Gleis
GleissystemID = GleissystemID, -- kann auch ueber .Gleis.GleisID ermittelt werden
GleisID = GleisID, -- kann auch ueber .Gleis.Gleissystem.GleisID ermittelt werden
name = xmlZugverband._attr.name,
FuhrparkID = Fuhrpark.FuhrparkID,
Position = Gleise[GleissystemID][GleisID].Position, -- Annahme: Zug steht am Enfang des Gleises
Geschwindigkeit = tonumber(xmlZugverband._attr.Geschwindigkeit) * 3.6 , -- Umgerechnet in km/h
Rollmaterialien = {}, -- Querverweise werden spaeter eingetragen
--.. hier weitere Felder zu einem Zugverband eintragen
}
local Zugverband = Zugverbaende[ZugID]
return Zugverband -- Elternknoten fuer die naechste Ebene
end
local function processRollmaterial(xmlRollmaterial, Zugverband)
if debug then print('processRollmaterial') end
local name = xmlRollmaterial._attr.name
local GleissystemID = Zugverband.GleissystemID
-- Felder des zugehoerigen Fuhrparks aktualisieren
Zugverband.Fuhrpark.Anzahl.Rollmaterialien = Zugverband.Fuhrpark.Anzahl.Rollmaterialien + 1
-- Felder des Gleissystems aktualisieren
Gleise[GleissystemID].Anzahl.Rollmaterialien = Gleise[GleissystemID].Anzahl.Rollmaterialien + 1
-- neues Rollmaterial anlegen
Rollmaterialien[name] = {
Zugverband = Zugverband, -- Elternknoten
name = name, -- Schluessel des aktuellen Eintrages
typ = xmlRollmaterial._attr.typ,
Zugmaschine = tableHasKey(xmlRollmaterial, 'Zugmaschine'),
ZugID = Zugverband.ZugID, -- kann auch ueber .Zugverband.ZugID ermittelt werden
--.. hier weitere Felder zu einem Rollmaterial eintragen
}
local Rollmaterial = Rollmaterialien[name]
-- Querverweis in Zugverband eintragen
table.insert( Zugverband.Rollmaterialien, Rollmaterial )
-- Speichere Datei
storeDatei('Rollmaterial',
xmlRollmaterial._attr.typ,
Rollmaterial.Zugverband.Position
)
return Rollmaterial -- Elternknoten fuer die naechste Ebene
end
local function processGebaeudesammlung(xmlGebaeudesammlung, Parameters)
if debug then print('processGebaeudesammlung') end
local GebaeudesammlungID = tonumber(xmlGebaeudesammlung._attr.GebaudesammlungID)
-- neue Gebaeudesammlung anlegen
Immobilien[GebaeudesammlungID] = {
--.. -- Elternknoten
GebaeudesammlungID = GebaeudesammlungID, -- Schluessel des aktuellen Eintrages
Anzahl = 0,
--.. hier weitere Felder zu einer Gebaeudesammlung eintragen
}
local Gebaeudesammlung = Immobilien[GebaeudesammlungID]
return Gebaeudesammlung -- Elternknoten fuer die naechste Ebene
end
local function processImmobile(xmlImmobile, Gebaeudesammlung)
if debug then print('processImmobile') end
local ImmoIdx = tonumber(xmlImmobile._attr.ImmoIdx)
-- Felder der uebergeordneten Gebaeudesammlung aktualisieren
Gebaeudesammlung.Anzahl = Gebaeudesammlung.Anzahl + 1
-- neue Immobilie anlegen
Gebaeudesammlung[ImmoIdx] = {
Gebaeudesammlung = Gebaeudesammlung, -- Elternknoten
ImmoIdx = ImmoIdx, -- Schluessel des aktuellen Eintrages
Position = {
x = tonumber(xmlImmobile.Dreibein.Vektor[1]._attr.x) / 100,
y = tonumber(xmlImmobile.Dreibein.Vektor[1]._attr.y) / 100,
z = tonumber(xmlImmobile.Dreibein.Vektor[1]._attr.z) / 100,
},
TipTxt = xmlImmobile._attr.TipTxt, -- Tipp-Text (kann auch nil sein)
--.. hier weitere Felder zu einer Immobilie eintragen
}
local Immobilie = Gebaeudesammlung[ImmoIdx]
-- Speichere Datei
storeDatei('Immobile',
xmlImmobile._attr.gsbname,
Immobilie.Position
)
return Immobilie -- Elternknoten fuer die naechste Ebene
end
local function processOptions(xmlOptions, Parameters)
if debug then print('processOptions') end
local I
local Route = {}
local Sound = {}
-- Sammele RouteItems und SoundItems
for key, value in pairs(xmlOptions._attr) do
if string.sub(key, 1, 8) == 'RouteId_' then
I = string.sub(key, 9 )
if not Route[I] then
Route[I] = {}
end
Route[I].RouteId = value
elseif string.sub(key, 1, 10) == 'RouteName_' then
I = string.sub(key, 11 )
if not Route[I] then
Route[I] = {}
end
Route[I].RouteName = value
elseif string.sub(key, 1, 6) == 'SndId_' then
I = string.sub(key, 7 )
if not Sound[I] then
Sound[I] = {}
end
Sound[I].SndId = value
elseif string.sub(key, 1, 8) == 'SndName_' then
I = string.sub(key, 9 )
if not Sound[I] then
Sound[I] = {}
end
Sound[I].SndName = value
end
end
-- Speichere Routen und Sounds
for key, value in pairs(Route) do
if value.RouteId then
Routen[value.RouteId] = value.RouteName
end
end
for key, value in pairs(Sound) do
if value.SndId then
Sounds[value.SndId] = value.SndName
-- Speichere Datei
storeDatei('Sound',
value.SndName
)
end
end
return Parameters
end
local function processGleisverbindung(xmlGleisverbindung, Gleissystem)
if debug then print('processGleisverbindung') end
-- Attribute: GleisID1, Anschluss1, GleisID2, Anschluss2
-- Werte fuer Anschluss: Anfang, Ende, EndeAbzweig, EndeKoAbzweig
-- neues Gleissystem?
if not Gleisverbindungen[Gleissystem.GleissystemID] then Gleisverbindungen[Gleissystem.GleissystemID] = {} end
-- Sammle Verbindungen
table.insert(
Gleisverbindungen[Gleissystem.GleissystemID],
{
GleisID1 = tonumber(xmlGleisverbindung._attr.GleisID1),
Anschluss1 = xmlGleisverbindung._attr.Anschluss1,
GleisID2 = tonumber(xmlGleisverbindung._attr.GleisID2),
Anschluss2 = xmlGleisverbindung._attr.Anschluss2,
Flags = tonumber(xmlGleisverbindung._attr.Flags), -- 1 = virtuelle Gleisverbindung
}
)
return Gleisverbindungen[Gleissystem.GleissystemID]
end
local function verbindeGleise()
-- Erst nachdem alle Tokens verarbeitet wurden, koennen mit dieser Funktion
-- die Verweise zwischen verbundenen Gleisen eingetragen werden
local function verbindeGleis(GleisA, AnschlussA, GleisB, AnschlussB, Flags)
-- Die Gleisverbindung wird in beiden Richtugen bei den Gleisen eingetragen.
-- Diese lokale Funktion macht das fuer jeweils eine Richtung
-- neue Verbindung
if not GleisA.Verbindung then GleisA.Verbindung = {} end
-- Verbindung eintragen
if AnschlussA == 'Anfang' then
table.insert(GleisA.Verbindung,
{
Anfang = GleisB, -- das andere Gleis eintragen
AnfangAnschluss = AnschlussB, -- den Anschluss des anderen Gleises eintragen
Virtuell = Flags == 1,
}
)
elseif AnschlussA == 'Ende' then
table.insert(GleisA.Verbindung,
{
Ende = GleisB,
EndeAnschluss = AnschlussB,
Virtuell = Flags == 1,
}
)
elseif AnschlussA == 'EndeAbzweig' then
table.insert(GleisA.Verbindung,
{
EndeAbzweig = GleisB,
EndeAbzweigAnschluss = AnschlussB,
Virtuell = Flags == 1,
}
)
elseif AnschlussA == 'EndeKoAbzweig' then
table.insert(GleisA.Verbindung,
{
EndeKoAbzweig = GleisB,
EndeKoAbzweigAnschluss = AnschlussB,
Virtuell = Flags == 1,
}
)
else
print('Fehler: Anschluss=', AnschlussA, ' nicht bekannt')
end
end -- function verbindeGleis
if debug then print('verbindeGleise') end
for GleissystemID, Gleissystem in pairs(Gleisverbindungen) do
for key, Gleisverbindung in pairs(Gleissystem) do
if debug then
print(
'GleissystemID=', GleissystemID, ' '
, 'key=', key, ' '
, 'GleisID1=', Gleisverbindung.GleisID1, ' '
, 'Anschluss1=', Gleisverbindung.Anschluss1, ' '
, 'GleisID2=', Gleisverbindung.GleisID2, ' '
, 'Anschluss2=', Gleisverbindung.Anschluss2, ' '
, ( Gleisverbindung.Flags == 1 and 'virtuell' or ''), ' '
)
end
-- verbinde Gleise 1->2
verbindeGleis(
Gleise[GleissystemID][Gleisverbindung.GleisID1],
Gleisverbindung.Anschluss1,
Gleise[GleissystemID][Gleisverbindung.GleisID2],