lsa-planer

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

/ tests domain regelbereichBezug.test.ts

9,0 KB Rohdatei
tests/domain/regelbereichBezug.test.ts — 190 Zeilen
1 import { describe, expect, it } from 'vitest';
2 import { resolveRilsaSettings, settingFields } from '@/domain/rilsa/settings';
3 import { ANLAGENART_GRENZEN, RILSA_DEFAULTS } from '@/domain/rilsa/constants';
4 import type { RilsaDefaults } from '@/domain/rilsa/constants';
5 import type { RilsaOverrides } from '@/domain/rilsa/settings';
6
7 /*
8 * Befund 48 (Fassung 5.11.0): An einer Fussgaengerschutzanlage wurde
9 * der Regelbereich der Umlaufzeit gegen den KNOTENPUNKTWERT abgeglichen.
10 *
11 * Die Felder mit `grenzeVonArt` holen ihre Schranken aus der Anlagenart, ihren
12 * Bezugswert fuer die Abweichungsliste aber unveraendert aus RILSA_DEFAULTS.
13 * Ohne Eintrag gilt an der Fussgaengerschutzanlage aber 40 bis 70 s
14 * (ANLAGENART_GRENZEN), nicht 60 bis 90 s. Daraus folgten zwei Fehlverhalten
15 * in derselben Unterlage:
16 *
17 * (a) Wer 40 s eintrug - also genau den Wert, der ohnehin gilt -, bekam eine
18 * Zeile unter "Abweichungen von den Regelwerten" ("Regelwert 60,
19 * Angesetzt 40") und im Pruefbericht die Aufforderung, sie fachlich zu
20 * begruenden. Begruendet werden sollte eine Eingabe, die an keiner Zahl
21 * des Plans etwas aendert.
22 * (b) Wer 60 s eintrug, verschob den wirksamen Regelbereich von 40 bis 70
23 * auf 60 bis 70 - und weil 60 der Knotenpunktwert ist, entstand KEINE
24 * Abweichungszeile: Die wirksame Aenderung stand in keiner Ausgabe.
25 *
26 * Der Bezugswert ist damit der Wert, der OHNE Eintrag fuer diese Anlagenart
27 * gilt. Am Knotenpunkt sind das dieselben Zahlen wie bisher.
28 *
29 * NUR DIE BEIDEN REGELBEREICHSFELDER: Bei der kleinsten und groessten
30 * Umlaufzeit bleibt der Knotenpunktwert der Bezug, weil er dort ein
31 * Regelwerkswert ist - belegt sind 30 bis 120 s, und die RiLSA staffelt sie
32 * nicht nach Anlagenart. Beim Regelbereich ist keine der beiden Zahlen eine des
33 * Regelwerks: Die 60 s sind "eine Zahl dieses Programms", und den Bereich der
34 * Fussgaengerschutzanlage setzt es ebenfalls selbst. Der Fall steht unten als
35 * Gegenprobe.
36 *
37 * GEGEN DEN ALTSTAND schlagen die fuenf Faelle zur Fussgaengerschutzanlage
38 * fehl; die drei Gegenproben bestehen dort ebenso.
39 */
40
41 function abweichungen(
42 overrides: RilsaOverrides,
43 anlagenart: 'knotenpunkt' | 'fussgaengerschutzanlage' | 'einstreifig',
44 ) {
45 return resolveRilsaSettings(overrides, RILSA_DEFAULTS, anlagenart).deviations;
46 }
47
48 /**
49 * Der Wert, den das Fundstellenverzeichnis als "angesetzt" fuehrt: pdf.ts liest
50 * ihn ueber `settingFields(plan.defaults, plan.anlagenart)` aus dem
51 * aufgeloesten Kennwertsatz, die Vorgabenansicht ebenso ("wirksam N s").
52 */
53 function angesetzt(defaults: RilsaDefaults): number | undefined {
54 return settingFields(defaults, 'fussgaengerschutzanlage').find(
55 (f) => f.key === 'cycleTimeRecommendedMin',
56 )?.standard;
57 }
58
59 describe('Bezugswert der Abweichung bei Feldern mit Schranken der Anlagenart', () => {
60 it('haelt an der Fussgaengerschutzanlage die Zahlen der Anlagenart bereit', () => {
61 // Voraussetzung des Aufbaus: Die Anlagenart fuehrt einen anderen
62 // Regelbereich als der Knotenpunkt.
63 expect(ANLAGENART_GRENZEN.fussgaengerschutzanlage.cycleTime.recommendedMin).toBe(40);
64 // Seit 5.27.0 ist der Vorgabewert der Richtwert der RiLSA (Abschnitt 2.6);
65 // zuvor standen hier 60 s, eine Zahl dieses Programms.
66 expect(RILSA_DEFAULTS.cycleTime.recommendedMin).toBe(30);
67 });
68
69 it('macht aus einer wirkungslosen Eingabe keine Abweichung', () => {
70 const deviations = abweichungen({ cycleTimeRecommendedMin: 40 }, 'fussgaengerschutzanlage');
71 expect(
72 deviations.map((d) => d.key),
73 'An der Fussgaengerschutzanlage gelten 40 s ohnehin - das ist keine Abweichung',
74 ).not.toContain('cycleTimeRecommendedMin');
75 });
76
77 it('weist eine wirksame Eingabe als Abweichung aus', () => {
78 const deviations = abweichungen({ cycleTimeRecommendedMin: 60 }, 'fussgaengerschutzanlage');
79 const zeile = deviations.find((d) => d.key === 'cycleTimeRecommendedMin');
80 expect(
81 zeile,
82 '60 s verschieben den Regelbereich von 40 auf 60 - das gehoert in die Unterlage',
83 ).toBeDefined();
84 expect(zeile!.standardValue).toBe(40);
85 expect(zeile!.appliedValue).toBe(60);
86 });
87
88 it('haelt beide Ausgaben derselben Unterlage gegeneinander', () => {
89 /*
90 * NACHGETRAGEN (Fassung 5.12.0): Der Bezugswert wirkte hier
91 * nur in `resolveRilsaSettings`, also in der Abweichungstabelle und im
92 * Pruefbericht. Das Fundstellenverzeichnis (services/export/pdf.ts) und die
93 * Spalte "Regelwert" der Vorgabenansicht (ui/views/settingsView.ts) lasen
94 * ihren Bezug aus `settingFields().standard`, und der ist der
95 * Knotenpunktwert - dieser Fall hielt fest, was die Unterlage damit an der
96 * Fussgaengerschutzanlage sagte. Inzwischen fuehrt die Vorgabenzeile ihren
97 * Regelwert selbst (`settingFields().regelwert`, gebildet in derselben
98 * Funktion wie der Bezug der Abweichungsliste), und beide Ausgaben lesen
99 * ihn: gemessen in tests/export/regelbereichFundstelle.test.ts und
100 * tests/ui/vorgabenRegelwert.test.ts.
101 *
102 * Der Fall bleibt, wie er ist: Er misst weiter `standard`, also den Wert,
103 * den der uebergebene Kennwertsatz fuehrt, und der ist ohne Eintrag
104 * unveraendert die Knotenpunktzahl. Genau daran haengt die Anzeige
105 * "wirksam N s"; er ist der Grund, warum der Regelwert ein eigenes Feld
106 * bekommen hat und nicht durch Umdeuten von `standard` entstand.
107 */
108 const verzeichnis = settingFields(RILSA_DEFAULTS, 'fussgaengerschutzanlage').find(
109 (f) => f.key === 'cycleTimeRecommendedMin',
110 );
111 expect(verzeichnis?.standard, 'Wert des Kennwertsatzes, nicht der Regelwert').toBe(30);
112 expect(verzeichnis?.regelwert, 'Regelwert der Vorgabenzeile - die Zahl der Anlagenart').toBe(
113 40,
114 );
115
116 // Vorgabe 40 s: Die Abweichungstabelle fuehrt sie zu Recht nicht - der
117 // angesetzte Wert ist der Regelwert der Zeile, und vom Knotenpunktwert
118 // weicht er ab, ohne dass das irgendwo eine Abweichung waere.
119 const mit40 = resolveRilsaSettings(
120 { cycleTimeRecommendedMin: 40 },
121 RILSA_DEFAULTS,
122 'fussgaengerschutzanlage',
123 );
124 expect(mit40.deviations.map((d) => d.key)).not.toContain('cycleTimeRecommendedMin');
125 expect(angesetzt(mit40.defaults)).toBe(verzeichnis?.regelwert);
126 expect(angesetzt(mit40.defaults)).not.toBe(verzeichnis?.standard);
127
128 // Vorgabe 30 s: Die Abweichungstabelle nennt "Regelwert 40, Angesetzt 30" -
129 // und ihr Bezugswert ist derselbe, den die Vorgabenzeile als Regelwert
130 // fuehrt. Das ist die Klammer, an der Bildschirm und Ausdruck haengen.
131 // Die Zahl war bis 5.26.0 die 60 des Vorgabenwerts; sie steht jetzt bei 30.
132 const mitVorgabewert = resolveRilsaSettings(
133 { cycleTimeRecommendedMin: RILSA_DEFAULTS.cycleTime.recommendedMin },
134 RILSA_DEFAULTS,
135 'fussgaengerschutzanlage',
136 );
137 expect(
138 mitVorgabewert.deviations.find((d) => d.key === 'cycleTimeRecommendedMin')?.standardValue,
139 ).toBe(verzeichnis?.regelwert);
140 expect(angesetzt(mitVorgabewert.defaults)).toBe(verzeichnis?.standard);
141 });
142
143 it('misst auch die obere Regelbereichsgrenze gegen die Anlagenart', () => {
144 const ohne = abweichungen({ cycleTimeRecommendedMax: 70 }, 'fussgaengerschutzanlage');
145 expect(ohne.map((d) => d.key)).not.toContain('cycleTimeRecommendedMax');
146
147 const mit = abweichungen({ cycleTimeRecommendedMax: 90 }, 'fussgaengerschutzanlage');
148 expect(mit.find((d) => d.key === 'cycleTimeRecommendedMax')?.standardValue).toBe(70);
149 });
150
151 it('misst die groesste Umlaufzeit weiterhin gegen den Regelwerkswert', () => {
152 /*
153 * GEGENPROBE, in beiden Staenden gruen: Bei der kleinsten und groessten
154 * Umlaufzeit bleibt der Knotenpunktwert der Bezug. Anders als beim
155 * Regelbereich ist er dort ein Regelwerkswert - belegt sind 30 bis 120 s,
156 * und die RiLSA staffelt sie nicht nach Anlagenart. Die 300 s der
157 * einstreifigen Fuehrung setzt dieses Programm; eine Vorgabe von 250 s ist
158 * deshalb eine Abweichung von 120 s, so wie die Vorgabenansicht sie seit
159 * Befund C10 ausweist.
160 */
161 expect(ANLAGENART_GRENZEN.einstreifig.cycleTime.max).toBe(300);
162 const zeile = abweichungen({ cycleTimeMax: 250 }, 'einstreifig').find(
163 (d) => d.key === 'cycleTimeMax',
164 );
165 expect(zeile).toBeDefined();
166 expect(zeile!.standardValue).toBe(120);
167 expect(zeile!.appliedValue).toBe(250);
168 });
169
170 it('laesst den Knotenpunkt unveraendert', () => {
171 const zeile = abweichungen({ cycleTimeRecommendedMin: 40 }, 'knotenpunkt').find(
172 (d) => d.key === 'cycleTimeRecommendedMin',
173 );
174 expect(zeile?.standardValue).toBe(30);
175 expect(abweichungen({ cycleTimeMax: 120 }, 'knotenpunkt').map((d) => d.key)).not.toContain(
176 'cycleTimeMax',
177 );
178 });
179
180 it('nennt bei einem ungueltigen Wert den Regelwert der Anlagenart', () => {
181 const { notes } = resolveRilsaSettings(
182 { cycleTimeRecommendedMin: Number.NaN },
183 RILSA_DEFAULTS,
184 'fussgaengerschutzanlage',
185 );
186 const note = notes.find((n) => n.code === 'vorgabe-ungueltig:cycleTimeRecommendedMin');
187 expect(note).toBeDefined();
188 expect(note!.message).toContain('40 s');
189 });
190 });