Protohaund: трассировка, фильтры и мониторинг

Друзья, добрый день! Эта заметка немного выходит за рамки автомобильной тематики. Последние несколько лет я занимаюсь разработкой различных устройств на базе ARM — от проектирования печатных плат до прошивок и вспомогательных утилит. В этой работе мне часто приходится иметь дело с распространёнными двухпроводными интерфейсами RS-485 и CAN, а также с различными протоколами, которые их используют. Для отладки устройства важно понимать, что происходит в обмене: какие сообщения оно отправляет, как отвечает на запросы и где возникает ошибка.

Заставка Protohaund

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

При отладке обмена хочется сразу понять, какой запрос отправлен, какой ответ получен и сколько времени прошло между ними. Но для этого сначала нужно выделить сообщения и разобраться в их структуре. Покажу это на двух примерах: Modbus RTU при работе через последовательный порт и мультикадровом обмене по CAN. В первом случае трудности начинаются с поиска границ сообщений в потоке байтов. Во втором — со сборки целого ответа из нескольких CAN-кадров.

Пример 1. Последовательный порт: границы сообщений Modbus RTU

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

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

Для Modbus RTU это особенно существенно: сообщения разделяются временными паузами, а специального байта «конец сообщения» нет. Эти правила описаны в спецификации Modbus RTU. Чтобы восстановить обмен по полученным данным, нужно учитывать структуру сообщений, их длину и CRC.

Например, без разделения два сообщения могут выглядеть так:

07 03 00 64 00 02 85 B2 07 03 04 12 34 56 78 E7 07

В этом примере за последовательностью байт скрывается простой обмен:

СообщениеБайтыЗначение
Запрос07 03 00 64 00 02 85 B2Устройство 7, функция 0x03, чтение двух регистров с адреса 100
Ответ07 03 04 12 34 56 78 E7 07Устройство 7 вернуло два значения: 0x1234 и 0x5678

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

Вид Tracer в режиме Modbus RTU

Tracer в Protohaund берёт на себя восстановление RTU-сообщений из поступающих данных: использует структуру поддерживаемых функций, длину, CRC и ожидание недостающих частей с учётом буферизованной доставки. После этого обмен можно читать как отдельные сообщения и фильтровать по их полям. Точное измерение физических пауз на линии — отдельная задача: время поступления порции данных в Windows не равно времени каждого байта на RS-485.

Пример 2. CAN: разбор CANopen, UDS и KWP2000

Теперь рассмотрим обмен по CAN. Здесь контроллер уже выделяет границы кадров, и через CAN-адаптер программа получает отдельные записи с идентификатором, длиной и данными. Какие байты принадлежат одному кадру, известно сразу. Описание CAN от CiA.

Однако границы кадров решают лишь часть задачи. Чтобы понять обмен, нужно определить назначение кадров, связать запросы с ответами и, когда данные передаются частями, восстановить их целиком.

В CANopen это хорошо видно при чтении длинной строки через сегментированную передачу SDO. Индекс и подындекс объекта задаются в начале обмена и не повторяются в каждом сегменте. Для восстановления значения нужно связать сегменты с исходным обращением, проследить их последовательность и подтверждения, учесть возможное прерывание передачи. Поэтому по одному кадру из середины обмена нельзя понять всё обращение. Описание SDO от CiA, структура сообщений SDO в CANopenNode.

При анализе UDS или KWP2000 поверх CAN добавляется разделение на транспортный и диагностический уровни. Сначала нужно собрать сообщение и отделить служебный обмен, затем определить сервис, его параметры и результат выполнения запроса. Например, в диагностике VAG длинный ответ UDS передаётся через ISO-TP: начальный кадр, кадры продолжения и управление потоком. Описание передачи ISO-TP.

В рассматриваемых сценариях VAG встречаются две цепочки:

Исходные кадрыТранспортный протоколДиагностический протокол
CANISO-TP: разделение и сборка сообщения, управление потокомUDS: сервисы, идентификаторы данных и ответы ЭБУ
CANTP2.0: установка канала, передача частей сообщения и подтвержденияKWP2000: диагностические запросы и ответы

Возьмём чтение VIN через UDS. Ниже пример для классического CAN с обычной адресацией ISO-TP, без дополнительного адресного байта. CAN ID 0x714 и 0x77E выбраны для примера, VIN также условный.

CAN IDДанные кадраЧто происходит
0x71403 22 F1 90 55 55 55 55Прибор запрашивает DID 0xF190 — VIN; запрос помещается в один кадр
0x77E10 14 62 F1 90 59 59 59ЭБУ начинает ответ общей длиной 20 байт; здесь только первые 6 байт сообщения UDS
0x71430 00 00 55 55 55 55 55Прибор разрешает продолжить передачу: это управление потоком ISO-TP
0x77E21 59 59 59 59 59 59 59Первая часть продолжения ответа
0x77E22 59 59 59 59 59 59 59Вторая часть продолжения ответа

