diff --git a/MainForm.pas b/MainForm.pas index 248f3c1..28029f6 100644 --- a/MainForm.pas +++ b/MainForm.pas @@ -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; diff --git a/WebAdapter.pas b/WebAdapter.pas new file mode 100644 index 0000000..acb13f1 --- /dev/null +++ b/WebAdapter.pas @@ -0,0 +1,264 @@ +unit WebAdapter; + +{$mode objfpc}{$H+} + +// Адаптер веб-интерфейса: мост между TWebServer (входящие команды из WS-потока) +// и TRadioController (ядро). Раньше эти WebOnXxx/SyncWebXxx жили в TMainForm — +// вынесены сюда, чтобы развязать UI от веба и подготовить headless-демон. +// +// Маршалинг: входящие команды приходят из потока веб-сервера; выполняем их в +// потоке контроллера через FController.Invoke (GUI = TThread.Synchronize; демон +// подставит свою очередь). Synchronize блокирующий и последовательный — поля +// FSync* для передачи параметров безопасны (гонок нет). +// +// Часть команд (тюнинг VFO/диапазон/MOX/...) идёт через UI-обёртки MainForm, +// которые ещё не headless — для них есть IWebHost (его реализует хозяин). По мере +// переноса оркестрации в контроллер интерфейс будет сужаться. + +interface + +uses + Classes, SysUtils, + RadioController, WebServer, WDSPEngine; + +type + // UI/оркестрационные операции, которые адаптер дёргает у хозяина (пока не + // headless — десктоп: методы MainForm; демон: своя реализация). Наполняется + // в WA2 по мере переноса host-зависимых хендлеров. + IWebHost = interface + ['{B3A1F7C2-9D54-4E18-9C6B-2E5A7F0D1A33}'] + end; + + { TWebAdapter } + TWebAdapter = class + private + FController: TRadioController; + FServer: TWebServer; + FHost: IWebHost; + // Поля для передачи параметров команды в Sync-метод (через Invoke). + FSyncInt: Integer; + FSyncBool: Boolean; + FSyncFreq: Double; + // Sync-методы (выполняются в потоке контроллера через Invoke). + procedure SyncMode; + procedure SyncFilter; + procedure SyncAGC; + procedure SyncAGCTop; + procedure SyncVolume; + procedure SyncWfAGC; + procedure SyncWfNF; + procedure SyncMute; + procedure SyncNR; + procedure SyncNB; + procedure SyncSNB; + procedure SyncANF; + procedure SyncFreqB; + procedure SyncCenter; + procedure SyncAttn; + procedure SyncActiveVfo; + procedure SyncCtun; + procedure SyncMOX; + procedure SyncTun; + procedure SyncFMStep; + public + constructor Create(AController: TRadioController; AServer: TWebServer; + AHost: IWebHost); + + // Колбэки веб-сервера (навешиваются хозяином на FServer.OnXxx). + procedure OnMode(Mode: Integer); + procedure OnFilter(BW: Integer); + procedure OnAGC(Mode: Integer); + procedure OnAGCTop(DB: Integer); + procedure OnVolume(V: Integer); + procedure OnWfAGC(On_: Boolean); + procedure OnWfNF(On_: Boolean); + procedure OnMute(On_: Boolean); + procedure OnNR(Mode: Integer); + procedure OnNB(Mode: Integer); + procedure OnSNB(On_: Boolean); + procedure OnANF(On_: Boolean); + procedure OnFreqB(Hz: Double); + procedure OnCenter(Hz: Double); + procedure OnAttn(Idx: Integer); + procedure OnActiveVfo(Idx: Integer); + procedure OnCtun(On_: Boolean); + procedure OnMOX(On_: Boolean); + procedure OnTun(On_: Boolean); + procedure OnFMStep(Idx: Integer); + procedure OnClientActiveChanged(Active: Boolean); + end; + +implementation + +constructor TWebAdapter.Create(AController: TRadioController; + AServer: TWebServer; AHost: IWebHost); +begin + inherited Create; + FController := AController; + FServer := AServer; + FHost := AHost; +end; + +// ── Входящие команды: запоминаем параметр, выполняем в потоке контроллера ───── + +procedure TWebAdapter.OnMode(Mode: Integer); +begin FSyncInt := Mode; FController.Invoke(@SyncMode); end; + +procedure TWebAdapter.SyncMode; +begin + if (FSyncInt < 0) or (FSyncInt > MODE_SAM) then Exit; + FController.SetMode(FSyncInt); // дефолт фильтра + DSP + cache; рендер через rfMode +end; + +procedure TWebAdapter.OnFilter(BW: Integer); +begin FSyncInt := BW; FController.Invoke(@SyncFilter); end; + +procedure TWebAdapter.SyncFilter; +begin + // Web-клиент шлёт полосу в Гц (кнопки панели фильтров = пресетные значения); + // команда применяет её и подсвечивает ближайший пресет (рендер — rfFilter). + FController.SetFilterBW(FSyncInt); +end; + +procedure TWebAdapter.OnAGC(Mode: Integer); +begin FSyncInt := Mode; FController.Invoke(@SyncAGC); end; + +procedure TWebAdapter.SyncAGC; +begin + FController.SetAGCMode(FSyncInt); // единый маппинг (исправляет старый web-баг) +end; + +procedure TWebAdapter.OnAGCTop(DB: Integer); +begin FSyncInt := DB; FController.Invoke(@SyncAGCTop); end; + +procedure TWebAdapter.SyncAGCTop; +begin + FController.SetAGCTop(FSyncInt); // клампит, применяет, OnControllerState обновит UI +end; + +procedure TWebAdapter.OnVolume(V: Integer); +begin FSyncInt := V; FController.Invoke(@SyncVolume); end; + +procedure TWebAdapter.SyncVolume; +begin FController.SetVolume(FSyncInt); end; + +procedure TWebAdapter.OnWfAGC(On_: Boolean); +begin FSyncBool := On_; FController.Invoke(@SyncWfAGC); end; + +procedure TWebAdapter.SyncWfAGC; +begin FController.SetWfAGC(FSyncBool); end; + +procedure TWebAdapter.OnWfNF(On_: Boolean); +begin FSyncBool := On_; FController.Invoke(@SyncWfNF); end; + +procedure TWebAdapter.SyncWfNF; +begin FController.SetWfNF(FSyncBool); end; + +procedure TWebAdapter.OnMute(On_: Boolean); +begin FSyncBool := On_; FController.Invoke(@SyncMute); end; + +procedure TWebAdapter.SyncMute; +begin FController.SetMute(FSyncBool); end; // идемпотентно; OnControllerState обновит UI + +procedure TWebAdapter.OnNR(Mode: Integer); +begin FSyncInt := Mode; FController.Invoke(@SyncNR); end; + +procedure TWebAdapter.SyncNR; +begin FController.SetNR(FSyncInt); end; // клампит и применяет; рендер через rfNR + +procedure TWebAdapter.OnNB(Mode: Integer); +begin FSyncInt := Mode; FController.Invoke(@SyncNB); end; + +procedure TWebAdapter.SyncNB; +begin FController.SetNB(FSyncInt); end; + +procedure TWebAdapter.OnSNB(On_: Boolean); +begin FSyncBool := On_; FController.Invoke(@SyncSNB); end; + +procedure TWebAdapter.SyncSNB; +begin FController.SetSNB(FSyncBool); end; + +procedure TWebAdapter.OnANF(On_: Boolean); +begin FSyncBool := On_; FController.Invoke(@SyncANF); end; + +procedure TWebAdapter.SyncANF; +begin FController.SetANF(FSyncBool); end; + +procedure TWebAdapter.OnFreqB(Hz: Double); +begin FSyncFreq := Hz; FController.Invoke(@SyncFreqB); end; + +procedure TWebAdapter.SyncFreqB; +begin + // Унифицировано с десктопом: команда хранит частоту, при активном B + // перестраивает приёмник (CTUN-aware) + сеть; рендер — через rfVfoB/rfBand. + FController.SetVfoB(FSyncFreq); +end; + +procedure TWebAdapter.OnCenter(Hz: Double); +begin FSyncFreq := Hz; FController.Invoke(@SyncCenter); end; + +procedure TWebAdapter.SyncCenter; +begin + // Сдвигаем DDC-центр без смены VFO (аналог CTUN-drag в десктопе). + FController.SetCenter(FSyncFreq); +end; + +procedure TWebAdapter.OnAttn(Idx: Integer); +begin FSyncInt := Idx; FController.Invoke(@SyncAttn); end; + +procedure TWebAdapter.SyncAttn; +begin FController.SetAtten(FSyncInt); end; + +procedure TWebAdapter.OnActiveVfo(Idx: Integer); +begin FSyncInt := Idx; FController.Invoke(@SyncActiveVfo); end; + +procedure TWebAdapter.SyncActiveVfo; +begin + if (FSyncInt < 0) or (FSyncInt > 1) then Exit; + if FSyncInt = FController.FActiveVfo then Exit; + FController.SetActiveVfo(FSyncInt); // перестройка RX-VFO; рендер rfActiveVfo +end; + +procedure TWebAdapter.OnCtun(On_: Boolean); +begin FSyncBool := On_; FController.Invoke(@SyncCtun); end; + +procedure TWebAdapter.SyncCtun; +begin + if FSyncBool <> FController.FCTun then + FController.SetCTun(FSyncBool); // band cache + center/shift + сеть; рендер rfCTun +end; + +procedure TWebAdapter.OnMOX(On_: Boolean); +begin FSyncBool := On_; FController.Invoke(@SyncMOX); end; + +procedure TWebAdapter.SyncMOX; +begin + // FWebClientActive держится актуальным OnClientActiveChanged — выбор mic-source. + FController.SetMOX(FSyncBool); // ядро TX (safety/PTT/DSP); рендер rfTransmitting +end; + +procedure TWebAdapter.OnTun(On_: Boolean); +begin FSyncBool := On_; FController.Invoke(@SyncTun); end; + +procedure TWebAdapter.SyncTun; +begin + FController.SetTune(FSyncBool); // ядро TUN (safety/тон/drive/MOX); рендер rfTuning +end; + +procedure TWebAdapter.OnFMStep(Idx: Integer); +begin FSyncInt := Idx; FController.Invoke(@SyncFMStep); end; + +procedure TWebAdapter.SyncFMStep; +begin + FController.SetFMStep(FSyncInt); // клампит 0..3 внутри; рендер rfFMStepIdx + FController.SaveCurrentBand; +end; + +procedure TWebAdapter.OnClientActiveChanged(Active: Boolean); +// Из потока веб-сервера при connect/disconnect клиента. Простая запись флага — +// ядро (SetMOX, в т.ч. HWPTT-путь) выбирает по нему mic-source. Без маршалинга. +begin + FController.FWebClientActive := Active; +end; + +end.