fix(tci): выключение TUNE не забывало хозяина эфира — уход клиента рубил чужую передачу

Задний фронт передачи ловился только по rfTransmitting, а выключение TUN гасит
эфир изнутри SetTune: там SetMOX(False) шлёт rfTransmitting, пока FTuning ещё
True (флаг сбрасывается строкой ниже, см. RadioController.SetTune), поэтому
TxNow оставался True и ForgetTxOwner не звался. Следующий за этим
Changed(rfTuning) обработчик игнорировал вовсе.

Итог: клиент сделал «tune:0,true» и стал хозяином эфира, оператор выключил TUN
кнопкой (тогда сброс в DispatchCommand не срабатывает — команды-то не было),
позже нажал PTT сам, а когда сокет клиента закрылся, HandleDisconnect →
StopTxOf всё ещё видел его хозяином и снимал ОПЕРАТОРСКУЮ передачу. Ровно то,
что комментарий у ForgetTxOwner обещает не допускать.

Теперь фронт ловится и по rfTuning. На стенде — сценарий целиком: клиент
поднимает TUN, оператор гасит его сам и встаёт в эфир, уход клиента передачу не
трогает (негативный контроль: с прежним условием проверка падает).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-20 16:06:21 +03:00
co-authored by Claude Opus 5
parent 4b715c8b03
commit 11d8e11ba5
2 changed files with 32 additions and 1 deletions
+26
View File
@@ -2217,6 +2217,32 @@ begin
Sleep(300);
Check('после ухода клиента источник снят', not Ctrl.TCIMicRequested);
Check('после ухода клиента MOX снят', not Ctrl.FTransmitting);
// ── TUN: хозяин эфира забывается и на выключении TUNE ──
// Клиент поднял TUN, оператор погасил его САМ (кнопкой, не командой TCI),
// а потом сам же встал на передачу. Уход клиента не имеет права снять эту
// чужую передачу: хозяина эфира обязано было забыть ещё выключение TUN.
// ★Задний фронт ловился только по rfTransmitting, а SetTune(False) шлёт
// его, пока FTuning ещё True, — владелец оставался протухшим навсегда.
C2 := TRawClient.Create;
try
Check('TUN: клиент подключился', C2.Connect(PORT));
C2.WaitText('ready;', 2000);
C2.SendText('tune:0,true;');
C2.WaitText('tune:', 1000);
Check('TUN: эфир поднят клиентом', Ctrl.FTuning and Ctrl.FTransmitting);
Ctrl.SetTune(False); // оператор гасит TUN сам
Ctrl.SetMOX(True); // и сам встаёт на передачу
Check('TUN: оператор в эфире', Ctrl.FTransmitting);
C2.Close_;
Sleep(300);
Check('TUN: уход клиента не снял чужую передачу', Ctrl.FTransmitting);
Ctrl.SetMOX(False);
finally
C2.Close_;
C2.Free;
end;
Ctrl.FWDSPReady := False;
finally
if C <> nil then begin C.Close_; C.Free; end;