lsa-planer
LSA-Planer Professional – Planungssoftware für Lichtsignalanlagen nach RiLSA 2015 und § 45 StVO. EUPL-1.2.
/ src domain rilsa engstelle.ts
| 1 | import type { KilometersPerHour, Meters, Seconds } from '../units'; |
| 2 | import { |
| 3 | ANLAGENART_GRENZEN, |
| 4 | ENGSTELLE_PRAXIS, |
| 5 | RILSA_DEFAULTS, |
| 6 | type RilsaDefaults, |
| 7 | } from './constants'; |
| 8 | import { computeEngstellenIntergreen } from './intergreen'; |
| 9 | import { minGreenTime } from './signalTimes'; |
| 10 | import { minimumCycleTime } from './cycle'; |
| 11 | |
| 12 | /** |
| 13 | * Was die Laenge einer Engstelle fuer den Betrieb der Anlage bedeutet. |
| 14 | * |
| 15 | * Stand bis 5.3.4 in src/ui/assistent.ts und damit in der Oberflaechenschicht - |
| 16 | * entgegen dem Grundsatz, dass ausschliesslich in src/domain gerechnet wird. |
| 17 | * Aufgefallen ist es, weil der Ausdruck dieselben Zahlen braucht: Die |
| 18 | * Planunterlage begruendet dort die Obergrenze der Umlaufzeit fuer eine |
| 19 | * Arbeitsstelle. Sie trug sie als FESTE Zahlen im Satz, waehrend die Laenge |
| 20 | * daneben gerechnet wurde - und die festen Zahlen waren falsch. Zwei Stellen, |
| 21 | * die dasselbe herleiten, laufen auseinander; eine gemeinsame rechnet. |
| 22 | */ |
| 23 | export interface EngstellenFolgen { |
| 24 | /** Zwischenzeit je Fahrtrichtung - fuer die angelegte Planung exakt. */ |
| 25 | readonly zwischenzeit: Seconds; |
| 26 | /** Kuerzester ueberhaupt schaltbarer Umlauf. */ |
| 27 | readonly umlaufzeit: Seconds; |
| 28 | /** Laengste Rotzeit einer Richtung, mindestens. */ |
| 29 | readonly wartezeit: Seconds; |
| 30 | readonly stufe: 'hinweis' | 'warnung' | 'fehler'; |
| 31 | /** |
| 32 | * Einstufung der Laenge nach Behoerdenpraxis (ENGSTELLE_PRAXIS) - getrennt |
| 33 | * von `stufe`, weil sie etwas anderes bewertet: nicht die Wartezeit, sondern |
| 34 | * ob die Behoerde eine Baustellenampel dieser Laenge ueberhaupt aufstellt. |
| 35 | * `null`, solange die Laenge unauffaellig ist. |
| 36 | */ |
| 37 | readonly praxisHinweis: EngstellenPraxisHinweis | null; |
| 38 | } |
| 39 | |
| 40 | /** |
| 41 | * Hinweis auf den Abstimmungsbedarf mit der Strassenverkehrsbehoerde. |
| 42 | * |
| 43 | * BEHOERDENPRAXIS, KEIN REGELWERKSWERT - deshalb ist der Text Teil des |
| 44 | * Ergebnisses: Wer ihn zeigt, zeigt auch die Einordnung, und Assistent und |
| 45 | * Pruefbericht sagen dasselbe (Fassung 5.4.0, Befund C5). |
| 46 | */ |
| 47 | export interface EngstellenPraxisHinweis { |
| 48 | readonly stufe: 'hinweis' | 'warnung'; |
| 49 | /** Schwelle in Metern, die ueberschritten ist. */ |
| 50 | readonly ab: Meters; |
| 51 | readonly text: string; |
| 52 | } |
| 53 | |
| 54 | /** |
| 55 | * Stuft eine Engstellenlaenge nach Behoerdenpraxis ein. |
| 56 | * |
| 57 | * Ab ENGSTELLE_PRAXIS.hinweisAb ein Hinweis, ab ENGSTELLE_PRAXIS.warnungAb eine |
| 58 | * Warnung, jeweils einschliesslich; darunter `null`. Vor Fassung 5.4.0 |
| 59 | * (Befund C5) meldete das Programm zwischen 400 und rund 835 m nichts. |
| 60 | */ |
| 61 | export function engstellenPraxisHinweis(laenge: Meters): EngstellenPraxisHinweis | null { |
| 62 | if (!Number.isFinite(laenge) || laenge < ENGSTELLE_PRAXIS.hinweisAb) return null; |
| 63 | const warnung = laenge >= ENGSTELLE_PRAXIS.warnungAb; |
| 64 | return { |
| 65 | stufe: warnung ? 'warnung' : 'hinweis', |
| 66 | ab: warnung ? ENGSTELLE_PRAXIS.warnungAb : ENGSTELLE_PRAXIS.hinweisAb, |
| 67 | text: |
| 68 | `Die Engstelle ist ${meter(laenge)} m lang. Behördenpraxis (z. B. Hessen Mobil), kein ` + |
| 69 | `Regelwerkswert: Baustellenampeln werden bis ${meter(ENGSTELLE_PRAXIS.hinweisAb)} m ` + |
| 70 | `aufgestellt, ausnahmsweise und mit Zustimmung bis ${meter(ENGSTELLE_PRAXIS.warnungAb)} m. ` + |
| 71 | // "erreicht oder ueberschreitet", nicht "liegt ueber": Die Schwellen |
| 72 | // gelten einschliesslich (>=), und bei genau 400 m bzw. 600 m waere |
| 73 | // "ueber 400 m" falsch. |
| 74 | (warnung |
| 75 | ? `Diese Länge erreicht oder überschreitet auch die Ausnahmegrenze von ` + |
| 76 | `${meter(ENGSTELLE_PRAXIS.warnungAb)} m; ohne ausdrückliche Zustimmung der ` + |
| 77 | 'Straßenverkehrsbehörde ist mit einer Ablehnung zu rechnen.' |
| 78 | : `Diese Länge erreicht oder überschreitet die übliche Obergrenze von ` + |
| 79 | `${meter(ENGSTELLE_PRAXIS.hinweisAb)} m; die Abstimmung mit der ` + |
| 80 | 'Straßenverkehrsbehörde ist vor der Anordnung zu suchen.'), |
| 81 | }; |
| 82 | } |
| 83 | |
| 84 | /** |
| 85 | * Engstellenlaenge, wie dieses Programm sie abbildet: der groesste Raeumweg der |
| 86 | * Konfliktbeziehungen. Bei einer einstreifigen Verkehrsfuehrung IST der Raeumweg |
| 87 | * die Engstelle; eine eigene Laengenangabe fuehrt das Modell nicht. Ohne |
| 88 | * Beziehung mit Raeumweg ist die Laenge unbekannt - `null`, nicht 0: Aus 0 m |
| 89 | * folgte sonst "kurze Engstelle". |
| 90 | */ |
| 91 | export function engstellenlaengeAusRaeumwegen( |
| 92 | conflicts: readonly { readonly clearingDistance: Meters }[], |
| 93 | ): Meters | null { |
| 94 | let laenge = 0; |
| 95 | for (const conflict of conflicts) { |
| 96 | if (Number.isFinite(conflict.clearingDistance) && conflict.clearingDistance > laenge) { |
| 97 | laenge = conflict.clearingDistance; |
| 98 | } |
| 99 | } |
| 100 | return laenge > 0 ? laenge : null; |
| 101 | } |
| 102 | |
| 103 | /** |
| 104 | * Rechnet aus, was die Laenge der Engstelle bewirkt. |
| 105 | * |
| 106 | * DAS MUSS DER ANWENDER VOR DEM ANLEGEN ERFAHREN: Die Laenge ist bei einer |
| 107 | * einstreifigen Fuehrung die einzige Stellschraube, und sie geht doppelt in den |
| 108 | * Umlauf ein - je Fahrtrichtung einmal. Wer 800 m absperrt, laesst die |
| 109 | * Gegenrichtung fast drei Minuten warten, und das erfaehrt er sonst erst, wenn |
| 110 | * die Planung fertig ist und der Pruefbericht sie beanstandet. |
| 111 | * |
| 112 | * Gerechnet wird ausschliesslich mit den Funktionen des Regelwerks, und zwar mit |
| 113 | * denselben Eingangsgroessen, die das angelegte Projekt spaeter traegt (Pkw als |
| 114 | * Fahrzeugart, Einfahrweg 0). Die genannte Zwischenzeit ist deshalb keine |
| 115 | * Schaetzung, sondern der Wert, der anschliessend im Plan steht. |
| 116 | * |
| 117 | * Umlaufzeit und Wartezeit sind dagegen UNTERGRENZEN: Sie unterstellen die |
| 118 | * kuerzest zulaessige Freigabezeit fuer die Gegenrichtung. Mit einer brauchbaren |
| 119 | * Freigabezeit fallen beide laenger aus - die Warnung schlaegt also eher zu spaet |
| 120 | * als zu frueh an, und das ist die sichere Seite fuer eine Warnung, die zum |
| 121 | * Verzicht auf die Ampel raten kann. |
| 122 | * |
| 123 | * `defaults` SIND DIE KENNWERTE DER PLANUNG (Fassung 5.4.0, Befund D1): Bis |
| 124 | * dahin rechnete diese Funktion fest mit RILSA_DEFAULTS, waehrend der |
| 125 | * Plan daneben mit den aufgeloesten Vorgaben des Projekts rechnete. Wer die |
| 126 | * Raeumgeschwindigkeit auf 8 m/s gesetzt hatte, bekam in Assistent und |
| 127 | * Ausdruck eine Zwischenzeit, die im Plan nicht vorkommt - und im Ausdruck ist |
| 128 | * genau diese Zahl die BEGRUENDUNG der Umlaufzeitobergrenze. Der Vorgabewert |
| 129 | * haelt alte Aufrufe uebersetzbar; jeder Aufrufer soll die Kennwerte seines |
| 130 | * Projekts durchreichen. |
| 131 | */ |
| 132 | export function engstellenFolgen( |
| 133 | laenge: Meters, |
| 134 | vZul: KilometersPerHour, |
| 135 | defaults: RilsaDefaults = RILSA_DEFAULTS, |
| 136 | ): EngstellenFolgen { |
| 137 | /* |
| 138 | * NACHGEZOGEN (Fassung 5.27.0): Gerechnet wird das Verfahren des Abschnitts |
| 139 | * 5.2 (Engstellensignalisierung) und nicht mehr das des Abschnitts 2. |
| 140 | * |
| 141 | * Der Grundsatz dieser Funktion bleibt und ist der Grund fuer die |
| 142 | * Aenderung: Die genannte Zwischenzeit ist keine Schaetzung, sondern der |
| 143 | * Wert, der anschliessend im Plan steht. Seit der Plan die Engstelle nach |
| 144 | * 5.2 rechnet (resolveIntergreens), muss diese Vorschau es auch - sonst |
| 145 | * nennt der Assistent eine Zahl, die der Plan daneben nicht fuehrt, und |
| 146 | * die Planunterlage begruendet ihre Umlaufzeitobergrenze mit einer dritten. |
| 147 | * |
| 148 | * `defaults` geht hier nicht mehr ein: Das Verfahren des Abschnitts 5.2 |
| 149 | * kennt keine Vorgabenwerte - Ueberfahrzeit und Raeumgeschwindigkeit stehen |
| 150 | * fest im Regelwerk. Der Parameter bleibt, weil `umlaufBeiFreigabe` und die |
| 151 | * Schranken der Anlagenart ihn weiter brauchen. |
| 152 | */ |
| 153 | const zwischenzeit = computeEngstellenIntergreen( |
| 154 | { mode: 'kfz', movement: 'geradeaus', clearingDistance: laenge, vehicleClass: 'pkw', vZul }, |
| 155 | { mode: 'kfz', movement: 'geradeaus', enteringDistance: 0 }, |
| 156 | defaults, |
| 157 | ).intergreen; |
| 158 | |
| 159 | const freigabe = minGreenTime('kfz', defaults); |
| 160 | const umlaufzeit = minimumCycleTime([freigabe, freigabe], [zwischenzeit, zwischenzeit]); |
| 161 | // Die Wartezeit ist der Umlauf ABZUEGLICH der eigenen Freigabezeit: die Rotzeit |
| 162 | // dessen, der die Haltlinie erreicht, sobald seine Richtung endet. |
| 163 | const wartezeit = minimumCycleTime([freigabe], [zwischenzeit, zwischenzeit]); |
| 164 | |
| 165 | /* |
| 166 | * Die Schranken bleiben die der ANLAGENART und werden bewusst nicht aus |
| 167 | * `defaults` genommen: Wartezeitwarnung, Wartezeitfehler und die 300 s |
| 168 | * Umlauf sind Schwellen dieses Programms (ANLAGENART_GRENZEN, Befund B8), |
| 169 | * keine anpassbaren Vorgaben. Aenderte sie ein Vorgabewert mit, liesse sich |
| 170 | * die Warnung, die vom Verzicht auf die Ampel abraet, wegstellen. |
| 171 | */ |
| 172 | const grenzen = ANLAGENART_GRENZEN.einstreifig; |
| 173 | const stufe: EngstellenFolgen['stufe'] = |
| 174 | wartezeit >= grenzen.warteZeitFehler || umlaufzeit > grenzen.cycleTime.max |
| 175 | ? 'fehler' |
| 176 | : wartezeit >= grenzen.warteZeitWarnung |
| 177 | ? 'warnung' |
| 178 | : 'hinweis'; |
| 179 | |
| 180 | return { |
| 181 | zwischenzeit, |
| 182 | umlaufzeit, |
| 183 | wartezeit, |
| 184 | stufe, |
| 185 | praxisHinweis: engstellenPraxisHinweis(laenge), |
| 186 | }; |
| 187 | } |
| 188 | |
| 189 | /** |
| 190 | * Umlauf bei einer BRAUCHBAREN Freigabezeit statt der kuerzest zulaessigen. |
| 191 | * |
| 192 | * Fuer Begruendungstexte: Die Untergrenze aus engstellenFolgen unterstellt 5 s |
| 193 | * Freigabe je Richtung und ist als Schaltvorschlag unbrauchbar. Wer erklaeren |
| 194 | * will, warum eine Obergrenze dort liegt, wo sie liegt, muss mit einer |
| 195 | * Freigabezeit rechnen, die man tatsaechlich anordnen wuerde. |
| 196 | * |
| 197 | * `defaults` wie bei engstellenFolgen (Befund D1): Der Satz, der diese Zahl |
| 198 | * traegt, steht im Ausdruck neben den Zwischenzeiten des Plans. |
| 199 | */ |
| 200 | export function umlaufBeiFreigabe( |
| 201 | laenge: Meters, |
| 202 | vZul: KilometersPerHour, |
| 203 | freigabe: Seconds, |
| 204 | defaults: RilsaDefaults = RILSA_DEFAULTS, |
| 205 | ): Seconds { |
| 206 | const { zwischenzeit } = engstellenFolgen(laenge, vZul, defaults); |
| 207 | return minimumCycleTime([freigabe, freigabe], [zwischenzeit, zwischenzeit]); |
| 208 | } |
| 209 | |
| 210 | function meter(wert: number): string { |
| 211 | return Number.isInteger(wert) ? String(wert) : wert.toFixed(1).replace('.', ','); |
| 212 | } |