diff --git a/DeviceStore.pas b/DeviceStore.pas
index e7eab44..8f0dcae 100644
--- a/DeviceStore.pas
+++ b/DeviceStore.pas
@@ -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.
diff --git a/RadioController.pas b/RadioController.pas
index b91d18e..7f5f6c7 100644
--- a/RadioController.pas
+++ b/RadioController.pas
@@ -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);
diff --git a/ewsdr.lpi b/ewsdr.lpi
index 2c969dc..421591e 100644
--- a/ewsdr.lpi
+++ b/ewsdr.lpi
@@ -17,9 +17,9 @@
-
+
-
+
@@ -128,6 +128,8 @@
+
+