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