feat: Pluto support in headless daemon (connect/autostart/discover/dev_add) + headless audio

The headless daemon (ewsdrd) only ever built HPSDR devices, so Pluto
could not be used: backend is chosen by Dev.Kind and Pluto opens by URI,
neither of which the daemon propagated. Also local audio played on the
host and web Discover never probed network Plutos.

Shared, backend-agnostic helpers in TRadioController (used by GUI + daemon):
- ResolveDevice(IP): discovered->saved lookup restoring Kind/URI/Serial/
  BoardType/MAC. MainForm.ResolveDevice now delegates here.
- AddSavedDevice(name, addr): web dev_add saves Pluto when addr has a URI
  scheme (ip:/usb:/local:), else HPSDR. Both web hosts use it.
- SeedPlutoProbeFromSaved: seed network-probe URIs from saved Plutos
  (no mDNS -> a network Pluto is only found by direct URI probe).
- LocalAudioEnabled flag (GUI=True): when False the local sound card is
  neither opened nor written; RX audio goes only to web (OnAudioConsume).

DeviceStore.AutoStartDevice(out Dev): full autostart record (Kind/URI/
Serial) so a saved Pluto (URI-addressed, empty IPAddress on USB) can
autostart.

Daemon (ewsdrd.lpr): LocalAudioEnabled:=False; SyncConnect via
ResolveDevice; autostart via AutoStartDevice; SyncDiscover seeds probe
URIs then Discover; SyncDevAdd via AddSavedDevice.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-23 13:32:05 +03:00
co-authored by Claude Opus 4.8
parent 3115b2b5bf
commit d78d835485
4 changed files with 174 additions and 88 deletions
+3 -35
View File
@@ -3578,41 +3578,9 @@ end;
function TMainForm.ResolveDevice(const IP: string): THPSDRDevice;
// По IP/URI ищет устройство среди discovered, затем saved, чтобы восстановить
// Kind/URI/Serial/MAC (нужно для Pluto). Если не найдено — простой HPSDR по IP.
var
i: Integer;
dd: TDiscoveredDevice;
sd: TSavedDevice;
// Kind/URI/Serial/MAC (нужно для Pluto). Логика общая с демономв контроллере.
begin
// Result — managed-поля (строки) при входе nil, FillChar безопасен.
FillChar(Result, SizeOf(Result), 0);
Result.Kind := bkHPSDR;
Result.IPAddress := IP;
i := FController.FDeviceStore.FindDiscoveredByIP(IP);
if i >= 0 then
begin
dd := FController.FDeviceStore.Discovered(i);
Result.Kind := dd.Kind;
Result.URI := dd.URI;
Result.Serial := dd.Serial;
Result.BoardType := dd.BoardType;
Move(dd.MAC[0], Result.MAC[0], 6);
Exit;
end;
for i := 0 to FController.FDeviceStore.SavedCount - 1 do
begin
sd := FController.FDeviceStore.Saved(i);
if SameText(sd.IPAddress, IP) or SameText(sd.URI, IP) then
begin
Result.Kind := sd.Kind;
Result.URI := sd.URI;
Result.Serial := sd.Serial;
Result.BoardType := sd.BoardType;
Exit;
end;
end;
Result := FController.ResolveDevice(IP);
end;
procedure TMainForm.DoStopAndDisconnect;
@@ -5012,7 +4980,7 @@ end;
procedure TMainForm.SyncWebDevAdd;
begin
FController.FDeviceStore.AddSaved(FWebSyncStr1, FWebSyncStr2, 0);
FController.AddSavedDevice(FWebSyncStr1, FWebSyncStr2); // Pluto по схеме URI
PushDeviceListToWeb;
end;