lsa-planer

LSA-Planer Professional – Planungssoftware für Lichtsignalanlagen nach RiLSA 2015 und § 45 StVO. EUPL-1.2.

/ tests domain rueckstaulaenge.test.ts

6,1 KB Rohdatei
tests/domain/rueckstaulaenge.test.ts — 137 Zeilen
1 import { readFileSync } from 'node:fs';
2 import { describe, expect, it } from 'vitest';
3 import { TrafficSimulation } from '@/domain/simulation/simulation';
4 // Mit Fassung 5.31.0 aus der Simulation nach `rilsa/delay.ts` gezogen: Die
5 // Umrechnung wird jetzt von zwei Rechnungen gebraucht, und zwei Fassungen
6 // derselben Formel waeren der Fehler des Altbestands.
7 import { rueckstaulaenge } from '@/domain/rilsa/delay';
8 import { buildSignalPlan } from '@/domain/plan/signalPlan';
9 import { createStandardIntersectionProject } from '@/domain/model/factory';
10 import { STAURAUMBEDARF_JE_FAHRZEUG } from '@/domain/rilsa/constants';
11 import { QUELLEN } from '@/domain/rilsa/quellen';
12 import type { Project } from '@/domain/model/project';
13
14 const FIXED_DATE = new Date('2026-01-01T00:00:00Z');
15 const SIMULATION_QUELLTEXT = readFileSync('src/domain/simulation/simulation.ts', 'utf8');
16 const DELAY_QUELLTEXT = readFileSync('src/domain/rilsa/delay.ts', 'utf8');
17
18 /**
19 * Fassung 5.28.0, Punkte A4 und A5.
20 *
21 * A4: Der Stauraumbedarf je Fahrzeug stand modullokal in `simulation.ts` -
22 * ohne Ausfuhr, ohne Eintrag in `constants.ts`, ohne Quelle, ohne
23 * Pruefstand und ohne einen einzigen Test. Angezeigt wurde er trotzdem.
24 * A5: Die daraus gebildete Laenge teilte nicht durch die Fahrstreifenzahl. An
25 * einer zweistreifigen Zufahrt war die angezeigte Laenge doppelt so lang
26 * wie die wirkliche - ein Fehler mit Faktor, kein Rundungsfehler.
27 */
28 describe('Rueckstaulaenge - der Kennwert hat eine Herkunft (A4)', () => {
29 it('steht in constants.ts und traegt 7 m', () => {
30 expect(STAURAUMBEDARF_JE_FAHRZEUG).toBe(7);
31 });
32
33 it('steht nicht mehr als eigene Zahl in der Simulation', () => {
34 // Die Hausregel: constants.ts ist die einzige Quelle aller Kennwerte.
35 expect(SIMULATION_QUELLTEXT).not.toContain('QUEUE_SPACING_METERS');
36 /*
37 * Seit 5.31.0 nennt die Simulation den Kennwert gar nicht mehr selbst -
38 * sie ruft `rueckstaulaenge` aus dem Fachkern, und dort steht er.
39 *
40 * GEPRUEFT WIRD DIE EINBINDUNG, NICHT DER NAME: Eine eigene Funktion
41 * gleichen Namens in der Simulation waere genau der Rueckfall, den dieser
42 * Fall verhindern soll - zwei Fassungen derselben Umrechnung. Ein
43 * `toContain('rueckstaulaenge')` haette sie durchgelassen.
44 */
45 expect(SIMULATION_QUELLTEXT).toMatch(
46 /import \{[^}]*rueckstaulaenge[^}]*\} from '\.\.\/rilsa\/delay'/u,
47 );
48 expect(SIMULATION_QUELLTEXT).not.toContain('STAURAUMBEDARF_JE_FAHRZEUG');
49 expect(DELAY_QUELLTEXT).toContain('STAURAUMBEDARF_JE_FAHRZEUG');
50 });
51
52 it('hat eine Quelle mit Pruefstand, und die behauptet kein Regelwerk', () => {
53 const quelle = QUELLEN['praxis-stauraum-je-fahrzeug'];
54 expect(quelle.pruefstand).toBe('abzugleichen');
55 // Weder RiLSA noch HBS nennen eine Laenge je wartendem Fahrzeug. Ein
56 // Praxiswert, der sich als Regelwerkswert ausgibt, ist der Fehlertyp, den
57 // dieses Verzeichnis verhindern soll.
58 expect(quelle.regelwerk).toBe('Planungspraxis');
59 expect(quelle.fundstelle).toBeUndefined();
60 expect(quelle.hinweis).toContain('Kein Regelwerkswert');
61 });
62 });
63
64 describe('Rueckstaulaenge - die Fahrstreifen zaehlen mit (A5)', () => {
65 it('verteilt die wartenden Fahrzeuge auf die Fahrstreifen', () => {
66 expect(rueckstaulaenge(10, 1)).toBe(70);
67 expect(rueckstaulaenge(10, 2)).toBe(35);
68 // 10 * 7 / 3 = 23,333... - auf eine Stelle wie jede andere Anzeige.
69 expect(rueckstaulaenge(10, 3)).toBe(23.3);
70 });
71
72 it('nimmt eine fehlende Fahrstreifenangabe als einen Fahrstreifen', () => {
73 // Dieselbe Behandlung wie in `saturationFlow`: keine Angabe heisst einer,
74 // nicht null - sonst entstuende eine unendliche Laenge.
75 expect(rueckstaulaenge(10, 0)).toBe(70);
76 expect(rueckstaulaenge(10, -2)).toBe(70);
77 expect(rueckstaulaenge(10, Number.NaN)).toBe(70);
78 });
79
80 it('meldet fuer eine leere Warteschlange keine Laenge', () => {
81 expect(rueckstaulaenge(0, 2)).toBe(0);
82 expect(rueckstaulaenge(Number.NaN, 2)).toBe(0);
83 });
84
85 it('zeigt am zweistreifigen Strom die halbe Laenge an', () => {
86 const project = zweistreifig(createStandardIntersectionProject('Rueckstau', FIXED_DATE));
87 const plan = buildSignalPlan(project);
88 const simulation = new TrafficSimulation(plan, project, { timeStep: 0.5 });
89
90 let gesehen = false;
91 for (let i = 0; i < 12; i += 1) {
92 simulation.advance(30);
93 for (const gruppe of simulation.snapshot().groups) {
94 if (gruppe.queue <= 0) continue;
95 gesehen = true;
96 /*
97 * DIE WACHE: Bis zur Behebung stand hier `queue * 7`, und zwar
98 * unabhaengig von der Zahl der Fahrstreifen. Bei zwei Fahrstreifen
99 * war die Anzeige damit doppelt so lang wie der wirkliche Rueckstau.
100 */
101 expect(gruppe.queueLength, gruppe.name).toBe(rueckstaulaenge(gruppe.queue, 2));
102 expect(gruppe.queueLength, gruppe.name).toBeLessThan(
103 gruppe.queue * STAURAUMBEDARF_JE_FAHRZEUG,
104 );
105 }
106 }
107 // Ein Test, der nie in die Pruefung laeuft, bewacht nichts.
108 expect(gesehen, 'in keinem Schritt stand ein Fahrzeug im Rueckstau').toBe(true);
109 });
110
111 it('laesst den einstreifigen Strom unveraendert', () => {
112 // Die Behebung darf den Regelfall nicht verschieben: Bei einem
113 // Fahrstreifen bleibt es bei Fahrzeuge mal Stauraumbedarf.
114 const project = createStandardIntersectionProject('Rueckstau einstreifig', FIXED_DATE);
115 const plan = buildSignalPlan(project);
116 const simulation = new TrafficSimulation(plan, project, { timeStep: 0.5 });
117
118 let gesehen = false;
119 for (let i = 0; i < 12; i += 1) {
120 simulation.advance(30);
121 for (const gruppe of simulation.snapshot().groups) {
122 if (gruppe.queue <= 0) continue;
123 gesehen = true;
124 expect(gruppe.queueLength, gruppe.name).toBe(gruppe.queue * STAURAUMBEDARF_JE_FAHRZEUG);
125 }
126 }
127 expect(gesehen, 'in keinem Schritt stand ein Fahrzeug im Rueckstau').toBe(true);
128 });
129 });
130
131 /** Dasselbe Projekt, aber jede Signalgruppe auf zwei Fahrstreifen. */
132 function zweistreifig(project: Project): Project {
133 return {
134 ...project,
135 signalGroups: project.signalGroups.map((gruppe) => ({ ...gruppe, lanes: 2 })),
136 };
137 }