В исходной таблице это пять CAN-кадров. Чтобы понять обмен вручную, нужно отличить служебные байты от данных, учесть объявленную длину, проверить порядок частей и собрать ответ. Например, 22 в начале последнего кадра — признак кадра продолжения с номером 2, а не сервис UDS 0x22. Поиск одного байта без учёта уровня протокола легко приводит к неверной интерпретации.

Вид Tracer уровня physical на примере UDS

После сборки ответ UDS выглядит так:

62 F1 90 59 59 59 59 59 59 59 59 59 59 59 59 59
59 59 59 59

Теперь его смысл можно выразить одной фразой: положительный ответ на чтение DID 0xF190, значение VIN – YYYYYYYYYYYYYYYYY. Кадр управления потоком в эти данные не входит. В Tracer на Physical видны исходные кадры, на Transport — транспортные сообщения, а на Protocol — диагностический запрос и собранный ответ. Поэтому можно отбирать обмен по uds.service и uds.did, не собирая сообщение вручную из строк CAN.

Вид Tracer уровня Protocol на примере UDS

При работе через TP2.0/KWP2000 задача похожая, но добавляется контекст канала: его параметры и CAN-идентификаторы согласуются при установлении связи. Для полноценного разбора полезно видеть и начало этого обмена. Просто найти знакомый CAN ID или байт сервиса в середине записи недостаточно.

Эти примеры показывают разные задачи анализа: восстановить сообщения Modbus RTU из потока байтов, связать сегменты SDO в CANopen или собрать и разобрать диагностический обмен UDS/KWP2000. В каждом случае важно учитывать правила протокола и связь сообщений между собой. Именно такой переход от исходных данных к понятному обмену лежит в основе Tracer.

Поэтому сейчас я представляю Protohaund — предварительную версию программы для анализа обмена данными в промышленных и автомобильных сетях. В этой заметке сосредоточимся на трёх направлениях: Modbus RTU, CANopen и автомобильной диагностике VAG. Главный инструмент обзора — Tracer. Также немного посмотрим на Monitor: он помогает следить за повторяющимися сообщениями и текущими наблюдаемыми значениями.

Tracer: увидеть последовательность событий

Tracer показывает историю обмена: время, адреса или идентификаторы, тип сообщения, данные и доступное описание. Можно изучать поступающий трафик или загрузить ранее сохранённую запись. Во время работы с подключённым источником трасса собирается автоматически; отдельного запуска записи не требуется. Кнопка Save сохраняет накопленные данные в файл.

Для CAN доступен взгляд с нескольких уровней. На Physical видны исходные кадры. В профиле Automotive VAG на уровне Transport можно разбирать ISO-TP и TP2.0, а на Protocol — UDS и KWP2000. В профиле Industrial CANopen используются исходный уровень CAN и прикладной уровень CANopen. Это позволяет переходить от вопроса «какие байты пришли?» к вопросу «какой объект читали или какой сервис вызвали?» одним движением слайдера.

Вид Tracer уровня protocol на примере CANopen

Важные места можно помечать собственными метками. Например, отметить начало проверки и сообщение, после которого изменилось поведение устройства. Сохранённая трасса позволяет вернуться к этому эпизоду позже, уже без подключения к оборудованию.

Фильтр, который понимает поля протокола

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

Например, в Modbus RTU нас интересуют только ответы с исключением от устройства с адресом 7:

modbus.unitId == 7 and modbus.exception == true 

Здесь modbus.unitId — адрес устройства, а modbus.exception — признак ответа с исключением. Программа проверяет разобранные поля сообщения. Совпадение случайного байта 07 внутри данных не попадёт в выборку только потому, что адрес тоже равен 7.

Условия можно объединять через and, or и not, группировать скобками, сравнивать числа и выбирать несколько значений через in. Для работы с данными есть поиск последовательности байтов и обращение к отдельному байту по индексу. Ввод поддерживает подсказки полей и значений; ошибки в выражении показываются рядом с фильтром.

Несколько полей, с которых удобно начать знакомство:

