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.