waffensachkunde

Waffensachkunde – Lernsoftware für die Sachkundeprüfung nach § 7 WaffG. Barrierefrei, offline, EUPL-1.2.

/ app src main energie.ts

4,0 KB Rohdatei
app/src/main/energie.ts — 106 Zeilen
1 /**
2 * Energiesparmodus des Rechners.
3 *
4 * **Wozu.** Die Prüfungssimulation misst ihre Restzeit an der Wanduhr:
5 * `endeMs = startMs + Minuten`, und `verbrauchtMs = Date.now() − startMs`.
6 * Klappt jemand mitten in einem 120-Minuten-Bogen den Laptop zu, zählt die
7 * gesamte Schlafzeit als Bearbeitungszeit – nach einer Mittagspause ist der
8 * Bogen beim Aufwachen abgelaufen, und der Herzschlag der Simulation schreibt
9 * die aufgeblähte Dauer sogar in die Datenbank. Dieses Modul misst, wie lange
10 * der Rechner wirklich geschlafen hat, und meldet es der Oberfläche; die
11 * schreibt ihren Zeitursprung um denselben Betrag vor. Die Uhr steht damit
12 * während des Schlafs still – dieselbe Zusage, die der Schließen-Dialog in
13 * {@link ./fenster} für das Beenden schon macht.
14 *
15 * **Warum das nicht im Renderer gemessen wird.** Naheliegend wäre ein
16 * Sekundentakt, der prüft, ob zwischen zwei Takten mehr als eine Sekunde
17 * vergangen ist. Chromium drosselt Zeitgeber in verdeckten Fenstern jedoch auf
18 * einen Takt pro Minute: Ein bloß **minimiertes** Fenster sähe damit aus wie
19 * ein schlafender Rechner, und die Prüfungsuhr ließe sich durch Minimieren
20 * anhalten. Das wäre ein Schummelweg, kein Nachteilsausgleich. `powerMonitor`
21 * meldet ausschließlich echten Energiesparmodus und ist von der Drosselung
22 * unberührt.
23 *
24 * **Was hier bewusst nicht behandelt wird.** Ein Sprung der Systemuhr (NTP,
25 * manuelles Stellen) verfälscht dieselbe Rechnung, wird hier aber nicht
26 * abgefangen: Ihn vom Energiesparmodus zu unterscheiden verlangt eine zweite,
27 * monotone Zeitquelle, deren Verhalten im Schlaf je nach Plattform abweicht –
28 * und beide Quellen zugleich zu korrigieren führte zur Doppelgutschrift. Der
29 * Bearbeitungsdauer ist damit weiterhin nur bis zur Grenze der
30 * Kappung zu trauen, wenn jemand während eines Laufs die Uhr stellt.
31 *
32 * **Die Bildschirmsperre zählt mit.** `lock-screen` wird nicht abgehört: Ein
33 * gesperrter, laufender Rechner ist etwas anderes als ein schlafender. Wer den
34 * Bildschirm sperrt und weggeht, bearbeitet den Bogen nicht – aber er könnte
35 * ihn auf einem zweiten Gerät nachschlagen. Die Uhr läuft dort weiter.
36 */
37
38 import { BrowserWindow, powerMonitor } from 'electron';
39
40 import { KANAL_ENERGIESPAREN_ENDE } from '../shared/ipc';
41
42 /**
43 * Kürzeste Schlafdauer, die gemeldet wird.
44 *
45 * Zwischen `suspend` und `resume` liegt selbst bei einem sofort abgebrochenen
46 * Standby ein Rest an Zeit. Eine Gutschrift von Bruchteilen einer Sekunde
47 * verschöbe den Zeitursprung ohne erkennbaren Anlass und ließe die Ansage
48 * grundlos erscheinen.
49 */
50 const MINDESTDAUER_MS = 2000;
51
52 /** Zeitpunkt des Einschlafens; `null`, solange der Rechner wach ist. */
53 let eingeschlafenUm: number | null = null;
54
55 /** Ob die Zuhörer hängen – ein zweites Anmelden meldete doppelt. */
56 let angemeldet = false;
57
58 function beiSuspend(): void {
59 eingeschlafenUm = Date.now();
60 }
61
62 function beiResume(): void {
63 const beginn = eingeschlafenUm;
64 eingeschlafenUm = null;
65 if (beginn === null) {
66 return;
67 }
68
69 const schlafMs = Date.now() - beginn;
70 /* Auch gegen einen Uhrsprung während des Schlafs abgesichert: Eine negative
71 oder winzige Dauer wird nicht gemeldet, statt den Ursprung zu verschieben. */
72 if (schlafMs < MINDESTDAUER_MS) {
73 return;
74 }
75
76 for (const fenster of BrowserWindow.getAllWindows()) {
77 if (!fenster.isDestroyed()) {
78 fenster.webContents.send(KANAL_ENERGIESPAREN_ENDE, schlafMs);
79 }
80 }
81 }
82
83 /**
84 * Beginnt, den Energiesparmodus zu beobachten.
85 *
86 * Mehrfaches Aufrufen ist folgenlos.
87 */
88 export function energieUeberwachen(): void {
89 if (angemeldet) {
90 return;
91 }
92 powerMonitor.on('suspend', beiSuspend);
93 powerMonitor.on('resume', beiResume);
94 angemeldet = true;
95 }
96
97 /** Gegenstück für Tests und sauberes Herunterfahren. */
98 export function energieVergessen(): void {
99 if (!angemeldet) {
100 return;
101 }
102 powerMonitor.off('suspend', beiSuspend);
103 powerMonitor.off('resume', beiResume);
104 angemeldet = false;
105 eingeschlafenUm = null;
106 }