Euripid

Euripid

Жизнь с ИИ
На Пикабу
Дата рождения: 22 ноября
121 рейтинг 1 подписчик 0 подписок 13 постов 0 в горячем
7

Серия статей «Пишем ELM327-совместимый адаптер на PIC18F25K80»

Серия ELM327

Дисклеймер. ELM327 — зарегистрированный продукт компании ELM Electronics. Эта серия описывает самостоятельную реализацию совместимого командного интерфейса в учебных целях и не является оригинальной микросхемой ELM Electronics. Проект не предназначен для коммерческого использования под именем «ELM327».


Вводная статья: зачем читать эту серию и что мы будем строить


OBD-II: главный диагностический разъём современных легковых автомобилей

Если вы когда-нибудь приезжали на техосмотр или к механику, вы, скорее всего, видели, как он достаёт небольшое устройство, вставляет его под торпедо и смотрит в ноутбук. Этот разъём — OBD-II (On-Board Diagnostics, версия II). С 1996 года он обязателен для всех легковых автомобилей, продаваемых в США, с 2001 — для бензиновых, а с 2004 — для дизельных легковых в Европе (стандарт EOBD). Сегодня OBD-II / EOBD присутствует в подавляющем большинстве легковых автомобилей на ключевых рынках. Грузовики, автобусы и спецтехника — отдельная история: у них другие разъёмы (9-pin Deutsch), другие протоколы (J1939), другое напряжение борт-сети (24 В) и другие требования к защите.

Через этот разъём можно прочитать коды ошибок (те самые «check engine»), посмотреть живые данные с датчиков — обороты двигателя, температуру охлаждающей жидкости, давление турбины, лямбда-зонды, скорость — и даже сбросить ошибки после ремонта.

Звучит просто. Но внутри — несколько несовместимых друг с другом протоколов, которые появлялись в разное время у разных производителей. CAN, K-line, J1850, ISO 9141 — каждый требует своего физического уровня, своей процедуры инициализации, своего формата кадров.

Именно поэтому появился ELM327.


Что такое ELM327

ELM327 — это микросхема канадской компании ELM Electronics, выпущенная в начале 2000-х. Внутри — маленький микроконтроллер с прошивкой, которая:

  1. Принимает простые текстовые команды через UART — так называемые AT-команды (по аналогии с модемами Hayes)

  2. Транслирует их в нужный протокол шины автомобиля

  3. Возвращает ответ тоже в виде читаемых ASCII-строк

Диагностическая программа на телефоне или ноутбуке не знает ничего про CAN или K-line. Она просто отправляет строку 0100\r (запрос PID 00, поддерживаемые параметры) и получает обратно что-то вроде 41 00 BE 3E B8 10\r\n>. ELM327 берёт на себя всю грязную работу с шиной.

Команды ELM327 — это отдельный небольшой язык. Несколько примеров:

Команда Смысл ATZ Сброс адаптера ATI Версия прошивки ATSP6 Установить протокол ISO 15765-4 CAN 11-бит 500 кбит/с ATH1 Включить заголовки фреймов 0100 Отправить OBD запрос: поддерживаемые PID 01-20

Всего спецификация ELM327 v2.3 определяет более 100 AT-команд и 13 протоколов шин. Наша реализация охватывает набор команд, достаточный для прохождения ELM Scan Adapter Validator и работы целевых диагностических приложений.


Проблема: значительная часть дешёвых «ELM327» на рынке — клоны с неполной реализацией

Поищите «ELM327 Bluetooth» на любом маркетплейсе. Дешёвые Bluetooth-адаптеры стоят на порядок дешевле профессиональных устройств (OBDLink, Kiwi), но значительная часть из них — клоны на дешёвых микроконтроллерах с частичной реализацией протокола. Это не оригинальная микросхема ELM Electronics.

Типичные проблемы клонов:

  • Поддерживают только CAN, на K-line машинах не работают вообще

  • Сообщают версию ELM327 v1.5 или v2.1, хотя реализация неполная

  • AT-команды возвращают мусор или не работают вовсе

  • Теряют соединение по Bluetooth под нагрузкой

  • Не сохраняют настройки между сессиями

Профессиональные адаптеры (Kiwi 3, OBDLink MX+) существенно дороже, но содержат нормальную прошивку. Их недостаток: прошивку нельзя изменить под свои нужды.


Зачем писать своё

Причина 1: Понять, как это работает

Диагностика — это не магия. Это конкретные протоколы с конкретными тайм-аутами и процедурами. Написав адаптер с нуля, вы будете точно знать, почему машина не отвечает, почему инициализация занимает 3 секунды, что значит «BUS INIT: ERROR».

Причина 2: Полный контроль

Хотите поддержку нестандартных PID? Особый формат вывода? Логирование на SD-карту? Всё это невозможно в закрытых устройствах, но тривиально, когда прошивка ваша.

Причина 3: Bluetooth-адаптер со своей идентификацией

Если вы разрабатываете диагностическое приложение, вам нужен адаптер с предсказуемым поведением. Свой адаптер = отсутствие сюрпризов от обновлений стороннего firmware.

Причина 4: Учебная задача мирового класса

Реализация ELM327 затрагивает практически все аспекты embedded-разработки: прерывания, UART, CAN, bit-banging, EEPROM, конечные автоматы, тайминги на уровне микросекунд, совместимость протоколов. Лучшей учебной задачи для начинающего embedded-разработчика трудно придумать.


Что мы будем строить

В этой серии мы разберём готовую рабочую ELM327 v2.3-совместимую прошивку, написанную на C для микроконтроллера PIC18F25K80 в среде MPLAB X / XC8.

