Phase 3 (WA1): extract web adapter into WebAdapter.pas

Move the pure controller-command web handlers out of MainForm into a
new TWebAdapter (bridge between TWebServer and TRadioController),
marshalling via FController.Invoke so it is daemon-ready.

- 20 handlers moved: Mode/Filter/AGC/AGCTop/Volume/WfAGC/WfNF/Mute/
  NR/NB/SNB/ANF/FreqB/Center/Attn/ActiveVfo/Ctun/MOX/Tun/FMStep +
  ClientActiveChanged. Each calls a controller command directly.
- CAT context (Ctx.Set*) and CATSetFilterIdx repointed to the adapter,
  since CAT reuses these as its command layer.
- Host-dependent handlers (Freq/Band/XvtrBand/Span/Run/Drive/connect/
  disconnect/CRUD/mic) and the outgoing PushSpectrum mirror stay in
  MainForm for now (WA2/WA3); IWebHost is the placeholder seam.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Uladzimir Karpenka
2026-06-08 16:03:49 +03:00
co-authored by Claude Opus 4.8
parent c4981fcb19
commit 2f8b0bdf48
2 changed files with 313 additions and 349 deletions
+49 -349
View File
@@ -30,7 +30,7 @@ uses
HPSDRProtocol, HPSDRNetwork,
WDSP, WDSPEngine, AudioOutput, AudioInput,
Settings,
WebServer,
WebServer, WebAdapter,
CATEngine, CATSerial, CATTcp,
SpectrumView, SpectrumViewOpengl,
WidebandView,
@@ -170,6 +170,7 @@ type
FSplitterRatio: Double;
// --- Settings ---
FWebServer: TWebServer;
FWebAdapter: TWebAdapter; // мост web-сервер ↔ контроллер (вынесенные WebOnXxx)
FWebEnabled: Boolean;
FWebPort: Integer;
FWebBindAddr: string;
@@ -434,34 +435,16 @@ type
procedure BtnNBClick(Sender: TObject);
procedure BtnSNBClick(Sender: TObject);
procedure BtnANFClick(Sender: TObject);
// Веб-интерфейс: callbacks от TWebServer
// Веб-интерфейс: host-зависимые callbacks от TWebServer (UI-обёртки).
// Чистые-контроллерные перенесены в TWebAdapter (Mode/Filter/AGC/AGCTop/Volume/
// WfAGC/WfNF/Mute/NR/NB/SNB/ANF/FreqB/Center/Attn/ActiveVfo/Ctun/MOX/Tun/FMStep/
// ClientActive). Здесь только то, что дёргает UI-обёртки MainForm.
procedure WebOnFreq(Hz: Double);
procedure WebOnMode(Mode: Integer);
procedure WebOnFilter(BW: Integer);
procedure WebOnAGC(Mode: Integer);
procedure WebOnAGCTop(DB: Integer);
procedure WebOnBand(Idx: Integer);
procedure WebOnSpan(Hz: Integer);
procedure WebOnVolume(V: Integer);
procedure WebOnWfAGC(On_: Boolean);
procedure WebOnWfNF(On_: Boolean);
procedure WebOnRun(On_: Boolean);
procedure WebOnMute(On_: Boolean);
procedure WebOnClientActiveChanged(Active: Boolean);
procedure WebOnCtun(On_: Boolean);
procedure WebOnNR(Mode: Integer);
procedure WebOnNB(Mode: Integer);
procedure WebOnSNB(On_: Boolean);
procedure WebOnANF(On_: Boolean);
procedure WebOnFreqB(Hz: Double);
procedure WebOnActiveVfo(Idx: Integer);
procedure WebOnCenter(Hz: Double);
procedure WebOnMOX(On_: Boolean);
procedure WebOnDrive(V: Integer);
procedure WebOnFreqA(Hz: Double);
procedure WebOnAttn(Idx: Integer);
procedure WebOnTun(On_: Boolean);
procedure WebOnFMStep(Idx: Integer);
procedure WebOnMic(Samples: PSingle; Count: Integer);
procedure WebOnXvtrBand(Idx: Integer);
// Discovery / connect / saved-CRUD из web (маршалятся в UI)
@@ -478,34 +461,14 @@ type
procedure SyncWebDevRemove;
procedure SyncWebDevAutoStart;
procedure PushDeviceListToWeb; // store → FWebServer.SetDeviceList
// Synchronize-обёртки (выполняются в UI-потоке)
// Synchronize-обёртки (выполняются в UI-потоке) — только host-зависимые
procedure SyncWebFreq;
procedure SyncWebMode;
procedure SyncWebFilter;
procedure SyncWebAGC;
procedure SyncWebAGCTop;
procedure SyncWebBand;
procedure SyncWebXvtrBand;
procedure SyncWebSpan;
procedure SyncWebVolume;
procedure SyncWebWfAGC;
procedure SyncWebWfNF;
procedure SyncWebRun;
procedure SyncWebMute;
procedure SyncWebCtun;
procedure SyncWebNR;
procedure SyncWebNB;
procedure SyncWebSNB;
procedure SyncWebANF;
procedure SyncWebFreqB;
procedure SyncWebActiveVfo;
procedure SyncWebCenter;
procedure SyncWebMOX;
procedure SyncWebDrive;
procedure SyncWebFreqA;
procedure SyncWebAttn;
procedure SyncWebTun;
procedure SyncWebFMStep;
// --- CAT callbacks -------------------------------------------------------
function CATGetVfoA: Double;
function CATGetVfoB: Double;
@@ -985,32 +948,40 @@ begin
FWebUser := WebCfg.User;
FWebPass := WebCfg.Pass;
FWebServer := TWebServer.Create(FWebUser, FWebPass, Word(FWebPort), FWebBindAddr);
// Вынесенные в TWebAdapter чистые-контроллерные команды (host=nil: их хендлеры
// ещё не нужны хосту; host-зависимые WebOnXxx пока на MainForm — переедут в WA2).
FWebAdapter := TWebAdapter.Create(FController, FWebServer, nil);
FWebServer.OnMode := FWebAdapter.OnMode;
FWebServer.OnFilter := FWebAdapter.OnFilter;
FWebServer.OnAGC := FWebAdapter.OnAGC;
FWebServer.OnAGCTop := FWebAdapter.OnAGCTop;
FWebServer.OnVolume := FWebAdapter.OnVolume;
FWebServer.OnWfAGC := FWebAdapter.OnWfAGC;
FWebServer.OnWfNF := FWebAdapter.OnWfNF;
FWebServer.OnMute := FWebAdapter.OnMute;
FWebServer.OnNR := FWebAdapter.OnNR;
FWebServer.OnNB := FWebAdapter.OnNB;
FWebServer.OnSNB := FWebAdapter.OnSNB;
FWebServer.OnANF := FWebAdapter.OnANF;
FWebServer.OnFreqB := FWebAdapter.OnFreqB;
FWebServer.OnCenter := FWebAdapter.OnCenter;
FWebServer.OnAttn := FWebAdapter.OnAttn;
// Web-слой держит FController.FWebClientActive актуальным (выбор mic-source
// при TX, в т.ч. для HWPTT из контроллера) — больше не зеркалим лениво.
FWebServer.OnClientActiveChanged := FWebAdapter.OnClientActiveChanged;
// Host-зависимые (UI-обёртки) и исходящий push — пока на MainForm:
FWebServer.OnCtun := FWebAdapter.OnCtun;
FWebServer.OnActiveVfo := FWebAdapter.OnActiveVfo;
FWebServer.OnMOX := FWebAdapter.OnMOX;
FWebServer.OnTun := FWebAdapter.OnTun;
FWebServer.OnFMStep := FWebAdapter.OnFMStep;
// Host-зависимые (UI-обёртки) — пока на MainForm:
FWebServer.OnFreq := WebOnFreq;
FWebServer.OnMode := WebOnMode;
FWebServer.OnFilter := WebOnFilter;
FWebServer.OnAGC := WebOnAGC;
FWebServer.OnAGCTop := WebOnAGCTop;
FWebServer.OnBand := WebOnBand;
FWebServer.OnSpan := WebOnSpan;
FWebServer.OnVolume := WebOnVolume;
FWebServer.OnWfAGC := WebOnWfAGC;
FWebServer.OnWfNF := WebOnWfNF;
FWebServer.OnRun := WebOnRun;
FWebServer.OnMute := WebOnMute;
FWebServer.OnCtun := WebOnCtun;
FWebServer.OnNR := WebOnNR;
FWebServer.OnNB := WebOnNB;
FWebServer.OnSNB := WebOnSNB;
FWebServer.OnANF := WebOnANF;
FWebServer.OnFreqB := WebOnFreqB;
FWebServer.OnActiveVfo := WebOnActiveVfo;
FWebServer.OnCenter := WebOnCenter;
FWebServer.OnMOX := WebOnMOX;
FWebServer.OnDrive := WebOnDrive;
FWebServer.OnFreqA := WebOnFreqA;
FWebServer.OnAttn := WebOnAttn;
FWebServer.OnTun := WebOnTun;
FWebServer.OnFMStep := WebOnFMStep;
FWebServer.OnWebMic := WebOnMic;
FWebServer.OnXvtrBand := WebOnXvtrBand;
FWebServer.OnConnect := WebOnConnect;
@@ -1019,9 +990,6 @@ begin
FWebServer.OnDevAdd := WebOnDevAdd;
FWebServer.OnDevRemove := WebOnDevRemove;
FWebServer.OnDevAutoStart := WebOnDevAutoStart;
// Web-слой держит FController.FWebClientActive актуальным (выбор mic-source
// при TX, в т.ч. для HWPTT из контроллера) — больше не зеркалим лениво.
FWebServer.OnClientActiveChanged := WebOnClientActiveChanged;
if FWebEnabled then FWebServer.Start;
PushXvtrToWeb;
PushDeviceListToWeb; // saved-устройства сразу видны web-overlay
@@ -1208,6 +1176,7 @@ begin
FController.FSettings.Save;
FreeAndNil(FController.FChannelStore);
FWebServer.Stop;
FreeAndNil(FWebAdapter); // адаптер не владеет сервером — освобождаем после Stop
FWebServer.Free;
if Assigned(FCATTcp) then begin FCATTcp.Stop; FreeAndNil(FCATTcp); end;
if Assigned(FCATSerial) then begin FCATSerial.StopAll; FreeAndNil(FCATSerial); end;
@@ -5037,56 +5006,8 @@ begin
FController.SetVfoB(FWebSyncFreq);
end;
procedure TMainForm.WebOnMode(Mode: Integer);
begin
FWebSyncInt := Mode;
FWebSyncM := SyncWebMode;
TThread.Synchronize(nil, FWebSyncM);
end;
procedure TMainForm.SyncWebMode;
begin
if (FWebSyncInt < 0) or (FWebSyncInt >= MODE_COUNT) then Exit;
FController.SetMode(FWebSyncInt); // дефолт фильтра + DSP + cache; рендер через rfMode
end;
procedure TMainForm.WebOnFilter(BW: Integer);
begin
FWebSyncInt := BW;
FWebSyncM := SyncWebFilter;
TThread.Synchronize(nil, FWebSyncM);
end;
procedure TMainForm.SyncWebFilter;
begin
// Web-клиент шлёт полосу в Гц (кнопки панели фильтров = пресетные значения);
// команда применяет её и подсвечивает ближайший пресет (рендер — rfFilter).
FController.SetFilterBW(FWebSyncInt);
end;
procedure TMainForm.WebOnAGC(Mode: Integer);
begin
FWebSyncInt := Mode;
FWebSyncM := SyncWebAGC;
TThread.Synchronize(nil, FWebSyncM);
end;
procedure TMainForm.SyncWebAGC;
begin
FController.SetAGCMode(FWebSyncInt); // единый маппинг (исправляет старый web-баг)
end;
procedure TMainForm.WebOnAGCTop(DB: Integer);
begin
FWebSyncInt := DB;
FWebSyncM := SyncWebAGCTop;
TThread.Synchronize(nil, FWebSyncM);
end;
procedure TMainForm.SyncWebAGCTop;
begin
FController.SetAGCTop(FWebSyncInt); // клампит, применяет, OnControllerState обновит UI
end;
// Mode/Filter/AGC/AGCTop/Volume/WfAGC/WfNF/Mute/NR/NB/SNB/ANF/FreqB/Center/Attn +
// ClientActiveChanged перенесены в TWebAdapter (чистые команды контроллера).
procedure TMainForm.WebOnBand(Idx: Integer);
begin
@@ -5153,42 +5074,6 @@ begin
end;
end;
procedure TMainForm.WebOnVolume(V: Integer);
begin
FWebSyncInt := V;
FWebSyncM := SyncWebVolume;
TThread.Synchronize(nil, FWebSyncM);
end;
procedure TMainForm.SyncWebVolume;
begin
FController.SetVolume(FWebSyncInt);
end;
procedure TMainForm.WebOnWfAGC(On_: Boolean);
begin
FWebSyncBool := On_;
FWebSyncM := SyncWebWfAGC;
TThread.Synchronize(nil, FWebSyncM);
end;
procedure TMainForm.SyncWebWfAGC;
begin
FController.SetWfAGC(FWebSyncBool);
end;
procedure TMainForm.WebOnWfNF(On_: Boolean);
begin
FWebSyncBool := On_;
FWebSyncM := SyncWebWfNF;
TThread.Synchronize(nil, FWebSyncM);
end;
procedure TMainForm.SyncWebWfNF;
begin
FController.SetWfNF(FWebSyncBool);
end;
// ── Run ────────────────────────────────────────────────────────────────────
procedure TMainForm.WebOnRun(On_: Boolean);
@@ -5340,156 +5225,8 @@ begin
FWebServer.SetDeviceList(Arr);
end;
// ── Mute ───────────────────────────────────────────────────────────────────
procedure TMainForm.WebOnMute(On_: Boolean);
begin
FWebSyncBool := On_;
FWebSyncM := SyncWebMute;
TThread.Synchronize(nil, FWebSyncM);
end;
procedure TMainForm.SyncWebMute;
begin
FController.SetMute(FWebSyncBool); // идемпотентно; OnControllerState обновит UI
end;
procedure TMainForm.WebOnClientActiveChanged(Active: Boolean);
// Вызывается из потока web-сервера при connect/disconnect клиента. Зеркалим в
// ядро — SetMOX (включая HWPTT-путь в контроллере) выбирает по этому mic-source.
begin
FController.FWebClientActive := Active;
end;
// ── CTUN ──────────────────────────────────────────────────────────────────
procedure TMainForm.WebOnCtun(On_: Boolean);
begin
FWebSyncBool := On_;
FWebSyncM := SyncWebCtun;
TThread.Synchronize(nil, FWebSyncM);
end;
procedure TMainForm.SyncWebCtun;
begin
if FWebSyncBool <> FController.FCTun then
BtnCTunClick(nil);
end;
// ── NR ────────────────────────────────────────────────────────────────────
procedure TMainForm.WebOnNR(Mode: Integer);
begin
FWebSyncInt := Mode;
FWebSyncM := SyncWebNR;
TThread.Synchronize(nil, FWebSyncM);
end;
procedure TMainForm.SyncWebNR;
begin
FController.SetNR(FWebSyncInt); // клампит и применяет; OnControllerState обновит кнопку
end;
// ── NB ────────────────────────────────────────────────────────────────────
procedure TMainForm.WebOnNB(Mode: Integer);
begin
FWebSyncInt := Mode;
FWebSyncM := SyncWebNB;
TThread.Synchronize(nil, FWebSyncM);
end;
procedure TMainForm.SyncWebNB;
begin
FController.SetNB(FWebSyncInt); // клампит и применяет; OnControllerState обновит кнопку
end;
procedure TMainForm.WebOnSNB(On_: Boolean);
begin
FWebSyncBool := On_;
FWebSyncM := SyncWebSNB;
TThread.Synchronize(nil, FWebSyncM);
end;
procedure TMainForm.SyncWebSNB;
begin
FController.SetSNB(FWebSyncBool);
end;
// ── ANF ───────────────────────────────────────────────────────────────────
procedure TMainForm.WebOnANF(On_: Boolean);
begin
FWebSyncBool := On_;
FWebSyncM := SyncWebANF;
TThread.Synchronize(nil, FWebSyncM);
end;
procedure TMainForm.SyncWebANF;
begin
FController.SetANF(FWebSyncBool);
end;
// ── VFO-B freq (из веба) ─────────────────────────────────────────────────
procedure TMainForm.WebOnFreqB(Hz: Double);
begin
FWebSyncFreq := Hz;
FWebSyncM := SyncWebFreqB;
TThread.Synchronize(nil, FWebSyncM);
end;
procedure TMainForm.SyncWebFreqB;
begin
// Унифицировано с десктопом: команда хранит частоту, при активном B
// перестраивает приёмник (CTUN-aware) + сеть; рендер — через rfVfoB/rfBand.
FController.SetVfoB(FWebSyncFreq);
end;
// ── Активный VFO (из веба) ───────────────────────────────────────────────
procedure TMainForm.WebOnActiveVfo(Idx: Integer);
begin
FWebSyncInt := Idx;
FWebSyncM := SyncWebActiveVfo;
TThread.Synchronize(nil, FWebSyncM);
end;
procedure TMainForm.SyncWebActiveVfo;
begin
if (FWebSyncInt < 0) or (FWebSyncInt > 1) then Exit;
if FWebSyncInt = FController.FActiveVfo then Exit;
ActivateVfo(FWebSyncInt);
end;
// ── CTUN drag center scroll (из веба) ────────────────────────────────────
procedure TMainForm.WebOnCenter(Hz: Double);
begin
FWebSyncFreq := Hz;
FWebSyncM := SyncWebCenter;
TThread.Synchronize(nil, FWebSyncM);
end;
procedure TMainForm.SyncWebCenter;
begin
// Сдвигаем DDC-центр без смены VFO (аналог CTUN-drag в десктопе).
// Движок (shift, сеть) + рендер (rfCenterFreq) — в контроллере SetCenter.
FController.SetCenter(FWebSyncFreq);
end;
procedure TMainForm.WebOnMOX(On_: Boolean);
begin
FWebSyncBool := On_;
FWebSyncM := SyncWebMOX;
TThread.Synchronize(nil, FWebSyncM);
end;
procedure TMainForm.SyncWebMOX;
begin
ApplyMOX(FWebSyncBool);
end;
procedure TMainForm.WebOnDrive(V: Integer);
begin
FWebSyncInt := V;
@@ -5515,43 +5252,6 @@ begin
ApplyVfoA(Round(FWebSyncFreq));
end;
procedure TMainForm.WebOnAttn(Idx: Integer);
begin
FWebSyncInt := Idx;
FWebSyncM := SyncWebAttn;
TThread.Synchronize(nil, FWebSyncM);
end;
procedure TMainForm.SyncWebAttn;
begin
FController.SetAtten(FWebSyncInt);
end;
procedure TMainForm.WebOnFMStep(Idx: Integer);
begin
FWebSyncInt := Idx;
FWebSyncM := SyncWebFMStep;
TThread.Synchronize(nil, FWebSyncM);
end;
procedure TMainForm.SyncWebFMStep;
begin
SetFMStep(Max(0, Min(FM_STEP_COUNT - 1, FWebSyncInt)));
SaveCurrentBand;
end;
procedure TMainForm.WebOnTun(On_: Boolean);
begin
FWebSyncBool := On_;
FWebSyncM := SyncWebTun;
TThread.Synchronize(nil, FWebSyncM);
end;
procedure TMainForm.SyncWebTun;
begin
ApplyTUN(FWebSyncBool);
end;
procedure TMainForm.WebOnMic(Samples: PSingle; Count: Integer);
var
Buf: array[0..5759] of Double;
@@ -6344,17 +6044,17 @@ begin
Ctx.GetCurrentBand := CATGetBand;
Ctx.SetVfoA := CATSetVfoA;
Ctx.SetVfoB := CATSetVfoB;
Ctx.SetMode := WebOnMode;
Ctx.SetActiveVfo := WebOnActiveVfo;
Ctx.SetAGCMode := WebOnAGC;
Ctx.SetVolume := WebOnVolume;
Ctx.SetMode := FWebAdapter.OnMode;
Ctx.SetActiveVfo := FWebAdapter.OnActiveVfo;
Ctx.SetAGCMode := FWebAdapter.OnAGC;
Ctx.SetVolume := FWebAdapter.OnVolume;
Ctx.SetDriveLevel := WebOnDrive;
Ctx.SetFilterIdx := CATSetFilterIdx;
Ctx.SetNRMode := WebOnNR;
Ctx.SetNBMode := WebOnNB;
Ctx.SetSNBEnabled := WebOnSNB;
Ctx.SetANFEnabled := WebOnANF;
Ctx.SetTransmitting := WebOnMOX;
Ctx.SetNRMode := FWebAdapter.OnNR;
Ctx.SetNBMode := FWebAdapter.OnNB;
Ctx.SetSNBEnabled := FWebAdapter.OnSNB;
Ctx.SetANFEnabled := FWebAdapter.OnANF;
Ctx.SetTransmitting := FWebAdapter.OnMOX;
Ctx.DoBandUp := CATDoBandUp;
Ctx.DoBandDown := CATDoBandDown;
Ctx.DoTuneUp := CATDoTuneUp;
@@ -6453,8 +6153,8 @@ end;
procedure TMainForm.CATSetFilterIdx(V: Integer);
begin
// WebOnFilter: negative value encodes 0-based index as -(idx+1)
WebOnFilter(-(V + 1));
// OnFilter: negative value encodes 0-based index as -(idx+1)
FWebAdapter.OnFilter(-(V + 1));
end;
procedure TMainForm.CATDoBandUp;