Phase 3 (WA3): move outgoing web state-mirror into the web adapter

The per-tick PushSpectrum mirror (status strings + ~40 state values + spectrum/
waterfall buffers) moves out of SpectrumTimerTick into TWebAdapter.PushState,
which reads FController for everything and takes the pixel buffers as params.
MainForm's timer now just calls FWebAdapter.PushState(FSpectrumBuf, FWaterfallBuf).

Server lifecycle/ownership stays in MainForm for now — it's still driven by the
remaining host-coupled handlers (PushXvtrToWeb, device CRUD); ownership transfer
completes when those move.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Uladzimir Karpenka
2026-06-08 20:36:01 +03:00
co-authored by Claude Opus 4.8
parent e5a13096dc
commit e1ecf947b8
2 changed files with 88 additions and 85 deletions
+4 -84
View File
@@ -3054,8 +3054,6 @@ procedure TMainForm.SpectrumTimerTick(Sender: TObject);
var var
NowTick: QWord; NowTick: QWord;
ElapsedTick: QWord; ElapsedTick: QWord;
WebStatusText, WebBoardText, WebIPText, WebSupplyText: string;
WebPLLText, WebRXText, WebTXText, WebSeqText: string;
begin begin
// Синхронизируем размеры если размер панели изменился (после ресайза). // Синхронизируем размеры если размер панели изменился (после ресайза).
// Важно: проверяем только видимые панели — когда спектр скрыт, // Важно: проверяем только видимые панели — когда спектр скрыт,
@@ -3195,88 +3193,10 @@ begin
FSpectrumDirty := False; FSpectrumDirty := False;
end; end;
// Пушим текущее состояние в веб-клиенты (если есть) // Исходящее зеркало состояния + спектр/водопад в web — в адаптере (читает
if Assigned(FWebServer) then // FController; буферы пикселей — у нас). Сервер сам гейтит по FRunning.
begin if Assigned(FWebAdapter) then
if FController.FNetwork.Connected then FWebAdapter.PushState(FSpectrumBuf, FWaterfallBuf);
begin
if FController.FRunning then
WebStatusText := 'Running'
else
WebStatusText := 'Connected';
WebBoardText := 'Board: ' + BoardTypeName(FController.FNetwork.Device.BoardType);
WebIPText := 'IP: ' + FController.FNetwork.Device.IPAddress;
end
else
begin
WebStatusText := 'Disconnected';
WebBoardText := 'Board --';
WebIPText := 'IP --';
end;
if not FController.FRunning then
begin
WebSupplyText := 'Supply --';
WebPLLText := 'PLL --';
end
else
begin
if FController.FLastPLLLock then
WebPLLText := 'PLL OK'
else
WebPLLText := 'PLL?';
if not BoardSupportsSupplyVoltage(FController.FNetwork.Device.BoardType) then
WebSupplyText := 'Supply n/a'
else if FController.FLastSupplyV >= 0 then
begin
if FController.FLastSupplyA >= 0 then
WebSupplyText := Format('Supply %.1fV %.1fA', [FController.FLastSupplyV, FController.FLastSupplyA])
else
WebSupplyText := Format('Supply %.1fV', [FController.FLastSupplyV]);
end
else
WebSupplyText := 'Supply --';
end;
if FController.FRunning then
WebRXText := 'RX running'
else
WebRXText := 'RX idle';
if FController.FTuning then
WebTXText := 'TX tune'
else if FController.FTransmitting then
WebTXText := 'TX active'
else
WebTXText := 'TX idle';
if FController.FSeqErrorCount > 0 then
WebSeqText := Format('SEQ ERR DDC%d', [FController.FLastSeqErrorDDC])
else if FController.FRunning then
WebSeqText := 'SEQ OK'
else
WebSeqText := 'SEQ --';
FWebServer.PushSpectrum(
FSpectrumBuf, 1024,
FWaterfallBuf,
FController.FLastSMeter,
FController.FVfoA, FController.FMode, FController.FFilterBW, FController.FAGCMode, FController.FAGCTop,
FController.FSpanHz, FController.FVolume,
FController.FWfAGCEnabled, FController.FWfNFEnabled,
FController.FCurrentBand,
FController.FRunning and FController.FNetwork.Connected,
FController.FRunning, FController.FMuted, FController.FCTun,
FController.FNRMode, FController.FNBMode, FController.FSNB, FController.FANF,
FController.FCenterFreq, FController.FFilter,
FController.FVfoB, FController.FActiveVfo,
FController.FTransmitting, FController.FDrivePercent,
FController.FAtten, FController.FTuning, FController.FDisplayDuplex,
FController.FLastFwdW, FController.FLastSWR, FController.FPAMaxPower,
WebStatusText, WebBoardText, WebIPText, WebSupplyText,
WebPLLText, WebRXText, WebTXText, WebSeqText);
end;
end; end;
// =========================================================================== // ===========================================================================
+84 -1
View File
@@ -19,7 +19,7 @@ interface
uses uses
Classes, SysUtils, Classes, SysUtils,
RadioController, WebServer, WDSPEngine; RadioController, WebServer, WDSPEngine, HPSDRNetwork, HPSDRProtocol, BoardUtils;
type type
// UI/оркестрационные операции, которые адаптер дёргает у хозяина (пока не // UI/оркестрационные операции, которые адаптер дёргает у хозяина (пока не
@@ -91,6 +91,11 @@ type
// TX-mic от web-клиента → WDSP. Вызывается из WS-потока; без маршалинга — // TX-mic от web-клиента → WDSP. Вызывается из WS-потока; без маршалинга —
// подача в движок thread-safe (как OnMicPacket/OnDDCIQ контроллера). // подача в движок thread-safe (как OnMicPacket/OnDDCIQ контроллера).
procedure OnMic(Samples: PSingle; Count: Integer); procedure OnMic(Samples: PSingle; Count: Integer);
// Исходящее зеркало: периодический снимок состояния + спектр/водопад в web.
// Зовётся хозяином из GUI-таймера (буферы пикселей — у хозяина, остальное из
// FController). Сервер сам гейтит по FRunning и шлёт активным клиентам.
procedure PushState(const ASpec, AWf: array of Single);
end; end;
implementation implementation
@@ -290,4 +295,82 @@ begin
FController.FDSPEngine.PushTXMicSamplesD(Buf, n); FController.FDSPEngine.PushTXMicSamplesD(Buf, n);
end; end;
// ── Исходящее зеркало состояния ──────────────────────────────────────────────
procedure TWebAdapter.PushState(const ASpec, AWf: array of Single);
var
StatusText, BoardText, IPText, SupplyText: string;
PLLText, RXText, TXText, SeqText: string;
begin
if FController.FNetwork.Connected then
begin
if FController.FRunning then StatusText := 'Running'
else StatusText := 'Connected';
BoardText := 'Board: ' + BoardTypeName(FController.FNetwork.Device.BoardType);
IPText := 'IP: ' + FController.FNetwork.Device.IPAddress;
end
else
begin
StatusText := 'Disconnected';
BoardText := 'Board --';
IPText := 'IP --';
end;
if not FController.FRunning then
begin
SupplyText := 'Supply --';
PLLText := 'PLL --';
end
else
begin
if FController.FLastPLLLock then PLLText := 'PLL OK'
else PLLText := 'PLL?';
if not BoardSupportsSupplyVoltage(FController.FNetwork.Device.BoardType) then
SupplyText := 'Supply n/a'
else if FController.FLastSupplyV >= 0 then
begin
if FController.FLastSupplyA >= 0 then
SupplyText := Format('Supply %.1fV %.1fA', [FController.FLastSupplyV, FController.FLastSupplyA])
else
SupplyText := Format('Supply %.1fV', [FController.FLastSupplyV]);
end
else
SupplyText := 'Supply --';
end;
if FController.FRunning then RXText := 'RX running'
else RXText := 'RX idle';
if FController.FTuning then TXText := 'TX tune'
else if FController.FTransmitting then TXText := 'TX active'
else TXText := 'TX idle';
if FController.FSeqErrorCount > 0 then
SeqText := Format('SEQ ERR DDC%d', [FController.FLastSeqErrorDDC])
else if FController.FRunning then
SeqText := 'SEQ OK'
else
SeqText := 'SEQ --';
FServer.PushSpectrum(
ASpec, 1024,
AWf,
FController.FLastSMeter,
FController.FVfoA, FController.FMode, FController.FFilterBW, FController.FAGCMode, FController.FAGCTop,
FController.FSpanHz, FController.FVolume,
FController.FWfAGCEnabled, FController.FWfNFEnabled,
FController.FCurrentBand,
FController.FRunning and FController.FNetwork.Connected,
FController.FRunning, FController.FMuted, FController.FCTun,
FController.FNRMode, FController.FNBMode, FController.FSNB, FController.FANF,
FController.FCenterFreq, FController.FFilter,
FController.FVfoB, FController.FActiveVfo,
FController.FTransmitting, FController.FDrivePercent,
FController.FAtten, FController.FTuning, FController.FDisplayDuplex,
FController.FLastFwdW, FController.FLastSWR, FController.FPAMaxPower,
StatusText, BoardText, IPText, SupplyText,
PLLText, RXText, TXText, SeqText);
end;
end. end.