Прошивка реализует:

  • ELM327 v2.3-совместимый набор AT-команд, достаточный для прохождения ELM Scan Adapter Validator и работы целевых приложений

  • ISO 15765-4 (CAN OBD) — 11/29-бит, 500/250 кбит/с, включая ISO-TP многофреймовый обмен

  • ISO 9141-2 — 5-baud slow init, K-line

  • ISO 14230-4 (KWP2000) — fast init и 5-baud init

  • SAE J1850 PWM и VPW — логика протокола реализована в коде; требует отдельного физического уровня (MC33390 и аналоги), не входит в базовый BOM

  • SAE J1939 — программная поддержка CAN 29-бит кадров; для грузовиков нужна аппаратная версия под 24 В и 9-pin Deutsch разъём

  • Bluetooth через модуль HC-05 с автонастройкой (Android/Windows; для iOS нужен BLE или Wi-Fi модуль)

  • Полное сохранение настроек в EEPROM

  • Диагностические счётчики и отладочные команды

Прошивка проходит тест совместимости ELM Scan Adapter Validator (ELM 2.3). В последней главе подробно разберём результаты тестирования, что именно проверялось и какие незначительные расхождения с оригинальным поведением остаются (они не мешают работе целевых приложений).


Требуемые знания

Серия рассчитана на начинающих embedded-разработчиков. Мы предполагаем, что вы:

  • Знаете основы C (указатели, структуры, битовые операции)

  • Слышали про микроконтроллеры и регистры периферии

  • Имеете общее представление о том, что такое UART и SPI

Знания CAN, K-line, J1850, ISO-TP не требуются — мы объясним каждый протокол с нуля, начиная с физического уровня.


Что вам понадобится для практики

Готовый dev-kit (собранная плата с PIC18F25K80, TJA1050, K-line драйвером и HC-05) доступен для приобретения — подробности в комментариях к статье.

Компонент Примечание PIC18F25K80 Основной МК. Доступен в DIP-28 PIC18F26K80 Альтернатива с большей Flash TJA1050 CAN трансивер Драйвер K-line (LIN) Например, L9637D или TJA1020 HC-05 Bluetooth модуль PICkit 3/4 Программатор MPLAB X + XC8 Бесплатный компилятор OBD-II J1962 male connector или пиг-тейл Для подключения к машине


Структура серии

# Статья Тема 0 Эта статья Обзор серии 1 Глава 1 Выбор железа и конфигурация МК 2 Глава 2 Архитектура прошивки: главный цикл, ISR, структуры 3 Глава 3 UART и Bluetooth: буферизация и автонастройка HC-05 4 Глава 4 Таймеры, EEPROM и системные утилиты 5 Глава 5 CAN шина: инициализация, отправка, приём, фильтры 6 Глава 6 ISO-TP: многофреймовый обмен поверх CAN 7 Глава 7 K-line: ISO 9141 и KWP2000 от физики до протокола 8 Глава 8 J1850 PWM и VPW 9 Глава 9 Диспетчер протоколов и OBD-запросы 10 Глава 10 Парсер AT-команд 11 Глава 11 Тестирование совместимости с ELM327

Полный исходный код со схемами и проектом MPLAB X будет опубликован в последней статье серии.

Показать полностью 3
0

А давайте по чесноку, излишества в еде...

филе трески 100г. соль 3 атома = 155р. в аэрогриле 20 минут при 175с, спаржа + морковь добавить за пять минут до окончания

филе трески 100г. соль 3 атома = 155р. в аэрогриле 20 минут при 175с, спаржа + морковь добавить за пять минут до окончания

Мой ежедневный обед, за два месяца сбросил 5кг. от 114кг

Язык не поворачивается описать это блюдо рецептом, но всё же, рецепт в описании к фото

Масштаб проблемы: цифры и реалии

Статистика, связанная с питанием и весом в России, вызывает серьезную тревогу у специалистов.

  • Согласно данным Института питания, избыточный вес есть у 60% женщин и половины мужчин старше 30 лет.

  • Более половины россиян (54%) признаются, что хотя бы иногда переедают, а 9% делают это регулярно. Особенно эта привычка распространена среди людей в возрасте 35–44 лет — здесь переедает каждый восьмой (12%).

  • При этом лишь около трети граждан (33%) целенаправленно стараются употреблять здоровую пищу.

Парадоксально, но финансовые ограничения часто становятся барьером на пути к здоровому рациону: 23% россиян заявили, что не имеют возможности думать о качестве пищи и едят то, что могут себе позволить.

Самый простой вкусный и полезный рецепт обеда для для КМ.

Показать полностью 1
4

Прощай, терминальный хаос: пишем свой TUI-менеджер port-forward для Kubernetes на Go

Прощай, терминальный хаос: пишем свой TUI-менеджер port-forward для Kubernetes на Go

Введение

Каждый, кто работает с Kubernetes, знает эту боль. Утро начинается с того, что нужно подключиться к базе данных в production для дебага, потом к Redis в staging для проверки кэша, затем к RabbitMQ для мониторинга очередей, и наконец к API-сервису для тестирования нового эндпоинта.

И вот уже восемь открытых терминалов, в каждом — свой kubectl port-forward. Окна перемешиваются, названия похожи, и найти нужный терминал становится квестом.

Ну да да, можно использовать Tmux, но это не сильно облегчает процесс.

С какими проблемами я столкнулся

  • Потеря контекста — когда одно из соединений падает, приходится перебирать все терминалы, чтобы найти нужный. На это уходит драгоценное время.

  • Ручная работа при смене контекста — переключил kubectl context на другой кластер, и всё нужно перенастраивать заново. Каждый раз одни и те же команды.

  • Отсутствие истории — вернулся после обеда, половина соединений отвалилась. Какие именно порты были нужны? Приходится вспоминать или искать в истории bash.

  • Сложность передачи знаний — коллега просит команду для подключения к сервису. Диктуешь по буквам namespace, имя пода, номера портов. Это неэффективно и приводит к ошибкам.

Я решил, что пора автоматизировать этот процесс и написать специализированный инструмент, исходник на GitHub PortFwd

Анализ существующих решений

Прежде чем писать своё, я изучил, что уже есть на рынке. Оказалось, что решения существуют, но каждое имеет свои плюсы и минусы.

kubectl port-forward

