lsa-planer

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

/ tests export ueberfahrzeitTabellenausgabe.test.ts

5,0 KB Rohdatei
tests/export/ueberfahrzeitTabellenausgabe.test.ts — 122 Zeilen
1 import { describe, expect, it } from 'vitest';
2 import { intergreenDetailCsv } from '@/services/export/csv';
3 import { ueberfahrzeitAnsatzSatz } from '@/services/export/bewertung';
4 import { buildSignalPlan } from '@/domain/plan/signalPlan';
5 import { createStandardIntersectionProject } from '@/domain/model/factory';
6 import type { Project } from '@/domain/model/project';
7 import { UEBERFAHRZEIT_ANSATZ_LABELS } from '@/domain/rilsa/constants';
8 import { resolveRilsaSettings } from '@/domain/rilsa/settings';
9
10 /**
11 * Was Tabellenausgabe und Unterlagentexte ueber die Ueberfahrzeit sagen duerfen
12 * (Fassung 5.5.0, Befunde F5 und B5).
13 *
14 * F5 Die Spalte neben der Ueberfahrzeit nannte in JEDER Zeile den
15 * Kfz-Rechenansatz - auch neben der 0,00 s eines Fussgaengerstroms und
16 * neben den 5 s eines OePNV-Stroms aus der Vmax-Staffel. Der Ansatz
17 * erreicht diese Stroeme nicht.
18 * B5 Der gedruckte Absatz unter der Zwischenzeitenmatrix nannte die
19 * Verkuerzung mit "bis zu 3 s je Uebergang" - eine Zahl, die nur fuer die
20 * Regelwerte 3 s/2 s gilt. Beide sind Vorgabenfelder.
21 */
22
23 const STICHTAG = new Date('2026-01-01T12:00:00Z');
24
25 /** Vorlage mit abbiegendem erstem Kfz-Strom und dem Ansatz "fest". */
26 function mitFesterUeberfahrzeit(): Project {
27 const basis = createStandardIntersectionProject('Prüffall', STICHTAG);
28 return {
29 ...basis,
30 signalGroups: basis.signalGroups.map((g, i) =>
31 i === 0 ? { ...g, movement: 'links' as const } : g,
32 ),
33 settings: { ...basis.settings, ueberfahrzeitAnsatz: 'fest' },
34 };
35 }
36
37 /** Zeilen der Zwischenzeit-Tabellenausgabe als Spaltenfelder. */
38 function tabelle(project: Project): { kopf: string[]; zeilen: string[][] } {
39 const csv = intergreenDetailCsv(project, buildSignalPlan(project));
40 const [kopfzeile, ...rest] = csv.split('\r\n');
41 const zerlege = (zeile: string): string[] =>
42 (zeile.match(/("([^"]|"")*"|[^;]*)(;|$)/g) ?? []).map((feld) =>
43 feld.replace(/;$/, '').replace(/^"|"$/g, '').replace(/""/g, '"'),
44 );
45 return {
46 kopf: zerlege((kopfzeile ?? '').replace(/^/, '')),
47 zeilen: rest.filter((z) => z.length > 0).map(zerlege),
48 };
49 }
50
51 describe('F5 - die Ueberfahrzeit-Spalte nennt die Herkunft je Verkehrsart', () => {
52 it('nennt den Kfz-Ansatz nur an Kfz-Raeumstroemen', () => {
53 const project = mitFesterUeberfahrzeit();
54 const { kopf, zeilen } = tabelle(project);
55 const spalte = kopf.indexOf('Überfahrzeit-Herkunft');
56 expect(spalte, 'Die Spalte fehlt').toBeGreaterThanOrEqual(0);
57
58 let kfz = 0;
59 let andere = 0;
60 for (const zeile of zeilen) {
61 const von = project.signalGroups.find((g) => g.name === zeile[0]);
62 if (von === undefined) continue;
63 const wert = zeile[spalte] ?? '';
64 if (von.mode === 'kfz') {
65 expect(wert, von.name).toBe(UEBERFAHRZEIT_ANSATZ_LABELS.fest);
66 kfz += 1;
67 } else {
68 // Der Ansatz gilt fuer sie nicht - und wird deshalb auch nicht
69 // neben ihre Ueberfahrzeit gedruckt.
70 expect(wert, von.name).not.toContain('feste Überfahrzeit');
71 expect(wert, von.name).not.toBe('');
72 andere += 1;
73 }
74 }
75 expect(kfz, 'Keine Kfz-Zeile geprüft').toBeGreaterThan(0);
76 expect(andere, 'Keine Zeile mit anderer Verkehrsart geprüft').toBeGreaterThan(0);
77 });
78
79 it('nennt eine von Hand gesetzte Ueberfahrzeit als solche', () => {
80 const basis = mitFesterUeberfahrzeit();
81 const project: Project = {
82 ...basis,
83 conflicts: basis.conflicts.map((c, i) => (i === 0 ? { ...c, crossingTimeOverride: 4 } : c)),
84 };
85 const { kopf, zeilen } = tabelle(project);
86 const spalte = kopf.indexOf('Überfahrzeit-Herkunft');
87 const erste = project.conflicts[0]!;
88 const von = project.signalGroups.find((g) => g.id === erste.fromId)!;
89 const nach = project.signalGroups.find((g) => g.id === erste.toId)!;
90 const zeile = zeilen.find((z) => z[0] === von.name && z[1] === nach.name)!;
91 expect(zeile[spalte]).toContain('von Hand');
92 });
93 });
94
95 describe('B5 - der Unterlagentext nennt die Groessenordnung der wirksamen Vorgaben', () => {
96 it('rechnet die Verkuerzung aus den eingetragenen Werten', () => {
97 const { defaults } = resolveRilsaSettings({
98 crossingTimeKfzGeradeaus: 4,
99 crossingTimeKfzAbbiegend: 3,
100 });
101 const satz = ueberfahrzeitAnsatzSatz('fest', defaults);
102 expect(satz).toContain('beim Abbiegen um bis zu 2 s');
103 expect(satz).toContain('geradeaus um bis zu 1 s');
104 expect(satz).not.toContain('bis zu 3 s');
105 });
106
107 it('behauptet keine Verkuerzung, wo keine ist', () => {
108 const { defaults } = resolveRilsaSettings({
109 crossingTimeKfzGeradeaus: 6,
110 crossingTimeKfzAbbiegend: 6,
111 });
112 const satz = ueberfahrzeitAnsatzSatz('fest', defaults);
113 expect(satz).not.toContain('VERKÜRZT');
114 expect(satz).toContain('nirgends kürzer');
115 // Begruendungspflichtig bleibt der Ansatz gleichwohl.
116 expect(satz).toContain('zu begründen');
117 });
118
119 it('haelt am Regelwertsatz die belegte Groessenordnung fest', () => {
120 expect(ueberfahrzeitAnsatzSatz('fest')).toContain('beim Abbiegen um bis zu 3 s');
121 });
122 });