mirror of
https://git.vladimir.cc/vladimir/ewsdr.git
synced 2026-08-25 19:45:09 +00:00
fix(tci): жизненный цикл, синхронизация слайсов и WebSocket по RFC
Разбор ревью ветки. Критичное — четыре отказа жизненного цикла и один пробел синхронизации. Use-after-free стора спотов: FTCIAdapter освобождается ДО FDXStore. Команда SPOT/SPOT_DELETE, пришедшая между их гибелью, обращалась к освобождённой памяти. Bind-адрес: TCIParseIPv4 стал строгим (out + Boolean, ровно четыре октета 0..255). Кривой адрес — отказ поднимать сокет, а не молчаливый INADDR_ANY: авторизации в TCI нет. В UI порт и адрес применяются по уходу фокуса и по Close, а не на каждую букву — набор «127.0.0.1» по дороге проходил через «127.0.0.» и открывал порт наружу. Остановка при висящем Synchronize: флаг Stopping (адаптер не начинает новых Invoke), прокачка CheckSynchronize в цикле ожидания Stop и запрет освобождать клиента, чей поток не вышел. Владение переделано: клиента освобождает только тик-поток (ReapClients), клиентский лишь помечает себя закрытым. Отправка больше не блокирует вызывающего: Send/Broadcast кладут строку в очередь клиента, в сокет пишет тик-поток вне общего лока, склеивая очередь в общие кадры. Медленный клиент морозил UI на таймаут отправки за каждое движение ручки VFO; теперь он просто вылетает. Слайсы: в контроллере появилось rfSliceState (нагрузка — FSliceFreqId), его шлют сами сеттеры слайса; SyncSetVfo зовёт SliceFreqChanged, как CAT. Адаптер разворачивает Id в пару (приёмник, канал) и рассылает состояние именно этого канала, а не канала 0 каждого пана. WebSocket по RFC 6455: маска обязательна, FIN/continuation собираются, RSV и незнакомые opcode рвут соединение, control-кадры ≤125 и только целиком, 64-битная длина не сворачивается в отрицательный Integer, пустой Sec-WebSocket-Key получает 400. Хвост пакета handshake больше не выбрасывается — первая команда не теряется. Клиент после исключения в разборе не остаётся висеть в массиве. Ещё: DSP и squelch доп. приёмников читаются и пишутся из TCtrlSlice (парные сеттеры сохраняли соседние поля значениями главного тракта); параметры потоков — в TTCIClient, они клиентские по спецификации; эхо под своим локом; ApplySettings возвращает результат, отказ старта виден оператору; инициализация объявляет только существующие каналы; SET_IN_FOCUS реализован через OnFocusRequest. Осознанно не сделано и записано в doc/TCI.md §3.1: AGC_GAIN для приёмников >0 (AGC-T один на тракт), цвет спота, KEYER, TX_FOOTSWITCH, арбитраж нескольких клиентов. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+20
-1
@@ -88,7 +88,8 @@ type
|
||||
rfDevice, // подключённое устройство сменилось
|
||||
rfDeviceList, // список discovered устройств обновился
|
||||
rfPanFreq, // центр доп. пана уехал (ретюн DDC извне)
|
||||
rfSliceFreq // слайс перестроен извне (CAT); Id — FSliceFreqId
|
||||
rfSliceFreq, // слайс перестроен извне (CAT); Id — FSliceFreqId
|
||||
rfSliceState // у слайса сменились мода/фильтр/АРУ/DSP/громкость; Id — FSliceFreqId
|
||||
);
|
||||
|
||||
TRadioStateEvent = procedure(Sender: TObject; Field: TRadioField) of object;
|
||||
@@ -907,6 +908,7 @@ type
|
||||
// Уведомление UI «частота слайса пришла извне»: перезалить флаг (цифры +
|
||||
// кромки фильтра), позиция флага и так живая.
|
||||
procedure SliceFreqChanged(Id: Integer);
|
||||
procedure SliceStateChanged(Id: Integer);
|
||||
|
||||
// ---- Панадаптеры на аппаратных DDC (этап 3.1; UI/движок — 3.2/3.3) ----
|
||||
// Создаёт пан PanId (1..MAX_PANS-1) на своём DDC: FreqHz = центр в ВИДИМЫХ
|
||||
@@ -2018,6 +2020,16 @@ begin
|
||||
Changed(rfSliceFreq);
|
||||
end;
|
||||
|
||||
procedure TRadioController.SliceStateChanged(Id: Integer);
|
||||
// У слайса сменилось что-то кроме частоты: режим, фильтр, АРУ, DSP, громкость,
|
||||
// мьют, шумоподавитель. Зовут сами сеттеры — слайс правят и UI, и CAT, и TCI, а
|
||||
// узнать об этом должны все фронтенды сразу (у главного тракта для этого есть
|
||||
// свои поля rfMode/rfFilter/…, у слайсов не было ничего).
|
||||
begin
|
||||
FSliceFreqId := Id;
|
||||
Changed(rfSliceState);
|
||||
end;
|
||||
|
||||
function TRadioController.TuneSliceInBand(Id: Integer; TargetHz: Double): Boolean;
|
||||
// Свобода внешней программы: она двигает слайс куда угодно ВНУТРИ включённого
|
||||
// диапазона (2 м на трансвертере: 144.174 при окне на 145.5 — можно), но сменить
|
||||
@@ -2548,6 +2560,7 @@ begin
|
||||
end;
|
||||
end;
|
||||
if FTxSliceId = Id then SyncCWKeyer;
|
||||
SliceStateChanged(Id);
|
||||
end;
|
||||
|
||||
procedure TRadioController.SetSliceFilter(Id, Low, High: Integer);
|
||||
@@ -2558,6 +2571,7 @@ begin
|
||||
FSlices[idx].FilterLo := Low;
|
||||
FSlices[idx].FilterHi := High;
|
||||
if Assigned(FDSPEngine) then FDSPEngine.SetSliceFilter(Id, Low, High);
|
||||
SliceStateChanged(Id);
|
||||
end;
|
||||
|
||||
procedure TRadioController.SetSliceAGCMode(Id: Integer; AGC: TWDSPAGCMode);
|
||||
@@ -2567,6 +2581,7 @@ begin
|
||||
if idx < 0 then Exit;
|
||||
FSlices[idx].AGC := AGC;
|
||||
if Assigned(FDSPEngine) then FDSPEngine.SetSliceAGC(Id, AGC);
|
||||
SliceStateChanged(Id);
|
||||
end;
|
||||
|
||||
procedure TRadioController.SetSliceVolume(Id: Integer; Vol: Double);
|
||||
@@ -2576,6 +2591,7 @@ begin
|
||||
if idx < 0 then Exit;
|
||||
FSlices[idx].Volume := Vol;
|
||||
if Assigned(FDSPEngine) then FDSPEngine.SetSliceVolume(Id, Vol);
|
||||
SliceStateChanged(Id);
|
||||
end;
|
||||
|
||||
procedure TRadioController.SetSliceMute(Id: Integer; Mute: Boolean);
|
||||
@@ -2585,6 +2601,7 @@ begin
|
||||
if idx < 0 then Exit;
|
||||
FSlices[idx].Muted := Mute;
|
||||
if Assigned(FDSPEngine) then FDSPEngine.SetSliceMute(Id, Mute);
|
||||
SliceStateChanged(Id);
|
||||
end;
|
||||
|
||||
procedure TRadioController.SetSliceRxMuteOnTx(Id: Integer; On_: Boolean);
|
||||
@@ -2639,6 +2656,7 @@ begin
|
||||
FDSPEngine.SetSliceSNB(Id, SNBOn);
|
||||
FDSPEngine.SetSliceANF(Id, ANFOn);
|
||||
end;
|
||||
SliceStateChanged(Id);
|
||||
end;
|
||||
|
||||
procedure TRadioController.SetSliceFMSquelch(Id: Integer; On_: Boolean; Level: Integer);
|
||||
@@ -2650,6 +2668,7 @@ begin
|
||||
FSlices[idx].FMSQLevel := Level;
|
||||
if Assigned(FDSPEngine) then
|
||||
FDSPEngine.SetSliceFMSquelch(Id, On_, Level);
|
||||
SliceStateChanged(Id);
|
||||
end;
|
||||
|
||||
function TRadioController.SliceSMeter(Id: Integer): Double;
|
||||
|
||||
Reference in New Issue
Block a user