ПолеЧто позволяет отобрать
can.id, can.frameTypeCAN-идентификатор и формат std / ext на исходном уровне
can.dataБайты CAN-кадра, включая поиск фрагмента данных
modbus.unitId, modbus.functionАдрес устройства и код функции Modbus
modbus.startAddress, modbus.quantityНачальный адрес и количество элементов, когда они есть в сообщении
modbus.exceptionCode, modbus.validКод исключения и результат проверки записи
canopen.nodeId, canopen.typeУзел и семейство сообщений CANopen
canopen.index, canopen.subIndexОбъект словаря CANopen
canopen.abortCode, canopen.emcyCodeПрерывание SDO/USDO и код аварийного сообщения
uds.service, uds.didСервис UDS и идентификатор данных
uds.negativeResponseCodeПричину отрицательного ответа UDS
kwp.service, kwp.localIdСервис и локальный идентификатор KWP2000
isotp.flowStatus, tp20.rnrУправление потоком ISO-TP и состояние готовности получателя TP2.0

Поля зависят от выбранного уровня. Условие по can.id вводится на Physical, а по uds.service — на Protocol. Для каждого уровня CAN хранится своё выражение. Полные таблицы и особенности каждого поля собраны
в справочнике.

Фильтр меняет отображение таблицы. Исходные сообщения продолжают собираться, а Save сохраняет исходную трассу в текущих границах документа, включая скрытые фильтром записи. Поэтому после сохранения можно проверить другую гипотезу и увидеть соседние события.

Modbus RTU: почему устройство не выполняет запрос

Представим стенд с несколькими устройствами. Ведущий циклически читает регистры, и среди обычных ответов иногда появляется исключение. Сначала оставим обмен с адресом 7:

modbus.unitId == 7

Теперь сузим выборку до чтения регистров хранения:

modbus.unitId == 7 and modbus.function == 0x03

Поле modbus.function содержит код функции без бита исключения. Поэтому такая выборка сохраняет запросы, обычные ответы и распознанные ответы с исключением для функции 0x03. Не нужно отдельно искать код ответа 0x83.

Если требуется разобраться с конкретным диапазоном, можно выбрать запросы по modbus.startAddress и modbus.quantity. Адреса в фильтре начинаются с нуля. Обозначение регистра вроде 40001 из инструкции устройства сначала нужно сопоставить с его адресом в протоколе.

Для чтения ответ обычно не содержит начальный адрес. Поэтому фильтр по диапазону покажет запрос, но не обязан показать соответствующий ответ. Для просмотра обеих сторон обмена удобнее оставить адрес устройства и функцию, а затем изучить последовательность сообщений.

Отдельная полезная проверка:

modbus.valid == false

Она выделяет записи, не прошедшие проверку структуры или CRC. Это повод проверить параметры линии и исходные данные. Сам фильтр не устанавливает физическую причину ошибки.

Monitor дополняет этот разбор: группирует наблюдаемые операции по устройству, функции и диапазону, показывает значения из сопоставленных ответов, наблюдаемый период и давность активности. Открытие Monitor не запускает опрос. Если запросов на линии нет, новые значения сами по себе не появятся =)

CANopen: найти нужный узел и объект

В сети CANopen рядом идут PDO, служебные сообщения, контроль состояния узлов и обращения к словарю объектов. В Tracer можно работать с их смыслом: выбрать узел, семейство сообщений или конкретный индекс и подындекс.

Допустим, нужно проверить обращение к объекту 0x1018:1 узла 5:

canopen.nodeId == 5 and canopen.type == sdo and canopen.index == 0x1018 and canopen.subIndex == 1

Такой фильтр оставит SDO-сообщения с указанным объектом. Для сегментированной передачи часть сообщений не несёт индекса и подындекса. Если нужна вся последовательность, условие по объекту лучше убрать и оставить узел с типом sdo.

Для поиска прерываний передачи достаточно проверить наличие кода:

canopen.nodeId == 5 and exists(canopen.abortCode)

А при разборе неожиданной остановки узла полезно собрать рядом команды управления сетью, запуск узла, сообщения о состоянии и авариях:

canopen.type in (nmt, bootup, heartbeat, nodeGuard, emcy)

Это даёт последовательность, по которой можно проверить: была ли команда NMT, появлялось ли сообщение запуска, передавался ли EMCY. Наличие или отсутствие одного сообщения ещё не доказывает причину остановки, но помогает сузить поиск.

Если к узлу привязано подходящее описание EDS или DCF, трасса может показывать имена и типы объектов, а при корректном отображении PDO — разобранные значения, но в бета версии возможность загрузить EDC/DCF отключена.

В Monitor удобно наблюдать повторяющиеся CAN-кадры: последнее содержимое, период, количество сообщений и подсветку изменившихся байтов и битов. Это помогает заметить, какой поток меняется при действии на стенде. Проверять точную последовательность после этого удобнее в Tracer.

Automotive VAG: от CAN-кадра к диагностическому ответу

При разборе автомобильной диагностики VAG приходится связывать несколько представлений одного обмена. Tracer в профиле VAG объединяет исходные CAN-кадры, транспортные сообщения ISO-TP / TP2.0 и прикладные сообщения UDS / KWP2000.