Встроенный инструмент, который работает надёжно и стабильно. Но у него принципиальное ограничение: один терминал — одно соединение. При работе с микросервисной архитектурой, где нужно одновременно держать открытыми 5-10 соединений, это превращается в хаос.

kubefwd

Интересный проект, который автоматически форвардит все сервисы из namespace. Звучит удобно, но есть серьёзные проблемы. Во-первых, он требует права sudo, потому что модифицирует /etc/hosts. Во-вторых, это слишком инвазивно — я хочу контролировать, какие именно сервисы форвардить, а не получать всё скопом.

Lens

Красивый графический интерфейс с множеством функций для работы с Kubernetes. Но это Electron-приложение, которое потребляет более 500 МБ оперативной памяти и 2% CPU даже в режиме простоя. Для такой простой задачи, как управление port-forward, это явный overkill. Плюс Lens — это комбайн, а мне нужен специализированный инструмент.

k9s

Отличный TUI для работы с Kubernetes, который я сам активно использую. Но port-forward там — не основная функция. Управлять множеством одновременных соединений неудобно, нет сохранения сессий, нет группировки.

После этого анализа я понял, что нужно писать своё решение. Лёгкое, специализированное, без Electron'а.

kftRay

Отдельно рассмотрю и сравню проект https://kftray.app/

Сравнение этих двух инструментов интересно тем, что они решают одну и ту же задачу — управление пробросом портов (port-forwarding) в Kubernetes — но делают это с совершенно разных позиций.

Характеристика kftray (Rust) portFwd (Go) Язык программирования Rust (фреймворк Tauri) Go Интерфейс Полноценный GUI (графическое окно в трее) CLI (командная строка) + кастомный UI Сложность Высокая (визуальное управление множеством конфигов) Минималистичная (быстрый запуск из терминала) Кроссплатформенность Отличная (Windows, macOS, Linux) Отличная (нативный бинарник) Целевая аудитория Те, кто хочет «настроить и забыть» через интерфейс Те, кто предпочитает терминал и скорость

Почему для такого проекта Go может быть лучше?

Несмотря на то, что kftray на Rust выглядит очень современно, использование Go для portFwd дает несколько фундаментальных преимуществ в контексте Kubernetes:

  1. Нативная экосистема Kubernetes: Весь Kubernetes (и kubectl) написан на Go. Используя Go, разработчик portFwd применяет те же самые библиотеки (client-go), которые используют создатели K8s. Это гарантирует 100% совместимость с конфигами kubeconfig, контекстами и методами аутентификации.

  2. Стабильность сетевых соединений: В Go работа с сетью и многопоточностью (Goroutines) реализована «из коробки» очень эффективно. Проброс портов — это постоянное ожидание трафика и пересылка пакетов. Горутины позволяют обрабатывать сотни таких соединений с минимальным потреблением памяти и без риска «уронить» поток.

  3. Скорость компиляции и деплоя: Если вам нужно быстро внести правку в логику проброса портов, Go скомпилирует бинарник за секунды. Rust (особенно с тяжелым Tauri/GUI) собирается значительно дольше.

  4. Размер и простота: Для системной утилиты, которая должна просто «перекидывать байты» из кластера на локальную машину, Go предлагает более простой и читаемый код. В Rust-проекте (как kftray) много времени уходит на управление памятью и согласование типов интерфейса, в то время как в Go-проекте фокус остается на сетевой логике.

Плюсы portFwd (на Go)

  • Минимализм: Идеально подходит для автоматизации и скриптов.

  • Низкий порог входа: Если вы захотите доработать инструмент под себя, разобраться в коде на Go будет в разы проще, чем в Rust-коде с GUI-обвязкой.

  • Работа с контекстами: Go-библиотеки лучше всего справляются с переключением между десятками кластеров и сложными методами SSO-логина в облака (AWS/GCP/Azure).

Плюсы kftray (на Rust)

  • Визуальный комфорт: Если у вас 20+ портов, управлять ими через иконку в трее удобнее, чем держать открытыми 5 вкладок терминала.

  • Безопасность памяти: Rust исключает целый класс ошибок сегментации, что важно для долгоживущих фоновых процессов.

Итог

Если вам нужен инструмент для ежедневной рутины с красивыми кнопками — выбирайте kftray. Если вам нужна надежная, быстрая и легкая утилита, которая работает так же, как сам Kubernetes — portFwd на Go будет более естественным и предсказуемым выбором.

Теория: как работает port-forward в Kubernetes

Прежде чем писать реализацию, важно понять, как port-forward работает под капотом. Без этого понимания легко написать код, который будет падать в неожиданных местах.

Архитектура соединения

Когда вы выполняете kubectl port-forward pod/nginx 8080:80, данные проходят через несколько компонентов (см. рис. 1). Клиент устанавливает соединение с API Server, который проксирует запрос на Kubelet ноды, где запущен под. Kubelet, в свою очередь, использует nsenter для доступа к network namespace контейнера и пересылает данные процессу внутри.

рис. 1: Архитектура port-forward в Kubernetes

Протокол SPDY

Для мультиплексирования потоков Kubernetes использует протокол SPDY — предшественник HTTP/2. Это позволяет передавать несколько потоков данных через одно TCP-соединение. Процесс установки соединения выглядит так:

  1. Клиент отправляет HTTP POST запрос на эндпоинт /api/v1/namespaces/{ns}/pods/{pod}/portforward

  2. В заголовке указывается Upgrade: SPDY/3.1, что инициирует переключение протокола

  3. API Server устанавливает SPDY-соединение и создаёт потоки для данных и ошибок

  4. Kubelet получает запрос и проксирует данные в контейнер

Критический нюанс: Service vs Pod

Это один из самых важных моментов, который я понял не сразу. Хотя Kubernetes API формально поддерживает эндпоинт /services/{service}/portforward, на практике kubectl резолвит сервис в под и подключается напрямую к поду.

Почему так? Потому что port-forward работает на уровне конкретного контейнера через Kubelet, а не через механизм балансировки kube-proxy. Это значит, что нам тоже придётся находить backing pod для сервиса и резолвить targetPort.

