Phase 3 (batch 31a): shared TDeviceStore for saved+discovered devices

Introduce DeviceStore.pas (TDeviceStore) as the single source of truth for
saved devices (hpsdr_devices.ini CRUD + autostart) and the current discovery
list. The controller owns one instance (created/freed in its ctor/dtor);
desktop and (later) web frontends edit/render through it so the lists never
diverge.

Refactor TDeviceDialog to use a TDeviceStore reference instead of its own
arrays + ini code. MainForm wires FDeviceDialog.Store to the controller's
store, routes discovery results (DoAddDevice) and the preload-rate lookup
through it (TDiscoveredDevice now carries the MAC), and drops the now-unused
FDevices/TDeviceItem. Behavior-preserving for the desktop dialog.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Uladzimir Karpenka
2026-06-08 12:36:43 +03:00
co-authored by Claude Opus 4.8
parent 783c8976c9
commit 7f7f429515
4 changed files with 368 additions and 212 deletions
+65 -175
View File
@@ -6,21 +6,10 @@ interface
uses
Classes, SysUtils, FlatButton, FlatEdit, FlatListBox, AppTheme, Forms, Controls, Graphics, Dialogs,
StdCtrls, ExtCtrls, ComCtrls, IniFiles,
BoardUtils, PlatformUtils;
const
DEVICE_CFG_NAME = 'hpsdr_devices.ini';
StdCtrls, ExtCtrls, ComCtrls,
BoardUtils, PlatformUtils, DeviceStore;
type
// Запись о сохранённом устройстве
TSavedDevice = record
Name: string; // пользовательское имя
IPAddress: string;
BoardType: Integer;
AutoStart: Boolean; // запускать автоматически при старте
end;
// Результат диалога
TDeviceDialogResult = record
Accepted: Boolean;
@@ -31,17 +20,11 @@ type
{ TDeviceDialog }
TDeviceDialog = class(TForm)
private
// Сохранённые устройства
FSavedDevices: array of TSavedDevice;
FSavedCount: Integer;
// Общее хранилище устройств (saved+discovered) — единый источник правды,
// владелец контроллер; диалог только рендерит и редактирует через него.
FStore: TDeviceStore;
FResult: TDeviceDialogResult;
// Discovered devices (IP strings)
FDiscoveredIPs: array of string;
FDiscoveredNames: array of string;
FDiscoveredBoardTypes: array of Integer;
FDiscoveredCount: Integer;
// UI
PanelTop: TPanel;
PanelBottom: TPanel;
@@ -70,8 +53,7 @@ type
procedure BuildUI;
procedure ApplyTheme;
procedure LoadSaved;
procedure SaveSaved;
procedure SetStore(AStore: TDeviceStore);
procedure RefreshSavedList;
procedure BtnDiscoverClick(Sender: TObject);
@@ -95,8 +77,10 @@ type
procedure SetTheme(const T: TAppTheme);
// Добавить найденное устройство (вызывается из MainForm при discovery)
procedure AddDiscovered(const IP, DisplayName: string; BoardType: Integer = 0);
procedure AddDiscovered(const IP, DisplayName: string; BoardType: Integer;
const MAC: array of Byte);
procedure ClearDiscovered;
procedure NoDevicesFound; // UI-сообщение «нет устройств» в список найденных
// Автозапуск: возвращает IP если есть устройство с AutoStart=True
function GetAutoStartIP: string;
@@ -105,9 +89,11 @@ type
// Получить/сбросить результат
procedure ClearResult;
// Общее хранилище устройств — назначается владельцем (MainForm/контроллер)
// до показа диалога; диалог рендерит и редактирует через него.
property Store: TDeviceStore read FStore write SetStore;
property DialogResult: TDeviceDialogResult read FResult;
property OnDiscover: TNotifyEvent read FOnDiscover write FOnDiscover;
property SavedCount: Integer read FSavedCount;
end;
implementation
@@ -145,14 +131,17 @@ begin
Font.Size := 8;
Font.Color := CLR_TEXT;
FSavedCount := 0;
FDiscoveredCount := 0;
FStore := nil; // назначается владельцем через property Store до показа
FResult.Accepted := False;
BuildUI;
SetTheme(DarkTheme);
LoadSaved;
RefreshSavedList;
end;
procedure TDeviceDialog.SetStore(AStore: TDeviceStore);
begin
FStore := AStore;
if FStore <> nil then RefreshSavedList;
end;
function TDeviceDialog.MakeBtn(AParent: TWinControl; const Cap: string;
@@ -348,80 +337,26 @@ begin
Invalidate;
end;
procedure TDeviceDialog.LoadSaved;
var
Ini: TIniFile;
I, N: Integer;
Section: string;
begin
FSavedCount := 0;
if not FileExists(GetAppCfgDir + DEVICE_CFG_NAME) then Exit;
Ini := TIniFile.Create(GetAppCfgDir + DEVICE_CFG_NAME);
try
N := Ini.ReadInteger('Devices', 'Count', 0);
SetLength(FSavedDevices, N);
for I := 0 to N - 1 do
begin
Section := 'Device' + IntToStr(I);
FSavedDevices[I].Name := Ini.ReadString (Section, 'Name', 'HPSDR');
FSavedDevices[I].IPAddress := Ini.ReadString (Section, 'IP', '');
FSavedDevices[I].BoardType := Ini.ReadInteger(Section, 'BoardType', 0);
FSavedDevices[I].AutoStart := Ini.ReadBool (Section, 'AutoStart', False);
Inc(FSavedCount);
end;
finally
Ini.Free;
end;
end;
procedure TDeviceDialog.SaveSaved;
var
Ini: TIniFile;
I: Integer;
Section: string;
begin
Ini := TIniFile.Create(GetAppCfgDir + DEVICE_CFG_NAME);
try
Ini.WriteInteger('Devices', 'Count', FSavedCount);
for I := 0 to FSavedCount - 1 do
begin
Section := 'Device' + IntToStr(I);
Ini.WriteString (Section, 'Name', FSavedDevices[I].Name);
Ini.WriteString (Section, 'IP', FSavedDevices[I].IPAddress);
Ini.WriteInteger(Section, 'BoardType', FSavedDevices[I].BoardType);
Ini.WriteBool (Section, 'AutoStart', FSavedDevices[I].AutoStart);
end;
finally
Ini.Free;
end;
end;
procedure TDeviceDialog.RefreshSavedList;
var
I: Integer;
S: string;
begin
LstSaved.Items.Clear;
for I := 0 to FSavedCount - 1 do
begin
S := FSavedDevices[I].Name + ' [' + FSavedDevices[I].IPAddress + ']';
if FSavedDevices[I].BoardType > 0 then
S := S + ' ' + BoardTypeName(FSavedDevices[I].BoardType);
if FSavedDevices[I].AutoStart then
S := '* ' + S;
LstSaved.Items.Add(S);
end;
if FStore = nil then Exit;
for I := 0 to FStore.SavedCount - 1 do
LstSaved.Items.Add(FStore.SavedDisplay(I));
end;
procedure TDeviceDialog.LstSavedClick(Sender: TObject);
var
Idx: Integer;
Dev: TSavedDevice;
begin
Idx := LstSaved.ItemIndex;
if (Idx < 0) or (Idx >= FSavedCount) then Exit;
EdName.Text := FSavedDevices[Idx].Name;
EdIP.Text := FSavedDevices[Idx].IPAddress;
if (FStore = nil) or (Idx < 0) or (Idx >= FStore.SavedCount) then Exit;
Dev := FStore.Saved(Idx);
EdName.Text := Dev.Name;
EdIP.Text := Dev.IPAddress;
end;
procedure TDeviceDialog.LstSavedDblClick(Sender: TObject);
@@ -444,29 +379,20 @@ end;
procedure TDeviceDialog.ClearDiscovered;
begin
FDiscoveredCount := 0;
SetLength(FDiscoveredIPs, 0);
SetLength(FDiscoveredNames, 0);
SetLength(FDiscoveredBoardTypes, 0);
if FStore <> nil then FStore.ClearDiscovered;
LstFound.Items.Clear;
end;
procedure TDeviceDialog.AddDiscovered(const IP, DisplayName: string; BoardType: Integer = 0);
procedure TDeviceDialog.AddDiscovered(const IP, DisplayName: string; BoardType: Integer;
const MAC: array of Byte);
var
Idx: Integer;
S: string;
S: string;
begin
if FStore = nil then Exit;
if (LstFound.Items.Count = 1) and (LstFound.Items[0] = 'Searching...') then
LstFound.Items.Clear;
Idx := FDiscoveredCount;
Inc(FDiscoveredCount);
SetLength(FDiscoveredIPs, FDiscoveredCount);
SetLength(FDiscoveredNames, FDiscoveredCount);
SetLength(FDiscoveredBoardTypes, FDiscoveredCount);
FDiscoveredIPs[Idx] := IP;
FDiscoveredNames[Idx] := DisplayName;
FDiscoveredBoardTypes[Idx] := BoardType;
FStore.AddDiscovered(IP, DisplayName, BoardType, MAC);
S := DisplayName;
if BoardType > 0 then
@@ -474,44 +400,34 @@ begin
LstFound.Items.Add(S);
end;
procedure TDeviceDialog.NoDevicesFound;
begin
ClearDiscovered;
LstFound.Items.Add('-- no device found --');
end;
procedure TDeviceDialog.BtnAddClick(Sender: TObject);
var
Idx: Integer;
begin
if FStore = nil then Exit;
if Trim(EdIP.Text) = '' then
begin
ShowMessage('Enter IP address');
Exit;
end;
Idx := FSavedCount;
Inc(FSavedCount);
SetLength(FSavedDevices, FSavedCount);
FSavedDevices[Idx].Name := Trim(EdName.Text);
if FSavedDevices[Idx].Name = '' then
FSavedDevices[Idx].Name := 'HPSDR';
FSavedDevices[Idx].IPAddress := Trim(EdIP.Text);
FSavedDevices[Idx].BoardType := 0;
FSavedDevices[Idx].AutoStart := False;
SaveSaved;
Idx := FStore.AddSaved(EdName.Text, EdIP.Text, 0);
RefreshSavedList;
LstSaved.ItemIndex := Idx;
end;
procedure TDeviceDialog.BtnRemoveClick(Sender: TObject);
var
Idx, I: Integer;
Idx: Integer;
begin
Idx := LstSaved.ItemIndex;
if (Idx < 0) or (Idx >= FSavedCount) then Exit;
for I := Idx to FSavedCount - 2 do
FSavedDevices[I] := FSavedDevices[I + 1];
Dec(FSavedCount);
SetLength(FSavedDevices, FSavedCount);
SaveSaved;
if (FStore = nil) or (Idx < 0) or (Idx >= FStore.SavedCount) then Exit;
FStore.RemoveSaved(Idx);
RefreshSavedList;
EdName.Text := '';
EdIP.Text := '';
@@ -519,45 +435,34 @@ end;
procedure TDeviceDialog.BtnSetAutoClick(Sender: TObject);
var
Idx, I: Integer;
Idx: Integer;
begin
Idx := LstSaved.ItemIndex;
if (Idx < 0) or (Idx >= FSavedCount) then
if (FStore = nil) or (Idx < 0) or (Idx >= FStore.SavedCount) then
begin
ShowMessage('Select a device first');
Exit;
end;
// Только одно устройство может быть AutoStart
for I := 0 to FSavedCount - 1 do
FSavedDevices[I].AutoStart := (I = Idx);
SaveSaved;
FStore.SetAutoStart(Idx);
RefreshSavedList;
LstSaved.ItemIndex := Idx;
end;
procedure TDeviceDialog.BtnAddFoundClick(Sender: TObject);
var
Idx: Integer;
Idx, NewIdx: Integer;
D: TDiscoveredDevice;
begin
Idx := LstFound.ItemIndex;
if (Idx < 0) or (Idx >= FDiscoveredCount) then
if (FStore = nil) or (Idx < 0) or (Idx >= FStore.DiscoveredCount) then
begin
ShowMessage('Select a discovered device first');
Exit;
end;
EdIP.Text := FDiscoveredIPs[Idx];
EdName.Text := FDiscoveredNames[Idx];
BtnAddClick(nil);
// Обновляем BoardType только что добавленной записи
if FSavedCount > 0 then
begin
FSavedDevices[FSavedCount - 1].BoardType := FDiscoveredBoardTypes[Idx];
SaveSaved;
RefreshSavedList;
LstSaved.ItemIndex := FSavedCount - 1;
end;
D := FStore.Discovered(Idx);
NewIdx := FStore.AddSaved(D.DisplayName, D.IPAddress, D.BoardType);
RefreshSavedList;
LstSaved.ItemIndex := NewIdx;
end;
procedure TDeviceDialog.BtnConnectClick(Sender: TObject);
@@ -566,20 +471,21 @@ var
Idx: Integer;
begin
IP := '';
if FStore = nil then Exit;
// Приоритет: выбранное сохранённое > выбранное найденное > ручной IP
Idx := LstSaved.ItemIndex;
if (Idx >= 0) and (Idx < FSavedCount) then
if (Idx >= 0) and (Idx < FStore.SavedCount) then
begin
IP := FSavedDevices[Idx].IPAddress;
IP := FStore.Saved(Idx).IPAddress;
FResult.SavedIdx := Idx;
end
else
begin
Idx := LstFound.ItemIndex;
if (Idx >= 0) and (Idx < FDiscoveredCount) then
if (Idx >= 0) and (Idx < FStore.DiscoveredCount) then
begin
IP := FDiscoveredIPs[Idx];
IP := FStore.Discovered(Idx).IPAddress;
FResult.SavedIdx := -1;
end
else if Trim(EdIP.Text) <> '' then
@@ -614,37 +520,21 @@ begin
end;
function TDeviceDialog.GetAutoStartIP: string;
var
I: Integer;
begin
Result := '';
for I := 0 to FSavedCount - 1 do
if FSavedDevices[I].AutoStart then
begin
Result := FSavedDevices[I].IPAddress;
Exit;
end;
if FStore <> nil then Result := FStore.AutoStartIP
else Result := '';
end;
function TDeviceDialog.GetAutoStartBoardType: Integer;
var
I: Integer;
begin
Result := 0;
for I := 0 to FSavedCount - 1 do
if FSavedDevices[I].AutoStart then
begin
Result := FSavedDevices[I].BoardType;
Exit;
end;
if FStore <> nil then Result := FStore.AutoStartBoardType
else Result := 0;
end;
function TDeviceDialog.GetSavedBoardType(Idx: Integer): Integer;
begin
if (Idx >= 0) and (Idx < FSavedCount) then
Result := FSavedDevices[Idx].BoardType
else
Result := 0;
if FStore <> nil then Result := FStore.SavedBoardType(Idx)
else Result := 0;
end;
end.
+279
View File
@@ -0,0 +1,279 @@
unit DeviceStore;
{$mode objfpc}{$H+}
// Общее хранилище устройств — единый источник правды для десктоп-диалога
// (DeviceForm) и web-оверлея. Владеет сохранёнными устройствами (persist в
// hpsdr_devices.ini) и текущим списком найденных при discovery.
// Контроллер (TRadioController) держит один экземпляр; фронтенды работают
// через него, поэтому списки не расходятся.
interface
uses
Classes, SysUtils, IniFiles, BoardUtils, PlatformUtils;
const
DEVICE_CFG_NAME = 'hpsdr_devices.ini';
type
// Запись о сохранённом устройстве
TSavedDevice = record
Name: string; // пользовательское имя
IPAddress: string;
BoardType: Integer;
AutoStart: Boolean; // запускать автоматически при старте
end;
// Найденное устройство (результат discovery)
TDiscoveredDevice = record
IPAddress: string;
DisplayName: string;
BoardType: Integer;
MAC: array[0..5] of Byte; // нужен для preload-rate lookup (LoadDevice по MAC)
end;
{ TDeviceStore }
TDeviceStore = class
private
FSaved: array of TSavedDevice;
FSavedCount: Integer;
FDiscovered: array of TDiscoveredDevice;
FDiscoveredCount: Integer;
public
constructor Create;
// ---- Сохранённые устройства ----
procedure LoadSaved;
procedure SaveSaved;
function SavedCount: Integer;
function Saved(Idx: Integer): TSavedDevice;
// Добавляет запись (Name пустое → 'HPSDR'); возвращает индекс новой записи.
function AddSaved(const AName, IP: string; BoardType: Integer): Integer;
procedure RemoveSaved(Idx: Integer);
procedure SetAutoStart(Idx: Integer); // эксклюзивно: только одно autostart
function AutoStartIP: string;
function AutoStartBoardType: Integer;
function SavedBoardType(Idx: Integer): Integer;
function SavedDisplay(Idx: Integer): string; // строка для списка (с * и board)
// ---- Найденные (discovery) ----
procedure ClearDiscovered;
procedure AddDiscovered(const IP, DisplayName: string; BoardType: Integer;
const MAC: array of Byte);
function DiscoveredCount: Integer;
function Discovered(Idx: Integer): TDiscoveredDevice;
// IP найденного устройства по IP (для preload-rate lookup и т.п.)
function FindDiscoveredByIP(const IP: string): Integer;
end;
implementation
{ TDeviceStore }
constructor TDeviceStore.Create;
begin
inherited Create;
FSavedCount := 0;
FDiscoveredCount := 0;
LoadSaved;
end;
procedure TDeviceStore.LoadSaved;
var
Ini: TIniFile;
I, N: Integer;
Section: string;
begin
FSavedCount := 0;
SetLength(FSaved, 0);
if not FileExists(GetAppCfgDir + DEVICE_CFG_NAME) then Exit;
Ini := TIniFile.Create(GetAppCfgDir + DEVICE_CFG_NAME);
try
N := Ini.ReadInteger('Devices', 'Count', 0);
SetLength(FSaved, N);
for I := 0 to N - 1 do
begin
Section := 'Device' + IntToStr(I);
FSaved[I].Name := Ini.ReadString (Section, 'Name', 'HPSDR');
FSaved[I].IPAddress := Ini.ReadString (Section, 'IP', '');
FSaved[I].BoardType := Ini.ReadInteger(Section, 'BoardType', 0);
FSaved[I].AutoStart := Ini.ReadBool (Section, 'AutoStart', False);
Inc(FSavedCount);
end;
finally
Ini.Free;
end;
end;
procedure TDeviceStore.SaveSaved;
var
Ini: TIniFile;
I: Integer;
Section: string;
begin
Ini := TIniFile.Create(GetAppCfgDir + DEVICE_CFG_NAME);
try
Ini.WriteInteger('Devices', 'Count', FSavedCount);
for I := 0 to FSavedCount - 1 do
begin
Section := 'Device' + IntToStr(I);
Ini.WriteString (Section, 'Name', FSaved[I].Name);
Ini.WriteString (Section, 'IP', FSaved[I].IPAddress);
Ini.WriteInteger(Section, 'BoardType', FSaved[I].BoardType);
Ini.WriteBool (Section, 'AutoStart', FSaved[I].AutoStart);
end;
finally
Ini.Free;
end;
end;
function TDeviceStore.SavedCount: Integer;
begin
Result := FSavedCount;
end;
function TDeviceStore.Saved(Idx: Integer): TSavedDevice;
begin
if (Idx >= 0) and (Idx < FSavedCount) then
Result := FSaved[Idx]
else
begin
Result.Name := ''; Result.IPAddress := '';
Result.BoardType := 0; Result.AutoStart := False;
end;
end;
function TDeviceStore.AddSaved(const AName, IP: string; BoardType: Integer): Integer;
begin
Result := FSavedCount;
Inc(FSavedCount);
SetLength(FSaved, FSavedCount);
if Trim(AName) <> '' then FSaved[Result].Name := Trim(AName)
else FSaved[Result].Name := 'HPSDR';
FSaved[Result].IPAddress := Trim(IP);
FSaved[Result].BoardType := BoardType;
FSaved[Result].AutoStart := False;
SaveSaved;
end;
procedure TDeviceStore.RemoveSaved(Idx: Integer);
var
I: Integer;
begin
if (Idx < 0) or (Idx >= FSavedCount) then Exit;
for I := Idx to FSavedCount - 2 do
FSaved[I] := FSaved[I + 1];
Dec(FSavedCount);
SetLength(FSaved, FSavedCount);
SaveSaved;
end;
procedure TDeviceStore.SetAutoStart(Idx: Integer);
var
I: Integer;
begin
if (Idx < 0) or (Idx >= FSavedCount) then Exit;
// Только одно устройство может быть AutoStart
for I := 0 to FSavedCount - 1 do
FSaved[I].AutoStart := (I = Idx);
SaveSaved;
end;
function TDeviceStore.AutoStartIP: string;
var
I: Integer;
begin
Result := '';
for I := 0 to FSavedCount - 1 do
if FSaved[I].AutoStart then
begin
Result := FSaved[I].IPAddress;
Exit;
end;
end;
function TDeviceStore.AutoStartBoardType: Integer;
var
I: Integer;
begin
Result := 0;
for I := 0 to FSavedCount - 1 do
if FSaved[I].AutoStart then
begin
Result := FSaved[I].BoardType;
Exit;
end;
end;
function TDeviceStore.SavedBoardType(Idx: Integer): Integer;
begin
if (Idx >= 0) and (Idx < FSavedCount) then
Result := FSaved[Idx].BoardType
else
Result := 0;
end;
function TDeviceStore.SavedDisplay(Idx: Integer): string;
begin
Result := '';
if (Idx < 0) or (Idx >= FSavedCount) then Exit;
Result := FSaved[Idx].Name + ' [' + FSaved[Idx].IPAddress + ']';
if FSaved[Idx].BoardType > 0 then
Result := Result + ' ' + BoardTypeName(FSaved[Idx].BoardType);
if FSaved[Idx].AutoStart then
Result := '* ' + Result;
end;
procedure TDeviceStore.ClearDiscovered;
begin
FDiscoveredCount := 0;
SetLength(FDiscovered, 0);
end;
procedure TDeviceStore.AddDiscovered(const IP, DisplayName: string; BoardType: Integer;
const MAC: array of Byte);
var
Idx, J: Integer;
begin
Idx := FDiscoveredCount;
Inc(FDiscoveredCount);
SetLength(FDiscovered, FDiscoveredCount);
FDiscovered[Idx].IPAddress := IP;
FDiscovered[Idx].DisplayName := DisplayName;
FDiscovered[Idx].BoardType := BoardType;
FillChar(FDiscovered[Idx].MAC, SizeOf(FDiscovered[Idx].MAC), 0);
for J := 0 to High(MAC) do
if J <= 5 then FDiscovered[Idx].MAC[J] := MAC[J];
end;
function TDeviceStore.DiscoveredCount: Integer;
begin
Result := FDiscoveredCount;
end;
function TDeviceStore.Discovered(Idx: Integer): TDiscoveredDevice;
begin
if (Idx >= 0) and (Idx < FDiscoveredCount) then
Result := FDiscovered[Idx]
else
begin
Result.IPAddress := ''; Result.DisplayName := ''; Result.BoardType := 0;
end;
end;
function TDeviceStore.FindDiscoveredByIP(const IP: string): Integer;
var
I: Integer;
begin
Result := -1;
for I := 0 to FDiscoveredCount - 1 do
if SameText(FDiscovered[I].IPAddress, IP) then
begin
Result := I;
Exit;
end;
end;
end.
+20 -36
View File
@@ -41,6 +41,7 @@ uses
FlatEdit, FMRepeater,
ChannelStore, ChannelsForm,
PowerInhibit,
DeviceStore,
RadioController;
const
@@ -107,11 +108,6 @@ const
LEFT_PANEL_BTN_GAP = 2;
type
TDeviceItem = record
Dev: THPSDRDevice;
Display: string;
end;
{ TMainForm }
TMainForm = class(TForm)
private
@@ -124,8 +120,7 @@ type
FSyncingFromController: Boolean;
// ---- Network ----
FDevices: array of TDeviceItem;
FDeviceCount: Integer;
// Список устройств (saved+discovered) → FController.FDeviceStore (общий с web).
FDeviceDialog: TDeviceDialog;
FPendingIP: string; // IP устройства для подключения
FVfoOverlay: TVfoOverlay; // накладка SmartSDR-стиль на спектре
@@ -1039,8 +1034,8 @@ begin
FController.FLastSupplyV:= -1;
FController.FLastSupplyA:= -1;
FController.FLastPLLLock:= False;
FDeviceCount := 0;
FDeviceDialog := TDeviceDialog.Create(Self);
FDeviceDialog.Store := FController.FDeviceStore;
FDeviceDialog.OnDiscover := BtnDiscoverFromDialog;
FVfoOverlay := TVfoOverlay.Create(Self);
FVfoOverlay.OnSelect := OnModeFilterSelect;
@@ -3285,19 +3280,12 @@ begin
end;
procedure TMainForm.DoAddDevice(const Dev: THPSDRDevice; const Entry: string);
var
Idx: Integer;
begin
Idx := Length(FDevices);
SetLength(FDevices, Idx + 1);
FDevices[Idx].Dev := Dev;
FDevices[Idx].Display := Entry;
Inc(FDeviceCount);
// Найденное устройство → общий store (через диалог, который пишет store +
// рендерит список). MAC нужен для preload-rate lookup в BtnStartStopClick.
if Assigned(FDeviceDialog) then
FDeviceDialog.AddDiscovered(Dev.IPAddress, Entry, Dev.BoardType);
SetStatusText(2, Format('Found: %s', [Dev.IPAddress]));
FDeviceDialog.AddDiscovered(Dev.IPAddress, Entry, Dev.BoardType, Dev.MAC);
SetStatusText(2, Format('Found: %s', [Dev.IPAddress]));
end;
@@ -3457,10 +3445,7 @@ begin
if DDCIdx = -1 then
begin
if Assigned(FDeviceDialog) then
begin
FDeviceDialog.ClearDiscovered;
FDeviceDialog.AddDiscovered('', '-- no device found --');
end;
FDeviceDialog.NoDevicesFound;
SetStatusText(2, 'No hardware found');
end
else
@@ -3555,6 +3540,7 @@ begin
// Открываем диалог выбора устройства
if not Assigned(FDeviceDialog) then
FDeviceDialog := TDeviceDialog.Create(Self);
FDeviceDialog.Store := FController.FDeviceStore;
FDeviceDialog.OnDiscover := BtnDiscoverFromDialog;
if FLightTheme then FDeviceDialog.SetTheme(LightTheme)
else FDeviceDialog.SetTheme(DarkTheme);
@@ -3567,8 +3553,7 @@ end;
procedure TMainForm.BtnDiscoverFromDialog(Sender: TObject);
// Запускается когда пользователь нажимает DISCOVER внутри диалога
begin
SetLength(FDevices, 0);
FDeviceCount := 0;
FController.FDeviceStore.ClearDiscovered;
SetStatusText(2, 'Discovering...');
FController.FNetwork.DirectIP := '';
TDiscoverThread.Create(FController.FNetwork, Self);
@@ -3576,10 +3561,8 @@ end;
procedure TMainForm.BtnStartStopClick(Sender: TObject);
var
Idx, i: Integer;
i: Integer;
Dev: THPSDRDevice;
GenPkt: TGeneralPacket;
G_Settings: TGlobalSettings;
PreG: TGlobalSettings;
PreBands: array[0..CFG_BAND_COUNT-1] of TBandSettings;
PreloadRate: Integer;
@@ -3641,6 +3624,7 @@ begin
begin
if not Assigned(FDeviceDialog) then
FDeviceDialog := TDeviceDialog.Create(Self);
FDeviceDialog.Store := FController.FDeviceStore;
FDeviceDialog.OnDiscover := BtnDiscoverFromDialog;
if FLightTheme then FDeviceDialog.SetTheme(LightTheme)
else FDeviceDialog.SetTheme(DarkTheme);
@@ -3664,14 +3648,14 @@ begin
// создаём WDSPEngine сразу с нужной частотой.
PreloadRate := FController.FSampleRate;
FoundByIP := False;
for i := 0 to High(FDevices) do
if SameText(FDevices[i].Dev.IPAddress, Dev.IPAddress) then
begin
FoundByIP := True;
if FController.FSettings.LoadDevice(FDevices[i].Dev.MAC, PreG, PreBands) and (PreG.SampleRate > 0) then
PreloadRate := PreG.SampleRate;
Break;
end;
i := FController.FDeviceStore.FindDiscoveredByIP(Dev.IPAddress);
if i >= 0 then
begin
FoundByIP := True;
if FController.FSettings.LoadDevice(FController.FDeviceStore.Discovered(i).MAC, PreG, PreBands)
and (PreG.SampleRate > 0) then
PreloadRate := PreG.SampleRate;
end;
if FoundByIP and (PreloadRate > 0) then
begin
FController.FSampleRate := PreloadRate;
+4 -1
View File
@@ -50,7 +50,7 @@ uses
Classes, SysUtils, Math,
HPSDRProtocol, HPSDRNetwork,
WDSPEngine, AudioOutput, AudioInput,
Settings, ChannelStore, FMRepeater, BoardUtils;
Settings, ChannelStore, FMRepeater, BoardUtils, DeviceStore;
type
// Поле, которое изменилось — для гранулярных уведомлений наружу.
@@ -128,6 +128,7 @@ type
FAudioIn: TAudioInput;
FSettings: TSettingsManager;
FChannelStore: TChannelStore;
FDeviceStore: TDeviceStore; // общий список устройств (saved+discovered)
FWDSPReady: Boolean;
// ---- Частоты / режим / фильтр / AGC ----
@@ -413,6 +414,7 @@ constructor TRadioController.Create;
begin
inherited Create;
FSettings := TSettingsManager.Create; // владелец настроек (GUI и демон)
FDeviceStore := TDeviceStore.Create; // общий список устройств (desktop+web)
// Дефолты (дублируют TMainForm.FormCreate; в GUI перезапишутся, в демоне нужны).
FVfoA := 14200000; FVfoB := 7100000; FActiveVfo := 0;
FMode := 1; FFilter := 5; FFilterBW := 2700;
@@ -459,6 +461,7 @@ end;
destructor TRadioController.Destroy;
begin
FreeEngines;
FreeAndNil(FDeviceStore);
FreeAndNil(FSettings);
inherited Destroy;
end;