added digital gauge and config for disable/enable it
This commit is contained in:
parent
bf7c924705
commit
15e390b693
142
main.go
142
main.go
|
|
@ -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));
|
||||
|
|
|
|||
Loading…
Reference in a new issue