lsa-planer
LSA-Planer Professional – Planungssoftware für Lichtsignalanlagen nach RiLSA 2015 und § 45 StVO. EUPL-1.2.
/ tests domain regelwertBezugJeFeld.test.ts
| 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 | }); |