From 9716b88b0c97a5147618abec920f230cee6e7bcb Mon Sep 17 00:00:00 2001 From: Uladzimir Karpenka Date: Tue, 26 May 2026 19:15:51 +0300 Subject: [PATCH] Extract helpers into BoardUtils, WisdomBuilder, UISync, PlatformUtils - BoardUtils: BoardTypeName (removes duplication with DeviceForm) - WisdomBuilder: TWisdomBuildThread + TWisdomProgressDialog - UISync: TDeviceFoundSync/TStatusUISync/TDDCSeqSync via callbacks (avoids circular dependency, removes TObject casts) - PlatformUtils: HasOpenGLSpectrumSwitch + CurrentScreenDPI Co-Authored-By: Claude Sonnet 4.6 --- BoardUtils.pas | 29 ++++++ DeviceForm.pas | 20 +--- MainForm.pas | 256 +--------------------------------------------- PlatformUtils.pas | 24 ++++- UISync.pas | 105 +++++++++++++++++++ WisdomBuilder.pas | 129 +++++++++++++++++++++++ ewsdr.lpi | 12 +++ 7 files changed, 305 insertions(+), 270 deletions(-) create mode 100644 BoardUtils.pas create mode 100644 UISync.pas create mode 100644 WisdomBuilder.pas diff --git a/BoardUtils.pas b/BoardUtils.pas new file mode 100644 index 0000000..f219c55 --- /dev/null +++ b/BoardUtils.pas @@ -0,0 +1,29 @@ +unit BoardUtils; + +{$IFDEF FPC} + {$MODE Delphi} +{$ENDIF} + +interface + +function BoardTypeName(BoardType: Integer): string; + +implementation + +uses SysUtils; + +function BoardTypeName(BoardType: Integer): string; +begin + case BoardType of + 1: Result := 'HERMES (ANAN-10/100)'; + 2: Result := 'HERMES-E (ANAN-10E/100B)'; + 3: Result := 'ANGELIA (ANAN-100D)'; + 4: Result := 'ORION (ANAN-200D)'; + 5: Result := 'ORION MkII (ANAN-7000/8000)'; + 6: Result := 'HERMES-LITE 2'; + 10: Result := 'SATURN (G2)'; + else Result := Format('Unknown Board #%d', [BoardType]); + end; +end; + +end. diff --git a/DeviceForm.pas b/DeviceForm.pas index a99d9d2..96a03c8 100644 --- a/DeviceForm.pas +++ b/DeviceForm.pas @@ -6,10 +6,8 @@ interface uses Classes, SysUtils, FlatButton, FlatEdit, FlatListBox, AppTheme, Forms, Controls, Graphics, Dialogs, - StdCtrls, ExtCtrls, ComCtrls, IniFiles; - -// Декодирование типа платы (совпадает с MainForm.BoardTypeName) -function BoardTypeName(BoardType: Integer): string; + StdCtrls, ExtCtrls, ComCtrls, IniFiles, + BoardUtils; const DEVICE_CFG_FILE = 'hpsdr_devices.ini'; @@ -114,20 +112,6 @@ type implementation -function BoardTypeName(BoardType: Integer): string; -begin - case BoardType of - 1: Result := 'HERMES (ANAN-10/100)'; - 2: Result := 'HERMES-E (ANAN-10E/100B)'; - 3: Result := 'ANGELIA (ANAN-100D)'; - 4: Result := 'ORION (ANAN-200D)'; - 5: Result := 'ORION MkII (ANAN-7000/8000)'; - 6: Result := 'HERMES-LITE 2'; - 10: Result := 'SATURN (G2)'; - else Result := Format('Unknown Board #%d', [BoardType]); - end; -end; - const CLR_BG = TColor($00121212); CLR_PANEL = TColor($001A1A1A); diff --git a/MainForm.pas b/MainForm.pas index c69c4d4..9872db9 100644 --- a/MainForm.pas +++ b/MainForm.pas @@ -26,7 +26,6 @@ uses FlatButton, FlatSlider, FlatDropDown, AppTheme, Forms, Controls, Graphics, Dialogs, StdCtrls, ExtCtrls, Buttons, Menus, Math, Types, OpenGLContext, - FlatProgressBar, LCLIntf, LCLType, GraphType, HPSDRProtocol, HPSDRNetwork, WDSP, WDSPEngine, AudioOutput, AudioInput, @@ -37,7 +36,8 @@ uses WidebandView, StatusBar, PlatformUtils, - WinFirewall; + WinFirewall, + BoardUtils, WisdomBuilder, UISync; const CLR_BG = TColor($00101010); @@ -138,45 +138,6 @@ type Display: string; end; - // Синхронизирующий объект для OnDeviceFound - TDeviceFoundSync = class - private - FForm: TObject; // TMainForm, через forward ref - FDev: THPSDRDevice; - FEntry: string; - public - constructor Create(AForm: TObject; const D: THPSDRDevice; const E: string); - procedure Execute; - end; - - // Синхронизирующий объект для HP Status - TStatusUISync = class - private - FForm: TObject; - FFwdW: Double; - FSWRV: Double; - FSupplyV: Double; - FSupplyA: Double; - FPLLLock: Boolean; - FHWPTT: Boolean; - FADCOverload: Byte; - public - constructor Create(AForm: TObject; FW, SW, SV, SA: Double; PLL, HWPTT: Boolean; - ADCOverload: Byte); - procedure Execute; - end; - - // Синхронизирующий объект для DDC IQ - TDDCSeqSync = class - private - FForm: TObject; - FDDCIdx: Integer; - FSeq: LongWord; - public - constructor Create(AForm: TObject; Idx: Integer; Seq: LongWord); - procedure Execute; - end; - { TMainForm } TMainForm = class(TForm) private @@ -750,30 +711,6 @@ type var Handled: Boolean); end; - TWisdomBuildThread = class(TThread) - private - FDirectory: string; - FError: string; - protected - procedure Execute; override; - public - constructor Create(const ADirectory: string); - property ErrorText: string read FError; - end; - - TWisdomProgressDialog = class(TForm) - private - FInfoLabel: TLabel; - FProgress: TFlatProgressBar; - FTimer: TTimer; - FThread: TWisdomBuildThread; - FPhase: Integer; - procedure TimerTick(Sender: TObject); - public - constructor Create(AOwner: TComponent; AThread: TWisdomBuildThread; - const ATheme: TAppTheme); reintroduce; - end; - var MainForm: TMainForm; @@ -783,20 +720,6 @@ uses SettingsForm; {$R *.lfm} -function HasOpenGLSpectrumSwitch: Boolean; -var - I: Integer; - S: string; -begin - Result := False; - for I := 1 to ParamCount do - begin - S := LowerCase(ParamStr(I)); - if (S = '--opengl') or (S = '-opengl') or (S = '/opengl') then - Exit(True); - end; -end; - function TMainForm.LeftPanelButtonWidth(PanelWidth, ColCount, ColIndex: Integer): Integer; var @@ -822,179 +745,10 @@ begin LEFT_PANEL_BTN_GAP); end; -constructor TWisdomBuildThread.Create(const ADirectory: string); -begin - inherited Create(False); - FreeOnTerminate := False; - FDirectory := ADirectory; - FError := ''; -end; - -procedure TWisdomBuildThread.Execute; -var - DirA: AnsiString; -begin - try - if not Assigned(@WDSPwisdom) then Exit; - DirA := AnsiString(FDirectory); - WDSPwisdom(PAnsiChar(DirA)); - except - on E: Exception do - FError := E.ClassName + ': ' + E.Message; - end; -end; - -constructor TWisdomProgressDialog.Create(AOwner: TComponent; - AThread: TWisdomBuildThread; const ATheme: TAppTheme); -begin - inherited CreateNew(AOwner); - FThread := AThread; - FPhase := 0; - - Caption := 'WDSP Wisdom'; - Width := 520; - Height := 140; - Position := poScreenCenter; - BorderStyle := bsDialog; - BorderIcons := []; - Color := ATheme.BG; - Font.Name := 'Courier New'; - Font.Size := 9; - Font.Color := ATheme.Text; - - FInfoLabel := TLabel.Create(Self); - FInfoLabel.Parent := Self; - FInfoLabel.SetBounds(16, 16, 480, 40); - FInfoLabel.AutoSize := False; - FInfoLabel.WordWrap := True; - FInfoLabel.Font.Color := ATheme.Text; - FInfoLabel.Caption := - 'Creating FFTW wisdom for WDSP. This is done once and may take a while on the first run.'; - - FProgress := TFlatProgressBar.Create(Self); - FProgress.Parent := Self; - FProgress.SetBounds(16, 72, 480, 22); - FProgress.Min := 0; - FProgress.Max := 100; - FProgress.Position := 0; - FProgress.SetAppTheme(ATheme); - - FTimer := TTimer.Create(Self); - FTimer.Interval := 250; - FTimer.OnTimer := TimerTick; - FTimer.Enabled := True; -end; - -procedure TWisdomProgressDialog.TimerTick(Sender: TObject); -var - P: PAnsiChar; - S: string; -begin - FPhase := (FPhase + 7) mod 101; - FProgress.Position := FPhase; - - if Assigned(@wisdom_get_status) then - begin - P := wisdom_get_status; - if P <> nil then - begin - S := Trim(string(AnsiString(P))); - if S <> '' then - FInfoLabel.Caption := S; - end; - end; - - if Assigned(FThread) and FThread.Finished then - begin - FTimer.Enabled := False; - ModalResult := mrOk; - end; -end; - -// =========================================================================== -// Общая функция декодирования типа платы — используется везде -// =========================================================================== - -function BoardTypeName(BoardType: Integer): string; -begin - case BoardType of - 1: Result := 'HERMES (ANAN-10/100)'; - 2: Result := 'HERMES-E (ANAN-10E/100B)'; - 3: Result := 'ANGELIA (ANAN-100D)'; - 4: Result := 'ORION (ANAN-200D)'; - 5: Result := 'ORION MkII (ANAN-7000/8000)'; - 6: Result := 'HERMES-LITE 2'; - 10: Result := 'SATURN (G2)'; - else Result := Format('Unknown Board #%d', [BoardType]); - end; -end; - -// =========================================================================== -// Sync helpers -// =========================================================================== - -constructor TDeviceFoundSync.Create(AForm: TObject; - const D: THPSDRDevice; const E: string); -begin - inherited Create; - FForm := AForm; - FDev := D; - FEntry := E; -end; - -procedure TDeviceFoundSync.Execute; -begin - TMainForm(FForm).DoAddDevice(FDev, FEntry); -end; - -constructor TStatusUISync.Create(AForm: TObject; - FW, SW, SV, SA: Double; PLL, HWPTT: Boolean; ADCOverload: Byte); -begin - inherited Create; - FForm := AForm; - FFwdW := FW; - FSWRV := SW; - FSupplyV := SV; - FSupplyA := SA; - FPLLLock := PLL; - FHWPTT := HWPTT; - FADCOverload := ADCOverload; -end; - -procedure TStatusUISync.Execute; -begin - TMainForm(FForm).DoUpdateStatus(FFwdW, FSWRV, FSupplyV, FSupplyA, FPLLLock, FHWPTT, - FADCOverload); -end; - -constructor TDDCSeqSync.Create(AForm: TObject; Idx: Integer; Seq: LongWord); -begin - inherited Create; - FForm := AForm; - FDDCIdx := Idx; - FSeq := Seq; -end; - -procedure TDDCSeqSync.Execute; -begin - TMainForm(FForm).DoUpdateDDCSeqOrNoDevice(FDDCIdx, FSeq); -end; - -// =========================================================================== -// FormCreate / FormDestroy -// =========================================================================== - - // =========================================================================== // Settings helpers // =========================================================================== -function CurrentScreenDPI: Integer; -begin - Result := Screen.PixelsPerInch; - if Result <= 0 then Result := 96; -end; - procedure TMainForm.RestoreWindowBounds; var L, T, Wd, Ht, SavedDPI, CurDPI: Integer; @@ -3394,7 +3148,7 @@ begin Entry := Format('%s %s FW:%d DDC:%d', [Dev.IPAddress, BoardName, Dev.FirmwareVersion, Dev.NumDDCs]); - Sync := TDeviceFoundSync.Create(Self, Dev, Entry); + Sync := TDeviceFoundSync.Create(DoAddDevice, Dev, Entry); try M := Sync.Execute; TThread.Synchronize(nil, M); @@ -3483,7 +3237,7 @@ begin end else SWRV := 1.0; if not IsTx then FwdW := 0; - Sync := TStatusUISync.Create(Self, FwdW, SWRV, SupplyV, SupplyA, + Sync := TStatusUISync.Create(DoUpdateStatus, FwdW, SWRV, SupplyV, SupplyA, (Status.StatusBits and HPS_PLL_LOCKED) <> 0, (Status.StatusBits and HPS_PTT) <> 0, Status.ADCOverload); @@ -3886,7 +3640,7 @@ begin if Length(Devs) = 0 then begin // DDCIdx = -1 is the sentinel for "no device found" - Sync := TDDCSeqSync.Create(FForm, -1, 0); + Sync := TDDCSeqSync.Create(FForm.DoUpdateDDCSeqOrNoDevice, -1, 0); try M := Sync.Execute; TThread.Synchronize(nil, M); diff --git a/PlatformUtils.pas b/PlatformUtils.pas index 0ab1404..55d955d 100644 --- a/PlatformUtils.pas +++ b/PlatformUtils.pas @@ -11,11 +11,13 @@ unit PlatformUtils; interface function QtPlatformAllowsOpenGLControls: Boolean; +function HasOpenGLSpectrumSwitch: Boolean; +function CurrentScreenDPI: Integer; implementation uses - SysUtils; + SysUtils, Forms; function QtPlatformAllowsOpenGLControls: Boolean; var @@ -39,4 +41,24 @@ begin Result := SessionType <> 'wayland'; end; +function HasOpenGLSpectrumSwitch: Boolean; +var + I: Integer; + S: string; +begin + Result := False; + for I := 1 to ParamCount do + begin + S := LowerCase(ParamStr(I)); + if (S = '--opengl') or (S = '-opengl') or (S = '/opengl') then + Exit(True); + end; +end; + +function CurrentScreenDPI: Integer; +begin + Result := Screen.PixelsPerInch; + if Result <= 0 then Result := 96; +end; + end. diff --git a/UISync.pas b/UISync.pas new file mode 100644 index 0000000..557cef7 --- /dev/null +++ b/UISync.pas @@ -0,0 +1,105 @@ +unit UISync; + +{$IFDEF FPC} + {$MODE Delphi} +{$ENDIF} + +interface + +uses + Classes, HPSDRNetwork; + +type + TAddDeviceProc = procedure(const Dev: THPSDRDevice; const Entry: string) of object; + TUpdateStatusProc = procedure(FwdW, SWRV, SupplyV, SupplyA: Double; + PLLLock, HWPTT: Boolean; ADCOverload: Byte) of object; + TUpdateDDCSeqOrNoDeviceProc = procedure(DDCIdx: Integer; Seq: LongWord) of object; + + TDeviceFoundSync = class + private + FCallback: TAddDeviceProc; + FDev: THPSDRDevice; + FEntry: string; + public + constructor Create(ACallback: TAddDeviceProc; const D: THPSDRDevice; + const E: string); + procedure Execute; + end; + + TStatusUISync = class + private + FCallback: TUpdateStatusProc; + FFwdW: Double; + FSWRV: Double; + FSupplyV: Double; + FSupplyA: Double; + FPLLLock: Boolean; + FHWPTT: Boolean; + FADCOverload: Byte; + public + constructor Create(ACallback: TUpdateStatusProc; + FW, SW, SV, SA: Double; PLL, HWPTT: Boolean; ADCOverload: Byte); + procedure Execute; + end; + + TDDCSeqSync = class + private + FCallback: TUpdateDDCSeqOrNoDeviceProc; + FDDCIdx: Integer; + FSeq: LongWord; + public + constructor Create(ACallback: TUpdateDDCSeqOrNoDeviceProc; + Idx: Integer; Seq: LongWord); + procedure Execute; + end; + +implementation + +constructor TDeviceFoundSync.Create(ACallback: TAddDeviceProc; + const D: THPSDRDevice; const E: string); +begin + inherited Create; + FCallback := ACallback; + FDev := D; + FEntry := E; +end; + +procedure TDeviceFoundSync.Execute; +begin + FCallback(FDev, FEntry); +end; + +constructor TStatusUISync.Create(ACallback: TUpdateStatusProc; + FW, SW, SV, SA: Double; PLL, HWPTT: Boolean; ADCOverload: Byte); +begin + inherited Create; + FCallback := ACallback; + FFwdW := FW; + FSWRV := SW; + FSupplyV := SV; + FSupplyA := SA; + FPLLLock := PLL; + FHWPTT := HWPTT; + FADCOverload := ADCOverload; +end; + +procedure TStatusUISync.Execute; +begin + FCallback(FFwdW, FSWRV, FSupplyV, FSupplyA, FPLLLock, FHWPTT, FADCOverload); +end; + +constructor TDDCSeqSync.Create(ACallback: TUpdateDDCSeqOrNoDeviceProc; + Idx: Integer; Seq: LongWord); +begin + inherited Create; + FCallback := ACallback; + FDDCIdx := Idx; + FSeq := Seq; +end; + +procedure TDDCSeqSync.Execute; +begin + FCallback(FDDCIdx, FSeq); +end; + +end. diff --git a/WisdomBuilder.pas b/WisdomBuilder.pas new file mode 100644 index 0000000..906cf75 --- /dev/null +++ b/WisdomBuilder.pas @@ -0,0 +1,129 @@ +unit WisdomBuilder; + +{$IFDEF FPC} + {$MODE Delphi} +{$ENDIF} + +interface + +uses + Classes, SysUtils, Forms, Controls, ExtCtrls, StdCtrls, + WDSP, AppTheme, FlatProgressBar; + +type + TWisdomBuildThread = class(TThread) + private + FDirectory: string; + FError: string; + protected + procedure Execute; override; + public + constructor Create(const ADirectory: string); + property ErrorText: string read FError; + end; + + TWisdomProgressDialog = class(TForm) + private + FInfoLabel: TLabel; + FProgress: TFlatProgressBar; + FTimer: TTimer; + FThread: TWisdomBuildThread; + FPhase: Integer; + procedure TimerTick(Sender: TObject); + public + constructor Create(AOwner: TComponent; AThread: TWisdomBuildThread; + const ATheme: TAppTheme); reintroduce; + end; + +implementation + +constructor TWisdomBuildThread.Create(const ADirectory: string); +begin + inherited Create(False); + FreeOnTerminate := False; + FDirectory := ADirectory; + FError := ''; +end; + +procedure TWisdomBuildThread.Execute; +var + DirA: AnsiString; +begin + try + if not Assigned(@WDSPwisdom) then Exit; + DirA := AnsiString(FDirectory); + WDSPwisdom(PAnsiChar(DirA)); + except + on E: Exception do + FError := E.ClassName + ': ' + E.Message; + end; +end; + +constructor TWisdomProgressDialog.Create(AOwner: TComponent; + AThread: TWisdomBuildThread; const ATheme: TAppTheme); +begin + inherited CreateNew(AOwner); + FThread := AThread; + FPhase := 0; + + Caption := 'WDSP Wisdom'; + Width := 520; + Height := 140; + Position := poScreenCenter; + BorderStyle := bsDialog; + BorderIcons := []; + Color := ATheme.BG; + Font.Name := 'Courier New'; + Font.Size := 9; + Font.Color := ATheme.Text; + + FInfoLabel := TLabel.Create(Self); + FInfoLabel.Parent := Self; + FInfoLabel.SetBounds(16, 16, 480, 40); + FInfoLabel.AutoSize := False; + FInfoLabel.WordWrap := True; + FInfoLabel.Font.Color := ATheme.Text; + FInfoLabel.Caption := + 'Creating FFTW wisdom for WDSP. This is done once and may take a while on the first run.'; + + FProgress := TFlatProgressBar.Create(Self); + FProgress.Parent := Self; + FProgress.SetBounds(16, 72, 480, 22); + FProgress.Min := 0; + FProgress.Max := 100; + FProgress.Position := 0; + FProgress.SetAppTheme(ATheme); + + FTimer := TTimer.Create(Self); + FTimer.Interval := 250; + FTimer.OnTimer := TimerTick; + FTimer.Enabled := True; +end; + +procedure TWisdomProgressDialog.TimerTick(Sender: TObject); +var + P: PAnsiChar; + S: string; +begin + FPhase := (FPhase + 7) mod 101; + FProgress.Position := FPhase; + + if Assigned(@wisdom_get_status) then + begin + P := wisdom_get_status; + if P <> nil then + begin + S := Trim(string(AnsiString(P))); + if S <> '' then + FInfoLabel.Caption := S; + end; + end; + + if Assigned(FThread) and FThread.Finished then + begin + FTimer.Enabled := False; + ModalResult := mrOk; + end; +end; + +end. diff --git a/ewsdr.lpi b/ewsdr.lpi index 55ed8f9..551070f 100644 --- a/ewsdr.lpi +++ b/ewsdr.lpi @@ -214,6 +214,18 @@ + + + + + + + + + + + +