diff --git a/MainForm.pas b/MainForm.pas index 93f6a36..d9313be 100644 --- a/MainForm.pas +++ b/MainForm.pas @@ -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 перестраивают diff --git a/RadioController.pas b/RadioController.pas index b542199..df44ee6 100644 --- a/RadioController.pas +++ b/RadioController.pas @@ -212,6 +212,9 @@ type FPABandCal: array[0..CFG_BAND_COUNT-1] of Double; FVHFBandCal: array[0..CFG_XVTR_COUNT-1] of Double; FDevMAC: array[0..5] of Byte; + // Глобальные настройки устройства, загруженные ConnectDevice. Хранятся для + // UI-рендера полей, которых нет в контроллере (DisplayFPS/FreqMhzDigits/CAT). + FLoadedGlobal: TGlobalSettings; // ---- Телеметрия (наполняется в Фазе 3/4 вместе с сетевыми колбэками) ---- FLastSMeter, FLastFwdW, FLastSWR, FLastSupplyV, FLastSupplyA: Double; @@ -396,7 +399,7 @@ type procedure SetRun(On_: Boolean); procedure StartRunning(SpectrumWidth: Integer); // Run=1 bring-up: General/DDC/DUC + DSP restore procedure StopRunning; // Run=0: graceful-стоп RX-потока + сброс TX/телеметрии - procedure ConnectDevice(const Dev: THPSDRDevice); + function ConnectDevice(const Dev: THPSDRDevice): Boolean; // Connect + load-by-MAC; рендер через rfDevice procedure Discover; // ---- События наружу ---- @@ -2044,8 +2047,117 @@ begin Changed(rfRunning); // безусловный сброс run-кнопки/MOX.Enabled/sleep (как старый хвост STOP) end; -procedure TRadioController.ConnectDevice(const Dev: THPSDRDevice); -begin { TODO wiring: DoConnectDevice } Changed(rfDevice); end; +function TRadioController.ConnectDevice(const Dev: THPSDRDevice): Boolean; +// Подключение + загрузка per-device настроек по MAC в поля контроллера + конфиг +// движка (DSP display, audio). Возвращает False, если Connect не удался (UI +// покажет сообщение по FNetwork.LastError). Рендер ВСЕХ device-виджетов — UI +// через OnControllerState(rfDevice). Перестройку диапазона/VFO (RestoreBand/ +// XVTR/ActiveVfo) делает UI-обёртка ПОСЛЕ (там финальная перестройка через +// ApplyVfoA + wideband/web-extras). FLoadedGlobal хранит загруженный G_Settings +// для рендера полей, которых нет в контроллере (FPS/FreqMhzDigits/CAT). +var + i: Integer; +begin + Result := FNetwork.Connect(Dev); + if not Result then Exit; + + // --- Загружаем настройки устройства по MAC --- + Move(FNetwork.Device.MAC[0], FDevMAC[0], 6); + FDevConnected := True; + FSettings.LoadDevice(FDevMAC, FLoadedGlobal, FBandCache); + // TX/Alex/XVTR per-device + FSettings.LoadTX(FDevMAC, FTXSettings); + FSettings.LoadAlex(FDevMAC, FAlexSettings); + FNetwork.SetAlexConfig(FAlexSettings); + FSettings.LoadXvtr(FDevMAC, FXvtrSettings); + FCurrentXvtr := -1; + ApplyXvtrToNetwork; + FTXSpecRefLevel := FTXSettings.TXSpecRefLevel; + FTXSpecRange := FTXSettings.TXSpecRange; + if FTXSettings.TXSpecGridStep > 0 then + FTXSpecGridStep := FTXSettings.TXSpecGridStep; + FVolume := FLoadedGlobal.Volume; + FActiveVfo := FLoadedGlobal.ActiveVfo; + FPAMaxPower := FLoadedGlobal.PAMaxPower; + for i := 0 to CFG_BAND_COUNT - 1 do + FPABandCal[i] := FLoadedGlobal.PABandCal[i]; + for i := 0 to CFG_XVTR_COUNT - 1 do + FVHFBandCal[i] := FLoadedGlobal.VHFBandCal[i]; + // drive% (0..100) — раньше грузился через TrkDrive.Position; теперь напрямую, + // слайдер выставит rfDevice-рендер. Калиброванный байт считаем после FCurrentBand. + FDrivePercent := EnsureRange(FLoadedGlobal.DriveLevel, 0, 100); + FCurrentBand := FLoadedGlobal.LastBand; + FDriveLevel := CalcDriveByte; + // SampleRate — глобальный, до RestoreBand + if FLoadedGlobal.SampleRate > 0 then + begin + FSampleRate := FLoadedGlobal.SampleRate; + FSpanHz := FSampleRate; + end; + FNRMode := EnsureRange(FLoadedGlobal.NRMode, 0, 4); + FNBMode := EnsureRange(FLoadedGlobal.NBMode, 0, 2); + FSNB := FLoadedGlobal.SNBEnabled; + FANF := FLoadedGlobal.ANFEnabled; + FWfAGCEnabled := FLoadedGlobal.WfAGCEnabled; + FWfNFEnabled := FLoadedGlobal.WfNFEnabled; + FDitherEnabled := FLoadedGlobal.DitherEnabled; + FRandomEnabled := FLoadedGlobal.RandomEnabled; + FSendAudioToRadio := FLoadedGlobal.SendAudioToRadio; + FWfManualHigh := FLoadedGlobal.WfManualHigh; + FWfManualLow := FLoadedGlobal.WfManualLow; + FWfAGCOffset := FLoadedGlobal.WfAGCOffset; + FShowSpectrum := FLoadedGlobal.ShowSpectrum; + FShowWaterfall := FLoadedGlobal.ShowWaterfall; + FShowWideband := FLoadedGlobal.ShowWideband; + FWidebandFill := FLoadedGlobal.WidebandFill; + FSpectrumFill := FLoadedGlobal.SpectrumFill; + FDisplayDuplex := FLoadedGlobal.DisplayDuplex; + // Настройки дисплея/сетки + DSP display + аудио-устройства + if FLoadedGlobal.FFTSize > 0 then + begin + FSpecRefLevel := FLoadedGlobal.SpecRefLevel; + FSpecRange := FLoadedGlobal.SpecRange; + if FLoadedGlobal.SpecGridStep > 0 then FSpecGridStep := FLoadedGlobal.SpecGridStep; + if FWDSPReady then + begin + FDSPEngine.SetFFTParams(FLoadedGlobal.FFTSize, FLoadedGlobal.WindowType); + FDSPEngine.SetSpectrumDisplay(FLoadedGlobal.SpecDetector, FLoadedGlobal.SpecAvgMode, + FLoadedGlobal.SpecAvgTimeMS); + FDSPEngine.SetWaterfallDisplay(FLoadedGlobal.WfDetector, FLoadedGlobal.WfAvgMode, + FLoadedGlobal.WfAvgTimeMS); + end; + // Аудио устройство вывода + FAudioOut.Close; + if FLoadedGlobal.AudioSampleRate > 0 then + FAudioOut.SampleRate := FLoadedGlobal.AudioSampleRate; + if FLoadedGlobal.AudioOutDevice <> '' then + begin + FAudioOut.DeviceIndex := FAudioOut.FindDeviceByName(FLoadedGlobal.AudioOutDevice); + FAudioOutDevName := FLoadedGlobal.AudioOutDevice; + end + else + begin + FAudioOut.DeviceIndex := -1; + FAudioOutDevName := ''; + end; + FAudioOut.Open; + // Аудио устройство ввода (TX mic). MicSource определяется в SetMOX по PTT — + // здесь только открываем устройство. + if FLoadedGlobal.AudioInDevice <> '' then + begin + FAudioIn.DeviceIndex := FAudioIn.FindDeviceByName(FLoadedGlobal.AudioInDevice); + FAudioInDevName := FLoadedGlobal.AudioInDevice; + FAudioIn.Open; + end + else + begin + FAudioIn.DeviceIndex := -1; + FAudioInDevName := ''; + end; + end; + + Changed(rfDevice); // UI: полная синхронизация device-виджетов из состояния +end; procedure TRadioController.Discover; begin { TODO wiring: FNetwork.Discover } end;