mirror of
https://git.vladimir.cc/vladimir/ewsdr.git
synced 2026-08-25 19:45:09 +00:00
c2fa1da2b0cb13ecf1eba87719084a8f73a26a68
5
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
6ed1be3dc7 |
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> |
||
|
|
84f710476f |
fix(dxcluster): сборка под win64 — Windows.pas перекрывал TCriticalSection
Сборщик под win64 падал в конструкторе клиента кластера: «identifier idents no
member Create». Windows.pas объявляет TCriticalSection как ЗАПИСЬ (алиас
TRTLCriticalSection), а в uses он стоял ПОСЛЕ SyncObjs — и поле FLock, и
TCriticalSection.Create разрешались в запись. На Linux эта ветка uses не
компилируется вовсе, поэтому баг жил с первого коммита DX-кластера (
|
||
|
|
d12d8239da |
feat(dxcluster): мода спота по бэндплану, когда комментарий молчит
В CW и SSB спотеры сплошь и рядом не пишут моду, и такой спот оставался dxmUnknown: ни фильтр по модам его не видел, ни QSY по нему моду не ставил. Теперь порядок такой: сначала комментарий (как было), и только если он молчит — участок бэндплана. DXSpotStore.DXModeFromFreq — таблица участков, читается сверху вниз, первое попадание выигрывает. Узкие «водопои» цифры стоят РАНЬШЕ широких сегментов: FT8 на 7074 живёт посреди телефонного участка R1, FT4 на 21140 — посреди 15-метрового, и без такого порядка они утонули бы в SSB. ★ Границы — по IARU Region 1 (наш регион), а споты прилетают со всего мира, поэтому там, где регионы расходятся, мода НЕ выводится вовсе: пропуск честнее ошибки. Отсюда дырки 1843-1850 (R1 телефон против R2 CW), 60 м (канальный), маячные щели, 2 м/70 см кроме FT8-окон. Два спорных куска всё же отданы R1: 3570-3600 — цифре (у R2 это ещё CW, но споты там почти сплошь FT8/RTTY) и 7053-7300 — телефону (у R2 ниже 7125 данные). QO-100 (даунлинк 10489.5-10490.0) — отдельной веткой по плану AMSAT-DL, теми же границами, что рисует BandPlanOverlay: CW, NB/DIGI, две SSB-зоны. Маяки и mixed modes не гадаем. Угаданное помечено (TDXSpot.ModeGuessed) и в окне списка выводится с '?' — 'CW?' против 'CW': спотер моду не называл, а на границах участков таблица может ошибаться, поэтому разницу видно. На спектре в чипе только позывной — там ничего не изменилось. Проверено консольным тестом на этих же юнитах: 25 частот + 6 полных строк кластера (комментарий перебивает таблицу; 7012.5 → CW guessed, 7145 → SSB guessed, 10489.680 → SSB guessed; 1846, 60 м и 144.300 остаются без моды). Сборка ewsdr (--ws=qt6) и ewsdrd — ОК. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
17fa965aec |
feat(dxcluster): споты на всех панадаптерах + кнопка DX в блоке RX
Подписи спотов рисовались только на главном пане. Теперь их видит каждый пан, в чьё окно попадает частота спота. Оверлей — ЭКЗЕМПЛЯР НА ПАН, а не один общий: кэш полосы подписей ключуется центром/спаном/шириной, а у панов они свои — общий оверлей пересобирался бы на каждый пан каждый кадр, то есть ровно то, ради чего кэш и заводился. Живёт в TPanafallPanel (AttachDXSpots/DetachDXSpots/DXOverlay, Owner=панель), база спотов по-прежнему одна на всех. FDXSpotOverlay в MainForm стал алиасом на оверлей пана 0 — как FSpecView для FPan.View: настройки, тема и тик затухания идут общим циклом по FPans, и главный пан перестал быть особым случаем (иначе тумблер DX гасил бы подписи только на нём). ★ Version стора глобальна, а окно у пана своё, поэтому в EnsureRendered на смену версии сначала берётся снимок окна и сверяется его подпись (FNV-1a по позывному, частоте, моде и метке времени). Совпала — ни пересборки битмапа, ни перезаливки GL-текстуры: спот, севший на чужой диапазон, до этого пана не доходит. Без этого цена пересборки множилась бы на число панов. Снимок берётся один раз и переиспользуется раскладкой — стор второй раз не дёргаем. Клик по подписи на пане N = QSY слайса ЭТОГО пана (активного, если он здесь, иначе первого; нет ни одного — создаём в точке) с модой по комментарию кластера и полосой пресета этой моды. Своего VFO у панов N нет, а ретюнить их DDC под спот нельзя — увезло бы весь пан. Мода спота → режим вынесена в общую DXSpotRadioMode для обоих путей. Низкий пан (грид): полоса подписей и штрихи пропускаются целиком — раньше в GL-пути полоса легла бы поверх спектра, а штрихи пошли бы снизу вверх. Кнопка DX переехала из тулбара в блок RX левой панели, сразу после CTUN (ряд стал четырёхколоночным: CTUN | DX | Channel | BEACON): споты — часть приёмного вида, а не глобальная команда уровня DISCOVER/START/SETUP. Заодно снят расчёт SafeLeft, резервировавший под неё место в шапке. Подписи по умолчанию ВЫКЛЮЧЕНЫ (show_spots: дефолт и фолбэк чтения были True), состояние переживает перезапуск. Клик по кнопке подливает состояние в открытый SETUP: там своя копия конфига, и первая же правка любого поля страницы вернула бы подписи обратно. fix: Stop клиента кластера больше не держит главный поток. Он делал Terminate + WaitFor, а поток в этот момент сидит в блокирующем recv и просыпается лишь по своему кванту (до 1 с), на застрявшем send — до SO_SNDTIMEO. Всё это время окно висело на выходе и на переподключении после правки настроек. Теперь Stop рвёт живой сокет SockShutdown (хэндл — зеркало под тем же локом, поток снимает его ДО close, поэтому shutdown по закрытому fd невозможен), а FormDestroy зовёт неблокирующий RequestStop первой строкой: поток доживает параллельно с разборкой радио, и join в конце уже никого не ждёт. Сборка ewsdr (--ws=qt6) и ewsdrd — ОК. На железе не проверено: доп. паны требуют подключённого радио. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
a2fdc3a741 |
feat(dxcluster): споты DX-кластера на панадаптере
Telnet-клиент DX-кластера, база спотов и подписи позывных прямо на спектре на своих частотах. Разложено на три слоя, как бэндплан и лупа маяка. DXClusterClient.pas — один рабочий поток: резолв, неблокирующий connect с select квантами по 200 мс (Stop не ждёт таймаут соединения), логин позывным, чтение строк, реконнект с backoff. Приглашения логина И пароля ловятся в незавершённом хвосте буфера — типичный telnet-prompt приходит без CR/LF. Ошибки recv отличаются от таймаута кванта (EAGAIN/EINTR/WSAETIMEDOUT), иначе на ECONNRESET поток крутился бы в пустом цикле вместо реконнекта. Отправка дописывает частичный send. LCL-free. DXSpotStore.pas — потокобезопасная база: дедуп по позывному, TTL, потолок записей, монотонный Version. Единственная точка обмена потока с UI: никакого Synchronize, UI сам замечает правки по Version, как оверлеи — по ключам кэша. DXSpotOverlay.pas — рендер по модели BandPlanOverlay/VfoOverlay: кэшируется только полоса подписей (W × BandH) в key-color битмап, пересборка строго по dirty-ключу, на кадр — один keyed-композит. Штрихи от полосы до низа спектра рисует вызывающая сторона теми же примитивами, что и прочие маркеры: CPU — RawVLine внутри RawBegin/RawEnd, GL — DrawLine по готовому списку X/цвет. GL берёт тот же битмап текстурой и заливает её только при смене RenderVersion. Пересекающиеся подписи раскладываются лесенкой, цвет гаснет с возрастом, свой позывной выделен; палитра парная под тёмную и светлую тему. DXClusterForm.pas — окно списка: споты, лог соединения, строка команды кластеру (диалект set/filter у всех свой — не угадываем). Двойной клик или Enter = QSY. Данные тянутся поллингом по Version/LogVersion. Интеграция: кнопка DX в тулбаре (ЛКМ — подписи на спектре, ПКМ — окно), клик по подписи спота = QSY с автовыбором моды по комментарию кластера, страница SETUP → DX Cluster с персистом в секции "dxcluster". Правки SETUP прилетают посимвольно, поэтому запись конфига, TTL стора и переподключение откладываются до паузы в наборе — иначе набор позывного стоил бы шесть реконнектов, а промежуточный TTL «3» необратимо выбросил бы споты. Частота спота кладётся как есть и сравнивается с GetViewWindow: на QO-100 кластеры постят downlink 10489.xxx, что совпадает со шкалой пана само собой. Побочно в общих юнитах: WebUtils.SockSetRcvTimeout, FlatMemo.OnChange, FlatListBox.OnKeyDown, BlendBitmapKey вынесен в interface VfoOverlay (одна копия дворд-блендера на проект). Проверено на локальном фейковом кластере: логин и пароль по prompt без CR/LF, разбор спотов (включая QO-100), уход в RETRY по RST, Stop за 200 мс на висящем connect. На железе рендер не проверялся. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |