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.
|
||||
|
||||
Reference in New Issue
Block a user