Архитектура приложения

После изучения теории я спроектировал архитектуру PortFwd. Приложение разделено на несколько слоёв, каждый из которых отвечает за свою область.

Слой Kubernetes (k8s/client.go)

Этот компонент инкапсулирует всю работу с Kubernetes API. Он отвечает за получение списка namespace'ов, подов и сервисов, а также за резолвинг сервисов в поды. Использует официальную библиотеку client-go.

Менеджер соединений (portforward/manager.go)

Центральный компонент, который управляет жизненным циклом всех port-forward соединений. Он создаёт SPDY-транспорт, отслеживает статус каждого соединения, обрабатывает ошибки и уведомляет UI об изменениях через callback.

Слой UI (ui/app.go, views.go, styles.go)

Терминальный интерфейс построен на фреймворке Bubble Tea с использованием библиотеки Lipgloss для стилизации. Bubble Tea реализует Elm Architecture: Model (состояние), Update (обработка событий), View (рендеринг).

Конфигурация (config/state.go)

Компонент для сохранения и восстановления сессий. При выходе из приложения текущие соединения сохраняются в YAML-файл, а при следующем запуске — восстанавливаются.

Реализация: работа с Kubernetes API

Инициализация клиента

Для работы с кластером нужно инициализировать клиент. Я использовал паттерн из официальных примеров client-go: сначала пробуем получить in-cluster конфиг (если приложение запущено внутри пода), а если не получается — читаем kubeconfig файл.

Ключевой момент — использование clientcmd.BuildConfigFromFlags, которая автоматически учитывает текущий context из kubeconfig. Это позволяет приложению работать с тем же кластером, что и kubectl.

Резолвинг Service в Pod

Когда пользователь выбирает сервис для port-forward, нам нужно найти backing pod. Алгоритм следующий:

  1. Получаем сервис — делаем GET-запрос к API для получения спецификации сервиса

  2. Резолвим targetPort — сервис может иметь port: 80, но контейнер слушает на targetPort: 8000. Нужно использовать именно targetPort

  3. Строим label selector — из поля spec.selector сервиса формируем строку для поиска подов

  4. Находим Running под — получаем список подов по selector и выбираем первый со статусом Running

Обработка Named Ports

Отдельная сложность — именованные порты. Сервис может указывать targetPort: http вместо числа. В этом случае нужно найти порт с таким именем в спецификации контейнера пода. Без этой обработки приложение будет пытаться подключиться к неправильному порту.

Это была одна из главных граблей в разработке. Я потратил несколько часов, разбираясь, почему соединение отказывает, пока не понял, что приложение стучится в порт 80, а процесс слушает на 8000.

Реализация: SPDY port-forward

Создание транспорта

Для установки port-forward соединения используется несколько компонентов из client-go:

  • spdy.RoundTripperFor — создаёт HTTP transport с поддержкой SPDY upgrade

  • spdy.NewDialer — создаёт dialer, который умеет устанавливать SPDY-соединения

  • portforward.NewOnAddresses — собственно создаёт port-forwarder

Важный момент: я использую NewOnAddresses с параметром []string{"127.0.0.1"}, а не стандартный New. Это заставляет слушать только на IPv4. По умолчанию forwarder слушает и на IPv4, и на IPv6, что на некоторых системах (особенно macOS) вызывает проблемы с dual-stack.

Управление жизненным циклом

Каждое соединение имеет несколько каналов для управления:

  • stopChan — сигнал остановки, закрытие этого канала прерывает ForwardPorts

  • readyChan — сигнал готовности, сообщает что туннель установлен

  • context с cancelFunc — для graceful shutdown

При запуске соединения мы ждём либо сигнала готовности (соединение установлено), либо ошибки, либо таймаута (30 секунд). После установки соединения переходим в режим ожидания завершения.

Реализация: TUI на Bubble Tea

Почему Bubble Tea?

Bubble Tea от Charm — это фреймворк для построения терминальных интерфейсов на Go, вдохновлённый Elm Architecture. Он предоставляет чистую модель программирования:

  • Model — иммутабельное состояние приложения (текущий view, список соединений, выбранный элемент)

  • Update — чистая функция, которая принимает сообщение и возвращает новое состояние

  • View — чистая функция, которая рендерит состояние в строку для терминала

Этот подход делает код предсказуемым и легко тестируемым. Состояние всегда консистентно, потому что изменяется только через Update.

Стилизация с Lipgloss

Для визуального оформления я использую Lipgloss — библиотеку для стилизации терминального вывода. Она позволяет задавать цвета, отступы, границы, выравнивание — всё, что нужно для красивого интерфейса.

Я выбрал цветовую схему в стиле киберпанка: неоновый зелёный для активных соединений, красный для ошибок, жёлтый для предупреждений. Это делает статусы соединений мгновенно различимыми.

Связь UI и Manager

Менеджер соединений работает асинхронно — соединения устанавливаются и падают в фоне. Чтобы UI узнавал об изменениях, я использую callback-паттерн. При инициализации UI устанавливает callback в менеджере, и при любом изменении состояния соединения менеджер вызывает этот callback, который отправляет сообщение в Bubble Tea.

Реализация: сохранение состояния

Зачем это нужно

Одна из главных фич PortFwd — сохранение сессий между запусками. Вы настроили 5 port-forward соединений, закрыли приложение, а при следующем запуске — все соединения автоматически восстанавливаются.

Формат хранения

Состояние сохраняется в YAML-файл по пути ~/.config/portfwd/state.yaml. Для каждого соединения сохраняются: namespace, тип ресурса (pod или service), имя ресурса, локальный и удалённый порты, и флаг wasActive — было ли соединение активно при сохранении.

Флаг wasActive важен: если пользователь вручную остановил соединение перед выходом, при следующем запуске оно должно остаться остановленным, а не переподключаться автоматически.

Логика восстановления

