Phase 5: move global-settings persistence into the controller

MakeGlobalSettings (the central persist builder, ~50 fields) lived in MainForm
and mixed radio state with UI fields, blocking headless persistence. Replace it
with TRadioController.BuildGlobalSettings (radio fields from live state) +
SaveGlobalSettings (self-gated on FDevConnected). The 7 scattered
SaveGlobal(FDevMAC, MakeGlobalSettings) call-sites collapse to FController.
SaveGlobalSettings.

Non-radio fields (theme/FPS/MHz-digits/CAT) are NOT controller state — they ride
through FLoadedGlobal (the loaded persist record the controller already holds for
round-trip). The GUI mirrors them into FLoadedGlobal at their change-points
(SetLightTheme/ApplyFPS/ApplyFreqMhzDigits/OnCATSettingsChange + connect for the
app-global theme). Controller never reads or acts on them.

Behavior-preserving (same blob, same save points); makes the daemon able to
persist radio settings without the UI.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Uladzimir Karpenka
2026-06-08 21:00:57 +03:00
co-authored by Claude Opus 4.8
parent e1ecf947b8
commit 2c356beb77
2 changed files with 101 additions and 87 deletions
+78
View File
@@ -314,6 +314,13 @@ type
function MakeBandSettings: TBandSettings;
procedure SaveCurrentBand;
procedure RestoreBand(BandIdx: Integer);
// Глобальные настройки устройства (persist по MAC). BuildGlobalSettings
// собирает радио-поля из своего состояния, а НЕ-радио поля (тема/FPS/digits/
// CAT) переносит сквозь из FLoadedGlobal — контроллер их не трактует, только
// round-trip в файл. SaveGlobalSettings пишет блоб (гейт по FDevConnected).
function BuildGlobalSettings: TGlobalSettings;
procedure SaveGlobalSettings;
// Применяет DSP-настройки диапазона (mode/filter/AGC/CTUN/FM) + шлёт события
// рендера. НЕ трогает частоты VFO/центр — это переиспользуется как RestoreBand
// (с загрузкой VFO), так и SetActiveVfo (bandstack: смена бэнда без сброса VFO).
@@ -994,6 +1001,77 @@ begin
FSettings.SaveBand(FDevMAC, FCurrentBand, FBandCache[FCurrentBand]);
end;
function TRadioController.BuildGlobalSettings: TGlobalSettings;
// Радио-поля — из живого состояния; тема/FPS/digits/CAT — сквозь из FLoadedGlobal
// (контроллер их не трактует). Сидируем загруженным блобом, поверх — радио.
var i: Integer;
begin
Result := FLoadedGlobal; // несёт не-радио поля (тема/FPS/digits/CAT) round-trip
Result.Volume := FVolume;
Result.DriveLevel := FDrivePercent;
Result.PAMaxPower := FPAMaxPower;
for i := 0 to CFG_BAND_COUNT - 1 do Result.PABandCal[i] := FPABandCal[i];
for i := 0 to CFG_XVTR_COUNT - 1 do Result.VHFBandCal[i] := FVHFBandCal[i];
Result.ActiveVfo := FActiveVfo;
Result.NRMode := FNRMode;
Result.NBMode := FNBMode;
Result.SNBEnabled := FSNB;
Result.ANFEnabled := FANF;
Result.AGCSlope := 0;
Result.AGCHangThreshold := 100;
Result.WfAGCEnabled := FWfAGCEnabled;
Result.WfNFEnabled := FWfNFEnabled;
Result.DitherEnabled := FDitherEnabled;
Result.RandomEnabled := FRandomEnabled;
Result.LastBand := FCurrentBand;
Result.LastXvtr := FCurrentXvtr;
Result.SampleRate := FSampleRate;
if FWDSPReady then
begin
Result.FFTSize := FDSPEngine.FFTSize;
Result.WindowType := FDSPEngine.WindowType;
Result.SpecDetector := FDSPEngine.SpecDetector;
Result.SpecAvgMode := FDSPEngine.SpecAvgMode;
Result.SpecAvgTimeMS := FDSPEngine.SpecAvgTimeMS;
Result.WfDetector := FDSPEngine.WfDetector;
Result.WfAvgMode := FDSPEngine.WfAvgMode;
Result.WfAvgTimeMS := FDSPEngine.WfAvgTimeMS;
end
else
begin
Result.FFTSize := 131072;
Result.WindowType := 2;
Result.SpecDetector := 0;
Result.SpecAvgMode := 3;
Result.SpecAvgTimeMS := 30.0;
Result.WfDetector := 0;
Result.WfAvgMode := 3;
Result.WfAvgTimeMS := 120.0;
end;
Result.WfManualHigh := FWfManualHigh;
Result.WfManualLow := FWfManualLow;
Result.WfAGCOffset := FWfAGCOffset;
Result.SpecRefLevel := FSpecRefLevel;
Result.SpecRange := FSpecRange;
Result.SpecGridStep := FSpecGridStep;
Result.AudioSampleRate := FAudioOut.SampleRate;
Result.ShowSpectrum := FShowSpectrum;
Result.ShowWaterfall := FShowWaterfall;
Result.ShowWideband := FShowWideband;
Result.WidebandFill := FWidebandFill;
Result.SpectrumFill := FSpectrumFill;
Result.DisplayDuplex := FDisplayDuplex;
Result.AudioOutDevice := FAudioOutDevName;
Result.AudioInDevice := FAudioInDevName;
Result.SendAudioToRadio := FSendAudioToRadio;
end;
procedure TRadioController.SaveGlobalSettings;
begin
if FDevConnected then FSettings.SaveGlobal(FDevMAC, BuildGlobalSettings);
end;
procedure TRadioController.ApplyBandDSP(const B: TBandSettings);
// DSP-настройки диапазона + события рендера. Частоты VFO/центр НЕ трогает.
begin