Выравнивание последовательных данных с помощью трейта Read в Rust - PullRequest
0 голосов
/ 19 июня 2020

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

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

С чертой Read я могу прочитать определенное количество байтов в свой буфер. Это может выглядеть так:

let mut serial_buf: Vec<u8> = vec![0; 16];
loop {
    match port.read(serial_buf.as_mut_slice()) {
        Ok(t) => print_data(&serial_buf[..t]),
        Err(ref e) if e.kind() == std::io::ErrorKind::TimedOut => (),
        Err(e) => eprintln!("{:?}", e),
    }
}

Но результат, который я получаю, будет выглядеть примерно так без выравнивания (с байтами abcd, отправляемыми каждые 2 секунды):

abcd
a
bcd
abc
d
a
bcd
ab
cd

Итак Каков наиболее практичный способ чтения и сброса до тех пор, пока не будет найден бит выравнивания, а затем убедиться, что все последующие чтения выровнены?

Спасибо,

1 Ответ

0 голосов
/ 20 июня 2020

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

+----------+-------+------+------+------+------+------+------+------+------+-----------+
| Byte     | 0     | 1    | 2    | 3    | 4    | 5    | 6    | 7    | 8    | 9         |
+==========+=======+======+======+======+======+======+======+======+======+===========+
| Function | Start | Data | Data | Data | Data | Data | Data | Data | Data | CheckSum  |
+----------+-------+------+------+------+------+------+------+------+------+-----------+
| Value    | 0     | X    | X    | X    | X    | X    | X    | X    | X    | XOR 0..=8 |
+----------+-------+------+------+------+------+------+------+------+------+-----------+

Итак, что у нас здесь? Наше сообщение всегда начинается с 0. Таким образом мы можем распознать начало сообщения. Данные все еще могут содержать 0, потому что мы не полагаемся на них, это просто упрощает нашу работу. (На самом деле это может быть любое значение, но 0 отлично подойдет)

После нашего стартового байта мы получаем наши данные. Это просто ABCD, который вы использовали в своем примере. Наш последний байт - это очень простая контрольная сумма. Мы просто выполняем операцию XOR вместе со всеми байтами.

Как это все работает тогда?

  1. Собираем некоторые данные.
  2. Если данные <10 байт, go чтобы 1 </li>
  3. Если data [0]! = 0 (наш начальный байт), то удалите первый байт из нашего буфера. Go до 2.
  4. XOR вместе со всеми байтами, чтобы получить нашу вычисленную контрольную сумму.
  5. Если data [9]! = Calculate_checksum, то удалите первый байт из нашего буфера. Go до 2.
    (Мы обнаружили начальный байт, но контрольная сумма неверна. Таким образом, мы знаем, что текущие данные не являются пакетом)
  6. Мы получили пакет! (Возможно *) Прочтите байты 1 .. = 8 для обработки. Затем удалите из буфера первые 10 байтов. Go на 2.

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

  1. Лучший начальный заголовок, поддерживающий динамическую c длину и несколько типов сообщений.
  2. Лучшая контрольная сумма. XOR не очень хорош для надежности. CR C - один из лучших.
  3. Управление буфером. Вы можете использовать циклический буфер, чтобы не удалять байты.
...