LINUX.ORG.RU

Как правильно обработать потоковые данные?

 ,


0

1

Контроллер посылает (нон-стоп) данные (uart 7e1) на 8-разрядный 7-сегментный дисплей с 4-мя статусными светодиодами.

Если логичкски сгруппировать данные, то каждая посылка начинается с 0x00 или 0x04, далее следуют 8 байт на индикаторы, байт на светодиоды и контрольная сумма:

0x00 0x3F 0x3F 0x3F 0x3f 0x3F 0x3F 0x3F 0x3F 0x00 0x78
0x00 0x3F 0x3F 0x3F 0x3f 0x3F 0x3F 0x3F 0x3F 0x00 0x78
0x00 0x3F 0x3F 0x3F 0x3f 0x3F 0x3F 0x3F 0x3F 0x00 0x78
0x04 0x3F 0x3F 0x3F 0x3f 0x3F 0x3F 0x3F 0x3F 0x00 0x7C
0x00 
0x00 0x3F 0x3F 0x3F 0x3f 0x3F 0x3F 0x3F 0x3F 0x00 0x78
0x04 0x3F 0x3F 0x3F 0x3f 0x3F 0x3F 0x3F 0x3F 0x00 0x7C
0x00 
0x00 0x3F 0x3F 0x3F 0x3f 0x3F 0x3F 0x3F 0x3F 0x00 0x78
0x04 0x3F 0x3F 0x3F 0x3f 0x3F 0x3F 0x3F 0x3F 0x00 0x7C
0x00 
0x00 0x3F 0x3F 0x3F 0x3f 0x3F 0x3F 0x3F 0x3F 0x00 0x21
0x04 0x3F 0x3F 0x3F 0x3f 0x3F 0x3F 0x3F 0x3F 0x21 0x1D
0x00 
0x00 0x3F 0x3F 0x3F 0x3f 0x3F 0x3F 0x3F 0x3F 0x21 0x19
0x04 0x3F 0x3F 0x3F 0x3f 0x3F 0x3F 0x3F 0x3F 0x21 0x1D
0x00 
0x00 0x3F 0x3F 0x3F 0x3f 0x3F 0x3F 0x3F 0x3F 0x21 0x19
0x00 0x3F 0x3F 0x3F 0x3f 0x3F 0x3F 0x3F 0x3F 0x21 0x19
0x00 0x3F 0x3F 0x3F 0x3f 0x3F 0x3F 0x3F 0x3F 0x00 0x78

Если строка начинается с 0x04, то просто нужно прочитать 11 байт включительно.

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

0x00 0x3F 0x00 0x00 0x3f 0x3F 0x3F 0x3F 0x3F 0x00 0x7A
★★★

Последнее исправление: nvl (всего исправлений: 2)

Короткий ответ FSM, читать группами тогда ты точно знаешь, что означает очередной 0

phoenix ★★★★
()
Ответ на: комментарий от phoenix

Чуть подробнее пожалуста.

nvl ★★★
() автор топика

Если я правильно понял проблему, то, как вариант, можно сделать кольцевой буфер на размер пакета (11 байт) и при каждой записи валидировать данные в нем рассчитывая контрольную сумму. Если сумма в пакете будет идентична расчетной, то можно считать, что принят верный пакет. Про «запаздывание» и пакеты из одного нулевого байта из дампа не очень понял.

anonymous
()
Ответ на: комментарий от anonymous

Про «запаздывание» и пакеты из одного нулевого байта из дампа не очень понял.

Про запаздывание.

Если читать так

Если (байт == 0x04)
	поместить в массив этот байт и 10 следующих;
Проверить сумму;
Обработать данные;
То я получаю данные, начинающиеся с 0x04 - 10 пакетов за, условно, 0.3 секунды. Далее идут пакеты без 0x04 в течение секунды, которые я не обрабатываю. Т.е. в это время уже могут измениться показания.

Про пакеты из одного нулевого байта

Это мое предполжение, на основе показаний на дисплее и чтения потока. Визуальное форматирование, чтобы четко понять где данные. Может при 0x04 надо читать 12 байт вместе с этим 0x00 и т.п.

nvl ★★★
() автор топика
Ответ на: комментарий от nvl

...что данных начинающихся с 0x00 значительно больше и если их не читать...

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

pathfinder ★★★★
()

Получается, что после «строки», начинающейся на 0x04 всегда следует «строка» из 12 байт, начинающийся с двух нулей. Вероятно, что такая система как раз и нужна для синхронизации.

anonymous
()

7e1

А ты в контроллере протокол не можешь поменять? Передавал бы по 8 бит, MSB бы заюзал под маркер старта пакета и тогда вообще красота бы была и никаких сложностей.

anonymous
()
Ответ на: комментарий от anonymous

А ты в контроллере протокол не можешь поменять?

Нет. Контроллер с проприетарной прошивкой.

nvl ★★★
() автор топика
Ответ на: комментарий от pathfinder

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

Собственно у меня в этом и вопрос. Как правильно организовать чтение всех данных?

nvl ★★★
() автор топика
Ответ на: комментарий от nvl

Тебе выше анон правильно сказал, храни 11 значений и проверяй что если первое 0х00 или 0х04 а последнее чексумма то вот он твой пакет.

ya-betmen ★★★★★
()
Последнее исправление: ya-betmen (всего исправлений: 1)
Ответ на: комментарий от anonymous

