iobroker.alias-workbench 0.9.10 → 0.9.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +66 -4
- package/admin/css/werkbank.css +122 -6
- package/admin/js/baum.js +8 -0
- package/admin/js/detail.js +63 -10
- package/admin/js/entwurf.js +416 -190
- package/admin/js/enums.js +2 -2
- package/admin/js/ergebnis.js +156 -23
- package/admin/js/erkennung.js +7 -0
- package/admin/js/mqtt.js +75 -5
- package/admin/js/musterwahl.js +196 -29
- package/admin/js/objekte.js +44 -0
- package/admin/js/pruefungen.js +37 -8
- package/admin/js/schreiben.js +150 -74
- package/admin/js/verwendung.js +478 -0
- package/admin/js/vorlagen.js +75 -6
- package/admin/js/vorlagenblatt.js +134 -17
- package/admin/js/vorschlagen.js +50 -17
- package/admin/js/werte.js +96 -6
- package/admin/js/zielleiste.js +8 -2
- package/admin/js/zuordnung.js +43 -13
- package/admin/js/zustand.js +3 -0
- package/admin/js/zustandsliste.js +56 -20
- package/admin/sprachen/de.json +64 -10
- package/admin/sprachen/en.json +64 -10
- package/admin/tab.html +44 -44
- package/admin/vorlagen/messpunkt.json +1 -0
- package/admin/vorlagen/tasmota-lampe.json +1 -0
- package/admin/vorlagen/tasmota-mehrfach.json +1 -0
- package/admin/vorlagen/tasmota-steckdose.json +1 -0
- package/io-package.json +38 -29
- package/package.json +1 -1
package/admin/js/musterwahl.js
CHANGED
|
@@ -12,7 +12,7 @@ import { D, el, $ } from './basis.js';
|
|
|
12
12
|
import { tr, sprachtext } from './sprache.js';
|
|
13
13
|
import { VORSCHAU, musterVon, erkenneEntwurf, platzFuerRolle, typenFuerRolle, typKonflikte } from './erkennung.js';
|
|
14
14
|
import { anMusterAnpassen } from './vorlagen.js';
|
|
15
|
-
import { ratePlaetze,
|
|
15
|
+
import { ratePlaetze, rateAnzahl } from './vorschlagen.js';
|
|
16
16
|
import { zeichneErgebnis, entwurfAngefasst, knopfFrisch } from './ergebnis.js';
|
|
17
17
|
import { musterName, musterZeile } from './musternamen.js';
|
|
18
18
|
|
|
@@ -52,51 +52,93 @@ export function baueMusterwahl(e, haupt, pflichtFehlt) {
|
|
|
52
52
|
Er wird hier gebaut, weil er zum Muster gehoert - haengt aber unten
|
|
53
53
|
in der Leiste ueber der Zustandsliste. Neben dem Musterfeld stand er
|
|
54
54
|
am Rand; die Liste, die er veraendert, faengt darunter an. */
|
|
55
|
+
/* Der Trockenlauf, der beides beantwortet: ob der Knopf etwas
|
|
56
|
+
anzubieten haette, und - wenn nicht - warum nicht. Frueher lief er
|
|
57
|
+
zweimal (einmal fuer `rateMoeglich`, einmal fuer den Klick); einmal
|
|
58
|
+
reicht, das Ergebnis traegt den Grund gleich mit. */
|
|
59
|
+
var probe = ratePlaetze(e, true);
|
|
60
|
+
|
|
61
|
+
/* Was frueher ein Sammelsatz war („entweder alle Plaetze besetzt, oder
|
|
62
|
+
kein Datenpunkt passt"), steht jetzt als der Fall da, der wirklich
|
|
63
|
+
vorliegt - mit den Namen der Plaetze, die frei bleiben. Ohne die
|
|
64
|
+
Namen war die Meldung nicht nachpruefbar.
|
|
65
|
+
|
|
66
|
+
Gezeigt wird sie, solange keine Vermutung offen ist: dann fuehrt die
|
|
67
|
+
blaue Leiste das Wort, und zwei Meldungen uebereinander widersprechen
|
|
68
|
+
sich nur. */
|
|
69
|
+
function plaetzeText(frei) {
|
|
70
|
+
var kurz = frei.slice(0, 6).join(', ');
|
|
71
|
+
return frei.length > 6 ? tr('guess.slotsMore', kurz, frei.length - 6) : kurz;
|
|
72
|
+
}
|
|
73
|
+
function meldungZu(p) {
|
|
74
|
+
if (p.anzahl > 0) { return null; }
|
|
75
|
+
var satz = p.grund === 'voll' ? tr('guess.noneFull')
|
|
76
|
+
: p.grund === 'vergeben' ? tr('guess.noneUsed', plaetzeText(p.frei))
|
|
77
|
+
: p.grund === 'keinTreffer' ? tr('guess.noneFit', plaetzeText(p.frei))
|
|
78
|
+
: null;
|
|
79
|
+
/* Der Zusatz zur Handentscheidung (U15): Steht ein Platz nur deshalb
|
|
80
|
+
leer, weil die Zeile dazu von Hand entschieden ist, muss genau das
|
|
81
|
+
dastehen - sonst sieht es aus wie ein Fehler des Ratens. */
|
|
82
|
+
if (satz && p.handWeg) { satz += ' ' + tr('guess.noneHand', p.handWeg); }
|
|
83
|
+
return satz;
|
|
84
|
+
}
|
|
85
|
+
/* Ungefragt gezeigt wird sie nur, wo sie etwas erklaert: dass kein
|
|
86
|
+
Knopf dasteht, obwohl Plaetze frei sind. Der Fall „alle Plaetze
|
|
87
|
+
besetzt" bleibt stumm - er sagt „alles gut, nichts zu tun", und das
|
|
88
|
+
stuende dann an fast jedem Geraet (dieselbe Ueberlegung wie C27).
|
|
89
|
+
Ueber den Klick ist er weiter erreichbar. */
|
|
90
|
+
e.rateMeldung = (rateAnzahl(e) || probe.grund === 'voll')
|
|
91
|
+
? null : meldungZu(probe);
|
|
92
|
+
|
|
55
93
|
/* Und nur, wenn er etwas anzubieten haette: ohne freie Plaetze oder
|
|
56
94
|
freie Zeilen ist der Knopf ein leeres Versprechen — dann faellt er
|
|
57
|
-
weg, statt beim Klick „nichts gefunden" zu melden. Zuruecknehmen
|
|
58
|
-
|
|
95
|
+
weg, statt beim Klick „nichts gefunden" zu melden. Das Zuruecknehmen
|
|
96
|
+
steht seit dem 15.09.2026 nicht mehr auch hier: derselbe Knopf hiess
|
|
97
|
+
je nach Lage „Freie Plaetze vorschlagen" oder „Vorschlaege
|
|
98
|
+
verwerfen", und daneben stand in der blauen Leiste ein zweiter mit
|
|
99
|
+
demselben Wort. Verworfen wird dort - bei dem, was es betrifft. */
|
|
100
|
+
/* Der Rueckfrage-Dialog, wenn eine Vorlage greift (U27). Er sitzt hier
|
|
101
|
+
als eigene Funktion, weil ihn zwei Knoepfe brauchen: der in der
|
|
102
|
+
Leiste und der kleine neben dem Hinweis, der Muster wechselt und
|
|
103
|
+
gleich vorschlaegt. */
|
|
104
|
+
function mitVorlagenfrage(dann) {
|
|
105
|
+
var vq = null;
|
|
106
|
+
S.VORLAGEN.forEach(function (v) { if (v.id === e.vorlage) { vq = v; } });
|
|
107
|
+
var body = $('#guess-body');
|
|
108
|
+
body.textContent = '';
|
|
109
|
+
body.appendChild(el('div', 'aside w',
|
|
110
|
+
tr('guess.tplAsk', vq ? sprachtext(vq.name) : e.vorlage)));
|
|
111
|
+
/* onclick statt addEventListener: der Dialog ist einer fuer alle
|
|
112
|
+
Geraete, der Griff muss beim Oeffnen den alten ersetzen. */
|
|
113
|
+
$('#btn-guess-go').onclick = function () {
|
|
114
|
+
$('#dlg-guess').close();
|
|
115
|
+
dann();
|
|
116
|
+
};
|
|
117
|
+
$('#dlg-guess').showModal();
|
|
118
|
+
}
|
|
119
|
+
|
|
59
120
|
var rateKnopf = null;
|
|
60
|
-
if (
|
|
121
|
+
if (probe.anzahl > 0) {
|
|
61
122
|
/* Mit passender Vorlage ist Raten nicht der normale Weg - die
|
|
62
123
|
Zuordnung steht ja schon. Der Knopf bleibt, tritt aber leise auf
|
|
63
124
|
und fragt vor dem Lauf nach (Ricardo, 25.08.2026). */
|
|
64
125
|
var beilaeufig = !!e.vorlage && !rateAnzahl(e);
|
|
65
|
-
rateKnopf = el('button', 'btn mini'
|
|
126
|
+
rateKnopf = el('button', 'btn mini'
|
|
66
127
|
+ (beilaeufig ? ' leise' : '')
|
|
67
128
|
/* Ein leiser Knopf pocht nicht - das Pochen wuerde genau die
|
|
68
129
|
Aufmerksamkeit holen, die er nicht verdient. */
|
|
69
|
-
+ (!beilaeufig && knopfFrisch('raten:' + S.current + ':
|
|
70
|
-
|
|
130
|
+
+ (!beilaeufig && knopfFrisch('raten:' + S.current + ':vor') ? ' frisch' : ''),
|
|
131
|
+
tr('guess.button'));
|
|
71
132
|
rateKnopf.title = beilaeufig ? tr('guess.tplHint') : tr('guess.hint');
|
|
72
133
|
var rateLos = function () {
|
|
73
|
-
e.rateMeldung = ratePlaetze(e)
|
|
134
|
+
e.rateMeldung = meldungZu(ratePlaetze(e));
|
|
74
135
|
entwurfAngefasst();
|
|
75
136
|
S.openRow = null;
|
|
76
137
|
zeichneErgebnis();
|
|
77
138
|
};
|
|
78
139
|
rateKnopf.addEventListener('click', function () {
|
|
79
|
-
if (rateAnzahl(e)) {
|
|
80
|
-
rateZurueck(e); e.rateMeldung = null;
|
|
81
|
-
entwurfAngefasst();
|
|
82
|
-
S.openRow = null;
|
|
83
|
-
zeichneErgebnis();
|
|
84
|
-
return;
|
|
85
|
-
}
|
|
86
140
|
if (!beilaeufig) { rateLos(); return; }
|
|
87
|
-
|
|
88
|
-
S.VORLAGEN.forEach(function (v) { if (v.id === e.vorlage) { vq = v; } });
|
|
89
|
-
var body = $('#guess-body');
|
|
90
|
-
body.textContent = '';
|
|
91
|
-
body.appendChild(el('div', 'aside w',
|
|
92
|
-
tr('guess.tplAsk', vq ? sprachtext(vq.name) : e.vorlage)));
|
|
93
|
-
/* onclick statt addEventListener: der Dialog ist einer fuer alle
|
|
94
|
-
Geraete, der Griff muss beim Oeffnen den alten ersetzen. */
|
|
95
|
-
$('#btn-guess-go').onclick = function () {
|
|
96
|
-
$('#dlg-guess').close();
|
|
97
|
-
rateLos();
|
|
98
|
-
};
|
|
99
|
-
$('#dlg-guess').showModal();
|
|
141
|
+
mitVorlagenfrage(rateLos);
|
|
100
142
|
});
|
|
101
143
|
}
|
|
102
144
|
|
|
@@ -184,6 +226,58 @@ export function baueMusterwahl(e, haupt, pflichtFehlt) {
|
|
|
184
226
|
}
|
|
185
227
|
function markeFuer(m) { return bewerte(m).mark; }
|
|
186
228
|
|
|
229
|
+
/* ---- Was nach dem Raten passen wuerde ----------------------------
|
|
230
|
+
|
|
231
|
+
Ricardos Klimaanlage ist der Fall: `thermostat` greift und belegt 3
|
|
232
|
+
von 24 Plaetzen, `airCondition` faellt durch, weil sein einziger
|
|
233
|
+
Pflichtplatz MODE die Rolle nicht traegt. Nach dem Raten stuenden
|
|
234
|
+
dort 9 von 29 statt 8 von 24 - nur sieht man das nirgends, weil
|
|
235
|
+
„passt nicht" das letzte Wort behaelt. Wer nicht weiss, dass es den
|
|
236
|
+
Knopf gibt, waehlt `airCondition` nie aus.
|
|
237
|
+
|
|
238
|
+
Also wird einmal durchgerechnet, was jedes Muster nach dem Raten
|
|
239
|
+
ergaebe. Gerechnet wird auf einer Kopie - am Entwurf aendert das
|
|
240
|
+
nichts. Das Raten liest nur Namen, Werteliste, Einheit und Typ,
|
|
241
|
+
keine Messwerte; das Ergebnis haengt also allein an den Zeilen und
|
|
242
|
+
wird beim blossen Neuzeichnen nicht ungueltig. Gemerkt wird es
|
|
243
|
+
deshalb an `e` und faellt erst, wenn eine Zeile sich aendert
|
|
244
|
+
(`entwurfAngefasst`) oder ein anderes Muster gewaehlt ist.
|
|
245
|
+
|
|
246
|
+
Gemessen am `midea.AC` (43 Punkte): ein Trockenlauf 1,2 ms, alle 51
|
|
247
|
+
Muster 35 ms - einmal je Geraeteauswahl, gegen rund 200 ms fuers
|
|
248
|
+
Auswaehlen selbst. */
|
|
249
|
+
function probeMuster(m) {
|
|
250
|
+
if (!musterVon(m)) { return null; }
|
|
251
|
+
var kopie = JSON.parse(JSON.stringify({ kanal: e.kanal, states: e.states, want: m }));
|
|
252
|
+
/* Dieselbe Vorbereitung wie in `bewerte`: beim Vorschlag entscheidet
|
|
253
|
+
nicht das Muster, sondern die Rollen, die das Auswaehlen setzen
|
|
254
|
+
wuerde. */
|
|
255
|
+
if (e.vorschlag && !e.roh && m !== e.want) { anMusterAnpassen(kopie, m); }
|
|
256
|
+
ratePlaetze(kopie);
|
|
257
|
+
var r = erkenneEntwurf(kopie, m);
|
|
258
|
+
if (!r.length) { return { passt: false }; }
|
|
259
|
+
return { passt: true, gesamt: r[0].states.length,
|
|
260
|
+
belegt: r[0].states.filter(function (x) { return x.id; }).length };
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
function aussicht() {
|
|
264
|
+
if (e.rateAussicht && e.rateAussicht.want === e.want) { return e.rateAussicht; }
|
|
265
|
+
var proM = {}, neuPassend = [];
|
|
266
|
+
if (e.states && e.states.length) {
|
|
267
|
+
alleTypen.forEach(function (m) {
|
|
268
|
+
var p = probeMuster(m);
|
|
269
|
+
if (!p) { return; }
|
|
270
|
+
proM[m] = p;
|
|
271
|
+
/* „Neu" heisst: passt jetzt nicht, nach dem Raten schon. Das
|
|
272
|
+
gewaehlte Muster gehoert nicht dazu - es ist kein anderes. */
|
|
273
|
+
if (p.passt && m !== e.want && !bewerte(m).passt) { neuPassend.push(m); }
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
e.rateAussicht = { want: e.want, proM: proM, neu: neuPassend,
|
|
277
|
+
selbst: proM[e.want] || null };
|
|
278
|
+
return e.rateAussicht;
|
|
279
|
+
}
|
|
280
|
+
|
|
187
281
|
/* Welche Muster ohne Suchtext angeboten werden. Das erkannte und das
|
|
188
282
|
gerade gewaehlte gehoeren immer dazu - sonst fiel rgbSingle heraus,
|
|
189
283
|
sobald man einmal auf light gewechselt hatte, und der Weg zurueck
|
|
@@ -262,6 +356,7 @@ export function baueMusterwahl(e, haupt, pflichtFehlt) {
|
|
|
262
356
|
return a.rang - b.rang;
|
|
263
357
|
});
|
|
264
358
|
mEintraege = treffer;
|
|
359
|
+
var auL = aussicht();
|
|
265
360
|
|
|
266
361
|
mlist.textContent = '';
|
|
267
362
|
if (!treffer.length) {
|
|
@@ -273,8 +368,13 @@ export function baueMusterwahl(e, haupt, pflichtFehlt) {
|
|
|
273
368
|
var z = el('div', 'vz' + (n === mMarkiert ? ' an' : '')
|
|
274
369
|
+ (t0.m === e.want ? ' gewaehlt' : ''));
|
|
275
370
|
z.appendChild(el('span', null, musterZeile(t0.m)));
|
|
371
|
+
/* Hinter „✕ MODE fehlt" steht, ob das Muster nach dem Raten doch
|
|
372
|
+
greift - sonst liest man nur, dass es nicht passt, und haelt es
|
|
373
|
+
fuer erledigt. */
|
|
374
|
+
var nachRaten = (!bewerte(t0.m).passt && auL.proM[t0.m] && auL.proM[t0.m].passt)
|
|
375
|
+
? ' ' + tr('guess.alsoRow') : '';
|
|
276
376
|
z.appendChild(el('span', 'tiefer',
|
|
277
|
-
(t0.via ? tr('pattern.viaRole', t0.via) + ' ' : '') + markeFuer(t0.m)));
|
|
377
|
+
(t0.via ? tr('pattern.viaRole', t0.via) + ' ' : '') + markeFuer(t0.m) + nachRaten));
|
|
278
378
|
/* mousedown statt click: sonst greift der Fokusverlust zuerst und
|
|
279
379
|
die Liste ist weg, bevor der Klick ankommt. */
|
|
280
380
|
z.addEventListener('mousedown', function (ev) {
|
|
@@ -321,6 +421,73 @@ export function baueMusterwahl(e, haupt, pflichtFehlt) {
|
|
|
321
421
|
};
|
|
322
422
|
mBeschriften();
|
|
323
423
|
|
|
424
|
+
/* Der Hinweis neben dem Feld: genannt wird EIN Muster - das beste -
|
|
425
|
+
und nur, wenn es wirklich besser ist als das gewaehlte.
|
|
426
|
+
|
|
427
|
+
Der erste Anlauf zaehlte bloss, wie viele Muster nach dem Vorschlag
|
|
428
|
+
dazukaemen, und schrieb bei mehreren „12 weitere Muster" hin. Am
|
|
429
|
+
`midea.AC` sind das tatsaechlich zwoelf - aber elf davon sind
|
|
430
|
+
Beifang: `blind` 4/19, `illuminance` 3/7, `windowTilt` 3/7 an einer
|
|
431
|
+
Klimaanlage. Eine Zahl, die Beifang mitzaehlt, verdeckt genau die
|
|
432
|
+
eine Auskunft, um die es geht (Ricardo, 15.09.2026): `airCondition`
|
|
433
|
+
9/29 gegen `thermostat` 8/24.
|
|
434
|
+
|
|
435
|
+
Gemessen wird an den belegten Plaetzen, nicht am Anteil. 9 von 29
|
|
436
|
+
ist besser als 8 von 24, weil neun Punkte des Geraets einen Platz
|
|
437
|
+
bekommen statt acht - der Anteil waere hier sogar knapp kleiner.
|
|
438
|
+
Beide Zahlen stehen NACH dem Vorschlag; „3/24" ist der Stand jetzt
|
|
439
|
+
und gehoert nicht daneben.
|
|
440
|
+
|
|
441
|
+
Ist nichts besser, steht hier nichts - der haeufigste Fall bleibt
|
|
442
|
+
damit stumm. */
|
|
443
|
+
var auF = aussicht();
|
|
444
|
+
var bestes = null;
|
|
445
|
+
auF.neu.forEach(function (m) {
|
|
446
|
+
if (!bestes || auF.proM[m].belegt > auF.proM[bestes].belegt) { bestes = m; }
|
|
447
|
+
});
|
|
448
|
+
/* Der Vergleichswert ist der Stand des gewaehlten Musters NACH dem
|
|
449
|
+
Vorschlag. Passt es auch dann nicht, ist jedes andere besser. */
|
|
450
|
+
var jetzt = (auF.selbst && auF.selbst.passt) ? auF.selbst.belegt : -1;
|
|
451
|
+
if (bestes && auF.proM[bestes].belegt > jetzt) {
|
|
452
|
+
var pB = auF.proM[bestes], nB = musterName(bestes) || bestes;
|
|
453
|
+
var hw = el('span', 'ratAussicht');
|
|
454
|
+
var htx = el('span', null, '· ' + tr('guess.better', nB));
|
|
455
|
+
htx.title = jetzt >= 0
|
|
456
|
+
? tr('guess.betterHint', nB, pB.belegt + '/' + pB.gesamt,
|
|
457
|
+
musterName(e.want) || e.want, auF.selbst.belegt + '/' + auF.selbst.gesamt)
|
|
458
|
+
: tr('guess.betterHintNone', nB, pB.belegt + '/' + pB.gesamt,
|
|
459
|
+
musterName(e.want) || e.want);
|
|
460
|
+
hw.appendChild(htx);
|
|
461
|
+
|
|
462
|
+
/* Der Weg dorthin in einem Griff: wechseln UND vorschlagen.
|
|
463
|
+
Sonst waere der Hinweis eine Ansage ohne Handlung - man muesste
|
|
464
|
+
das Muster in der Liste suchen und danach noch den Knopf in der
|
|
465
|
+
Leiste finden (Ricardo, 16.09.2026).
|
|
466
|
+
|
|
467
|
+
Ein Zeichen statt eines Wortes: neben dem Satz bleiben rund 55 px,
|
|
468
|
+
„wechseln" braucht 58. Dieselbe Bauart wie die Uebernahmeknoepfe
|
|
469
|
+
im Detail (`←`, `⟲`) - kleines Zeichen, ausfuehrlicher Tooltip. */
|
|
470
|
+
var hgo = el('button', 'btn winzig rtgo', '→');
|
|
471
|
+
hgo.title = tr('guess.betterGoHint', nB, tr('guess.button'));
|
|
472
|
+
hgo.addEventListener('click', function (ev) {
|
|
473
|
+
ev.stopPropagation();
|
|
474
|
+
var los = function () {
|
|
475
|
+
/* Beim Vorschlag entscheidet nicht das Muster, sondern die
|
|
476
|
+
Rollen, die das Auswaehlen setzt - genau wie in `mWaehlen`. */
|
|
477
|
+
if (e.vorschlag && !e.roh) { anMusterAnpassen(e, bestes); } else { e.want = bestes; }
|
|
478
|
+
e.rateMeldung = meldungZu(ratePlaetze(e));
|
|
479
|
+
entwurfAngefasst();
|
|
480
|
+
S.openRow = null;
|
|
481
|
+
zeichneErgebnis();
|
|
482
|
+
};
|
|
483
|
+
/* Greift eine Vorlage, fragt auch dieser Weg nach (U27) - er raet
|
|
484
|
+
ja genauso ueber eine fertige Zuordnung hinweg. */
|
|
485
|
+
if (e.vorlage && !rateAnzahl(e)) { mitVorlagenfrage(los); } else { los(); }
|
|
486
|
+
});
|
|
487
|
+
hw.appendChild(hgo);
|
|
488
|
+
vw.appendChild(hw);
|
|
489
|
+
}
|
|
490
|
+
|
|
324
491
|
sel.addEventListener('focus', function () {
|
|
325
492
|
sel.value = '';
|
|
326
493
|
mMarkiert = -1;
|
package/admin/js/objekte.js
CHANGED
|
@@ -41,6 +41,7 @@ export function ladeObjekte(danach) {
|
|
|
41
41
|
if (--offen === 0) {
|
|
42
42
|
S.keysSorted = Object.keys(S.objects).sort();
|
|
43
43
|
S.kleinIndex = {};
|
|
44
|
+
kaputteAliaseNeu();
|
|
44
45
|
ladeEnums();
|
|
45
46
|
/* Erst die Einstellungen, dann die Vorlagen - sonst holt die
|
|
46
47
|
Werkbank sie auch dann, wenn sie abgeschaltet sind. */
|
|
@@ -150,6 +151,19 @@ export function horcheAufObjekte() {
|
|
|
150
151
|
return;
|
|
151
152
|
}
|
|
152
153
|
|
|
154
|
+
/* Nur die vier Arten, aus denen der Baum besteht.
|
|
155
|
+
|
|
156
|
+
Beim Laden holt `ladeObjekte` gezielt `ARTEN`, beim Nachziehen
|
|
157
|
+
wurde dagegen alles aufgenommen: eine geaenderte Instanz, ein
|
|
158
|
+
Adapter-Update, `system.config` nach dem Umstellen der Sprache.
|
|
159
|
+
Der Zaehler in der Kopfzeile ging dadurch von 3145 auf 3146, und
|
|
160
|
+
mit jeder Stunde offenem Reiter wurden es mehr (A4, 17.09.2026).
|
|
161
|
+
Wechselt ein Objekt seine Art, faellt es hier ebenfalls heraus. */
|
|
162
|
+
if (obj && ARTEN.indexOf(obj.type) === -1) {
|
|
163
|
+
if (S.objects[id] === undefined) { return; }
|
|
164
|
+
obj = null;
|
|
165
|
+
}
|
|
166
|
+
|
|
153
167
|
/* Alles Uebrige: nur was zaehlt, nicht jeder Schreibvorgang. */
|
|
154
168
|
var vorher = kern(S.objects[id]);
|
|
155
169
|
if (vorher === nachher) { return; }
|
|
@@ -206,9 +220,39 @@ export function uebernimmObjekt(id, o) {
|
|
|
206
220
|
/* Der Baum liest aus S.keysSorted, nicht aus S.objects. Wer einen Punkt
|
|
207
221
|
hinzufuegt oder entfernt, muss den Index neu bauen — sonst kennt der
|
|
208
222
|
Baum ihn nicht, egal wie oft man zeichnet. */
|
|
223
|
+
/* Aliase, bei denen mindestens ein Punkt auf eine Quelle zeigt, die es
|
|
224
|
+
nicht (mehr) gibt.
|
|
225
|
+
|
|
226
|
+
Der teuerste Fehler am Alias: der js-controller merkt sich „Quelle
|
|
227
|
+
fehlt" dauerhaft und korrigiert das nie - nur loeschen und neu anlegen
|
|
228
|
+
hilft (siehe schreiben.js). Bis zum 14.09.2026 stand das nur im
|
|
229
|
+
aufgeklappten Detail und im Pruefungen-Kasten; die Zeile in der Liste
|
|
230
|
+
schwieg, weil sie „ist eine Quelle eingetragen" fragte statt „gibt es
|
|
231
|
+
sie".
|
|
232
|
+
|
|
233
|
+
Einmal gerechnet und gemerkt, nicht bei jedem Zeichnen: den Baum baut
|
|
234
|
+
die Werkbank an sechzehn Stellen neu. Gemessen am Produktivsystem:
|
|
235
|
+
531 Alias-Punkte in 5,2 ms. */
|
|
236
|
+
export function kaputteAliaseNeu() {
|
|
237
|
+
var raus = {};
|
|
238
|
+
Object.keys(S.objects).forEach(function (id) {
|
|
239
|
+
if (id.indexOf('alias.') !== 0) { return; }
|
|
240
|
+
var ob = S.objects[id];
|
|
241
|
+
if (!ob || ob.type !== 'state') { return; }
|
|
242
|
+
var a = (ob.common || {}).alias;
|
|
243
|
+
if (!a || !a.id) { return; }
|
|
244
|
+
var ziele = (typeof a.id === 'string') ? [a.id] : [a.id.read, a.id.write];
|
|
245
|
+
if (!ziele.some(function (q) { return q && !S.objects[q]; })) { return; }
|
|
246
|
+
var kanal = id.split('.').slice(0, -1).join('.');
|
|
247
|
+
raus[kanal] = (raus[kanal] || 0) + 1;
|
|
248
|
+
});
|
|
249
|
+
S.aliasKaputt = raus;
|
|
250
|
+
}
|
|
251
|
+
|
|
209
252
|
export function indexNeu() {
|
|
210
253
|
S.keysSorted = Object.keys(S.objects).sort();
|
|
211
254
|
S.kleinIndex = {};
|
|
255
|
+
kaputteAliaseNeu();
|
|
212
256
|
zeigeObjektzahl();
|
|
213
257
|
}
|
|
214
258
|
|
package/admin/js/pruefungen.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
/* Die
|
|
1
|
+
/* Die acht Pruefungen - laufen immer mit, fassen nichts an. */
|
|
2
2
|
|
|
3
3
|
import { S } from './zustand.js';
|
|
4
4
|
import { $, el } from './basis.js';
|
|
5
5
|
import { tr } from './sprache.js';
|
|
6
|
-
import { wertVon } from './werte.js';
|
|
6
|
+
import { wertVon, zielKann } from './werte.js';
|
|
7
7
|
import './erkennung.js';
|
|
8
|
-
import { sofortRueckmeldung, mqttEinstellung } from './mqtt.js';
|
|
8
|
+
import { sofortRueckmeldung, mqttEinstellung, telegesteuert } from './mqtt.js';
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
/* Kommt die Rueckmeldung sofort oder erst mit der naechsten Telemetrie?
|
|
@@ -21,11 +21,14 @@ import { sofortRueckmeldung, mqttEinstellung } from './mqtt.js';
|
|
|
21
21
|
da, tele/STATE kam erst nach der naechsten Telemetrie — bei
|
|
22
22
|
TelePeriod 300 also bis zu fuenf Minuten spaeter. */
|
|
23
23
|
export function pruefungRueckmeldung(e) {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
24
|
+
/* Dieselbe Frage wie die MQTT-Karte, damit beide nicht auseinander
|
|
25
|
+
laufen (Ricardo, 16.09.2026). */
|
|
26
|
+
var lahm = telegesteuert(e);
|
|
27
|
+
/* Kein Haken, sondern ein Punkt: Hier wurde nichts geprueft, weil es
|
|
28
|
+
nichts zu pruefen gab. „alle in Ordnung" las sich, als haette die
|
|
29
|
+
Werkbank etwas nachgesehen - an Ricardos Steckdosenleiste lesen alle
|
|
30
|
+
Zeilen aus stat/..., SetOption59 ist dort folgenlos. */
|
|
31
|
+
if (!lahm.length) { return { s: 'mut', t: tr('check.feedback'), d: tr('check.feedbackNone') }; }
|
|
29
32
|
/* Nur warnen, wenn es wirklich langsam ist. Mit SetOption59 meldet
|
|
30
33
|
das Geraet jede Aenderung sofort — dann ist die Bauform zwar
|
|
31
34
|
dieselbe, aber kein Problem. Gemessen: drei Sekunden. */
|
|
@@ -94,6 +97,31 @@ export function pruefungSenden(e) {
|
|
|
94
97
|
}
|
|
95
98
|
|
|
96
99
|
|
|
100
|
+
/* Kann das Ziel, was der Punkt von ihm verlangt?
|
|
101
|
+
|
|
102
|
+
Drei Fragen an fremde Objekte, die die Werkbank vorher nie gestellt hat:
|
|
103
|
+
Nimmt das Schreibziel den Wert der Formel an? Laesst es sich ueberhaupt
|
|
104
|
+
beschreiben? Meldet die Lesequelle etwas? Was dabei schiefgeht, faellt
|
|
105
|
+
nirgends auf — geschrieben wird trotzdem, gelesen auch, es kommt nur
|
|
106
|
+
nichts dabei heraus (Ricardo, 15.09.2026).
|
|
107
|
+
|
|
108
|
+
Hinweis, nicht Fehler: Ob am Ende wirklich nichts passiert, entscheidet
|
|
109
|
+
der Adapter hinter dem Punkt, und den kennt die Werkbank nicht. */
|
|
110
|
+
export function pruefungZielKann(e) {
|
|
111
|
+
var treffer = [];
|
|
112
|
+
e.states.forEach(function (s) {
|
|
113
|
+
if (!s.on) { return; }
|
|
114
|
+
zielKann(s).forEach(function (x) { treffer.push({ n: s.n, art: x.art, typ: x.typ }); });
|
|
115
|
+
});
|
|
116
|
+
if (!treffer.length) { return { s: 'ok', t: tr('check.targetCan'), d: tr('check.targetCanFine') }; }
|
|
117
|
+
var text = treffer.map(function (x) {
|
|
118
|
+
if (x.art === 'formelBlind') { return tr('check.targetVoidFormula', x.n, x.typ); }
|
|
119
|
+
if (x.art === 'nichtSchreibbar') { return tr('check.targetNoWrite', x.n); }
|
|
120
|
+
return tr('check.targetNoRead', x.n);
|
|
121
|
+
}).join(', ');
|
|
122
|
+
return { s: 'warn', t: tr('check.targetCan'), d: text };
|
|
123
|
+
}
|
|
124
|
+
|
|
97
125
|
export function baueChecks(e, haupt, pflichtFehlt) {
|
|
98
126
|
var an = e.states.filter(function (s) { return s.on; });
|
|
99
127
|
var ohneQuelle = an.filter(function (s) { return s.srcR && !S.objects[s.srcR]; });
|
|
@@ -113,6 +141,7 @@ export function baueChecks(e, haupt, pflichtFehlt) {
|
|
|
113
141
|
: haupt.type) : tr('check.noPatternFits') },
|
|
114
142
|
pruefungSenden(e),
|
|
115
143
|
pruefungRueckmeldung(e),
|
|
144
|
+
pruefungZielKann(e),
|
|
116
145
|
pruefungDopplung(e)
|
|
117
146
|
];
|
|
118
147
|
|