lsa-planer

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

/ tests domain regelwertBezugJeFeld.test.ts

7,3 KB Rohdatei
tests/domain/regelwertBezugJeFeld.test.ts — 148 Zeilen
1 import { describe, expect, it } from 'vitest';
2 import { ANLAGENART_GRENZEN, RILSA_DEFAULTS } from '@/domain/rilsa/constants';
3 import { resolveRilsaSettings, settingFields } from '@/domain/rilsa/settings';
4 import type { RilsaOverrides, SettingField } from '@/domain/rilsa/settings';
5 import type { Anlagenart } from '@/domain/rilsa/types';
6
7 /*
8 * DER BEZUGSWERT STEHT AN EINER STELLE - fuer jedes Feld und jede Anlagenart.
9 *
10 * Die Zahl, gegen die eine Vorgabe als Abweichung gemessen wird, faellt in
11 * settings.ts an einer einzigen Stelle (`regelwertVon`). Zwei Ausgaben lesen
12 * sie: die Abweichungsliste (`resolveRilsaSettings(...).deviations`, aus der
13 * die gedruckte Aufstellung "Abweichungen von den Regelwerten" und der
14 * Pruefbericht entstehen) und das Feld `regelwert` der Vorgabenzeile
15 * (`settingFields`), aus dem Vorgabenansicht und Fundstellenverzeichnis lesen.
16 *
17 * DASS BEIDE DIESELBE ZAHL FUEHREN, IST DIE BEHAUPTUNG, AUF DER DAS
18 * FUNDSTELLENVERZEICHNIS RUHT. `fundstellenkennwerte`
19 * (services/export/bewertung.ts) hat den Bezug bis Fassung 5.12.0 ueber einen
20 * Umweg geholt: eine Vorlage, in der jeder Kennwert auf `standard` steht, durch
21 * `resolveRilsaSettings` gefuehrt, und aus den gemeldeten Abweichungen deren
22 * `standardValue` gelesen. Seither nimmt es `regelwert` unmittelbar. Der Umweg
23 * war gegen genau eine Gefahr gebaut - dass die beiden Seiten auseinanderlaufen
24 * und die Anordnungsunterlage zwei Regelwerte fuer denselben Kennwert nennt;
25 * dreimal ist das schon geschehen (tests/domain/regelbereichBezug.test.ts,
26 * tests/export/regelbereichFundstelle.test.ts,
27 * tests/ui/vorgabenRegelwert.test.ts). Diese Wache haelt sie stattdessen
28 * zusammen, und zwar breiter als der Umweg es konnte: nicht nur an den beiden
29 * Regelbereichsfeldern der Umlaufzeit, sondern an JEDEM Kennwert und JEDER
30 * Anlagenart.
31 *
32 * FUER DIE VEREINFACHUNG SELBST GIBT ES KEINEN ROTEN FALL, und das ist keine
33 * Nachlaessigkeit: Sie ist wirkungsgleich, gemessen ueber alle Kennwerte und
34 * alle Anlagenarten (Kopfkommentar von `fundstellenkennwerte`). Rot wird diese
35 * Wache gegen die Gefahr, die dahintersteht - gemessen an zwei Staenden, die es
36 * beide einmal gegeben hat: Fuehrt die Vorgabenzeile ihren Regelwert wieder aus
37 * `standard` (der Stand vor Fassung 5.12.0), schlagen 3 der 5 Faelle fehl - die
38 * Vorbedingung und die beiden Anlagenarten mit eigenem Regelbereich. Misst die
39 * Abweichungsliste wieder gegen `standard` (der Stand vor Fassung 5.11.0), sind
40 * es 2 von 5: Dort bleibt die Vorbedingung gruen, weil die Vorgabenzeile die
41 * Verschiebung weiterhin fuehrt.
42 */
43
44 const ARTEN = Object.keys(ANLAGENART_GRENZEN) as readonly Anlagenart[];
45
46 /**
47 * Ein Eintrag, der innerhalb der Schranken des Feldes liegt und vom Regelwert
48 * abweicht - damit die Abweichungsliste den Bezug ueberhaupt nennt.
49 *
50 * Bevorzugt nach UNTEN, denn nach oben kappen die zusammenhaengenden Schranken
51 * (Abbieger gegen Geradeausstrom, enger Innenradius, erhoehter Zeitbedarf) den
52 * Wert auf den Nachbarn und koennen die Zeile dadurch verschwinden lassen. Wo
53 * der Regelwert selbst die Untergrenze ist - bei den Einfahrgeschwindigkeiten
54 * und der kleinsten Umlaufzeit -, geht es nach oben.
55 *
56 * DICHT AM REGELWERT, nicht in die Mitte bis zur Schranke: Eine kleinste
57 * Umlaufzeit von 165 s (die Mitte zwischen 30 und den 300 s der einstreifigen
58 * Fuehrung) ist groesser als die groesste Umlaufzeit von 120 s, und dieser
59 * Widerspruch setzt zu Recht ALLE Vorgaben auf die Regelwerte zurueck -
60 * gemessen: Die Abweichungsliste ist dann leer und nennt gar keinen Bezug
61 * mehr. Ein Schritt von hoechstens einer Einheit bleibt innerhalb der
62 * Schranken beider Umlaufzeitfelder.
63 */
64 function eintragNebenDemRegelwert(feld: SettingField): number {
65 if (Number.isFinite(feld.min) && feld.min < feld.regelwert) {
66 return feld.regelwert - Math.min(1, (feld.regelwert - feld.min) / 2);
67 }
68 if (Number.isFinite(feld.max) && feld.max > feld.regelwert) {
69 return feld.regelwert + Math.min(1, (feld.max - feld.regelwert) / 2);
70 }
71 return feld.regelwert + 1;
72 }
73
74 describe('Der Bezugswert der Abweichungsliste ist der Regelwert der Vorgabenzeile', () => {
75 it('haelt die Vorbedingung: die Anlagenart verschiebt wenigstens einen Regelwert', () => {
76 /*
77 * Ohne ein Feld, bei dem `regelwert` und `standard` auseinandertreten,
78 * pruefte alles Weitere nichts: Dann waere jede Verwechslung der beiden
79 * folgenlos. Die beiden Regelbereichsfelder der Umlaufzeit sind dieses
80 * Feld - an der Fussgaengerschutzanlage und bei einstreifiger Fuehrung.
81 */
82 const verschoben = ARTEN.flatMap((art) =>
83 settingFields(RILSA_DEFAULTS, art)
84 .filter((f) => f.regelwert !== f.standard)
85 .map((f) => `${art}/${f.key}`),
86 );
87 /*
88 * Seit 5.27.0 steht der Vorgabewert der Untergrenze bei 30 s (RiLSA 2015,
89 * Abschnitt 2.6) statt bei 60 s. Damit tritt auch die einstreifige
90 * Verkehrsfuehrung mit ihren 60 s auseinander - vorher fiel sie mit dem
91 * Vorgabewert zusammen und stand deshalb nicht in dieser Liste.
92 */
93 expect(verschoben).toEqual([
94 'fussgaengerschutzanlage/cycleTimeRecommendedMin',
95 'fussgaengerschutzanlage/cycleTimeRecommendedMax',
96 'einstreifig/cycleTimeRecommendedMin',
97 'einstreifig/cycleTimeRecommendedMax',
98 ]);
99 });
100
101 it.each(ARTEN)('misst bei %s jeden Kennwert gegen den Regelwert seiner Zeile', (art) => {
102 const felder = settingFields(RILSA_DEFAULTS, art);
103 expect(felder.length, 'Vorbedingung: die Kennwerte stehen alle in der Liste').toBeGreaterThan(
104 40,
105 );
106
107 for (const feld of felder) {
108 const eintrag = eintragNebenDemRegelwert(feld);
109 // Weit genug weg: `resolveRilsaSettings` rundet den Vergleich auf sechs
110 // Nachkommastellen, ein noch kleinerer Abstand waere keine Abweichung.
111 expect(
112 Math.abs(eintrag - feld.regelwert),
113 `${feld.key}: der Probeeintrag weicht ab`,
114 ).toBeGreaterThan(1e-6);
115
116 const overrides: RilsaOverrides = { [feld.key]: eintrag };
117 const { deviations } = resolveRilsaSettings(overrides, RILSA_DEFAULTS, art);
118 const gemeldet = deviations.find((d) => d.key === feld.key);
119 expect(gemeldet, `${feld.key}: die Abweichungsliste fuehrt die Zeile`).toBeDefined();
120 expect(gemeldet?.standardValue, `${feld.key}: Bezugswert der Abweichungsliste`).toBe(
121 feld.regelwert,
122 );
123
124 /*
125 * Auch die Zeilen, die der Eintrag NEBENBEI ausloest: Wer den
126 * Geradeausstrom absenkt, kappt den Abbieger mit, und die Abweichung
127 * dieses Nachbarn entsteht erst dabei (settings.ts,
128 * `berichtigeAbweichung`). Sie muss denselben Bezug nennen.
129 */
130 for (const d of deviations) {
131 const nachbar = felder.find((f) => f.key === d.key);
132 expect(d.standardValue, `${feld.key} loest ${d.key} aus: Bezugswert`).toBe(
133 nachbar?.regelwert,
134 );
135 }
136 }
137 });
138
139 it('nennt ohne Eintrag keine Abweichung und keinen abweichenden Bezug', () => {
140 // Die Gegenprobe: Der Regelwertsatz selbst ist keine Abweichung - auch
141 // dort nicht, wo die Anlagenart einen anderen Regelwert fuehrt als die
142 // Zahl des Regelwerkssatzes.
143 for (const art of ARTEN) {
144 expect(resolveRilsaSettings(undefined, RILSA_DEFAULTS, art).deviations).toEqual([]);
145 expect(resolveRilsaSettings({}, RILSA_DEFAULTS, art).deviations).toEqual([]);
146 }
147 });
148 });