mirror of
https://git.vladimir.cc/vladimir/ewsdr.git
synced 2026-08-25 20:37:33 +00:00
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:
@@ -26,6 +26,10 @@ type
|
|||||||
Kind: TBackendKind; // bkHPSDR / bkPluto
|
Kind: TBackendKind; // bkHPSDR / bkPluto
|
||||||
URI: string; // Pluto: 'ip:..' / 'usb:..'
|
URI: string; // Pluto: 'ip:..' / 'usb:..'
|
||||||
Serial: string; // Pluto serial
|
Serial: string; // Pluto serial
|
||||||
|
// MAC радио, узнанный при прошлом подключении. Настройки персистятся по MAC,
|
||||||
|
// а старт по сохранённому IP (AutoStart/CONNECT) идёт без дискавери — без
|
||||||
|
// этого поля профиль уезжал бы в «нулевой» MAC. Пусто = ещё не знаем.
|
||||||
|
MAC: array[0..5] of Byte;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
// Найденное устройство (результат discovery)
|
// Найденное устройство (результат discovery)
|
||||||
@@ -78,10 +82,53 @@ type
|
|||||||
function Discovered(Idx: Integer): TDiscoveredDevice;
|
function Discovered(Idx: Integer): TDiscoveredDevice;
|
||||||
// IP найденного устройства по IP (для preload-rate lookup и т.п.)
|
// IP найденного устройства по IP (для preload-rate lookup и т.п.)
|
||||||
function FindDiscoveredByIP(const IP: string): Integer;
|
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;
|
end;
|
||||||
|
|
||||||
|
// MAC == 00:00:00:00:00:00 — «неизвестен».
|
||||||
|
function MacIsZero(const MAC: array of Byte): Boolean;
|
||||||
|
|
||||||
implementation
|
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 }
|
{ TDeviceStore }
|
||||||
|
|
||||||
constructor TDeviceStore.Create;
|
constructor TDeviceStore.Create;
|
||||||
@@ -116,6 +163,7 @@ begin
|
|||||||
FSaved[I].Kind := TBackendKind(Ini.ReadInteger(Section, 'Kind', 0));
|
FSaved[I].Kind := TBackendKind(Ini.ReadInteger(Section, 'Kind', 0));
|
||||||
FSaved[I].URI := Ini.ReadString (Section, 'URI', '');
|
FSaved[I].URI := Ini.ReadString (Section, 'URI', '');
|
||||||
FSaved[I].Serial := Ini.ReadString (Section, 'Serial', '');
|
FSaved[I].Serial := Ini.ReadString (Section, 'Serial', '');
|
||||||
|
IniStrToMac(Ini.ReadString(Section, 'MAC', ''), FSaved[I].MAC);
|
||||||
Inc(FSavedCount);
|
Inc(FSavedCount);
|
||||||
end;
|
end;
|
||||||
finally
|
finally
|
||||||
@@ -142,6 +190,10 @@ begin
|
|||||||
Ini.WriteInteger(Section, 'Kind', Ord(FSaved[I].Kind));
|
Ini.WriteInteger(Section, 'Kind', Ord(FSaved[I].Kind));
|
||||||
Ini.WriteString (Section, 'URI', FSaved[I].URI);
|
Ini.WriteString (Section, 'URI', FSaved[I].URI);
|
||||||
Ini.WriteString (Section, 'Serial', FSaved[I].Serial);
|
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;
|
end;
|
||||||
finally
|
finally
|
||||||
Ini.Free;
|
Ini.Free;
|
||||||
@@ -161,6 +213,7 @@ begin
|
|||||||
begin
|
begin
|
||||||
Result.Name := ''; Result.IPAddress := '';
|
Result.Name := ''; Result.IPAddress := '';
|
||||||
Result.BoardType := 0; Result.AutoStart := False;
|
Result.BoardType := 0; Result.AutoStart := False;
|
||||||
|
FillChar(Result.MAC, SizeOf(Result.MAC), 0);
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@@ -177,6 +230,7 @@ begin
|
|||||||
FSaved[Result].Kind := bkHPSDR;
|
FSaved[Result].Kind := bkHPSDR;
|
||||||
FSaved[Result].URI := '';
|
FSaved[Result].URI := '';
|
||||||
FSaved[Result].Serial := '';
|
FSaved[Result].Serial := '';
|
||||||
|
FillChar(FSaved[Result].MAC, SizeOf(FSaved[Result].MAC), 0);
|
||||||
SaveSaved;
|
SaveSaved;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@@ -193,6 +247,7 @@ begin
|
|||||||
FSaved[Result].Kind := bkPluto;
|
FSaved[Result].Kind := bkPluto;
|
||||||
FSaved[Result].URI := Trim(AURI);
|
FSaved[Result].URI := Trim(AURI);
|
||||||
FSaved[Result].Serial := Trim(ASerial);
|
FSaved[Result].Serial := Trim(ASerial);
|
||||||
|
FillChar(FSaved[Result].MAC, SizeOf(FSaved[Result].MAC), 0);
|
||||||
SaveSaved;
|
SaveSaved;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@@ -330,4 +385,34 @@ begin
|
|||||||
end;
|
end;
|
||||||
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.
|
end.
|
||||||
|
|||||||
+73
-2
@@ -821,6 +821,12 @@ type
|
|||||||
// Kind/URI/Serial/BoardType/MAC. Нужно для Pluto (открывается по URI, выбор
|
// Kind/URI/Serial/BoardType/MAC. Нужно для Pluto (открывается по URI, выбор
|
||||||
// бэкенда — по Kind). Не найдено → простой HPSDR по IP. Общий путь GUI+демон.
|
// бэкенда — по Kind). Не найдено → простой HPSDR по IP. Общий путь GUI+демон.
|
||||||
function ResolveDevice(const IP: string): THPSDRDevice;
|
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:')
|
// Сохраняет устройство по введённому адресу. Схема URI ('ip:'/'usb:'/'local:')
|
||||||
// → Pluto (AddSavedPluto), иначе HPSDR-IP. Общий путь web-dev_add для GUI и
|
// → Pluto (AddSavedPluto), иначе HPSDR-IP. Общий путь web-dev_add для GUI и
|
||||||
// демона (десктоп-диалог сохраняет Pluto своим путём через AddSavedPluto).
|
// демона (десктоп-диалог сохраняет Pluto своим путём через AddSavedPluto).
|
||||||
@@ -5104,11 +5110,66 @@ begin
|
|||||||
Result.URI := sd.URI;
|
Result.URI := sd.URI;
|
||||||
Result.Serial := sd.Serial;
|
Result.Serial := sd.Serial;
|
||||||
Result.BoardType := sd.BoardType;
|
Result.BoardType := sd.BoardType;
|
||||||
|
Move(sd.MAC[0], Result.MAC[0], 6); // MAC с прошлого connect — ключ профиля настроек
|
||||||
Exit;
|
Exit;
|
||||||
end;
|
end;
|
||||||
end;
|
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);
|
procedure TRadioController.AddSavedDevice(const AName, AAddr: string);
|
||||||
// web-dev_add: Pluto по схеме URI ('ip:'/'usb:'/'local:'), иначе HPSDR-IP.
|
// web-dev_add: Pluto по схеме URI ('ip:'/'usb:'/'local:'), иначе HPSDR-IP.
|
||||||
// Serial при ручном добавлении неизвестен (допишется при connect/probe).
|
// Serial при ручном добавлении неизвестен (допишется при connect/probe).
|
||||||
@@ -5133,11 +5194,21 @@ function TRadioController.ConnectDevice(const Dev: THPSDRDevice): Boolean;
|
|||||||
// для рендера полей, которых нет в контроллере (FPS/FreqMhzDigits/CAT).
|
// для рендера полей, которых нет в контроллере (FPS/FreqMhzDigits/CAT).
|
||||||
var
|
var
|
||||||
i: Integer;
|
i: Integer;
|
||||||
|
D: THPSDRDevice;
|
||||||
begin
|
begin
|
||||||
|
D := Dev;
|
||||||
// Подбираем бэкенд под тип устройства (HPSDR/Pluto) до подключения.
|
// Подбираем бэкенд под тип устройства (HPSDR/Pluto) до подключения.
|
||||||
EnsureBackend(Dev.Kind);
|
EnsureBackend(D.Kind);
|
||||||
Result := FNetwork.Connect(Dev);
|
// MAC — ключ профиля настроек. Старт по сохранённому IP (AutoStart/CONNECT без
|
||||||
|
// дискавери) приносит нулевой MAC, и весь профиль устройства уехал бы в чужую
|
||||||
|
// секцию конфига; добираем MAC до Connect.
|
||||||
|
if D.Kind = bkHPSDR then EnsureDeviceMac(D);
|
||||||
|
Result := FNetwork.Connect(D);
|
||||||
if not Result then Exit;
|
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 ---
|
// --- Загружаем настройки устройства по MAC ---
|
||||||
Move(FNetwork.Device.MAC[0], FDevMAC[0], 6);
|
Move(FNetwork.Device.MAC[0], FDevMAC[0], 6);
|
||||||
|
|||||||
@@ -17,9 +17,9 @@
|
|||||||
<UseVersionInfo Value="True"/>
|
<UseVersionInfo Value="True"/>
|
||||||
<AutoIncrementBuild Value="True"/>
|
<AutoIncrementBuild Value="True"/>
|
||||||
<MinorVersionNr Value="9"/>
|
<MinorVersionNr Value="9"/>
|
||||||
<BuildNr Value="277"/>
|
<BuildNr Value="278"/>
|
||||||
</VersionInfo>
|
</VersionInfo>
|
||||||
<MacroValues Count="108">
|
<MacroValues Count="110">
|
||||||
<Macro1 Name="LCLWidgetType" Value="qt6"/>
|
<Macro1 Name="LCLWidgetType" Value="qt6"/>
|
||||||
<Macro2 Name="LCLWidgetType" Value="qt6"/>
|
<Macro2 Name="LCLWidgetType" Value="qt6"/>
|
||||||
<Macro3 Name="LCLWidgetType" Value="qt6"/>
|
<Macro3 Name="LCLWidgetType" Value="qt6"/>
|
||||||
@@ -128,6 +128,8 @@
|
|||||||
<Macro106 Name="LCLWidgetType" Value="qt6"/>
|
<Macro106 Name="LCLWidgetType" Value="qt6"/>
|
||||||
<Macro107 Name="LCLWidgetType" Value="qt6"/>
|
<Macro107 Name="LCLWidgetType" Value="qt6"/>
|
||||||
<Macro108 Name="LCLWidgetType" Value="qt6"/>
|
<Macro108 Name="LCLWidgetType" Value="qt6"/>
|
||||||
|
<Macro109 Name="LCLWidgetType" Value="qt6"/>
|
||||||
|
<Macro110 Name="LCLWidgetType" Value="qt6"/>
|
||||||
</MacroValues>
|
</MacroValues>
|
||||||
<BuildModes>
|
<BuildModes>
|
||||||
<Item Name="Debug" Default="True"/>
|
<Item Name="Debug" Default="True"/>
|
||||||
|
|||||||
Reference in New Issue
Block a user