Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,23 @@

Alle noemenswaardige wijzigingen aan de Duikmonitor worden hier bijgehouden.

## [1.22.0] - 2026-06-28

De controle op een onmogelijke ademdrukmeting is verruimd. Tot nu toe gaf de luchtbewaking alleen een melding wanneer de laatste of einddruk hoger was dan de begindruk. Voortaan kijkt de bewaking ook naar de metingen onderling: als een latere meting een hogere druk laat zien dan een eerdere meting, verschijnt een korte controle-melding. Dit is en blijft een waarschuwing, geen drempel, grens of blokkade. De luchtberekening (verbruikstrend, resterende tijd tot 100 bar en de kritieke meldingen) blijft in alle gevallen gewoon doorlopen. Uitvoering van besluit B-0032.

### Toegevoegd

- De live-luchtbewaking aan de waterkant en de samenvatting na het boven komen geven nu ook een controle-melding wanneer een latere ademdrukmeting hoger is dan een eerdere meting in dezelfde reeks. De reeks wordt op tijd geordend en op geldige druk gefilterd; bevat zij ergens een stijging tussen twee opeenvolgende metingen, dan volgt een enkele samenvattende melding, niet een melding per paar. De bestaande melding voor een laatste of einddruk hoger dan de begindruk blijft daarnaast bestaan zoals zij was.

### Gewijzigd

- Appversielabel verhoogd naar v1.22.0; de bijbehorende zelftest controleert mee op v1.22.0.
- Het opstart-zelftest-aantal gaat van 417 naar 422 door vijf toegevoegde interactietests. Twee tests doen een echte metingenreeks met een tussenstijging na en controleren in de teruggegeven meldingen dat de nieuwe controle-melding verschijnt, in zowel de live-bewaking als de samenvatting na het boven komen. Eén test bevestigt dat de luchtberekening bij zo'n stijging blijft doorlopen. Twee tegencontroles met een nette dalende reeks bevestigen dat de melding dan uitblijft, zodat de tests niet vanzelf slagen.

### Niet gewijzigd

- De rekenkern, de DCIEM-tabellen, de rekenbronfingerprint, de blokkeerlogica (waaronder de blokkade boven 15 m en de grens bij 100 bar), het statusmodel en de validatie zijn niet aangeraakt. De luchtberekening zelf (verbruikstrend, resterende tijd, het ademverbruik per minuut en de bijbehorende meldingen) is ongewijzigd: de verruiming voegt uitsluitend een melding toe en stelt niets uit, maakt niets leeg en blokkeert niets. Er is geen drempel, grens, minimum of maximum op de begindruk of op enige drukmeting toegevoegd; het toegestane drukbereik blijft gelijk. De OSOD-recordlaag, de doelvalidator en Export/Import OSOD zijn ongewijzigd, evenals het schema. De boot-verificatie en de standen van de verificatiebadge blijven werken zoals voorheen. De Content-Security-Policy is ongewijzigd. _headers, site.webmanifest en de iconen blijven byte-identiek.

## [1.21.0] - 2026-06-28

