unit DeviceStore; {$mode objfpc}{$H+} // Общее хранилище устройств — единый источник правды для десктоп-диалога // (DeviceForm) и web-оверлея. Владеет сохранёнными устройствами (persist в // hpsdr_devices.ini) и текущим списком найденных при discovery. // Контроллер (TRadioController) держит один экземпляр; фронтенды работают // через него, поэтому списки не расходятся. interface uses Classes, SysUtils, IniFiles, BoardUtils, PlatformUtils; const DEVICE_CFG_NAME = 'hpsdr_devices.ini'; type // Запись о сохранённом устройстве TSavedDevice = record Name: string; // пользовательское имя IPAddress: string; BoardType: Integer; AutoStart: Boolean; // запускать автоматически при старте end; // Найденное устройство (результат discovery) TDiscoveredDevice = record IPAddress: string; DisplayName: string; BoardType: Integer; MAC: array[0..5] of Byte; // нужен для preload-rate lookup (LoadDevice по MAC) end; { TDeviceStore } TDeviceStore = class private FSaved: array of TSavedDevice; FSavedCount: Integer; FDiscovered: array of TDiscoveredDevice; FDiscoveredCount: Integer; public constructor Create; // ---- Сохранённые устройства ---- procedure LoadSaved; procedure SaveSaved; function SavedCount: Integer; function Saved(Idx: Integer): TSavedDevice; // Добавляет запись (Name пустое → 'HPSDR'); возвращает индекс новой записи. function AddSaved(const AName, IP: string; BoardType: Integer): Integer; procedure RemoveSaved(Idx: Integer); procedure SetAutoStart(Idx: Integer); // эксклюзивно: только одно autostart function AutoStartIP: string; function AutoStartBoardType: Integer; function SavedBoardType(Idx: Integer): Integer; function SavedDisplay(Idx: Integer): string; // строка для списка (с * и board) // ---- Найденные (discovery) ---- procedure ClearDiscovered; procedure AddDiscovered(const IP, DisplayName: string; BoardType: Integer; const MAC: array of Byte); function DiscoveredCount: Integer; function Discovered(Idx: Integer): TDiscoveredDevice; // IP найденного устройства по IP (для preload-rate lookup и т.п.) function FindDiscoveredByIP(const IP: string): Integer; end; implementation { TDeviceStore } constructor TDeviceStore.Create; begin inherited Create; FSavedCount := 0; FDiscoveredCount := 0; LoadSaved; end; procedure TDeviceStore.LoadSaved; var Ini: TIniFile; I, N: Integer; Section: string; begin FSavedCount := 0; SetLength(FSaved, 0); if not FileExists(GetAppCfgDir + DEVICE_CFG_NAME) then Exit; Ini := TIniFile.Create(GetAppCfgDir + DEVICE_CFG_NAME); try N := Ini.ReadInteger('Devices', 'Count', 0); SetLength(FSaved, N); for I := 0 to N - 1 do begin Section := 'Device' + IntToStr(I); FSaved[I].Name := Ini.ReadString (Section, 'Name', 'HPSDR'); FSaved[I].IPAddress := Ini.ReadString (Section, 'IP', ''); FSaved[I].BoardType := Ini.ReadInteger(Section, 'BoardType', 0); FSaved[I].AutoStart := Ini.ReadBool (Section, 'AutoStart', False); Inc(FSavedCount); end; finally Ini.Free; end; end; procedure TDeviceStore.SaveSaved; var Ini: TIniFile; I: Integer; Section: string; begin Ini := TIniFile.Create(GetAppCfgDir + DEVICE_CFG_NAME); try Ini.WriteInteger('Devices', 'Count', FSavedCount); for I := 0 to FSavedCount - 1 do begin Section := 'Device' + IntToStr(I); Ini.WriteString (Section, 'Name', FSaved[I].Name); Ini.WriteString (Section, 'IP', FSaved[I].IPAddress); Ini.WriteInteger(Section, 'BoardType', FSaved[I].BoardType); Ini.WriteBool (Section, 'AutoStart', FSaved[I].AutoStart); end; finally Ini.Free; end; end; function TDeviceStore.SavedCount: Integer; begin Result := FSavedCount; end; function TDeviceStore.Saved(Idx: Integer): TSavedDevice; begin if (Idx >= 0) and (Idx < FSavedCount) then Result := FSaved[Idx] else begin Result.Name := ''; Result.IPAddress := ''; Result.BoardType := 0; Result.AutoStart := False; end; end; function TDeviceStore.AddSaved(const AName, IP: string; BoardType: Integer): Integer; begin Result := FSavedCount; Inc(FSavedCount); SetLength(FSaved, FSavedCount); if Trim(AName) <> '' then FSaved[Result].Name := Trim(AName) else FSaved[Result].Name := 'HPSDR'; FSaved[Result].IPAddress := Trim(IP); FSaved[Result].BoardType := BoardType; FSaved[Result].AutoStart := False; SaveSaved; end; procedure TDeviceStore.RemoveSaved(Idx: Integer); var I: Integer; begin if (Idx < 0) or (Idx >= FSavedCount) then Exit; for I := Idx to FSavedCount - 2 do FSaved[I] := FSaved[I + 1]; Dec(FSavedCount); SetLength(FSaved, FSavedCount); SaveSaved; end; procedure TDeviceStore.SetAutoStart(Idx: Integer); var I: Integer; begin if (Idx < 0) or (Idx >= FSavedCount) then Exit; // Только одно устройство может быть AutoStart for I := 0 to FSavedCount - 1 do FSaved[I].AutoStart := (I = Idx); SaveSaved; end; function TDeviceStore.AutoStartIP: string; var I: Integer; begin Result := ''; for I := 0 to FSavedCount - 1 do if FSaved[I].AutoStart then begin Result := FSaved[I].IPAddress; Exit; end; end; function TDeviceStore.AutoStartBoardType: Integer; var I: Integer; begin Result := 0; for I := 0 to FSavedCount - 1 do if FSaved[I].AutoStart then begin Result := FSaved[I].BoardType; Exit; end; end; function TDeviceStore.SavedBoardType(Idx: Integer): Integer; begin if (Idx >= 0) and (Idx < FSavedCount) then Result := FSaved[Idx].BoardType else Result := 0; end; function TDeviceStore.SavedDisplay(Idx: Integer): string; begin Result := ''; if (Idx < 0) or (Idx >= FSavedCount) then Exit; Result := FSaved[Idx].Name + ' [' + FSaved[Idx].IPAddress + ']'; if FSaved[Idx].BoardType > 0 then Result := Result + ' ' + BoardTypeName(FSaved[Idx].BoardType); if FSaved[Idx].AutoStart then Result := '* ' + Result; end; procedure TDeviceStore.ClearDiscovered; begin FDiscoveredCount := 0; SetLength(FDiscovered, 0); end; procedure TDeviceStore.AddDiscovered(const IP, DisplayName: string; BoardType: Integer; const MAC: array of Byte); var Idx, J: Integer; begin Idx := FDiscoveredCount; Inc(FDiscoveredCount); SetLength(FDiscovered, FDiscoveredCount); FDiscovered[Idx].IPAddress := IP; FDiscovered[Idx].DisplayName := DisplayName; FDiscovered[Idx].BoardType := BoardType; FillChar(FDiscovered[Idx].MAC, SizeOf(FDiscovered[Idx].MAC), 0); for J := 0 to High(MAC) do if J <= 5 then FDiscovered[Idx].MAC[J] := MAC[J]; end; function TDeviceStore.DiscoveredCount: Integer; begin Result := FDiscoveredCount; end; function TDeviceStore.Discovered(Idx: Integer): TDiscoveredDevice; begin if (Idx >= 0) and (Idx < FDiscoveredCount) then Result := FDiscovered[Idx] else begin Result.IPAddress := ''; Result.DisplayName := ''; Result.BoardType := 0; end; end; function TDeviceStore.FindDiscoveredByIP(const IP: string): Integer; var I: Integer; begin Result := -1; for I := 0 to FDiscoveredCount - 1 do if SameText(FDiscovered[I].IPAddress, IP) then begin Result := I; Exit; end; end; end.