Вопрос

Это довольно длинный вопрос, поэтому, пожалуйста, держитесь за меня.

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

Люди, которые написали протокол для оборудования, использовали кастом версия SUN XDR называется INCA_XDR. Это инструмент для сериализации и десериализация сообщений. Он написан на C, и мы хотим избежать нативный код, поэтому мы анализируем данные протокола вручную.

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

  

[ГОЛОВА] [ВВЕДЕНИЕ] [ДАННЫЕ] [ХВОСТ]

[HEAD] =
    byte sync 0x03
    byte length X       [MSB]       X = length of [HEADER] + [INTRO] + [DATA]
    byte length X       [LSB]       X = length of [HEADER] + [INTRO] + [DATA]
    byte check X        [MSB]       X = crc of [INTRO] [DATA]
    byte check X        [LSB]       X = crc of [INTRO] [DATA]
    byte headercheck X              X = XOR over [SYNC] [LENGTH] [CHECK]

[INTRO]
    byte version 0x03
    byte address X                  X = 0 for point-to-point, 1-254 for specific controller, 255 = broadcast
    byte sequence X                 X = sequence number
    byte group X        [MSB]       X = The category of the message
    byte group X        [LSB]       X = The category of the message
    byte type X         [MSB]       X = The id of the message
    byte type X         [LSB]       X = The id of the message

[DATA] =
    The actuall data for the specified message,
    this format really differs a lot.

    It always starts with a DRCode which is one byte.
    It more or less specifies the general structure of
    the data, but even within the same structure the data
    can mean many different things and have different lenghts.
    (I think this is an artifact of the INCA_XDR tool)

[TAIL] =
    byte 0x0D

Как видите, много служебных данных, но это потому, что протокол должен работать как с RS232 (точка-многоточка), так и с TCP / IP (p2p).

    name        size    value
    drcode      1       1   
    name        8               contains a name that can be used as a file name (only alphanumeric characters allowed)
    timestamp   14              yyyymmddhhmmss  contains timestamp of bitmap library
    size        4               size of bitmap library to be loaded
    options     1               currently no options

Или это может иметь совершенно другую структуру:

    name        size    value
    drcode      1       2   
    lastblock   1       0 - 1   1 indicates last block. Firmware can be stored
    blocknumber 2               Indicates block of firmware
    blocksize   2       N       size of block to load
    blockdata   N               data of block of firmware

Иногда это просто код DRC и никаких дополнительных данных.

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

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

Мы решили разбить вещи на мелкие кусочки.

Прежде всего, это IDataProcessor.

Классы, реализующие этот интерфейс, несут ответственность для проверки необработанных данных и создания экземпляров класса Message. Они не несут ответственности за связь, им просто передается байт []

Проверка необработанных данных означает проверку заголовка на наличие ошибок контрольной суммы, crc и длины.

Полученное сообщение передается в класс, который реализует IMessageProcessor. Даже если исходные данные считались недействительными, потому что IDataProcessor не имеет Понятие ответных сообщений или чего-либо еще, все, что он делает, проверяет необработанные данные.

Чтобы информировать IMessageProcessor об ошибках, были добавлены некоторые дополнительные свойства к классу сообщений:

bool nakError = false;
bool tailError = false;
bool crcError = false;
bool headerError = false;
bool lengthError = false;

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

IMessageProcessor - это место, где выполняется настоящая работа. Из-за различных групп и типов сообщений я решил используйте F # для реализации интерфейса IMessageProcessor, потому что сопоставление с образцом казалось хорошим способом избежать множества вложенных операторов if / else и caste. (У меня нет опыта работы с F # или даже с другими функциональными языками, кроме LINQ и SQL)

IMessageProcessor анализирует данные и решает, какие методы он должен вызывать на контроллере IHardware. Может показаться излишним иметь IHardwareController, но мы хотим иметь возможность поменять его с другой реализацией и не будет вынужден использовать F # либо. Текущая реализация представляет собой окна WPF, но это может быть окно Cocoa # или просто консоль, например.

IHardwareController также отвечает за управление состоянием, потому что разработчики должны иметь возможность манипулировать аппаратными параметрами и ошибками через пользовательский интерфейс.

Так что, как только IMessageProcessor вызвал правильные методы в IHardwareController, он должен генерировать ответ MEssage. Опять же ... данные в этих ответных сообщениях может иметь много разных структур.

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

В этом нет ничего "трудного" о написании этого кода, но все разные Команды и структуры данных требуют много и много

Это было полезно?

Решение

Я думаю, что F # подходит для представления сообщений в этом домене через различимые объединения; Я воображаю, например.

type Message =
    | Message1 of string * DateTime * int * byte //name,timestamp,size,options
    | Message2 of bool * short * short * byte[]  //last,blocknum,blocksize,data
    ...

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

Мне не совсем понятно, как обрабатывать сообщения, но в целом, исходя из вашего описания, кажется, что вы справились с этим.

Меня немного беспокоит ваша «гибкость инструмента» - каковы ваши ограничения? (Например, .Net, должны поддерживаться программистами, которые знают технологии X, Y, Z, должны соответствовать определенным критериям перфоманс ...)

Другие советы

Вот мои 2 цента (предостережение: я не знаю F #): у вас есть точно указанный входной файл, даже с полной грамматикой. Вы хотите сопоставить содержимое файла с действиями. Поэтому я предлагаю вам разобрать файл. F # является функциональным языком, он может соответствовать технике синтаксического анализа, которая называется Разбор рекурсивного спуска . Книга« Эксперт F # » содержит обсуждение анализа рекурсивного спуска.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top