added digital gauge and config for disable/enable it

This commit is contained in:
Dejan R 2026-04-19 09:22:16 +02:00
parent bf7c924705
commit 15e390b693

142
main.go
View file

@ -30,7 +30,7 @@ import (
//go:embed static
var staticFiles embed.FS
const version = "0.8.1"
const version = "0.8.2"
// ---------------------------------------------------------------------------
// Config structs
@ -100,6 +100,7 @@ type ModulesConfig struct {
ShowIntelligence *bool `yaml:"show_intelligence,omitempty"`
ShowAlarmTimeline *bool `yaml:"show_alarm_timeline,omitempty"`
ShowGauges *bool `yaml:"show_gauges,omitempty"`
ShowGaugeDigital *bool `yaml:"show_gauge_digital,omitempty"`
ShowTrendChart *bool `yaml:"show_trend_chart,omitempty"`
}
@ -171,6 +172,7 @@ func defaultConfig() Config {
ShowIntelligence: boolPtr(true),
ShowAlarmTimeline: boolPtr(true),
ShowGauges: boolPtr(true),
ShowGaugeDigital: boolPtr(false),
ShowTrendChart: boolPtr(true),
},
DB: DBConfig{
@ -272,6 +274,7 @@ func normalizeConfig(cfg *Config) {
setIfNilBool(&cfg.Modules.ShowIntelligence, boolValue(def.Modules.ShowIntelligence, true))
setIfNilBool(&cfg.Modules.ShowAlarmTimeline, boolValue(def.Modules.ShowAlarmTimeline, true))
setIfNilBool(&cfg.Modules.ShowGauges, boolValue(def.Modules.ShowGauges, true))
setIfNilBool(&cfg.Modules.ShowGaugeDigital, boolValue(def.Modules.ShowGaugeDigital, false))
setIfNilBool(&cfg.Modules.ShowTrendChart, boolValue(def.Modules.ShowTrendChart, true))
setIfEmpty(&cfg.DB.Path, def.DB.Path)
@ -443,6 +446,7 @@ type PageData struct {
ShowIntelligence bool
ShowAlarmTimeline bool
ShowGauges bool
ShowGaugeDigital bool
ShowTrendChart bool
}
@ -1608,6 +1612,7 @@ func initCachedUI() {
ShowIntelligence: boolValue(cfg.Modules.ShowIntelligence, true),
ShowAlarmTimeline: boolValue(cfg.Modules.ShowAlarmTimeline, true),
ShowGauges: boolValue(cfg.Modules.ShowGauges, true),
ShowGaugeDigital: boolValue(cfg.Modules.ShowGaugeDigital, false),
ShowTrendChart: boolValue(cfg.Modules.ShowTrendChart, true),
}
@ -1857,14 +1862,60 @@ const uiHTML = `<!DOCTYPE html>
.soft-glow-yellow { box-shadow: 0 0 0 1px rgba(234,179,8,0.28), 0 0 38px rgba(234,179,8,0.08); }
.soft-glow-red { box-shadow: 0 0 0 1px rgba(239,68,68,0.28), 0 0 38px rgba(239,68,68,0.08); }
.gauge-header-row {
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: 24px;
margin-bottom: 10px;
}
.gauge-head {
display: flex;
align-items: center;
justify-content: center;
gap: 16px;
text-align: center;
margin-bottom: 10px;
}
.gauge-head.with-digital {
justify-content: flex-start;
text-align: left;
margin-bottom: 0;
}
.gauge-head-copy {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.gauge-head-copy.with-digital {
align-items: flex-start;
}
.gauge-digital {
text-align: right;
flex-shrink: 0;
}
.gauge-container {
position: relative;
width: 100%;
max-width: 500px;
height: 360px;
max-width: 720px;
margin: 0 auto;
}
.gauge-container.no-digital {
height: clamp(430px, 48vw, 560px);
}
.gauge-container.with-digital {
height: clamp(360px, 42vw, 500px);
}
.gauge-canvas {
width: 100%;
height: 100%;
@ -2296,46 +2347,66 @@ const uiHTML = `<!DOCTYPE html>
{{if .ShowGauges}}
<div class="grid grid-cols-1 2xl:grid-cols-2 gap-8 mb-8">
<div id="card-l" class="glass border border-white/10 rounded-3xl p-6 md:p-8 transition-all duration-300">
<div class="flex justify-between items-start mb-4 gap-6">
<div class="flex items-center gap-4">
<div id="card-l" class="glass border border-white/10 rounded-3xl p-5 md:p-6 xl:p-8 transition-all duration-300">
{{if .ShowGaugeDigital}}
<div class="gauge-header-row">
<div class="gauge-head with-digital">
<div id="led-l" class="w-6 h-6 bg-emerald-500 rounded-full shadow-lg shadow-emerald-600/40"></div>
<div>
<div class="gauge-head-copy with-digital">
<h2 class="text-2xl md:text-3xl xl:text-4xl font-bold tracking-wider">{{.LeftLabel}}</h2>
<div id="state-l" class="text-sm text-zinc-400 mt-1">NORMAL</div>
</div>
</div>
<div id="digital-l" class="text-right">
<div id="digital-l" class="gauge-digital">
<div class="percent text-5xl md:text-6xl xl:text-7xl font-mono font-bold text-sky-100 leading-none">0.0</div>
<div class="text-xl text-sky-400 mt-1">{{.UnitPct}}</div>
<div class="kn text-lg text-zinc-300 font-mono mt-3">0.0 {{.UnitForce}}</div>
</div>
</div>
{{else}}
<div class="gauge-head">
<div id="led-l" class="w-6 h-6 bg-emerald-500 rounded-full shadow-lg shadow-emerald-600/40"></div>
<div class="gauge-head-copy">
<h2 class="text-2xl md:text-3xl xl:text-4xl font-bold tracking-wider">{{.LeftLabel}}</h2>
<div id="state-l" class="text-sm text-zinc-400 mt-1">NORMAL</div>
</div>
</div>
{{end}}
<div class="gauge-container">
<div class="gauge-container {{if .ShowGaugeDigital}}with-digital{{else}}no-digital{{end}}">
<canvas id="gaugeL" class="gauge-canvas"></canvas>
</div>
</div>
<div id="card-r" class="glass border border-white/10 rounded-3xl p-6 md:p-8 transition-all duration-300">
<div class="flex justify-between items-start mb-4 gap-6">
<div class="flex items-center gap-4">
<div id="card-r" class="glass border border-white/10 rounded-3xl p-5 md:p-6 xl:p-8 transition-all duration-300">
{{if .ShowGaugeDigital}}
<div class="gauge-header-row">
<div class="gauge-head with-digital">
<div id="led-r" class="w-6 h-6 bg-emerald-500 rounded-full shadow-lg shadow-emerald-600/40"></div>
<div>
<div class="gauge-head-copy with-digital">
<h2 class="text-2xl md:text-3xl xl:text-4xl font-bold tracking-wider">{{.RightLabel}}</h2>
<div id="state-r" class="text-sm text-zinc-400 mt-1">NORMAL</div>
</div>
</div>
<div id="digital-r" class="text-right">
<div id="digital-r" class="gauge-digital">
<div class="percent text-5xl md:text-6xl xl:text-7xl font-mono font-bold text-violet-100 leading-none">0.0</div>
<div class="text-xl text-violet-400 mt-1">{{.UnitPct}}</div>
<div class="kn text-lg text-zinc-300 font-mono mt-3">0.0 {{.UnitForce}}</div>
</div>
</div>
{{else}}
<div class="gauge-head">
<div id="led-r" class="w-6 h-6 bg-emerald-500 rounded-full shadow-lg shadow-emerald-600/40"></div>
<div class="gauge-head-copy">
<h2 class="text-2xl md:text-3xl xl:text-4xl font-bold tracking-wider">{{.RightLabel}}</h2>
<div id="state-r" class="text-sm text-zinc-400 mt-1">NORMAL</div>
</div>
</div>
{{end}}
<div class="gauge-container">
<div class="gauge-container {{if .ShowGaugeDigital}}with-digital{{else}}no-digital{{end}}">
<canvas id="gaugeR" class="gauge-canvas"></canvas>
</div>
</div>
@ -2394,6 +2465,7 @@ const uiHTML = `<!DOCTYPE html>
const SHOW_INTELLIGENCE = {{if .ShowIntelligence}}true{{else}}false{{end}};
const SHOW_ALARM_TIMELINE = {{if .ShowAlarmTimeline}}true{{else}}false{{end}};
const SHOW_GAUGES = {{if .ShowGauges}}true{{else}}false{{end}};
const SHOW_GAUGE_DIGITAL = {{if .ShowGaugeDigital}}true{{else}}false{{end}};
const SHOW_TREND_CHART = {{if .ShowTrendChart}}true{{else}}false{{end}};
const START_ANGLE = Math.PI * 0.75;
@ -2429,11 +2501,6 @@ const uiHTML = `<!DOCTYPE html>
if (el) el.textContent = text;
}
function setTextBySelector(selector, text) {
const el = document.querySelector(selector);
if (el) el.textContent = text;
}
function colorMix(c1, c2, t) {
return {
r: Math.round(lerp(c1.r, c2.r, t)),
@ -2530,18 +2597,18 @@ const uiHTML = `<!DOCTYPE html>
const light = isLightTheme();
const cx = w / 2;
const cy = h * 0.57;
const radius = Math.min(w, h) * 0.34;
const trackWidth = Math.max(18, radius * 0.16);
const radius = Math.min(w * 0.35, h * 0.42);
const cy = h * 0.58;
const trackWidth = Math.max(20, radius * 0.17);
const value = clamp(Number(percentValue) || 0, 0, GAUGE_MAX_PERCENT);
const valueAngle = valueToAngle(value);
ctx.save();
ctx.beginPath();
ctx.arc(cx, cy, radius + 22, 0, Math.PI * 2);
ctx.arc(cx, cy, radius + 24, 0, Math.PI * 2);
ctx.fillStyle = light ? 'rgba(15,23,42,0.04)' : 'rgba(255,255,255,0.015)';
ctx.shadowColor = light ? 'rgba(15,23,42,0.12)' : 'rgba(0,0,0,0.45)';
ctx.shadowBlur = 30;
ctx.shadowBlur = 32;
ctx.fill();
ctx.restore();
@ -2632,9 +2699,9 @@ const uiHTML = `<!DOCTYPE html>
ctx.stroke();
const valueText = value.toFixed(1);
let valueFontPx = 52;
if (value >= 100) valueFontPx = 46;
if (w < 420) valueFontPx -= 4;
let valueFontPx = 58;
if (value >= 100) valueFontPx = 50;
if (w < 420) valueFontPx -= 6;
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
@ -2644,12 +2711,12 @@ const uiHTML = `<!DOCTYPE html>
ctx.fillText(valueText, cx, cy - 6);
ctx.fillStyle = sideAccent;
ctx.font = '700 18px system-ui, sans-serif';
ctx.fillText(UNIT_PCT, cx, cy + 28);
ctx.font = '700 19px system-ui, sans-serif';
ctx.fillText(UNIT_PCT, cx, cy + 30);
ctx.fillStyle = light ? '#334155' : '#a1a1aa';
ctx.font = '600 16px system-ui, sans-serif';
ctx.fillText((Number(knValue) || 0).toFixed(1) + ' ' + UNIT_FORCE, cx, cy + 54);
ctx.font = '600 17px system-ui, sans-serif';
ctx.fillText((Number(knValue) || 0).toFixed(1) + ' ' + UNIT_FORCE, cx, cy + 58);
}
function getZone(percentValue) {
@ -2664,8 +2731,6 @@ const uiHTML = `<!DOCTYPE html>
return 'ok';
}
function setProcessVisualState(connected) {
const el = document.getElementById('process-content');
if (!el) return;
@ -3051,8 +3116,6 @@ const uiHTML = `<!DOCTYPE html>
const connected = !!d.connected;
const leftPercent = Number(d.sila_l) || 0;
const rightPercent = Number(d.sila_r) || 0;
const leftKN = Number(d.sila_l_kn) || 0;
const rightKN = Number(d.sila_r_kn) || 0;
const sumPercent = Number(d.sum_percent) || 0;
const sumKN = Number(d.sum_kn) || 0;
const imbalance = Number(d.imbalance_percent) || 0;
@ -3069,13 +3132,6 @@ const uiHTML = `<!DOCTYPE html>
setConnectionIndicator(connected, stale);
setProcessVisualState(connected && !stale);
if (SHOW_GAUGES) {
setTextBySelector('#digital-l .percent', leftPercent.toFixed(1));
setTextBySelector('#digital-l .kn', leftKN.toFixed(1) + ' ' + UNIT_FORCE);
setTextBySelector('#digital-r .percent', rightPercent.toFixed(1));
setTextBySelector('#digital-r .kn', rightKN.toFixed(1) + ' ' + UNIT_FORCE);
}
if (SHOW_OVERVIEW) {
setTextById('sum-percent', sumPercent.toFixed(1));
setTextById('sum-kn', sumKN.toFixed(1));