Drie kleine verbeteringen aan de bediening en de weergave aan de waterkant, zonder gevolgen voor de rekenkern of de registratie. De duikerkaartjes op het scherm Dagregistratie krijgen een duidelijke scheidingslijn tussen opeenvolgende kaartjes, zodat ze ook in fel zonlicht beter uit elkaar te houden zijn. Het tabblad Planning vooraf verdwijnt uit de onderste tabbalk; de planningsfunctie en het bijbehorende scherm blijven in hun geheel in de app aanwezig. De verificatiebadge bovenin wordt aantikbaar en opent de releasepagina van het project; kleur, tekst en standen van de badge blijven precies gelijk.
Expand Down
2 changes: 1 addition & 1 deletion CHECKSUMS.sha256
Original file line number Diff line number Diff line change
@@ -1 +1 @@
7b1a430c07841fd10e37fdbb085240c473dab18dee0942bf41db2ddf92e075b9 index.html
d3f50753939a53d991f00d749abf612e6587b66db55c803c76d350a00779ae3d index.html
49 changes: 47 additions & 2 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -579,7 +579,7 @@ <h1>Duikmonitor</h1>
(() => {
"use strict";

const APP_VERSION = "v1.21.0";
const APP_VERSION = "v1.22.0";
const EXPECTED_VIEWPORT = "width=device-width, initial-scale=1, maximum-scale=1, viewport-fit=cover";
const PUBLIC_APP_URL = "https://duikmonitor.nl/";
const REPO_URL = "https://github.com/OSOD-NL/duikmonitor";
Expand Down Expand Up @@ -1737,6 +1737,7 @@ <h1>Duikmonitor</h1>
if(!last) add(1, "Einddruk of laatste luchtmeting ontbreekt.");
if(last){
if(startBar != null && last.bar > startBar) add(1, "Laatste/einddruk is hoger dan begindruk.");
if(hasRisingPair(ms)) add(1, "Een latere meting is hoger dan een eerdere; controleer invoer.");
if(last.bar <= AIR_STOP_BAR) add(2, "100 bar bereikt of onderschreden.");
}
return {severity, messages};
Expand Down Expand Up @@ -2662,6 +2663,19 @@ <h2>Daggegevens</h2>
.filter(m=>m.time && m.bar != null)
.sort((a,b)=>a.time-b.time);
}
// Signaleert of de op tijd gesorteerde, op geldige bar gefilterde metingenreeks ergens
// een opeenvolgend paar bevat waarbij de latere meting een hogere druk heeft dan de
// eerdere. Een ademdrukmeting hoort tijdens een duik niet te stijgen; dit is een
// controlesignaal voor de invoer, geen drempel of grens. Eenmalige uitkomst (true/false):
// een reeks met meerdere stijgingen levert een enkele samenvattende melding, geen melding
// per paar. De luchtberekening verandert hier niet door; dit is uitsluitend een melding.
function hasRisingPair(ms){
if(!Array.isArray(ms)) return false;
for(let i=1;i<ms.length;i++){
if(ms[i].bar > ms[i-1].bar) return true;
}
return false;
}
function addAirMeasurement(d, bar, time=new Date(), depth=""){
ensureAirFields(d);
const n = validBar(bar);
Expand Down Expand Up @@ -2756,6 +2770,7 @@ <h2>Daggegevens</h2>
}
if(!timerNeedsCheck && last){
if(startBar != null && last.bar > startBar) messages.push("Laatste druk is hoger dan begindruk; controleer invoer.");
if(hasRisingPair(ms)) messages.push("Een latere meting is hoger dan een eerdere; controleer invoer.");
if(last.bar <= AIR_STOP_BAR){ cls="bad"; label="100 bar bereikt"; messages.push("100 bar bereikt of onderschreden."); }
else if(rateBarMin == null){ cls="warn"; label="Trend onbekend"; messages.push("Nog geen bruikbare verbruikstrend."); }
else if(remainingMin <= 5){ cls="bad"; label="Lucht kritiek"; messages.push(`Resterend tot 100 bar is ${remainingMin} min.`); }
Expand Down Expand Up @@ -3573,7 +3588,7 @@ <h3>${esc(displayDiver(d))} <span class="pill ${s.cls}">${s.txt}</span> <button
};
const today = "2026-05-23";
const ev = (diverId,start,dtMin,mdd,daypart="1",rule="auto") => ({id:uid(),diverId,start:`${today}T${start}`,dtMin,mdd,daypart,rule,notes:"zelftest"});
push("basis","Appversielabel", "v1.21.0", APP_VERSION, APP_VERSION==="v1.21.0");
push("basis","Appversielabel", "v1.22.0", APP_VERSION, APP_VERSION==="v1.22.0");
// UI-interactietest (B-0024): de drie standen van de boot-gate-badge worden via
// updateVerifyBadge() echt gerenderd in het verifyBadge-element en de gerenderde
// uitkomst wordt gecontroleerd, niet alleen een interne variabele. Eén zelftest dekt
Expand Down Expand Up @@ -4868,6 +4883,36 @@ <h3>${esc(displayDiver(d))} <span class="pill ${s.cls}">${s.txt}</span> <button
push("lucht","Hogere laatste ademhaling stelt resterend bij", "13 min", `${airSpike.remainingMin} min`, airSpike.remainingMin===13);
const airOverdue = testState({live:[ensureAirFields({id:"air4",diverId:"D1",start:`${today}T09:00:00`,mdd:6,daypart:"1",rule:"auto",notes:"",airStartBar:300,airMeasurements:[{time:`${today}T09:05:00`,bar:270}]})]},()=>airAnalysis(state.live[0], new Date(`${today}T09:16:00`)));
push("lucht","Controle na 10 minuten wordt bewaakt", "verlopen", airOverdue.overdue ? "verlopen" : "niet verlopen", airOverdue.overdue===true);
// B-0032: een latere ademdrukmeting hoger dan een eerdere geeft een controle-melding,
// zonder de luchtbewaking stil te zetten. We voeren de analyse echt uit en toetsen op de
// teruggegeven messages (niet op een losse hulpfunctie), in beide paden: de live-bewaking
// (surface nog niet gezet) en de na-duik-samenvatting (surface gezet). De begindruk ligt
// binnen bereik (300) zodat de laatste-tegen-begindruk-melding hier NIET vanzelf afgaat en
// we zuiver de onderlinge-stijging-melding meten. Reeks 200 -> 150 -> 170 bevat een echte
// tussenstijging (150 -> 170).
const intervalMsg = "Een latere meting is hoger dan een eerdere; controleer invoer.";
const riseLive = testState({live:[ensureAirFields({id:"riseLive",diverId:"D1",start:`${today}T09:00:00`,ascentStart:"",surface:"",mdd:6,daypart:"1",rule:"auto",notes:"",airStartBar:300,airMeasurements:[{time:`${today}T09:05:00`,bar:200},{time:`${today}T09:10:00`,bar:150},{time:`${today}T09:15:00`,bar:170}]})]},()=>airAnalysis(state.live[0], new Date(`${today}T09:15:30`)));
push("ui","B-0032 live: tussenmeting hoger dan de vorige geeft de controle-melding",
"melding aanwezig", riseLive.messages.includes(intervalMsg) ? "melding aanwezig" : "melding afwezig",
riseLive.messages.includes(intervalMsg)===true);
push("ui","B-0032 live: luchtberekening blijft doorlopen bij een onmogelijke stijging",
"rateBarMin en remainingMin aanwezig", `rateBarMin=${riseLive.rateBarMin!=null?'ja':'nee'}, remainingMin=${riseLive.remainingMin!=null?'ja':'nee'}`,
riseLive.rateBarMin!=null && riseLive.remainingMin!=null);
const riseSurface = testState({live:[ensureAirFields({id:"riseSurf",diverId:"D1",start:`${today}T09:00:00`,surface:`${today}T09:20:00`,mdd:6,daypart:"1",rule:"auto",notes:"",airStartBar:300,airMeasurements:[{time:`${today}T09:05:00`,bar:200},{time:`${today}T09:10:00`,bar:150},{time:`${today}T09:15:00`,bar:170}]})]},()=>airStatus(state.live[0]));
push("ui","B-0032 na-duik: tussenmeting hoger dan de vorige geeft de controle-melding",
"melding aanwezig + severity>=1", `melding=${riseSurface.messages.includes(intervalMsg)?'ja':'nee'}, severity=${riseSurface.severity}`,
riseSurface.messages.includes(intervalMsg)===true && riseSurface.severity>=1);
// Negatieve controle: een nette dalende reeks 200 -> 150 -> 120 mag de melding NIET geven.
// Zo faalt de test als de melding onvoorwaardelijk of verkeerd-om zou afgaan; de test is
// dus geen tautologie. Ook hier blijft de berekening lopen.
const dropLive = testState({live:[ensureAirFields({id:"dropLive",diverId:"D1",start:`${today}T09:00:00`,ascentStart:"",surface:"",mdd:6,daypart:"1",rule:"auto",notes:"",airStartBar:300,airMeasurements:[{time:`${today}T09:05:00`,bar:200},{time:`${today}T09:10:00`,bar:150},{time:`${today}T09:15:00`,bar:120}]})]},()=>airAnalysis(state.live[0], new Date(`${today}T09:15:30`)));
push("ui","B-0032 live (negatieve controle): nette dalende reeks geeft de melding niet",
"melding afwezig", dropLive.messages.includes(intervalMsg) ? "melding aanwezig" : "melding afwezig",
dropLive.messages.includes(intervalMsg)===false);
const dropSurface = testState({live:[ensureAirFields({id:"dropSurf",diverId:"D1",start:`${today}T09:00:00`,surface:`${today}T09:20:00`,mdd:6,daypart:"1",rule:"auto",notes:"",airStartBar:300,airMeasurements:[{time:`${today}T09:05:00`,bar:200},{time:`${today}T09:10:00`,bar:150},{time:`${today}T09:15:00`,bar:120}]})]},()=>airStatus(state.live[0]));
push("ui","B-0032 na-duik (negatieve controle): nette dalende reeks geeft de melding niet",
"melding afwezig", dropSurface.messages.includes(intervalMsg) ? "melding aanwezig" : "melding afwezig",
dropSurface.messages.includes(intervalMsg)===false);
// ---- voorbeeld- en privacytests ----
const ex1 = testState({}, ()=>{ loadExample1(); return {liveCount:state.live.length, codes:state.divers.slice(0,2).map(d=>d.code).join("/"), names:hasPersonName(JSON.stringify(state)), buitenwater:(JSON.stringify(state).toLowerCase().match(/buitenwater/g)||[]).length}; });
push("voorbeeld","Voorbeeld 1 laadt gecombineerde duikregels (D1/D2)", "≥2 / D1/D2", `${ex1.liveCount} / ${ex1.codes}`, ex1.liveCount>=2 && ex1.codes==="D1/D2");
Expand Down
Loading