При запуске приложение читает файл состояния и для каждого сохранённого соединения проверяет:

  1. Если wasActive: false — добавляем как остановленное, не подключаемся

  2. Если wasActive: true — проверяем доступность ресурса (существует ли под/сервис)

  3. Если ресурс доступен — автоматически переподключаемся

  4. Если ресурс недоступен — добавляем как остановленное с ошибкой

Грабли, на которые я наступил

Эта секция — самая ценная часть статьи. Здесь собраны реальные проблемы, на которые я потратил часы отладки.

Проблема 1: Зависание при выходе

Симптом: после нажатия q приложение не завершается, висит бесконечно.

Причина: callback onChange вызывает program.Send() в Bubble Tea. После вызова tea.Quit программа закрывает канал сообщений, и Send() блокируется навечно, ожидая возможности отправить сообщение.

Решение: отключать callback до остановки соединений. Сначала делаем m.onChange = nil, потом останавливаем соединения. Тогда при изменении состояния соединения callback не вызывается и deadlock не возникает.

Проблема 2: connection refused при подключении к сервису

Симптом: при port-forward к сервису получаем ошибку connection refused внутри контейнера.

Причина: я использовал port сервиса (например, 80) вместо targetPort (например, 8000). Приложение в контейнере слушало на 8000, а мы стучались в 80.

Решение: полный резолвинг targetPort, включая обработку named ports. Нужно всегда использовать targetPort, а не port сервиса.

Проблема 3: panic при повторном закрытии канала

Симптом: panic: close of closed channel при быстром двойном нажатии на «Stop».

Причина: канал stopChan закрывается дважды. В Go закрытие уже закрытого канала вызывает panic.

Решение: использовать sync.Once для защиты операции закрытия. Once гарантирует, что функция выполнится только один раз, даже при конкурентных вызовах.

Проблема 4: IPv6 connection refused

Симптом: на некоторых системах получаем ошибку IPv6 dial tcp6 [::1]:80: connection refused.

Причина: по умолчанию port-forwarder слушает и на IPv4, и на IPv6. На системах с определёнными сетевыми настройками это вызывает проблемы.

Решение: явно указывать только IPv4 через NewOnAddresses с параметром []string{"127.0.0.1"}.

Проблема 5: соединения не восстанавливаются при рестарте

Симптом: при запуске приложения список соединений пустой, хотя файл состояния существует.

Причина: я вызывал saveSessionState() после StopAll(). К моменту сохранения все соединения уже были удалены из менеджера.

Решение: изменить порядок: сначала сохраняем состояние (когда соединения ещё существуют), потом останавливаем их.

Альтернативы, от которых я отказался

WebSocket вместо SPDY: Kubernetes поддерживает оба протокола, но client-go имеет готовую и протестированную реализацию именно для SPDY. Писать свой WebSocket-клиент — лишняя работа без явных преимуществ.

Прямое подключение к Kubelet: Теоретически можно обойти API Server. Но это требует отдельной аутентификации, обхода сетевых ограничений и не даёт никаких преимуществ для нашей задачи.

Тестирование и результаты

Производительность

Я измерил потребление ресурсов PortFwd в сравнении с альтернативами при 5 активных соединениях в режиме простоя:

  • PortFwd: ~30 МБ RAM, ~0.1% CPU

  • Lens: ~500 МБ RAM, ~2% CPU

  • 5 отдельных kubectl: ~100 МБ RAM, ~0.5% CPU

PortFwd потребляет в 15 раз меньше памяти, чем Lens, и запускается мгновенно (менее 100 мс против ~5 секунд у Lens).

Функциональность

  • ✅ Единое окно для всех соединений

  • ✅ Интерактивный выбор: namespace → тип ресурса → pod/service → порты

  • ✅ Автоматический резолвинг targetPort для сервисов

  • ✅ Сохранение и восстановление сессий между запусками

  • ✅ Отдельные логи для каждого соединения

  • ✅ Graceful shutdown без zombie-процессов

  • ✅ Справка по горячим клавишам

Горячие клавиши

  • ↑/↓ или j/k — навигация по списку

  • n — создать новое соединение

  • d — отключить выбранное соединение

  • D — отключить все соединения

  • r — переподключить выбранное

  • l — показать логи соединения

  • x — удалить остановленное соединение

  • ? — показать справку

  • q — выход

Заключение

Что получилось

Я создал специализированный инструмент, который решает конкретную проблему — управление множеством port-forward соединений в Kubernetes. PortFwd легковесный, быстрый и делает одну вещь хорошо.

Что можно улучшить

  • Профили — сохранение наборов соединений для разных окружений (dev, staging, prod) с быстрым переключением

  • Multi-cluster — работа с несколькими кластерами одновременно в одном окне

  • Auto-reconnect — автоматическое переподключение при потере связи без участия пользователя

  • Import — импорт соединений из kubectl команд или YAML-манифестов

Уроки, которые я извлёк

  1. Читай исходники kubectl — это лучшая документация по работе с Kubernetes API. Официальная документация не покрывает многие нюансы.

  2. Тестируй на разных системах — dual-stack IPv4/IPv6 ведёт себя по-разному на Linux и macOS. То, что работает у тебя, может сломаться у пользователя.

  3. sync.Once — твой друг — каналы в Go закрываются только один раз, и это легко забыть при конкурентном доступе.

  4. Порядок операций критичен — особенно при graceful shutdown. Сначала сохраняй состояние, потом останавливай процессы.

Полезные ссылки


Буду рад звёздам на GitHub, issue с багами и pull request'ам с улучшениями. Спасибо за внимание!

Показать полностью

ИИ кодинг — это чит? Давайте запретим?

Серия ИИшница

Я написал первую строку кода в 10 лет, ИИ написала мне полностью рабочую программу в 2025

ИИ кодинг — это чит? Давайте запретим?

Технические аспекты: инструмент или «костыль»?

