mirror of
https://git.vladimir.cc/vladimir/ewsdr.git
synced 2026-08-25 17:27:32 +00:00
fix(dxcluster): резолв имени, гейт логина, TTL и мелочи UI по итогам ревизии
Сеть и резолв (DXClusterClient): - Резолв ушёл в отдельный поток: системный резолвер блокирующий и не прерывается, а сокета в этот момент ещё нет — Stop из UI-потока висел на DNS-таймауте. Сессия ждёт квантами по 200 мс, просыпаясь на FStopEvent; Stop теперь отрабатывает за 100-200 мс в любой фазе. - Реестр запросов: по одному резолверу на имя, не больше DX_MAX_RESOLVERS. Не дождавшись, сессия оставляет запрос в реестре и на следующей попытке цепляется к нему же (иначе зависший резолвер плодил бы вечные потоки). Слотов несколько, чтобы смена адреса работала поверх зависшего прежнего; литеральный IP разбирается до реестра — ввод адреса руками обязан работать всегда. Запись живёт по счётчику ссылок, исключение в резолвере не оставляет слот занятым. - getaddrinfo вместо netdb.ResolveHostByName: тот ходит в DNS сам и /etc/hosts не читает вовсе (getent находит localhost, ResolveHostByName — нет), т.е. локальный алиас кластера не работал. Заодно реентерабельно. - WSAStartup перенесён в initialization, WSACleanup убран: Stop не ждёт резолвер, а тот может сидеть в gethostbyname. Логин (DXClusterClient): - Команды пользователя больше не уходят в незавершённый логин: очередь разбирается только после post-login, SendCommand говорит в лог, что команда ждёт. - ONLINE не по таймеру, а по существу: LoginSettled требует ответа сервера после учётки (с потолком молчания), пароль ждёт своего приглашения. Подтверждение ставится ПОСЛЕ разбора куска, а не на приход байтов — иначе исход логина зависел от границ TCP-пакетов. - Приглашения и отказы: строгий детектор (текст, заканчивающийся двоеточием) и для отправки пароля, и для вердикта — по вхождению слова пароль улетал командой от строки приветствия. Повтор приглашения пароля или позывного = отказ авторизации (dxsError, без реконнекта); опоздавшее приглашение после слепой отправки позывного отказом не считается. Эхо уже отвеченного приглашения гасится окном в одну строку. - Ошибка отправки post-login рвёт сессию, а не только цикл команд; неотправленная очередь возвращается на следующее соединение. UI и данные: - QSY по споту крутит активный VFO, а не всегда A (MainForm). - TTL спотов чистится тиком независимо от видимости оверлея (DXSpotStore .Purge + ServiceDXCluster). - Выделение в окне списка держится по позывному И частоте: один позывной живёт на разных диапазонах (DXClusterForm). - Кнопка DX правит и отложенную копию настроек, иначе debounce SETUP возвращал прежнее состояние подписей (MainForm). Проверено на фейковом кластере: приглашения с CRLF и без, границы TCP-пакетов, отказ по паролю и по позывному, опоздавшее приглашение, ловушки ложного срабатывания. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+18
-6
@@ -283,12 +283,22 @@ procedure TDXClusterForm.RefreshSpots;
|
||||
var
|
||||
i, j, Keep: Integer;
|
||||
T: TDXSpot;
|
||||
Sel: string;
|
||||
SelCall: string;
|
||||
SelFreq: Double;
|
||||
begin
|
||||
if FStore = nil then Exit;
|
||||
// Что выделено — запоминаем позывным И частотой: стор намеренно держит один
|
||||
// позывной на разных диапазонах, и по одному позывному выделение после
|
||||
// обновления перескочило бы на первый совпавший, а Enter/двойной клик увёл бы
|
||||
// радио не на тот диапазон.
|
||||
Keep := FList.ItemIndex;
|
||||
Sel := '';
|
||||
if (Keep >= 0) and (Keep < Length(FSpots)) then Sel := FSpots[Keep].Call;
|
||||
SelCall := '';
|
||||
SelFreq := 0;
|
||||
if (Keep >= 0) and (Keep < Length(FSpots)) then
|
||||
begin
|
||||
SelCall := FSpots[Keep].Call;
|
||||
SelFreq := FSpots[Keep].FreqHz;
|
||||
end;
|
||||
|
||||
FStore.Snapshot(FSpots); // приходит отсортированным по частоте
|
||||
if not FSortFreq then
|
||||
@@ -314,10 +324,12 @@ begin
|
||||
FList.Items.EndUpdate;
|
||||
end;
|
||||
|
||||
// Держим выделение на том же позывном, если он ещё в списке.
|
||||
if Sel <> '' then
|
||||
// Держим выделение на том же споте, если он ещё в списке. Допуск по частоте —
|
||||
// тот же, что у дедупа стора: спот того же позывного мог чуть подвинуться.
|
||||
if SelCall <> '' then
|
||||
for i := 0 to High(FSpots) do
|
||||
if SameText(FSpots[i].Call, Sel) then
|
||||
if SameText(FSpots[i].Call, SelCall) and
|
||||
(Abs(FSpots[i].FreqHz - SelFreq) <= DX_DEDUP_HZ) then
|
||||
begin
|
||||
FList.ItemIndex := i;
|
||||
Break;
|
||||
|
||||
Reference in New Issue
Block a user