Лучший подход к синтаксическому анализу строки буфера в Python

Я работаю над встроенной системой, которая отправляет команды через Uart. Uart работает на 115200 бод

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

Я выбираю Python в качестве языка для создания скрипта.

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

S;SEND;40;{"ID":"asg01","T":1,"P":{"T":180}};E

Каждое сообщение начинается с S и заканчивается на E. С сообщением связана команда «ОТПРАВИТЬ», а длина полезной нагрузки - 40.

Моя идея - прочитать байты, поступающие из UART, и:

  • проверьте, начинается ли сообщение с буквы S
  • проверьте, заканчивается ли сообщение буквой E
  • если вышеприведенные предположения верны, разделите сообщение, чтобы найти команду и полезную нагрузку.

Какой лучший способ проанализировать все байты, поступающие от асинхронного uart?

Меня беспокоит потеря сообщения из-за неправильного (или медленного) синтаксического анализа.

Спасибо за помощь!

BR, Федерико

Какая часть этой «полезной нагрузки»? Бит JSON составляет 34 байта, всего 46, все это за вычетом битов S и E составляет 42… Я не вижу ничего очевидного, длиной в 40 байтов.

abarnert 02.05.2018 09:55
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
0
1
660
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Этот формат почти может быть проанализирован как csv, но не совсем, потому что четвертое поле - это JSON, и вы не можете гарантировать, что JSON не содержит никаких строк со встроенными точками с запятой. Итак, я думаю, вы, вероятно, захотите просто использовать функции манипулирования строками (или, скорее, байтами):

def parsemsg(buf):
    s, cmd, length, rest = buf.split(b';', 3)
    j, _, e = rest.rpartition(b';')
    if s != b'S' or e != b'E':
        raise ValueError('must start with S and end with E')
    return cmd.decode('utf-8'), int(length), json.loads(j)

Потом:

>>> parsemsg(b'S,SEND,40,{"ID":"asg01","T":1,"P":{"T":180}},E')
('SEND', 40, {'ID': 'asg01', 'T': 1, 'P': {'T': 180}})

Фактический анализ точки с запятой занимает 602 нс на моем ноутбуке, decode и int увеличивают это значение до 902 нс. json.loads, напротив, требует 10 мкс. Итак, если вы беспокоитесь о производительности, часть JSON - это действительно единственная часть, которая имеет значение (пробуя сторонние библиотеки JSON, которые я установил, самая быстрая из них по-прежнему 8.1us, что не намного лучше). Вы могли бы также сделать все остальное простым и надежным.

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

Да, я начал использовать запятую для разделения сообщения, но хочу отправить JSON. ТАК Я изменил "," на ";"

Federico 02.05.2018 09:56

@Federico В примере в вашем вопросе используется ,, а не ;. Но в любом случае, можете ли вы гарантировать, что в JSN не будет строк с точкой с запятой? Если нет, вам все равно придется делать это в два этапа вместо одного.

abarnert 02.05.2018 09:56

Обновляю пример прямо сейчас. Что касается JSON с столбцами, я добавил json во встроенную систему и могу избежать использования ";" в json. Есть ли у вас какие-либо предложения о том, как создать сообщение на встроенной стороне?

Federico 02.05.2018 09:57

Если вы можете гарантировать отсутствие точек с запятой, возможно, вы можете сделать это немного проще, обернув csv.reader(f, delimiter=';') вокруг входного потока, а затем просто позволив ему выполнить разделение за вас. Но я думаю, что код, использующий split и partition, уже должен быть достаточно простым, поэтому я бы не стал слишком об этом беспокоиться. Но между тем ... есть ли причина, по которой вы не можете просто сделать все, включая команду и длину, текстом JSON?

abarnert 02.05.2018 10:08
Ответ принят как подходящий

В своей повседневной работе я написал программное обеспечение для встраиваемой системы и ПК, обменивающихся данными друг с другом через USB-кабель, используя протокол UART на скорости 115 200 бод.

Я вижу, что вы пометили свой пост с помощью PySerial, так что вы уже знаете о самом популярном пакете Python для связи через последовательный порт. Я добавлю, что если вы используете PyQt, в этот пакет также включен последовательный модуль.

115 200 бод - это не так быстро для современного настольного ПК. Я сомневаюсь, что любой синтаксический анализ, который вы делаете на стороне ПК, не успеет. Я анализирую потоки данных и строю графики своих данных в реальном времени с помощью PyQt.

Что я заметил в своей работе со связью между встроенной системой и ПК через UART, так это то, что некоторые данные иногда повреждаются. Байт может быть искажен, повторен или отброшен. Кроме того, даже если байты не добавляются или не отбрасываются, вы можете иногда выполнять чтение, пока в буфере находится только часть пакета, и чтение завершится раньше. Если вы используете фиксированную длину чтения в 40 байтов и уверены, что каждое чтение всегда будет соответствовать пакету данных, как показано выше, вы часто ошибаетесь.

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

Еще несколько рекомендаций: работайте с Python 3, если у вас есть выбор, он чище. Используйте байты и объекты байтового массива. Не используйте str, потому что вы обнаружите, что конвертируете туда и обратно между Unicode и ASCII.

Спасибо за предложение. Я буду следовать вашим подсказкам. BR

Federico 03.05.2018 09:37

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