С технической стороны, ИИ-инструменты вроде Copilot, ChatGPT и прочих для написания кода — это не магические «чёрные ящики», а сложные модели, обученные на огромных объёмах открытого кода и документации. Они:

  • Ускоряют рутинные задачи — генерация шаблонного кода, написание тестов, документация.

  • Помогают искать решения — предлагают варианты, которые программист может адаптировать.

  • Снижают порог входа — новички быстрее осваивают синтаксис и паттерны.

Но есть и риски: ИИ может предлагать устаревшие, небезопасные или некорректные решения, как вверху. Бездумное копирование сгенерированного кода без понимания его работы ведёт к хрупким, плохо поддерживаемым системам. ИИ — это не замена программисту, а инструмент, эффективность которого зависит от квалификации пользователя.

Моральные аспекты: где грань между помощью и обманом?

1. Авторство и интеллектуальная собственность

ИИ обучается на открытых репозиториях, что вызывает вопросы: не является ли его вывод плагиатом? Пока код не копируется дословно, а переосмысливается — это ближе к тому, как учится человек. Однако ответственность за конечный продукт несёт программист, а не ИИ.

2. Честность и профессиональная этика

Использование ИИ становится «читерством», если:

· Выдаётся сгенерированный код за полностью свой.

· Маскируется некомпетентность.

· Нарушаются правила конкурсов или экзаменов.

В обычной работе, где цель — эффективный и качественный продукт, использование ИИ этично, если оно прозрачно для команды и заказчика.

3. Потеря навыков

Опасение, что ИИ сделает программистов ненужными, преувеличено. Скорее, смещается фокус навыков: меньше рутинного кодинга, больше — архитектурного мышления, анализа требований, проверки и адаптации результатов ИИ.

Запретить или адаптироваться?

Запрет ИИ в программировании напоминает историю с запретом калькуляторов в на экзамене. Да, бездумное использование вредит, но разумное — освобождает время для сложных задач. Вместо запретов нужны:

1. Новые стандарты образования — учить не только писать код, но и эффективно взаимодействовать с ИИ, критически оценивать его предложения.

2. Корпоративные политики — определить, где использование ИИ допустимо, а где требует обязательной проверки.

3. Этические кодексы — закрепить прозрачность в использовании ИИ-инструментов.

Заключение: ИИ — это новый «компилятор»

Раньше программисты писали на ассемблере, потом появились языки высокого уровня и компиляторы. Это не сделало разработку «чит-кодом», а подняло её на новый уровень. ИИ — следующая ступень абстракции.

Запрещать ИИ — значит отрицать эволюцию инструментов. Задача профессионала — не отвергать технологии, а интегрировать их с сохранением ответственности, критического мышления и этических принципов. ИИ не заменит программиста, но программист, использующий ИИ, заменит того, кто его игнорирует.

Вопрос не в том, «читерство» ли это, а в том, как мы, как сообщество, адаптируем нашу профессию к новым реалиям, сохранив её суть: создание качественных, безопасных и полезных цифровых решений.

Показать полностью 1

Моя профессия тихо сливается в с ИИ. Это красиво, но грустно

Серия ИИшница

Привет, Пикабу! Сижу, наблюдаю, как нейросеть за пять секунд пишет код, на который я в своё время потратил бы вечер, пачку печенек и серию нервных тиков. И знаете что? Я не грущу. Я… восхищаюсь.

Моя работа теперь — не писать код, а быть продвинутым заказчиком для безмозглого (пока что) железа. Перешёл из разряда «рабочий у станка» в «начальник цеха, который тыкает пальцем в планшет и говорит: “Сделай красиво”».

Мои новые профессиональные навыки:

1. Искусство надиктовывания. Раньше думал о синтаксисе, теперь думаю, как объяснить задачу так, чтобы ИИ не сделал калькулятор вместо интернет-магазина. «Нет, дружок, “сделай как у Amazon” — это не конкретно. Вот тебе 15 уточняющих пунктов».

2. Величайший верификатор. Я — тот самый человек, который смотрит на идеальный сгенерированный код и говорит: «А вот здесь, милый, у тебя вечная петля. Учи матчасть!» Чувствую себя сэнсеем, проверяющим ученика.

3. Мастер кнопки «Обновить». Когда ИИ выдаёт дичь, я не плачу. Я с загадочной улыбкой жму F5 и говорю: «Давай ещё разок, только с чувством, с толком, с расстановкой».

Обнаружил, что теперь у меня в два раза больше времени! Раньше тратил его на гугление ошибок, а теперь — на гугление мемов, который гуглит ошибки за меня. Круг замкнулся. Все счастливы.

Коллега пытался устроить соревнование: он против ChatGPT в написании скрипта. ИИ сделал за 10 секунд. Коллега — за полчаса, но с тремя чашками кофе и самоутверждением. Победила дружба. И кофе.

Следующий этап карьеры — стать «шаманом промптов». Буду водить курсы: «Как шепнуть нейросети на ушко, чтобы она сделала всё правильно и принесла вам кофе». Дорого беру.

А если серьёзно, я теперь не «программист-кодер». Я — «программист-стратег, дирижёр цифрового оркестра и ловец глюков». Звучит солиднее, да?

В общем, я не умираю. Я эволюционирую. Просто моя эволюция теперь измеряется не в строках кода, а в креативности запросов и скорости удаления дичи из ответов ИИ.

И да, этот пост, конечно же, писал я. А то вдруг подумаете, что я уже делегировал и это машина. Пока не делегировал. Но идея хорошая…

Ща спрошу у ИИ, как он сам к этому относится.

Показать полностью
10

Призраки Авито: как площадка с «объявлениями от соседей» стала царством фантомных контор

Только мой опыт или знакомо всем ?

Открываешь Авито в поисках чего-то простого. Нужен мастер, услуга, конкретная вещь с возможностью посмотреть прямо сегодня. Вбиваешь в поиск: Выдаёт десятки предложений. «Отлично!» — думаешь ты. Теперь только выбрать по отзывам и близости к дому. А вот и нет.

1. Карта, которая обманывает лучше любого навигатора

