Phase 5 (batch 30): move device connect/load-by-MAC into the controller

Fill controller.ConnectDevice with the DoConnectDevice load core: Connect,
load per-device settings by MAC (TX/Alex/XVTR, PA cal, drive%, sample rate,
NR/NB/SNB/ANF, Wf/Show/Spec params) into controller fields, configure DSP
display + open audio devices. Returns False on connect failure (UI shows the
message). New FLoadedGlobal field retains the loaded TGlobalSettings so the UI
can render fields the controller doesn't own (FPS/FreqMhzDigits/CAT).

New OnControllerState(rfDevice) renders all device-level widgets from state
(drive slider, NR/NB buttons, FSpecView/Wideband params, DUP, sample-rate
overlay, CAT apply, web push, FPS, freq digits). drive% now loads directly
into FDrivePercent instead of via TrkDrive.Position; the slider reflects it in
the render. DoConnectDevice collapses to a thin wrapper: ConnectDevice ->
RestoreBand/XVTR/active-VFO -> StartRunning.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Uladzimir Karpenka
2026-06-08 12:10:26 +03:00
co-authored by Claude Opus 4.8
parent 04f621bf3d
commit 783c8976c9
2 changed files with 156 additions and 129 deletions
+41 -126
View File
@@ -1435,6 +1435,39 @@ begin
if (EdFMRptOffset <> nil) and not EdFMRptOffset.Focused then
EdFMRptOffset.Text := RptFormatOffset(FController.FFMRptOffsetHz);
end;
rfDevice:
begin
// Полная синхронизация device-виджетов после ConnectDevice (load-by-MAC).
// Состояние уже в контроллере; здесь только рендер + UI/CAT/web-побочки.
FCATLastGlobal := FController.FLoadedGlobal;
CATApplySettings(FController.FLoadedGlobal);
RebuildXvtrButtons;
PushXvtrToWeb;
TrkDrive.Position := FController.FDrivePercent; // слайдер ← drive%
if Assigned(FSampleRateOverlay) then
FSampleRateOverlay.SetCurrentRate(FController.FSampleRate);
UpdateNRButton; UpdateNBButton; UpdateSNBButton; UpdateANFButton;
FSpecView.WfAGCEnabled := FController.FWfAGCEnabled;
FSpecView.WfNFEnabled := FController.FWfNFEnabled;
FSpecView.WfManualHigh := FController.FWfManualHigh;
FSpecView.WfManualLow := FController.FWfManualLow;
FSpecView.WfAGCOffset := FController.FWfAGCOffset;
FSpecView.ResetWfAvgBuf;
if Assigned(FWidebandView) then
FWidebandView.SetFillSpectrum(FController.FWidebandFill);
FSpecView.FillSpectrum := FController.FSpectrumFill;
if BtnDUP <> nil then StyleButton(BtnDUP, FController.FDisplayDuplex);
if FController.FLoadedGlobal.DisplayFPS > 0 then
ApplyFPS(FController.FLoadedGlobal.DisplayFPS);
ApplyFreqMhzDigits(EnsureRange(FController.FLoadedGlobal.FreqMhzDigits, 3, 5));
if FController.FLoadedGlobal.FFTSize > 0 then
begin
FSpecView.SpecRefLevel := FController.FSpecRefLevel;
FSpecView.SpecRange := FController.FSpecRange;
FSpecView.SpecGridStep := FController.FSpecGridStep;
InvalidateGridCache;
end;
end;
end;
finally
FSyncingFromController := False;
@@ -3678,147 +3711,29 @@ begin
end;
procedure TMainForm.DoConnectDevice(const Dev: THPSDRDevice);
var
G_Settings: TGlobalSettings;
i: Integer;
begin
BtnStartStop.Enabled := True;
if not FController.FNetwork.Connect(Dev) then
// Connect + загрузка per-device настроек в контроллер; рендер ВСЕХ device-
// виджетов идёт через OnControllerState(rfDevice) внутри ConnectDevice.
if not FController.ConnectDevice(Dev) then
begin
ShowMessage('Failed to connect to ' + Dev.IPAddress +
IfThen(FController.FNetwork.LastError <> '', ': ' + FController.FNetwork.LastError, ''));
Exit;
end;
// --- Загружаем настройки устройства по MAC ---
Move(FController.FNetwork.Device.MAC[0], FController.FDevMAC[0], 6);
FController.FDevConnected := True;
FController.FSettings.LoadDevice(FController.FDevMAC, G_Settings, FController.FBandCache);
FCATLastGlobal := G_Settings;
CATApplySettings(G_Settings);
// TX-настройки per-device — отдельная JSON-секция "tx"
FController.FSettings.LoadTX(FController.FDevMAC, FController.FTXSettings);
// Alex-настройки per-device (антенны/маршрутизация)
FController.FSettings.LoadAlex(FController.FDevMAC, FController.FAlexSettings);
FController.FNetwork.SetAlexConfig(FController.FAlexSettings);
// XVTR-настройки per-device (трансвертеры)
FController.FSettings.LoadXvtr(FController.FDevMAC, FController.FXvtrSettings);
FController.FCurrentXvtr := -1;
RebuildXvtrButtons;
ApplyXvtrToNetwork;
PushXvtrToWeb;
FController.FTXSpecRefLevel := FController.FTXSettings.TXSpecRefLevel;
FController.FTXSpecRange := FController.FTXSettings.TXSpecRange;
if FController.FTXSettings.TXSpecGridStep > 0 then
FController.FTXSpecGridStep := FController.FTXSettings.TXSpecGridStep;
FController.FVolume := G_Settings.Volume;
FController.FActiveVfo := G_Settings.ActiveVfo;
// PA settings
FController.FPAMaxPower := G_Settings.PAMaxPower;
for i := 0 to BAND_COUNT - 1 do
FController.FPABandCal[i] := G_Settings.PABandCal[i];
for i := 0 to CFG_XVTR_COUNT - 1 do
FController.FVHFBandCal[i] := G_Settings.VHFBandCal[i];
// Slider position (0..100) and calibrated drive byte
TrkDrive.Position := EnsureRange(G_Settings.DriveLevel, 0, 100);
FController.FCurrentBand := G_Settings.LastBand;
FController.FDriveLevel := CalcDriveByte;
// SampleRate — глобальный, загружаем до RestoreBand
if G_Settings.SampleRate > 0 then
begin
FController.FSampleRate := G_Settings.SampleRate;
FController.FSpanHz := FController.FSampleRate;
if Assigned(FSampleRateOverlay) then
FSampleRateOverlay.SetCurrentRate(FController.FSampleRate);
end;
FController.FNRMode := EnsureRange(G_Settings.NRMode, 0, 4); UpdateNRButton;
FController.FNBMode := EnsureRange(G_Settings.NBMode, 0, 2); UpdateNBButton;
FController.FSNB := G_Settings.SNBEnabled; UpdateSNBButton;
FController.FANF := G_Settings.ANFEnabled; UpdateANFButton;
FController.FWfAGCEnabled := G_Settings.WfAGCEnabled;
FController.FWfNFEnabled := G_Settings.WfNFEnabled;
FController.FDitherEnabled := G_Settings.DitherEnabled;
FController.FRandomEnabled := G_Settings.RandomEnabled;
FController.FSendAudioToRadio := G_Settings.SendAudioToRadio;
FController.FWfManualHigh := G_Settings.WfManualHigh;
FController.FWfManualLow := G_Settings.WfManualLow;
FController.FWfAGCOffset := G_Settings.WfAGCOffset;
FSpecView.WfAGCEnabled := FController.FWfAGCEnabled;
FSpecView.WfNFEnabled := FController.FWfNFEnabled;
FSpecView.WfManualHigh := FController.FWfManualHigh;
FSpecView.WfManualLow := FController.FWfManualLow;
FSpecView.WfAGCOffset := FController.FWfAGCOffset;
FSpecView.ResetWfAvgBuf;
FController.FShowSpectrum := G_Settings.ShowSpectrum;
FController.FShowWaterfall := G_Settings.ShowWaterfall;
FController.FShowWideband := G_Settings.ShowWideband;
FController.FWidebandFill := G_Settings.WidebandFill;
if Assigned(FWidebandView) then
FWidebandView.SetFillSpectrum(FController.FWidebandFill);
FController.FSpectrumFill := G_Settings.SpectrumFill;
FSpecView.FillSpectrum := FController.FSpectrumFill;
FController.FDisplayDuplex := G_Settings.DisplayDuplex;
if BtnDUP <> nil then StyleButton(BtnDUP, FController.FDisplayDuplex);
if G_Settings.DisplayFPS > 0 then
ApplyFPS(G_Settings.DisplayFPS);
ApplyFreqMhzDigits(EnsureRange(G_Settings.FreqMhzDigits, 3, 5));
// Настройки дисплея и сетки
if G_Settings.FFTSize > 0 then
begin
FController.FSpecRefLevel := G_Settings.SpecRefLevel;
FController.FSpecRange := G_Settings.SpecRange;
if G_Settings.SpecGridStep > 0 then FController.FSpecGridStep := G_Settings.SpecGridStep;
FSpecView.SpecRefLevel := FController.FSpecRefLevel;
FSpecView.SpecRange := FController.FSpecRange;
FSpecView.SpecGridStep := FController.FSpecGridStep;
InvalidateGridCache;
if FController.FWDSPReady then
begin
FController.FDSPEngine.SetFFTParams(G_Settings.FFTSize, G_Settings.WindowType);
FController.FDSPEngine.SetSpectrumDisplay(G_Settings.SpecDetector, G_Settings.SpecAvgMode,
G_Settings.SpecAvgTimeMS);
FController.FDSPEngine.SetWaterfallDisplay(G_Settings.WfDetector, G_Settings.WfAvgMode,
G_Settings.WfAvgTimeMS);
end;
// Аудио устройство вывода
FController.FAudioOut.Close;
if G_Settings.AudioSampleRate > 0 then
FController.FAudioOut.SampleRate := G_Settings.AudioSampleRate;
if G_Settings.AudioOutDevice <> '' then
begin
FController.FAudioOut.DeviceIndex := FController.FAudioOut.FindDeviceByName(G_Settings.AudioOutDevice);
FController.FAudioOutDevName := G_Settings.AudioOutDevice;
end
else
begin
FController.FAudioOut.DeviceIndex := -1;
FController.FAudioOutDevName := '';
end;
FController.FAudioOut.Open;
// Аудио устройство ввода (TX mic). MicSource (Radio/SoundCard) определяется
// автоматически в ApplyMOX по источнику PTT — здесь только открываем устройство.
if G_Settings.AudioInDevice <> '' then
begin
FController.FAudioIn.DeviceIndex := FController.FAudioIn.FindDeviceByName(G_Settings.AudioInDevice);
FController.FAudioInDevName := G_Settings.AudioInDevice;
FController.FAudioIn.Open;
end
else
begin
FController.FAudioIn.DeviceIndex := -1;
FController.FAudioInDevName := '';
end;
end;
// Перестройка диапазона/VFO — обёртки с UI-extras (финальная перестройка через
// ApplyVfoA; XVTR — wideband-view+web). Состояние уже загружено ConnectDevice.
RestoreBand(FController.FCurrentBand);
// Восстанавливаем XVTR-режим, если он был активен при выходе.
// FController.FCurrentBand уже загружен (HF-диапазон, на который пользователь зашёл
// до XVTR), его FController.FBandCache не пострадает — ActivateXvtrBand только
// выставит FController.FVfoA на XVTR-частоту и FController.FCurrentXvtr.
if (G_Settings.LastXvtr >= 0) and (G_Settings.LastXvtr < CFG_XVTR_COUNT)
and FController.FXvtrSettings.Entries[G_Settings.LastXvtr].Enabled then
ActivateXvtrBand(G_Settings.LastXvtr);
if (FController.FLoadedGlobal.LastXvtr >= 0) and (FController.FLoadedGlobal.LastXvtr < CFG_XVTR_COUNT)
and FController.FXvtrSettings.Entries[FController.FLoadedGlobal.LastXvtr].Enabled then
ActivateXvtrBand(FController.FLoadedGlobal.LastXvtr);
// Активный VFO загружен из настроек (выше), но подсветку рисовали в FormCreate
// (тогда FActiveVfo ещё = 0 → A), а RestoreBand/ActivateXvtrBand перестраивают