fix(settings): профиль устройства уезжал в нулевой MAC при старте по AutoStart

Настройки персистятся секцией по MAC радио, но ResolveDevice брал MAC только из
списка найденных дискавери. У сохранённых устройств поля MAC не было вовсе, и
START по AutoStart/CONNECT без дискавери шёл с MAC 00:00:00:00:00:00 — грузился
и сохранялся чужой профиль. Пользователь видел «настройки сбросились»: выключался
wideband, менялся sample rate, терялись банды и паны, причём в зависимости от
того, нажимал ли он перед стартом DISCOVER.

- DeviceStore: у TSavedDevice поле MAC + persist в hpsdr_devices.ini (MAC=..),
  FindSavedByAddr/SetSavedMac (пишет ini только при изменении), MacIsZero.
- RadioController.ResolveDevice: saved-ветка восстанавливает и MAC.
- RadioController.EnsureDeviceMac: добор неизвестного MAC — найденное в этой
  сессии → ini → короткий служебный поиск в сети (700 мс, unicast на DirectIP,
  OnDeviceFound временно снят, чтобы не трогать список устройств в UI).
- ConnectDevice: добор MAC до Connect, после успеха MAC запоминается за
  сохранённым устройством, так что следующий AutoStart идёт без пробника.

Проверено на железе (ANAN 172.16.2.200): в hpsdr_devices.ini появился
MAC=04:91:62:FD:7B:86, секция нулевого MAC больше не создаётся, старт по
AutoStart и старт после DISCOVER дают один и тот же профиль.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-21 22:43:32 +03:00
co-authored by Claude Opus 4.8
parent 317e9b732c
commit 66c9fa63b9
3 changed files with 162 additions and 4 deletions
+85
View File
@@ -26,6 +26,10 @@ type
Kind: TBackendKind; // bkHPSDR / bkPluto
URI: string; // Pluto: 'ip:..' / 'usb:..'
Serial: string; // Pluto serial
// MAC радио, узнанный при прошлом подключении. Настройки персистятся по MAC,
// а старт по сохранённому IP (AutoStart/CONNECT) идёт без дискавери — без
// этого поля профиль уезжал бы в «нулевой» MAC. Пусто = ещё не знаем.
MAC: array[0..5] of Byte;
end;
// Найденное устройство (результат discovery)
@@ -78,10 +82,53 @@ type
function Discovered(Idx: Integer): TDiscoveredDevice;
// IP найденного устройства по IP (для preload-rate lookup и т.п.)
function FindDiscoveredByIP(const IP: string): Integer;
// ---- MAC сохранённого устройства ----
// Поиск сохранённой записи по IP или URI (Pluto хранит адрес в обоих полях).
function FindSavedByAddr(const Addr: string): Integer;
// Запоминает MAC за адресом (после успешного connect). Пишет ini только при
// изменении, чтобы не дёргать диск на каждом старте.
procedure SetSavedMac(const Addr: string; const MAC: array of Byte);
end;
// MAC == 00:00:00:00:00:00 — «неизвестен».
function MacIsZero(const MAC: array of Byte): Boolean;
implementation
function MacIsZero(const MAC: array of Byte): Boolean;
var I: Integer;
begin
Result := True;
for I := 0 to High(MAC) do
if MAC[I] <> 0 then Exit(False);
end;
function MacToIniStr(const MAC: array of Byte): string;
var I: Integer;
begin
Result := '';
for I := 0 to 5 do
begin
if I > 0 then Result := Result + ':';
Result := Result + IntToHex(MAC[I], 2);
end;
end;
procedure IniStrToMac(const S: string; out MAC: array of Byte);
var Parts: TStringArray; I, V: Integer;
begin
FillChar(MAC[0], 6, 0);
Parts := S.Split([':']);
if Length(Parts) <> 6 then Exit;
for I := 0 to 5 do
begin
V := StrToIntDef('$' + Parts[I], -1);
if (V < 0) or (V > 255) then begin FillChar(MAC[0], 6, 0); Exit; end;
MAC[I] := Byte(V);
end;
end;
{ TDeviceStore }
constructor TDeviceStore.Create;
@@ -116,6 +163,7 @@ begin
FSaved[I].Kind := TBackendKind(Ini.ReadInteger(Section, 'Kind', 0));
FSaved[I].URI := Ini.ReadString (Section, 'URI', '');
FSaved[I].Serial := Ini.ReadString (Section, 'Serial', '');
IniStrToMac(Ini.ReadString(Section, 'MAC', ''), FSaved[I].MAC);
Inc(FSavedCount);
end;
finally
@@ -142,6 +190,10 @@ begin
Ini.WriteInteger(Section, 'Kind', Ord(FSaved[I].Kind));
Ini.WriteString (Section, 'URI', FSaved[I].URI);
Ini.WriteString (Section, 'Serial', FSaved[I].Serial);
if MacIsZero(FSaved[I].MAC) then
Ini.DeleteKey(Section, 'MAC')
else
Ini.WriteString(Section, 'MAC', MacToIniStr(FSaved[I].MAC));
end;
finally
Ini.Free;
@@ -161,6 +213,7 @@ begin
begin
Result.Name := ''; Result.IPAddress := '';
Result.BoardType := 0; Result.AutoStart := False;
FillChar(Result.MAC, SizeOf(Result.MAC), 0);
end;
end;
@@ -177,6 +230,7 @@ begin
FSaved[Result].Kind := bkHPSDR;
FSaved[Result].URI := '';
FSaved[Result].Serial := '';
FillChar(FSaved[Result].MAC, SizeOf(FSaved[Result].MAC), 0);
SaveSaved;
end;
@@ -193,6 +247,7 @@ begin
FSaved[Result].Kind := bkPluto;
FSaved[Result].URI := Trim(AURI);
FSaved[Result].Serial := Trim(ASerial);
FillChar(FSaved[Result].MAC, SizeOf(FSaved[Result].MAC), 0);
SaveSaved;
end;
@@ -330,4 +385,34 @@ begin
end;
end;
function TDeviceStore.FindSavedByAddr(const Addr: string): Integer;
var
I: Integer;
begin
Result := -1;
if Trim(Addr) = '' then Exit;
for I := 0 to FSavedCount - 1 do
if SameText(FSaved[I].IPAddress, Addr) or SameText(FSaved[I].URI, Addr) then
begin
Result := I;
Exit;
end;
end;
procedure TDeviceStore.SetSavedMac(const Addr: string; const MAC: array of Byte);
var
Idx, J: Integer;
Cur: array[0..5] of Byte;
begin
if MacIsZero(MAC) then Exit;
Idx := FindSavedByAddr(Addr);
if Idx < 0 then Exit;
FillChar(Cur, SizeOf(Cur), 0);
for J := 0 to High(MAC) do
if J <= 5 then Cur[J] := MAC[J];
if CompareByte(Cur, FSaved[Idx].MAC, 6) = 0 then Exit; // уже записан
Move(Cur[0], FSaved[Idx].MAC[0], 6);
SaveSaved;
end;
end.