Открываешь карту. Точки — как россыпь конфет. Вот оно, нужное, буквально в двух шагах, в соседнем бизнес-центре! Звонишь.

— Алло, вы находитесь по адресу на карте, улица Центральная, 10?

— Да, слушаю вас! — отвечает бодрый голос.

— Супер, я рядом, могу через 10 минут подъехать, посмотреть/обсудить?

Пауза. Затем стандартная скороговорка:

— Нет, это у нас офис для документов, а склад/мастер/специалист сейчас в [другом конце города]. Может подъехать к вам через три часа. Или у нас есть другой вариант, но дороже.

Копаешь дальше. Из первых 20 объявлений 18 оказываются вот такими «призраками». Их «офисы» на карте — это:

Виртуальные адреса в бизнес-центрах (аренда почтового ящика, по сути).

Адреса массовой регистрации фирм-однодневок.

*  Просто случайные точки в центре для красоты и привлечения кликов.

2. Великий и ужасный Агрегатор из Ниоткуда

Решаешь докопаться. Начинаешь звонить подряд, спрашиваешь: «А какой у вас точный адрес, куда можно приехать?». В ответ — нервный смешок, заученный скрипт или откровенное: «Мы агрегатор, у нас специалисты по всему региону».

Одна контора из какого-нибудь условного Нефтеюганска натыкала сотни объявлений по всем городам России. У них нет НИ ОДНОГО реального представительства в твоём городе. Они просто берут заявку, а потом в панике ищут в своих чатах какого-нибудь свободного исполнителя, предлагая ему копейки. А ты платишь втридорога.

Что это за зверь? Признаки Агрегатора-Призрака:

1.  «Офис» на карте есть, но на панораме Yandex — жилой дом, пустырь или совсем другая контора.

2.  Один телефон на 100 разных услуг — от услуг мастера до продажи шин и переездов. а Авито это еще и скрыл для пользователей

3.  В профиле 300+ одинаковых объявлений в географии от Калининграда до Владивостока.

4.  На прямой вопрос «Где ваш офис?» начинается путаница и перевод на «менеджера».

5.  Отзывы либо пусто, либо шаблонные, как под копирку.

Акт 3. Почему это не просто «ой, ну что ты придрался»?

1.  Цена. Ты платишь накрутку дяде из другого города, который просто нажал кнопку «разместить».

2.  Качество. Его не существует. Агрегатору всё равно, кого он найдёт. Гарантии, ответственность, репутация — нулевые.

3.  Безопасность. В случае с услугами — ты впускаешь в дом или отдаёшь вещь человеку, о котором посредник знает только номер. Ни тебе договора, ни проверки.

Итог. Что делать? Инструкция по выживанию в эпоху фантомов:

1.  Забудьте про карту на Авито. Она теперь — игровое поле для «найди призрака», а не инструмент поиска.

2.  Ищите в обход. Вбивайте в Yandex или 2ГИС «[Нужная услуга/товар] [Ваш город] отзывы форум». Чаще найдёте нормальных местных специалистов или продавцов на тематических форумах или в пабликах.

3.  Проверяйте профиль. Одна-две услуги, один город, живые фото с объектов или товара, история отзывов — хорошие признаки.

4.  Требуйте чек и документы. Любой мало-мальски серьёзный исполнитель (ИП или ООО) сможет его предоставить. Агрегатор-однодневка — нет.

Это же полный провал идеи? Авито из условной «доски объявлений от соседей» превратился в помойку агрегаторов-паразитов. Они душат честных мастеров и продавцов, которым лень или некогда бороться с этими ботами за первые места в поиске. Я один такой «везучий» ?

Давайте в комментариях:

1.  + если сталкивались.

2. Советуйте, как всё-таки находить реальных людей в 2025 году. Может, есть другие площадки?

Показать полностью
10

Мой Туарег решил, что антифриз — это слёзы моих денег. История старая, но в трёх радиаторах

Серия низ Авторынка

Всем привет.

Сижу, пью валермейстер, ибо мой Volkswagen Touareg устроил мне квест под названием «угадай, откуда потечёт в этот раз». Делюсь историей, чтобы вы либо посмеялись, либо содрогнулись.

Эпизод 1. Классика жанра.

Жил-был патрубок. А на нём — быстросъёмная фиговина. Как и у 90% владельцев VAG, она благополучно треснула. Заказал новую, поставил. На 5 минут почувствовал себя Вольфсбургским механиком. «Легчайшая работа!» — подумал я.

Наивный.

Эпизод 2. Сюжетный поворот.

Через пару поездок бортовой компьютер выдал: «Низкий уровень антифриза». Смотрю — капает уже не с патрубка, а с самого радиатора. Тот самый стык пластика и алюминия. «Хм, — подумал я, — значит, дело глубже».

Полез в интернеты. Оказалось, есть теория заговора: виновата может быть крышка расширительного бачка. Если она не стравливает давление, система превращается в скороварку и ломает всё, что сделано из пластика. Логично.

Эпизод 3. Экономическая версия.

Решил: меняю радиатор и крышку разом. Выбор пал на радиатор Luzar LRc1858 и на крышку SWAG 40940722

— дёшево, и вроде как отзывы нормальные. Поставил, залил, проверил — сухо!

«Вот оно, счастье!» — подумал я.

Всего на одну неделю.

Потом заметил лёгкое запотевание на том же стыке. «Не может быть! — подумал я. — Это крышка-убийца!». Начитался ужасов, что даже оригинальные крышки — лотерея.

Эпизод 4. Паранойя и наука.

Купил переходник под крышку. Проверил давление срабатывания старой и новой крышки. Обе — в норме.

Моя теория рассыпалась. Значит, дело в радиаторе? В неудачном экземпляре? Могло ли быть такое? Оказалось, запросто.

Эпизод 5. Решение ядерной войны.

Настал момент Истины. Погуглил и понял, что Luzar, Natec и им подобные — это часто просто бренды-упаковщики. Заказывают на разных заводах в Китае/Турции, и качество может плавать.

