Add web status bar fields

This commit is contained in:
2026-05-21 21:37:25 +03:00
parent 3bf8c1c162
commit dede74d0a5
3 changed files with 151 additions and 14 deletions
+67 -1
View File
@@ -2943,6 +2943,8 @@ procedure TMainForm.SpectrumTimerTick(Sender: TObject);
var
NowTick: QWord;
ElapsedTick: QWord;
WebStatusText, WebBoardText, WebIPText, WebSupplyText: string;
WebPLLText, WebRXText, WebTXText, WebSeqText: string;
begin
// Синхронизируем размеры если размер панели изменился (после ресайза).
// Важно: проверяем только видимые панели — когда спектр скрыт,
@@ -3072,6 +3074,67 @@ begin
// Пушим текущее состояние в веб-клиенты (если есть)
if Assigned(FWebServer) then
begin
if FNetwork.Connected then
begin
if FRunning then
WebStatusText := 'Running'
else
WebStatusText := 'Connected';
WebBoardText := 'Board: ' + BoardTypeName(FNetwork.Device.BoardType);
WebIPText := 'IP: ' + FNetwork.Device.IPAddress;
end
else
begin
WebStatusText := 'Disconnected';
WebBoardText := 'Board --';
WebIPText := 'IP --';
end;
if not FRunning then
begin
WebSupplyText := 'Supply --';
WebPLLText := 'PLL --';
end
else
begin
if FLastPLLLock then
WebPLLText := 'PLL OK'
else
WebPLLText := 'PLL?';
if not BoardSupportsSupplyVoltage(FNetwork.Device.BoardType) then
WebSupplyText := 'Supply n/a'
else if FLastSupplyV >= 0 then
begin
if FLastSupplyA >= 0 then
WebSupplyText := Format('Supply %.1fV %.1fA', [FLastSupplyV, FLastSupplyA])
else
WebSupplyText := Format('Supply %.1fV', [FLastSupplyV]);
end
else
WebSupplyText := 'Supply --';
end;
if FRunning then
WebRXText := 'RX running'
else
WebRXText := 'RX idle';
if FTuning then
WebTXText := 'TX tune'
else if FTransmitting then
WebTXText := 'TX active'
else
WebTXText := 'TX idle';
if FSeqErrorCount > 0 then
WebSeqText := Format('SEQ ERR DDC%d', [FLastSeqErrorDDC])
else if FRunning then
WebSeqText := 'SEQ OK'
else
WebSeqText := 'SEQ --';
FWebServer.PushSpectrum(
FSpectrumBuf, 1024,
FWaterfallBuf,
@@ -3087,7 +3150,10 @@ begin
FVfoB, FActiveVfo,
FTransmitting, TrkDrive.Position,
FAtten, FTuning, FDisplayDuplex,
FLastFwdW, FLastSWR, FPAMaxPower);
FLastFwdW, FLastSWR, FPAMaxPower,
WebStatusText, WebBoardText, WebIPText, WebSupplyText,
WebPLLText, WebRXText, WebTXText, WebSeqText);
end;
end;
// ===========================================================================
+24 -5
View File
@@ -95,8 +95,11 @@ begin
'.ruler-area canvas{position:absolute;top:0;left:0;width:100%;height:100%}' +
'.wf-area{flex:0 0 405px;position:relative;background:#000;border:1px solid #252525;border-radius:3px;overflow:hidden}' +
'.wf-area canvas{position:absolute;top:0;left:0;width:100%;height:100%}' +
'.sbar{flex:0 0 16px;display:flex;align-items:center;padding:0 6px;gap:10px;background:#0e0e0e;border-top:1px solid #222;font-size:10px;color:#555}' +
'#latEl{margin-left:auto}' +
'.sbar{flex:0 0 24px;display:grid;grid-template-columns:minmax(120px,170px) minmax(190px,280px) minmax(120px,160px) minmax(135px,170px) minmax(70px,90px) minmax(120px,190px) minmax(120px,180px) minmax(90px,1fr) minmax(82px,96px) minmax(92px,112px);align-items:stretch;background:#0e0e0e;border-top:1px solid #222;font:10px Courier New,monospace;color:#ccc;overflow-x:auto;overflow-y:hidden}' +
'.sb-cell{min-width:0;display:flex;align-items:center;padding:0 8px;border-left:1px solid #222;background:#181818;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}' +
'.sb-cell:first-child{border-left:0}' +
'.sb-dim{color:#777}.sb-seq{color:#ffaa00}.sb-status{color:#90c090}.sb-time{justify-content:center;color:#bbb}.sb-lat{display:none}' +
'@media screen and (max-width:640px){.sbar{grid-template-columns:minmax(72px,1fr) minmax(104px,1.25fr) minmax(74px,1fr) minmax(78px,1fr);overflow:hidden}.sbar #sbBoard,.sbar #sbIP,.sbar #sbPLL,.sbar #sbRX,.sbar #sbTX,.sbar #sbLocal{display:none}.sb-cell{padding:0 5px;font-size:9px}}' +
'@media screen and (max-width:1300px) and (min-width:641px){' +
'.tbar{gap:3px;padding-left:4px;padding-right:4px;}' +
'button{padding:0 6px;font-size:10px;}' +
@@ -227,7 +230,19 @@ begin
'<div class="spec-area"><canvas id="spec"></canvas><div class="span-ov" id="spanOv"></div></div>' +
'<div class="ruler-area"><canvas id="ruler"></canvas></div>' +
'<div class="wf-area"><canvas id="wf"></canvas></div>' +
'<div class="sbar"><span id="stEl">connecting...</span><span id="latEl"></span></div>' +
'<div class="sbar">' +
'<span class="sb-cell sb-status" id="stEl">connecting...</span>' +
'<span class="sb-cell" id="sbBoard">Board --</span>' +
'<span class="sb-cell" id="sbIP">IP --</span>' +
'<span class="sb-cell" id="sbSupply">Supply --</span>' +
'<span class="sb-cell" id="sbPLL">PLL --</span>' +
'<span class="sb-cell sb-dim" id="sbRX">RX idle</span>' +
'<span class="sb-cell sb-dim" id="sbTX">TX idle</span>' +
'<span class="sb-cell sb-seq" id="sbSEQ">SEQ --</span>' +
'<span class="sb-cell sb-time" id="sbUTC">UTC --:--:--</span>' +
'<span class="sb-cell sb-time" id="sbLocal">Local --:--:--</span>' +
'<span class="sb-lat" id="latEl"></span>' +
'</div>' +
'</div>' +
'<script>' +
@@ -257,6 +272,11 @@ begin
'const cl=(v,a,b)=>Math.max(a,Math.min(b,v));' +
'const q=id=>document.getElementById(id);' +
'function sw(id,on){const b=q(id);if(b)b.classList.toggle("on",!!on);}' +
'function sb(id,txt){const e=q(id);if(e){e.textContent=txt||"";e.title=txt||"";}}' +
'function fmtTime(d,utc){const p=n=>String(n).padStart(2,"0");return(utc?p(d.getUTCHours()):p(d.getHours()))+":"+p(utc?d.getUTCMinutes():d.getMinutes())+":"+p(utc?d.getUTCSeconds():d.getSeconds());}' +
'function updClock(){const d=new Date();sb("sbUTC","UTC "+fmtTime(d,true));sb("sbLocal","Local "+fmtTime(d,false));}' +
'function updStatusBar(){sb("stEl",cur.status_text||((cur.connected?"OK":"OFF")+" | "+(cur.running?"RUN":"STOP")));sb("sbBoard",cur.board_text||"Board --");sb("sbIP",cur.ip_text||"IP --");sb("sbSupply",cur.supply_text||"Supply --");sb("sbPLL",cur.pll_text||"PLL --");sb("sbRX",cur.rx_text||"RX idle");sb("sbTX",cur.tx_text||"TX idle");sb("sbSEQ",cur.seq_text||"SEQ --");}' +
'updClock();setInterval(updClock,1000);' +
'function ws2(obj){if(ws&&ws.readyState===1)ws.send(JSON.stringify(obj));}' +
'function cmd(c,v){ws2({cmd:c,on:v});}' +
'function updNrBtn(){const b=q("nrBtn");if(!b)return;const m=cur.nr_mode|0;b.textContent=(m===2)?"NR2":(m===3)?"NR3":(m===4)?"NR4":"NR";sw("nrBtn",m!==0);}' +
@@ -622,8 +642,7 @@ begin
'if(!isPending("attn")){const at=q("attSel");if(at)at.value=cur.attn_idx||0;}' +
'updBand(activeVfo==="A"?(cur.vfo_a_hz||0):(vfoB||0));updSpan(cur.span_hz||192000);' +
'paintSM(Number(cur.smeter_dbm||-130));' +
'const mn=cur.mode_name||(MODE_N[cur.mode||0]||"?");' +
'stEl.textContent=(cur.connected?"OK":"OFF")+" | "+(cur.running?"RUN":"STOP")+" | "+(cur.transmitting?"TX":"RX")+" | "+mn+" | "+((activeVfoHz()/1e6).toFixed(3))+" MHz";' +
'updStatusBar();' +
'const nowTx=!!cur.transmitting;if(nowTx!==prevTx){prevTx=nowTx;if(nowTx)startTXMic();else stopTXMic();}}' +
'function pal(db){const v=Math.round(cl((db-wfLo)/Math.max(1,wfHi-wfLo),0,1)*255);const c=PAL[v]|0;return[(c>>16)&255,(c>>8)&255,c&255];}' +
+60 -8
View File
@@ -284,6 +284,14 @@ type
FFwdW: Double;
FSWR: Double;
FPAMaxPower: Double;
FStatusText: string;
FBoardText: string;
FIPText: string;
FSupplyText: string;
FPLLText: string;
FRXText: string;
FTXText: string;
FSeqText: string;
FWebClientActive: Boolean;
@@ -333,7 +341,9 @@ type
VfoB: Double; ActiveVfo: Integer;
Transmitting: Boolean; DriveLevel: Integer;
AttnIdx: Integer; Tuning: Boolean; Duplex: Boolean;
FwdW, SWRV, PAMaxPower: Double);
FwdW, SWRV, PAMaxPower: Double;
const StatusText, BoardText, IPText, SupplyText, PLLText,
RXText, TXText, SeqText: string);
property WebClientActive: Boolean read FWebClientActive;
property FreqMhzDigits: Integer read FFreqMhzDigits write FFreqMhzDigits;
@@ -472,6 +482,14 @@ begin
FFwdW := 0;
FSWR := 1.0;
FPAMaxPower := 100.0;
FStatusText := 'Disconnected';
FBoardText := 'Board --';
FIPText := 'IP --';
FSupplyText := 'Supply --';
FPLLText := 'PLL --';
FRXText := 'RX idle';
FTXText := 'TX idle';
FSeqText := 'SEQ --';
FBandIdx := 5;
FCurrentXvtr := -1;
FFreqMhzDigits := 3;
@@ -1406,6 +1424,25 @@ end;
Формирование JSON-состояния
═══════════════════════════════════════════════════════════════════════════ }
function JsonEscape(const S: string): string;
var
I: Integer;
begin
Result := '';
for I := 1 to Length(S) do
case S[I] of
'\': Result := Result + '\\';
'"': Result := Result + '\"';
#8: Result := Result + '\b';
#9: Result := Result + '\t';
#10: Result := Result + '\n';
#12: Result := Result + '\f';
#13: Result := Result + '\r';
else
Result := Result + S[I];
end;
end;
function TWebServer.BuildStateJson: string;
const
MODE_N: array[0..7] of string =
@@ -1438,10 +1475,12 @@ begin
'"wf_agc":%s,"wf_nf":%s,"band_idx":%d,"smeter_dbm":%.1f,' +
'"running":%s,"mute":%s,"ctun":%s,' +
'"nr_mode":%d,"nr":%s,"nb_mode":%d,"nb":%s,"snb":%s,"anf":%s,"connected":%s,' +
'"transmitting":%s,"drive":%d,"attn_idx":%d,"tuning":%s,"dup":%s,' +
'"fwd_w":%.1f,"swr":%.2f,"pa_max_power":%.0f,' +
'"xvtr_current":%d,"xvtr_bands":%s,"freq_mhz_digits":%d,' +
'"fmstep_idx":%d}',
'"transmitting":%s,"drive":%d,"attn_idx":%d,"tuning":%s,"dup":%s,' +
'"fwd_w":%.1f,"swr":%.2f,"pa_max_power":%.0f,' +
'"status_text":"%s","board_text":"%s","ip_text":"%s","supply_text":"%s",' +
'"pll_text":"%s","rx_text":"%s","tx_text":"%s","seq_text":"%s",' +
'"xvtr_current":%d,"xvtr_bands":%s,"freq_mhz_digits":%d,' +
'"fmstep_idx":%d}',
[FFreq, FVfoB, FActiveVfo,
FMode, MODE_N[FMode mod 8],
FFilterIdx, FFilterBW,
@@ -1465,8 +1504,11 @@ begin
FAttnIdx,
BoolToStr(FTuning, 'true', 'false'),
BoolToStr(FDuplex, 'true', 'false'),
FFwdW, FSWR, FPAMaxPower,
FCurrentXvtr, XvtrJson, FFreqMhzDigits,
FFwdW, FSWR, FPAMaxPower,
JsonEscape(FStatusText), JsonEscape(FBoardText), JsonEscape(FIPText),
JsonEscape(FSupplyText), JsonEscape(FPLLText), JsonEscape(FRXText),
JsonEscape(FTXText), JsonEscape(FSeqText),
FCurrentXvtr, XvtrJson, FFreqMhzDigits,
FFMStepIdx
], FS);
finally
@@ -1533,7 +1575,9 @@ procedure TWebServer.PushSpectrum(
VfoB: Double; ActiveVfo: Integer;
Transmitting: Boolean; DriveLevel: Integer;
AttnIdx: Integer; Tuning: Boolean; Duplex: Boolean;
FwdW, SWRV, PAMaxPower: Double);
FwdW, SWRV, PAMaxPower: Double;
const StatusText, BoardText, IPText, SupplyText, PLLText,
RXText, TXText, SeqText: string);
var N, i: Integer;
begin
if not FRunning then Exit;
@@ -1573,6 +1617,14 @@ begin
FFwdW := FwdW;
FSWR := SWRV;
FPAMaxPower := PAMaxPower;
FStatusText := StatusText;
FBoardText := BoardText;
FIPText := IPText;
FSupplyText := SupplyText;
FPLLText := PLLText;
FRXText := RXText;
FTXText := TXText;
FSeqText := SeqText;
finally
FStateLock.Leave;
end;