Как стать автором
Обновить

Парсер бинарных данных с WPF визуализацией в виде дерева структур и их полей. Структура, управляемая данными

Уровень сложностиСредний
Время на прочтение5 мин
Количество просмотров4.7K

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

Адаптированный проект для публичного использования, рабочий на Гите, компилируется в простой exe-файл. Можно скачать как exe-файл, если доверяете своему антивирусу.  Надеюсь, кому-то пригодится. Но чтобы начать пользоваться надо научиться писать XАML определения вложенных структур, по которым работает парсер. Ссылка на проект с исходниками в конце статьи.

Не будет никаких модных слов, только то, что нужно для работы.


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

Приложение в работе
Приложение в работе

На приведенном интерфейсе пользователя приложения можно выделить:

Внизу:

  • интерфейс для выбора файла описания структуры данных для парсинга (задание для парсера) по кнопке “LoadXaml”;

  • интерфейс для выбора файла данных, который будет анализироваться-визуализироваться по кнопке “ParseArr”.

Панель управления с

  • кнопками “LoadXaml” и “ParseArr”,

  • количеством загруженных объектов описания полей/структур (DeclCnt=46),

  • количеством считанных объектов значений полей/структур (ValueCnt=307).

Слева направо:

  • Окно логирования процессов загрузки описания/анализа данных;

  • Окно визуализации прочитанных данных-структур в виде дерева записей, выбор записи синхронизирован с выделением области в шестнадцатеричном представлении потока данных;

  • Окно визуализации бинарных данных, в котором выделение синхронизировано с выбором в окне визуализации данных.

На картинке показана предопределенная структура и предопределенный файл данных из состава проекта. Если скомпилировать, запустить проект и нажать на кнопку “ParseArr” должна воспроизвестись такая же картинка после очевидных манипуляций с деревом объектов.

 Задание для парсера

В нашем случае файл читался из энергонезависимой памяти некоторого устройства (Non Volatile Memory). Данные хранятся в памяти устройства в виде С-подобных вложенных или последовательных структур или массивов. Естественно, эти структуры и их вложенность- последовательность должны быть известны, и их в каком-то виде нужно описать и передать парсеру.

Как это не покажется кому-то странным, самым простым способом описания таких структур оказался XML, а точнее XAML. Идея в том, что мы создаем дерево объектов с описаниями структур, их полей и зависимостей от прочитанных данных. Последнее надо пояснить с примером, который вы сможете найти в проекте на Гите.

<local:StreamBitsFld bitLen="2" Name="StrId_CNT"/>
<local:FldDefArray Name="StrIds" SizeLink="StrId_CNT" ZeroBase="1">
    <local:NamedByFirst Name="StrId">
        <local:StreamBitsFld bitLen="10" Name="StrId"/>
        <local:StreamBitsFld bitLen="6" Name="Datid_CNT"/>
        <local:FldDefArray Name="Datids" SizeLink="Datid_CNT">
            <local:NamedByFirst Name="StrId" nameAdditionIndex="2">
                <local:StreamBitsFld bitLen="1" Name="Grub_REQUIRED"/>
                <local:StreamBitsFld bitLen="5" Name="RFU2"/>
                <local:StreamBitsFld bitLen="10" Name="Datid"/>
            </local:NamedByFirst>
        </local:FldDefArray>
    </local:NamedByFirst>
</local:FldDefArray>

Запись задает вполне интуитивный способ описания вложенных структур и даже массива структур размер которого определяется значением из поля внешней структуры. Разберем подробно:

Запись:

<local:StreamBitsFld bitLen="2" Name="StrId_CNT"/>

задает элемент типа local:StreamBitsFld который определяет битовое поле размером bitLen (от 1 до 16 бит), значение которого будет прочитано в переменную типа INT для отображения в виде конечного элемента в дереве считанных из бинарного потока значений. Значение будет подписано строкой, заданной в поле Name.

Элементы, которые содержат вложенные элементы:

<local:FldDefArray Name="StrIds" SizeLink="StrId_CNT" ZeroBase="1">

<local:NamedByFirst Name="StrId">

<local:FldDefArray Name="Datids" SizeLink="Datid_CNT">

Определяют вложенные структуры и даже массивы структур, например запись