Для такого монстра как Touareg лучше брать того, кто делает радиаторы для конвейера. Выбор пал на MAHLE/KNECHT CR1183000P

(это OEM-поставщик, а не просто имя на коробке).

Цена вопроса была, конечно, ощутимой. Но что делать — купил, поставил.

Итог: Прошло уже более 3 месяцев — ПОЛНОЕ БЕЗМОЛВИЕ И СУХО. Уровень антифриза не двигается. Я наконец-то сплю спокойно.

А вот качество наглядно это и в других радиаторах встречается наверно до 1.5 бар выдержит и такое соединение

а вот такое выдержит и 6 бар

Мораль этой истории для тех, у кого похожая беда:

1. Первое подозрение при любой течи на VAG — крышка расширительного бачка. Проверьте её, даже если она новая. Бракованных — море.

2. Радиаторы «нонейм» (Luzar и аналоги) — это лотерея. Может повезёт и отъездит 5 лет. А может, как у меня, попадётся косячный. Это не производитель, а перекупщик.

3. На таких машинах иногда лучше сразу переплатить за OEM-уровень (BEHR, Nissens, Mahle), чем менять дважды, теряя время, антифриз и нервы. Дешевле в долгосрочной перспективе.

4. Самый главный вывод: Если вы не готовы к таким квестам — не покупайте сложные немецкие внедорожники б/у. Покупайте Ладу. У неё тоже всё течёт, но вы хотя бы будете знать, куда точно и почему это дешевле.

Всем добра и сухих патрубков!

Показать полностью 2
10

Купил низ рынка, Tuareg год спустя

Серия низ Авторынка

Привет, пикабушники!

Ровно год назад купил Volkswagen Touareg NF.

Конкретика:

  • · Год: 2012

  • · Двигатель: 3.6 FSI (249л.с.)

  • · Коробка: tr80sd

Покупался за свои, осознанно. Имею доступ к хорошим мастерам. Хочу поделиться не эмоциями, а конкретным списком проблем, решений и, что важнее всего, сумм, которые пришлось вложить помимо планового ТО (масла, фильтры).

Честно, за год машина проявила характер. Вот чек-лист «радостей» первого года владения с таким пробегом:

1. Проблема: Двигатель начал глохнуть и подтраивать на холостых, особенно после холодного пуска. Сканер показывал пропуски.

· Решение: Замена одной топливной форсунки. Диагностика показала, что одна "умерла".

· Что куплено/сделано: Одна новая форсунка (оригинал), замена, промывка рампы, адаптация.

· Стоимость: ≈ 25000руб.

2. Проблема: Легкие, но неприятные пинки (толчки) в АКПП при переключениях.

· Решение: Полная замена масла ATF в АКПП с фильтром и последующая адаптация на спецоборудовании.

· Что куплено/сделано: Комплект масла Toyota ws (8 литров), оригинальный фильтр, работа.

· Стоимость: ≈ 23000руб.

3. Проблема: Появился отчетливый металлический стук спереди слева на мелких кочках.

· Решение: Замена левого нижнего рычага передней подвески в сборе.

· Что куплено/сделано: Рычаг в сборе vika 44071717901 работа.

· Стоимость: ≈ 16 000 руб.

4. Проблема: Вибрация на руле при торможении. Диски "повело".

· Решение: Замена передних тормозных дисков и колодок. Заодно, для равномерности, поменял задние колодки.

· Что куплено/сделано: Диски ctr GD0012 и колодки передние vika 66980008101, колодки задние vika 66981695401, работа.

· Стоимость: ≈ 18 000 руб.

5. Проблема: Запотевание двигателя, запах горелого, следы масла в районе клапанной крышки.

· Решение: Замена клапанной крышки.

· Что куплено/сделано: Комплект прокладок клапанной крышки vika 11031808101, работа.

· Стоимость: ≈ 12 000 руб.

Итоговая сумма на ремонт за год:

≈ 94 000 рублей. (Это БЕЗ учета плановой замены моторного масла, фильтров и прочих жидкостей).

Выводы и наблюдения:

· Пробег почти 400к км — это серьезно. Большинство узлов выработали свой ресурс и живут на латании. Нужно быть готовым ко всему.

· Двигатель 3.6 FSI — не самый проблемный, но дорогой в запчастях (те же форсунки). Расход топлива в смешанном цикле — около 14-15 л/100км.

· АКПП — очень надежная коробка, но требует своевременной замены масла.

ОТдельные темы допишу о качестве современных запчастей…

#авто #tuareg #фольксваген #volkswagen #опытэксплуатации #история #ремонт #пикабуавто #немецкиеавто

Показать полностью
Отличная работа, все прочитано!

Темы

Политика

Теги

Популярные авторы

Сообщества

18+

Теги

Популярные авторы

Сообщества

Игры

Теги

Популярные авторы

Сообщества

Юмор

Теги

Популярные авторы

Сообщества

Отношения

Теги

Популярные авторы

Сообщества

Здоровье

Теги

Популярные авторы

Сообщества

Путешествия

Теги

Популярные авторы

Сообщества

Спорт

Теги

Популярные авторы

Сообщества

Хобби

Теги

Популярные авторы

Сообщества

Сервис

Теги

Популярные авторы

Сообщества

Природа

Теги

Популярные авторы

Сообщества

Бизнес

Теги

Популярные авторы

Сообщества

Транспорт

Теги

Популярные авторы

Сообщества

Общение

Теги

Популярные авторы

Сообщества

Юриспруденция

Теги

Популярные авторы

Сообщества

Наука

Теги

Популярные авторы

Сообщества

IT

Теги

Популярные авторы

Сообщества

Животные

Теги

Популярные авторы

Сообщества

Кино и сериалы

Теги

Популярные авторы

Сообщества

Экономика

Теги

Популярные авторы

Сообщества

Кулинария

Теги

Популярные авторы

Сообщества

История

Теги

Популярные авторы

Сообщества

Недвижимость и ремонт

Теги

Популярные авторы

Сообщества