mirror of
https://git.vladimir.cc/vladimir/ewsdr.git
synced 2026-08-25 20:37:33 +00:00
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:
co-authored by
Claude Opus 4.8
parent
783c8976c9
commit
7f7f429515
+65
-175
@@ -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
@@ -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
@@ -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
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user