<local:FldDefArray Name="StrIds" SizeLink="StrId_CNT" ZeroBase="1">

Определяет массив структур, состоящих из вложенных элементов этого XML узла. Размер массива задается значением прочитанным парсером из первого предыдущего поля с именем, заданным в поле SizeLink если идти от данного элемента вверх по дереву определений, то есть из элемента:

<local:StreamBitsFld bitLen="2" Name="StrId_CNT"/>

Обратите внимание мы не задаем размер массива, мы задаем откуда парсер будет читать размер массива во время работы. Таким образом размер массива определяется данными. Поле ZeroBase задает как интерпретировать значение «ноль» для длины массива. «1» означает что в массиве будет хотя бы один элемент.

При таком способе описания структуры данных для парсера, порядок разбора для парсера задается самым естественным с точки зрения человеческого восприятия, способом. Мы последовательно выписали все структуры, массивы поля, которые парсер соответственно последовательно будет читать из бинарного потока данных. С другой стороны, мы определяем структуру данных в виде дерева, и именно в таком виде это описание и загружается-сохраняется как задание для парсера в памяти. Собственно алгоритм работы парсера и заключается в том, чтобы в заданной последовательности применять объекты описания полей-структур (которые можно определить как сложные поля) к бинарному потоку данных в текущей позиции этого потока. Применяя объект описания поля, парсер создает объект значения поля и смещает текущую позицию в потоке в соответствии с размером прочитанного поля, любая прочитанная структура смещает текущую позицию в потоке на суммарный размер всех своих вложенных (дочерних) элементов. Вряд ли можно придумать более простой алгоритм для реализации парсера.

WPF визуализация в виде дерева

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

Чтобы отобразить полученное дерево объектов в памяти в виде визуального дерева визуальных объектов на форме, достаточно передать корневой объект этого дерева в визуальный объект окна fieldsTree

            List<object> lst = new List<object>(1);
            lst.Add(rootElmnt);
            wnd.fieldsTree.ItemsSource = lst;

Хотя это конечно не совсем все, еще надо определить способ представления объектов для ветвей и листьев дерева через HierarchicalDataTemplate:

<HierarchicalDataTemplate DataType="{x:Type local:parentBinValue}"
                        ItemsSource="{Binding Path=Children}">
    <StackPanel Orientation="Horizontal" Background="LightSkyBlue" Margin="2" >
        <TextBlock Text="{Binding Path=Descript}" Padding="3,1, 1,1"/>
        <TextBlock Text="{Binding Path=startPos}" Padding="1,1, 10,1"/>
    </StackPanel>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type local:BaseBinValue}"      >
    <StackPanel Orientation="Horizontal" Background="LightGreen"  Margin="1" >
        <TextBlock Text="{Binding Path=Descript}" Padding="10,1, 1,1"/>
        <TextBlock Text=": " Padding="1,1, 0,1"/>
        <TextBlock Text="{Binding Path=fldVal}" Padding="1,1, 10,1"/>
        <TextBlock Text="{Binding Path=BitPos }" Padding="1,1, 10,1"/>
    </StackPanel>
</HierarchicalDataTemplate>

Если, конечно, на Хабре еще остались те кому интересны такие практические задачи которые приходится решать в повседневной работе.

Ссылки

Проект парсера на Гите

ZIP-архивы с исходниками и с файлами примеров, скомпилированный исполняемый файл.

Теги:
Хабы:
Всего голосов 11: ↑11 и ↓0+11
Комментарии6

Публикации

Истории

Работа

Ближайшие события

27 августа – 7 октября
Премия digital-кейсов «Проксима»
МоскваОнлайн
19 сентября
CDI Conf 2024
Москва
20 – 22 сентября
BCI Hack Moscow
Москва
24 сентября
Конференция Fin.Bot 2024
МоскваОнлайн
24 сентября
Astra DevConf 2024
МоскваОнлайн
25 сентября
Конференция Yandex Scale 2024
МоскваОнлайн
28 – 29 сентября
Конференция E-CODE
МоскваОнлайн
28 сентября – 5 октября
О! Хакатон
Онлайн
30 сентября – 1 октября
Конференция фронтенд-разработчиков FrontendConf 2024
МоскваОнлайн
3 – 18 октября
Kokoc Hackathon 2024
Онлайн