Получается, что после «строки», начинающейся на 0x04 всегда следует «строка» из 12 байт, начинающийся с двух нулей.

У меня сейчас такой алгоритм вырисовался.

Дождаться 0x04;
Прочитать и верифицировать 12 байт включительно;
Далее читать и верифицировать по 11 байт если data[0] == 0x00 или 12 байт если data[0] = 0x04;
Если (!верификация) goto top;

nvl ★★★
() автор топика
Ответ на: комментарий от ya-betmen

если первое 0х00 или 0х04 а последнее чексумма то вот он твой пакет.

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

nvl ★★★
() автор топика
Последнее исправление: nvl (всего исправлений: 1)
Ответ на: комментарий от nvl

Я бы на твоем месте завел массив на 12 байт и на каждом цикле сдвигал все на 1 вперед и читал 1 байт в хвост, после чего делал провеку первого байта, если если там 0х00 то считал чексумму и сверял с 11 байтом, совпало - процессим, если в первом 0х04, то сверял чексумму с 12 байтом, совпало - процессим.

ya-betmen ★★★★★
()
Ответ на: комментарий от ya-betmen

Мысль понял. При длине посылки 11 байт, 11 раз нужно проверить 1-й байт массива и байт котрольной суммы и проверить его по сумме остальных. Это не считается накладным?

nvl ★★★
() автор топика
Последнее исправление: nvl (всего исправлений: 2)
Ответ на: комментарий от nvl

Да, только проверку на 0 или на 4 (что, возможно, удобнее) надо делать на каждом байте, пока не найдёшь совпадение. Бетмен, вроде, правильно подсказывает.

anonymous
()
Ответ на: комментарий от nvl

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

ya-betmen ★★★★★
()
Ответ на: комментарий от ya-betmen

Спасибо! Так и сделаю.

nvl ★★★
() автор топика
Ответ на: комментарий от ya-betmen

Сделал массив на 12 и добалил проверку еще последнего байта на 0x00 или 0x04. Читает все, вроде пока ошибок не замечал.

................
			while (true) {
				count = Posix.read (fd, buf, buf.length);
				if (count > 0) {
					byte_array_add_tail_byte (ref byte_array, buf[0]);
					if (is_valid_data (ref byte_array) ) {
						data_collected (byte_array); //emit sigmal
					}

				}
...............
		private bool is_valid_data (ref uint8[] arr) {

			bool result = false;
			if ( (arr[0] == 0x00) ||
			     (arr[0] == 0x04) ) {
				if ( (arr[11] == 0x00) ||
				     (arr[11] == 0x04) ) {
					if (check_summ (ref arr) ) {
						result = true;
					}
				}
			}
			return result;
		}

nvl ★★★
() автор топика
Ответ на: комментарий от nvl

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

void stream_sync() {
    attempts_limit = 100;
    new buf[12];
    buf[1..11] = read(11);
    while (attempts_limit--) {
        buf[0..10] = buf[1..11];
        buf[11] = read(1);
        if (buf[0] == 0x04 && buf[11] == 0x00 && check_sum(buf)) {
            process_data(buf[0..10]); // emit signal
            del buf;
            return;
        }
    }
    del buf;
    raise CouldNotSyncError;
}

void uart_proc_loop() {
    stream_sync();
    new arr[11];
    while (true) {
        arr = read(11);
        if (len(arr) < 11) {raise UartReadError;}
        if (arr[0] == 0x04) {discard read(1);}
        if (check_sum(arr)) {process_data(arr);}
        else {raise CheckSumError;}
    }
}
anonymous
()
Ответ на: комментарий от anonymous

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

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

if (arr[0] == 0x04) {discard read(1);}

Вот это место не понимаю. Что тут подразумевается?

nvl ★★★
() автор топика
Ответ на: комментарий от nvl

Здесь читается и отбрасывается (читается в никуда) двенадцатый байт из последовательности, где до нормальных данных встречаются два нулевых байта подряд. Такие посылки бывают только после того, как придёт последовательность, начинающаяся с 0x04. В обычной посылке 11 байт и она начинается с 0x00, так что условие не выполняется.

anonymous
()
Ответ на: комментарий от nvl

Это не считается накладным?

Зависит от алгоритма расчёта чексуммы, очевидно.

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

Stanson ★★★★★
()
Последнее исправление: Stanson (всего исправлений: 1)
Ответ на: комментарий от nvl

Собственно у меня в этом и вопрос. Как правильно организовать чтение всех данных?

Я вагон всякой фигни написал из разряда обмена по serial и tcp протоколам. Мне больше всего нравится парсить через промежуточный кольцевой буфер.

Читаешь данные, запихиваешь их в кольцевой буфер. Как поступили данные в буфер, пытаешься найти там кадр с сообщением. Если что-то не сходится, стартовый байт, контрольная сумма или что-то ещё, то выкидываешь из головы буфера 1 байт и начинаешь по новой искать. И так делаешь, пока не убедишься, что в кольцевом буфере точно полного пакета не будет. В твоем случае это будет, когда данных в буфере меньше 11 байт. Если найден полноценный кадр, то после его обработки выкидываем из буфера все его байты.

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

pathfinder ★★★★
()
Ответ на: комментарий от pathfinder

Если найден полноценный кадр, то после его обработки выкидываем из буфера все его байты.

И заполняем новыми. Действительно. Спасибо!

nvl ★★★
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.