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
+73 -2
View File
@@ -821,6 +821,12 @@ type
// Kind/URI/Serial/BoardType/MAC. Нужно для Pluto (открывается по URI, выбор
// бэкенда — по Kind). Не найдено → простой HPSDR по IP. Общий путь GUI+демон.
function ResolveDevice(const IP: string): THPSDRDevice;
// Добирает MAC устройства, если он неизвестен (старт по сохранённому IP /
// AutoStart идёт без дискавери). MAC — ключ профиля настроек, без него
// настройки уезжают в «нулевой» профиль. Источники по порядку: список
// найденного в этой сессии → MAC из hpsdr_devices.ini → короткий поиск в
// сети (ответ discovery несёт MAC). False = добыть не удалось.
function EnsureDeviceMac(var Dev: THPSDRDevice): Boolean;
// Сохраняет устройство по введённому адресу. Схема URI ('ip:'/'usb:'/'local:')
// → Pluto (AddSavedPluto), иначе HPSDR-IP. Общий путь web-dev_add для GUI и
// демона (десктоп-диалог сохраняет Pluto своим путём через AddSavedPluto).
@@ -5104,11 +5110,66 @@ begin
Result.URI := sd.URI;
Result.Serial := sd.Serial;
Result.BoardType := sd.BoardType;
Move(sd.MAC[0], Result.MAC[0], 6); // MAC с прошлого connect — ключ профиля настроек
Exit;
end;
end;
end;
function TRadioController.EnsureDeviceMac(var Dev: THPSDRDevice): Boolean;
// См. комментарий в объявлении. Поиск в сети делаем с временно снятым
// OnDeviceFound: это служебный пробник, а не пользовательский DISCOVER —
// список устройств в UI он трогать не должен.
const
MAC_PROBE_MS = 700; // хватает на LAN: пакет уходит сразу, дальше только ожидание
var
i: Integer;
sd: TSavedDevice;
SavedCb: TOnDeviceFound;
SavedIP: string;
Found: THPSDRDeviceArray;
begin
Result := not MacIsZero(Dev.MAC);
if Result then Exit;
i := FDeviceStore.FindDiscoveredByIP(Dev.IPAddress);
if (i >= 0) and (not MacIsZero(FDeviceStore.Discovered(i).MAC)) then
begin
Move(FDeviceStore.Discovered(i).MAC[0], Dev.MAC[0], 6);
Exit(True);
end;
i := FDeviceStore.FindSavedByAddr(Dev.IPAddress);
if i >= 0 then
begin
sd := FDeviceStore.Saved(i);
if not MacIsZero(sd.MAC) then
begin
Move(sd.MAC[0], Dev.MAC[0], 6);
Exit(True);
end;
end;
SavedCb := FNetwork.OnDeviceFound;
SavedIP := FNetwork.DirectIP;
FNetwork.OnDeviceFound := nil;
FNetwork.DirectIP := Dev.IPAddress; // unicast — достанет радио и вне broadcast-домена
try
Found := FNetwork.Discover(MAC_PROBE_MS);
finally
FNetwork.OnDeviceFound := SavedCb;
FNetwork.DirectIP := SavedIP;
end;
for i := 0 to High(Found) do
if SameText(Found[i].IPAddress, Dev.IPAddress) and (not MacIsZero(Found[i].MAC)) then
begin
Move(Found[i].MAC[0], Dev.MAC[0], 6);
if Dev.BoardType = 0 then Dev.BoardType := Found[i].BoardType;
Exit(True);
end;
end;
procedure TRadioController.AddSavedDevice(const AName, AAddr: string);
// web-dev_add: Pluto по схеме URI ('ip:'/'usb:'/'local:'), иначе HPSDR-IP.
// Serial при ручном добавлении неизвестен (допишется при connect/probe).
@@ -5133,11 +5194,21 @@ function TRadioController.ConnectDevice(const Dev: THPSDRDevice): Boolean;
// для рендера полей, которых нет в контроллере (FPS/FreqMhzDigits/CAT).
var
i: Integer;
D: THPSDRDevice;
begin
D := Dev;
// Подбираем бэкенд под тип устройства (HPSDR/Pluto) до подключения.
EnsureBackend(Dev.Kind);
Result := FNetwork.Connect(Dev);
EnsureBackend(D.Kind);
// MAC — ключ профиля настроек. Старт по сохранённому IP (AutoStart/CONNECT без
// дискавери) приносит нулевой MAC, и весь профиль устройства уехал бы в чужую
// секцию конфига; добираем MAC до Connect.
if D.Kind = bkHPSDR then EnsureDeviceMac(D);
Result := FNetwork.Connect(D);
if not Result then Exit;
// Узнанный MAC запоминаем за сохранённым устройством — следующий старт по
// AutoStart возьмёт его без поиска в сети.
if D.IPAddress <> '' then FDeviceStore.SetSavedMac(D.IPAddress, D.MAC)
else FDeviceStore.SetSavedMac(D.URI, D.MAC);
// --- Загружаем настройки устройства по MAC ---
Move(FNetwork.Device.MAC[0], FDevMAC[0], 6);