Например, нас интересует чтение данных UDS:

uds.service == 0x22

uds.service — нормализованный сервис. В выборку попадают распознанный запрос с SID 0x22, положительный ответ с SID 0x62 и отрицательный ответ на этот сервис. Если нужен именно фактический первый байт сообщения, используется другое поле — uds.rawSid.

Чтение VIN можно сузить по DID:

uds.service == 0x22 and uds.did == 0xF190 

Но отрицательный ответ вида 7F 22 … не содержит DID. Чтобы не потерять его при анализе, нужно вернуться к более широкому условию по сервису или отдельно посмотреть отрицательные ответы.

uds.negativeResponse == true

Здесь полезен uds.negativeResponseCode: например, 0x78 означает, что запрос принят, но ответ ещё не готов. Это нельзя автоматически считать тайм-аутом или окончательным отказом. В трассе нужно посмотреть, что пришло следом.

Для KWP2000 действует та же логика: есть поля kwp.service, kwp.rawSid, признаки запроса и ответа, код отрицательного ответа, локальный и общий идентификаторы. На транспортном уровне можно отдельно исследовать управление потоком ISO-TP или сообщения канала TP2.0. Периодические TesterPresent можно скрыть отдельным переключателем, чтобы сосредоточиться на интересующем обмене.

Этот обзор касается анализа диагностического трафика VAG по CAN. Он не обещает универсальной поддержки всех блоков и автомобилей или готового сценария программирования ЭБУ.

Требования к ПК и поддерживаемые адаптеры

Для работы с программой потребуется:

  • ПК с процессором x86 или x64 и Windows 10 либо Windows 11. Версия и редакция Windows должны соответствовать требованиям .NET 10.
  • Среда .NET 10 Desktop Runtime. Она входит в установщик Protohaund и устанавливается автоматически при необходимости.
  • Доступ к интернету для загрузки и обновления дополнительных пакетов программы.

Для подключения к устройствам предусмотрены следующие варианты:

ИнтерфейсАдаптерУсловия подключения
RS-485, Modbus RTUUSB–RS-485 или другой преобразователь с COM-портомСтандартный последовательный порт Windows
CANCANable и CANable 2.0Совместимая прошивка SLCAN для CANable
CANLawicel CANUSBДрайвер FTDI
CANUCCBПодключение через COM-порт
CANVCDS-совместимые адаптеры на FTDIСовместимость зависит от исполнения адаптера; используется драйвер FTDI
CANJ2534 Pass-ThruДрайвер производителя с поддержкой обмена исходными CAN-кадрами

Поддержка CAN-адаптеров устанавливается через раздел пакетов в настройках программы: доступны пакеты CANable, J2534, FTDI (Lawicel и VCDS) и UCCB. Драйверы самих устройств при необходимости устанавливаются отдельно. Возможности подключения зависят от конкретного адаптера, его прошивки и драйвера.

Для некоторых сценариев работы с J2534 предусмотрен DriverBridge — вспомогательное приложение, позволяющее 64-разрядной версии Protohaund использовать старые 32-разрядные библиотеки драйвера адаптера. Компонент входит в пакет J2534.

Для открытия и анализа сохранённой трассы адаптер не нужен — знакомство с Tracer и фильтрами можно начать без оборудования.

Вместо заключения

Если вы, как и я, разрабатываете устройства, отлаживаете обмен или исследуете протоколы, буду рад, если попробуете Protohaund на своих задачах и поделитесь впечатлениями. Мне интересно узнать, где программа окажется полезной, чего вам будет не хватать и что можно сделать удобнее. Возможно, ваши задачи похожи на мои, но, возможно, вы увидите применение, о котором я пока не думал.

В ближайшей перспективе планирую открыть для тестирования ещё несколько инструментов, которые по разным причинам пока готовы широкому тестированию, — в том числе симулятор виртуальных устройств и плеер для воспроизведения трасс. За рамками этого обзора остался и сервер MCP: часть его возможностей уже доступна, и их можно использовать для подключения ИИ-ассистентов и автоматизации работы с программой. Если будет интерес к этому сценарию, я обязательно расскажу об этом отдельно.

К сожалению, в программе пока ещё есть ошибки и белые пятна в реализации, поэтому для сообщений об ошибках в Protohaund встроена специальная форма. Если получится приложить сохранённую трассу или описать, при каких действиях возникла проблема, мне будет проще разобраться. Пользоваться формой можно не только для отправки баг-репортов, но и для предложений по развитию, пишите, я обязательно рассмотрю каждое из них.

Если программа вас заинтересовала, то ссылку на пакет установщика и тестовую лицензию вы найдёте в личном кабинете пользователя на моем сайте https://osoTEC.com

А на этом пока все. Всем добра!