Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: SIM900. Прием команд от TCP сервера.
Форум разработчиков электроники ELECTRONIX.ru > Интерфейсы > Форумы по интерфейсам > Сотовая связь и ее приложения
Иван Плетнев
Здравствуйте!

Разрабатываю устройство, которое собирает различные данные с датчиков и с периодичностью в одну минуту отправляет их на сервер посредством SIM900. Использую обычный режим AT-команд, не transparent, то есть команда AT+CIPSEND, приходит приглашение ">", отсылаю в порт строку, дожидаюсь SEND OK, всё. В данный момент добился надежной отсылки данных. Встал вопрос приёма команд от сервера. Все в общем, работает, за исключением случая, когда микроконтроллер ждет ответа от SIM900 и в этот момент приходит команда от сервера. Аппаратное управление потоком в этом случае вряд ли подойдет..
Подскажите, пожалуйста, кто как справился с этой проблемой?
zebrox
Так понимаю, что процессор ожидает ответ от сима на какую-либо АТ команду?

Думаю у команды с сервера должен быть заголовок, по которому ее можно отличить от ответа сима.
Выполнить ее, и дальше ждать ответа на АТ?
Иван Плетнев
Цитата(zebrox @ Apr 23 2014, 05:40) *
Так понимаю, что процессор ожидает ответ от сима на какую-либо АТ команду?

Думаю у команды с сервера должен быть заголовок, по которому ее можно отличить от ответа сима.
Выполнить ее, и дальше ждать ответа на АТ?

Ну да, это понятно. У команды действительно есть заголовок, и, кроме этого, по этому признаку я помещаю эти команды в другой буфер для дальнейшего парсинга. Видите ли, у меня конец сообщения от SIM900 определяется по таймауту. То есть, после приема последнего символа ответа, таймер отсчитывает 2мс и поднимается флаг, что ответ готов. Если команда приходит во время ответа, когда SIM900 что-то кидает в порт, или во время этой задержки, то целостность ответа нарушается и, соответственно, парсер ответов не может распознать его. Вот в чем проблема.
V_G
Таймаут приема (большой) использую только для защиты от зависаний.
При обычном обмене между процем и GSM-модулем (у меня был SIM700) сообщение разбираю по получении символа ">" (готовность к дальнейшему обмену).

Самостоятельные посылки от SIM700 начинаю разбирать при получении символа ":", далее в зависимости от того, что пришло до двоеточия. Если это "+IPD:" (данные от сервера, работаю только с двоичными (нетекстовыми) посылками), жду прихода длины посылки, после чего завожу счетчик на количество принимаемых байтов)
tdocs.su
Автомат конечный писать надо. Были проблемы когда-то давно с коммуникационной программой для двух модемов, только конечный автомат и позволил добиться устойчивой работы. Его ничем из "седла" не вышибить.
Иван Плетнев
Цитата(tdocs.su @ Apr 23 2014, 13:15) *
Автомат конечный писать надо. Были проблемы когда-то давно с коммуникационной программой для двух модемов, только конечный автомат и позволил добиться устойчивой работы. Его ничем из "седла" не вышибить.

Так у меня вся работа с модемом на конечных автоматах. Иначе с ним вообще невозможно работать. В общем, решил проблему "заплатками". Если команда от сервера приходит в неподходящий момент и возникает коллизия с ответом от модема, по таймауту ожидание ответа прекращается и программа выпадает в "диспетчер задач". И если команда не распозналась, сервер не получает подтверждения принятой команды и отсылает её вновь.
tdocs.su
Как-то странно... Ведь в автомате после отправки чего-либо в линию вы принудительно переводите его в новое состояние, в котором он ожидает конкретный ответ из линии, и никакой другой. Может, что-то не так делаете? См. личку.

Не принимает ваш ящик личку. См. здесь - http://tdocs.su/4199
Иван Плетнев
Цитата(tdocs.su @ Apr 23 2014, 14:19) *
Как-то странно... Ведь в автомате после отправки чего-либо в линию вы принудительно переводите его в новое состояние, в котором он ожидает конкретный ответ из линии, и никакой другой. Может, что-то не так делаете? См. личку.

Не принимает ваш ящик личку. См. здесь - http://tdocs.su/4199


Спасибо, почитаю.

Проблема-то как раз в том, что жду одно, а приходит другое ))
tdocs.su
Цитата(Иван Плетнев @ Apr 23 2014, 10:27) *
Спасибо, почитаю.

Проблема-то как раз в том, что жду одно, а приходит другое ))

Так вот автомат на другое и реагировать не должен вовсе. Если после перехода в case сразу задать ему новое состояние, он в нем и будет находиться и ждать только то, что надо, все прочее просто игнорировать.
Иван Плетнев
Все-таки не получается у меня в стандартном, командном режиме принимать сообщения от сервера без грабель. Буду пробовать transparent mode.
tdocs.su
Цитата(Иван Плетнев @ Apr 23 2014, 13:50) *
Все-таки не получается у меня в стандартном, командном режиме принимать сообщения от сервера без грабель. Буду пробовать transparent mode.

С утра я чё-та не догнал. А сейчас вспомнил детали той своей давней задачки. Обрисую.

Было 2 компика с зухелом на каждом из них (через COM-порты) и поганейшая линия связи, чуть ли не АТС с шаговым искателем sm.gif В исходном положении модемы были инициализированы и выведены на режим лежачей трубы (командный режим). При вызове одного другим они "договаривались" друг с другом и из командного режима переходили в режим обмена данными. И тоже возникало что-то такое, когда один еще был в режиме обмена, а другой уже бросал трубу и переходил в командный.

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

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

Позже еще приходилось решать задачку автоматом - каждое утро высасывать обновленный прайс-лист одного крупного поставщика компьютерной комплектухи, сравнивать его со своим локальным, обходить "старую", уже имеющуюся у нас комплектуху, а затем еще декодировать и конвертировать HTML-код так, чтобы поставщик не мог предъявить претензий по поводу таких ежедневных утренних краж sm.gif Задачка четко решалась автоматом.

Короче, что-то у вас именно с переключением режимов. Если ждете данные, а приходит от сервера команда, то так и есть. Все вспомнил sm.gif

zebrox
И почему конец сообщения определяется таймаутом?
Почему не использовать последовательность CR LF?
Иван Плетнев
Цитата(zebrox @ Apr 23 2014, 21:18) *
И почему конец сообщения определяется таймаутом?
Почему не использовать последовательность CR LF?

Потому что встречаются ответы модема, в которых есть и две последовательности CR LF, и три.
RadikX
Цитата(Иван Плетнев @ Apr 23 2014, 19:21) *
Потому что встречаются ответы модема, в которых есть и две последовательности CR LF, и три.

Это как раз не страшно. У меня идет работа с голосом (прием звонка, дозвон), SMS (прием и отправка) и GPRS (клиент) , плюс полный набор обслуживающих команд и всего в сумме получается не более 60 вариантов заголовков команд/ответов. Если делать не побуквенное сравнение, а более оптимизированный парсер, то получается быстрое автоматическое определение что пришло. Но с GPRS, да, в непрозрачном режиме приходится выкручиваться, а в прозрачном нет мультисокетов. Как вариант - реализовать режим мультиплексирования (CMUX).
Было бы очень удобно, если бы китайцы добавили аналог +CIPRXGET, но не по незапрашиваемому ответу, а по запросу.
Alechek
Цитата(Иван Плетнев @ Apr 23 2014, 21:21) *
Потому что встречаются ответы модема, в которых есть и две последовательности CR LF, и три.

Самое первое правило - обрабатывать ВСЕ!!! последовательности от модема.

Второе - последовательность AT команд четко регламетирована (см GSM 07.07 раздел 4).
Заметьте, у всех команд стандарта есть final result code (чего не скажешь творчестве собственных расширений производителей модемов)
Все что между - результат выполения команды.

И еще есть ансинхронные ответы. Могут влезть куда угодно. либо пропускайте их, либо применяйте. НО РАСПОЗНАТЬ их надо!!
tdocs.su
Цитата(Alechek @ Apr 25 2014, 12:29) *
Самое первое правило - обрабатывать ВСЕ!!! последовательности от модема.

Второе - последовательность AT команд четко регламетирована (см GSM 07.07 раздел 4).
Заметьте, у всех команд стандарта есть final result code (чего не скажешь творчестве собственных расширений производителей модемов)
Все что между - результат выполения команды.

И еще есть ансинхронные ответы. Могут влезть куда угодно. либо пропускайте их, либо применяйте. НО РАСПОЗНАТЬ их надо!!

Однозначно! Все прочее (кроме автомата) - просто мертвому припарки. Выкрутиться не получится.
Кстати, у оператора case в любом "языковом исполнении" всегда имеется свой else, позволяющий пропустить что-угодно как с распознаванием, так и без, если со стороны пришел некий "невнятный" и не вписывающийся в рамки протокола ответ - команда вместо данных и в таком духе.
zebrox
Тоже сначала делал обработку по таймауту.
Все было очень плохо.

Потом перешел на обработку по концу строки.
По приходу строки выставляю сигнал ртс, в основном цикле обрабатываю строку, убираю ртс когда процессор готов к приему.
Имею проблему только с командой чтения имея, т.к. имей возвращается во второй строке и без заголовка.
В этом случае обрабатываю второую строку и проверяю что-бы ее длинна была 14 символов.
При этом эхо включено, и оно не мешает процессору вылавливать необходимые ответы.
Иван Плетнев
Цитата(tdocs.su @ Apr 25 2014, 16:42) *
Однозначно! Все прочее (кроме автомата) - просто мертвому припарки. Выкрутиться не получится.
Кстати, у оператора case в любом "языковом исполнении" всегда имеется свой else, позволяющий пропустить что-угодно как с распознаванием, так и без, если со стороны пришел некий "невнятный" и не вписывающийся в рамки протокола ответ - команда вместо данных и в таком духе.

Немного не понял про else. Что Вы имеете ввиду?

Цитата(zebrox @ Apr 25 2014, 23:24) *
Тоже сначала делал обработку по таймауту.
Все было очень плохо.

Потом перешел на обработку по концу строки.
По приходу строки выставляю сигнал ртс, в основном цикле обрабатываю строку, убираю ртс когда процессор готов к приему.
Имею проблему только с командой чтения имея, т.к. имей возвращается во второй строке и без заголовка.
В этом случае обрабатываю второую строку и проверяю что-бы ее длинна была 14 символов.
При этом эхо включено, и оно не мешает процессору вылавливать необходимые ответы.

Кстати, вот что хотел спросить. Если мы выставляем ртс, и за это время в буфере SIM900 накопится, например, три сообщения, то, когда мы отпустим ртс, они вывалятся все разом, без пауз между ними?

Цитата(Alechek @ Apr 25 2014, 16:29) *
Самое первое правило - обрабатывать ВСЕ!!! последовательности от модема.

Второе - последовательность AT команд четко регламетирована (см GSM 07.07 раздел 4).
Заметьте, у всех команд стандарта есть final result code (чего не скажешь творчестве собственных расширений производителей модемов)
Все что между - результат выполения команды.

И еще есть ансинхронные ответы. Могут влезть куда угодно. либо пропускайте их, либо применяйте. НО РАСПОЗНАТЬ их надо!!

Я всё понимаю, и стараюсь учесть все возможные ответы от модема. Но если пытаться определять конец посылки по CR/LF, учитывая то, что CR/LF может быть несколько, например \r\nOK\r\nблаблабла\r\n, то нам как минимум нужно знать, сколько \r\n будет в ответе от модема. Посчитать их, конечно, легко, но если асинхронное сообщение появилось, с ним как быть? Направьте меня в нужную сторону. Вообще, конечно, то, что форматы ответов так разнятся от команды к команде, сильно осложняет их обработку и даже немного раздражает.

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

Кстати, по поводу собственно сабжа. У меня трудности, описанные в первом посте этой темы возникали в основном из-за ошибки переполнения буфера UART, связанной с отключением прерываний в другом, параллельном процессе. Так что проблема, можно сказать, почти решена. Сейчас тестирую прозрачный режим, обсуждение в соседней теме . Буду рад выслушать Ваши мнения.

Цитата(RadikX @ Apr 25 2014, 13:59) *
Это как раз не страшно. У меня идет работа с голосом (прием звонка, дозвон), SMS (прием и отправка) и GPRS (клиент) , плюс полный набор обслуживающих команд и всего в сумме получается не более 60 вариантов заголовков команд/ответов. Если делать не побуквенное сравнение, а более оптимизированный парсер, то получается быстрое автоматическое определение что пришло. Но с GPRS, да, в непрозрачном режиме приходится выкручиваться, а в прозрачном нет мультисокетов. Как вариант - реализовать режим мультиплексирования (CMUX).
Было бы очень удобно, если бы китайцы добавили аналог +CIPRXGET, но не по незапрашиваемому ответу, а по запросу.

Мне как раз мультисокет и не нужен, мне достаточно одного соединения. Значит ли это, что мне лучше воспользоваться прозрачным режимом?
И второе. Намекните, если можно, про устройство Вашего оптимизированного парсера. Как он работает?
zebrox
После снятия ртса сим выдаст следующую строку, проц выставит ртс, обработает ее, уберет ртс и так до тех пор, пока сим все строки не выдаст.

Большинство сообщений от сима имею заголовок, +IPR например. В програме заданы все заголовки на которые надо реагировать.
Если в пришедшей строке найден один из заголовков, то проц уходит в его обработчик, который знает в какой что читать.
ОК и ЕРРОР так-же определены как заголовки. При их приходе проверяется проверяется последняя переданная команда (храню условный номер).
Если на эту команду ответ должен быть ОК, а пришел ЕРРОР, то снова передаю ту команду.
Если пришел ОК то переход к слудующей команде. Это уже обычная машина сосостояний.
Alechek
Добавлю еще, что большинство ответов можно расматривать как заголовок и набор параметров, разделенных запятыми. параметры могут быть int, hex, str
в большинстве случаев достаточно распознавать не более 4-х параметров.
tdocs.su
Цитата(zebrox @ Apr 28 2014, 02:46) *
Если пришел ОК то переход к слудующей команде. Это уже обычная машина сосостояний.

Не-а. Надо сначала "пропихнуть" автомат в следующее состояние, которое будет ждать прихода именно Ок.
Цитата
Немного не понял про else. Что Вы имеете ввиду?

Вот - http://www.delphisources.ru/forum/showthread.php?t=6547 Обратите внимание, что case намного прозрачнее if. Но это его удобство, проще читать код, все видно хорошо.
А ссылку на статью давал, там есть рисунки с кодами автомата. Для возвртата в исходное состояние используется оператор goto. И под каждым case устанавливается следующее состояние.
Короче, все просто оказалось, но намучился с этим всем конкретно, когда шел не автоматным путем.
alexdos
Цитата(Иван Плетнев @ Apr 27 2014, 19:21) *
Я всё понимаю, и стараюсь учесть все возможные ответы от модема. Но если пытаться определять конец посылки по CR/LF, учитывая то, что CR/LF может быть несколько, например \r\nOK\r\nблаблабла\r\n, то нам как минимум нужно знать, сколько \r\n будет в ответе от модема. Посчитать их, конечно, легко, но если асинхронное сообщение появилось, с ним как быть? Направьте меня в нужную сторону. Вообще, конечно, то, что форматы ответов так разнятся от команды к команде, сильно осложняет их обработку и даже немного раздражает.

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

Тоже определял конец строки по таймауту. Работало.
Потом перешел по \r\n. По \r\n оказалось более работоспособно, и нет никакой необходимости в знании сколько их будет в ответе. К примеру посмотрим на строку \r\nOK\r\nблаблабла\r\n, по первому \n мы видим что это начало строки (так как количество принятых байтов равно 2). По второму получаем ответ OK. По третьему получаем ответ блаблабла. Поэтому при разборе ответов, задача сводится к проверке "наборов байтов находящихся между \r\n". Единственное что портит всю картину это приглашение к вводу данных "> ", но и это обходится очень просто.
tdocs.su
Лексема (Lexical) token Lexical unit - Языковая конструкция, по соглашению представляющая элементарную синтаксическую единицу [из п. 2 Табл. 1 ГОСТ 28397-89] sm.gif
zebrox
Цитата(alexdos @ Apr 28 2014, 08:28) *
...Единственное что портит всю картину это приглашение к вводу данных "> "...


Жду 1 сек, и подразумеваю, что пришло приглашение. Не видел ситуации, когда бы приглашение не пришло при отправке смса.
А при передаче данных, мне мой сервер подтверждает получение посылки.

Немного сложно обрабатывать ответы, которые не содержат заголовка (чтение имей).
alexdos
Цитата(zebrox @ Apr 28 2014, 12:14) *
Жду 1 сек, и подразумеваю, что пришло приглашение. Не видел ситуации, когда бы приглашение не пришло при отправке смса.
А при передаче данных, мне мой сервер подтверждает получение посылки.

Немного сложно обрабатывать ответы, которые не содержат заголовка (чтение имей).

Аж целую секунду, я не могу позволить себе столько ждать.
В случае с \r\n никаких сложностей.
zebrox
Блокируется только машина юарта, она переходит в состояние ожидания, а все остальные машины продолжают работать.
Lotor
Цитата(alexdos @ Apr 28 2014, 10:28) *
Единственное что портит всю картину это приглашение к вводу данных "> ", но и это обходится очень просто.

А как обходите, просто конечный автомат парсера настраиваете на поиск ">" без обрамления \r\n?
zebrox
Я думаю.
В прерывании прихода символа из юарта, проверяем, если автомат находится в режиме ожидания приглашения, и пришел символ приглашения, то переходим к следующему состоянию (отправка текста смса, отправка сообщения серверу).
Параллельно тикает таймер, если таймер истек, значит приглашение не пришло, снова пробуем.
Lotor
Цитата(zebrox @ Apr 28 2014, 17:14) *
В прерывании прихода символа из юарта, проверяем, если автомат находится в режиме ожидания приглашения, и пришел символ приглашения, то переходим к следующему состоянию (отправка текста смса, отправка сообщения серверу).

У Вас автомат общий - и на отправку и на прием? По крайней мере так понял из сообщения.
Мне удобнее иметь отдельный конечный автомат на прием, который периодически запускается и которому можно указать, какой токен в данный момент ожидается. Да и в прерывании уарта лучше просто заполнять кольцевой буфер. Всё imho. =)
zebrox
Юарт у это разделяемый ресурс. Доступом к нему управляет структура из двух полей, первое это режим работы юарта, второе время в этом режиме.
Разным независимым задачам необходим доступ к юарту (0-свободен, 1-залочен (поломан), 2-конфигурация сима, 3-чтение температуры, 4-чтение уровня сигнала, 5-чтение регистрации, 6-подключение с серверу, 7-передача данных ...).
Все эти задачи это независымые автоматы.

Если в режиме юарта записан 0, значит юарт свободен и любая задача (автомат) может его занять (записать в режим свой номер) и запустить таймер освобождения юарта (на случай если задачу заглючит).

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

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

Надеюсь из этого можно что-то понять sm.gif

alexdos
Цитата(Lotor @ Apr 28 2014, 15:38) *
А как обходите, просто конечный автомат парсера настраиваете на поиск ">" без обрамления \r\n?

Да, ожидаю с таймаутом:

Код
       case While_Prigl: if ((Bufer_Rx_GPRS[0] == '>') && (Bufer_Rx_GPRS[1] == ' ')) { Main_cicle = Send_String3;}
                else if ((Ticks_20ms - curTicks) > 10)  Main_cicle = Power_off;
                         break;
Иван Плетнев
Цитата(tdocs.su @ Apr 28 2014, 14:11) *
Не-а. Надо сначала "пропихнуть" автомат в следующее состояние, которое будет ждать прихода именно Ок.

Вот - http://www.delphisources.ru/forum/showthread.php?t=6547 Обратите внимание, что case намного прозрачнее if. Но это его удобство, проще читать код, все видно хорошо.
А ссылку на статью давал, там есть рисунки с кодами автомата. Для возвртата в исходное состояние используется оператор goto. И под каждым case устанавливается следующее состояние.
Короче, все просто оказалось, но намучился с этим всем конкретно, когда шел не автоматным путем.

Да я ж говорю, что тоже на автоматах всё у меня
Код
            case 0:
                ExtraBufferCopyFlag = 1; //флаг копирования нужного фрагмента буфера (со смещением)
                send_command0(CIPSTATUS);                
                gprsstate++;
                response_timeout=2000;
                break;
            case 1:
                if (!response_wait) { //если дождались ответа
                    switch (response_code) {
                        case 15: // INITIAL STATE
                            gprsstate++;
                            ready_to_send=0;
                            break;
                        case 17: // PDP DEACT STATE (Отключен сетью)
                            gprsstate = 100; //закрываем все подключения
                            ready_to_send=0;
                            break;
                        case 16: //TCP CLOSED
                            gprsstate = 12;
//                            gsm_delay=300;
                            ready_to_send=0;
                            break;
                        case 18: //CONNECT OK
                            gprsstate = 20;
                            send_flag=1;
                            break;
                        case 20: //CONNECTING
                            gprsstate = 30;
                            break;
                        case 3:
                            gprsstate=30;
                            break;
                    }
                }

Ну и так далее.

Цитата(alexdos @ Apr 28 2014, 14:28) *
Тоже определял конец строки по таймауту. Работало.
Потом перешел по \r\n. По \r\n оказалось более работоспособно, и нет никакой необходимости в знании сколько их будет в ответе. К примеру посмотрим на строку \r\nOK\r\nблаблабла\r\n, по первому \n мы видим что это начало строки (так как количество принятых байтов равно 2). По второму получаем ответ OK. По третьему получаем ответ блаблабла. Поэтому при разборе ответов, задача сводится к проверке "наборов байтов находящихся между \r\n". Единственное что портит всю картину это приглашение к вводу данных "> ", но и это обходится очень просто.


Это Вы в прерывании всё делаете? Если так, то приличный такой обработчик в прерывании будет крутиться. А если нет, тогда опять не ясно, как конец сообщения определить?
alexdos
Цитата(Иван Плетнев @ Apr 28 2014, 18:38) *
Это Вы в прерывании всё делаете? Если так, то приличный такой обработчик в прерывании будет крутиться. А если нет, тогда опять не ясно, как конец сообщения определить?

Да нет, в прерывании я мало что делаю
Код
/* завершим текстовую строку*/
           if(Bufer_Rx_GPRS[Count_Byte_Rx_GPRS - 1] == '\r')
                      {
                         Bufer_Rx_GPRS[Count_Byte_Rx_GPRS - 1] = 0;
                         strncpy(Bufer_Rx_GPRS_Parser, Bufer_Rx_GPRS, Count_Byte_Rx_GPRS);    /* скопируем строку в буфер парсера*/
                        }
                     else
                      {
                         Bufer_Rx_GPRS[Count_Byte_Rx_GPRS] = 0;
             strncpy(Bufer_Rx_GPRS_Parser, Bufer_Rx_GPRS, Count_Byte_Rx_GPRS + 1);    /* скопируем строку в буфер парсерв*/                            
                        }

           Count_Byte_Rx_GPRS = 0;
           GPRS_rxgap = 1;                        
                  }    
                 else Bufer_Rx_GPRS[Count_Byte_Rx_GPRS++] = GPRS_Byte_in; /* принятый байт сохраним в приемный буфер */
         if (Count_Byte_Rx_GPRS >= 512) Count_Byte_Rx_GPRS = 0;
        }
RadikX
Цитата(Иван Плетнев @ Apr 27 2014, 20:21) *
Мне как раз мультисокет и не нужен, мне достаточно одного соединения. Значит ли это, что мне лучше воспользоваться прозрачным режимом?
И второе. Намекните, если можно, про устройство Вашего оптимизированного парсера. Как он работает?

В прозрачном режиме проще: могут придти только данные.
Устройство парсера
CODE
typedef enum
{
ANSWER_PARAM_TYPE_UNKNOWN =0, // Тип параметра не определился
ANSWER_PARAM_TYPE_STRING =1, // Строка выделенная кавычками
ANSWER_PARAM_TYPE_CONSTANT =2, // Строка - константа, заменяем на значение их дефйна
ANSWER_PARAM_TYPE_INTEGER =3, // Целое
ANSWER_PARAM_TYPE_LIST =4, // Список возможных значений - строка в круглых скобках
ANSWER_PARAM_TYPE_COMMAND =5 // Заголовок-опознаватель команды
} TEnum_AnswerParamType;

typedef struct
{
TEnum_AnswerParamType ParamType;
uint8_t IsCmd;
char* Value;
uint16_t IntVal;
uint16_t Len;
} TStruct_AnswerParam;
char* GetAnswerParam(char * Buf, TStruct_AnswerParam *AnswerParam);

На вход подается указатель на входную строку и на заполняемую структуру. На выходе возвращает указатель на следующий за последним обработанным байт. Можно вызывать циклически для разбора всей команды. CR LF заменяется на \0.
CODE
char* GetAnswerParam(char * Buf, TStruct_AnswerParam *AnswerParam)
{
AnswerParam->ParamType = ANSWER_PARAM_TYPE_UNKNOWN;
AnswerParam->IsCmd = 0;
int8_t BracketCnt=0;
int8_t QuotesCnt=0;

uint8_t FlagNewParam=1;
uint8_t FlagCmd=0;

uint16_t Len=0;
char tmp;

Buf=ReMapRxBufferPoint(Buf);
AnswerParam->IntVal = 0;
AnswerParam->Value =Buf;

do
{
switch (*Buf)
{
case ':':
FlagCmd=1;
break;
case ' ':
if (FlagCmd)
{
AnswerParam->IsCmd=1;
Buf++;
Buf=ReMapRxBufferPoint(Buf);
}
else
{
FlagCmd=0;
break;
}
Len++;
case ',':
if ((BracketCnt)||(QuotesCnt%2))
{
FlagCmd=0;
break;
}
case 0:
{
if ((AnswerParam->ParamType == ANSWER_PARAM_TYPE_LIST)&&BracketCnt)
AnswerParam->ParamType = ANSWER_PARAM_TYPE_UNKNOWN;
if ((AnswerParam->ParamType == ANSWER_PARAM_TYPE_STRING)&&(*ReMapRxBufferPoint(Buf-1)!='\"'))
AnswerParam->ParamType = ANSWER_PARAM_TYPE_UNKNOWN;

if ((AnswerParam->ParamType == ANSWER_PARAM_TYPE_CONSTANT)||(AnswerParam->ParamType == ANSWER_PARAM_TYPE_STRING))
{

if (AnswerParam->IsCmd)
Len-=2;
AnswerParam->IntVal=0;
AnswerParam->Len=Len;
for (uint8_t i=1; i<(sizeof(strCmdArray)/sizeof(strCmdArray[0])); i++)
{
if (AnswerParam->ParamType == ANSWER_PARAM_TYPE_CONSTANT)
{
if (Len == strCmdArray[i].Len)
if (CompareStrFStr(AnswerParam->Value, strCmdArray[i].Val))
{
AnswerParam->IntVal = strCmdArray[i].Code;
break;
}
}
else
{
if (Len-2 == strCmdArray[i].Len)
if (CompareStrFStr(AnswerParam->Value+1, strCmdArray[i].Val))
{
AnswerParam->IntVal = strCmdArray[i].Code;
break;
}
}

}

if (AnswerParam->IntVal == 0)
{


}

}
return Buf;
}
case '(':
{
if (FlagNewParam)
{
AnswerParam->ParamType = ANSWER_PARAM_TYPE_LIST;
AnswerParam->Value = Buf;
}
if ((AnswerParam->ParamType == ANSWER_PARAM_TYPE_INTEGER)
||(AnswerParam->ParamType == ANSWER_PARAM_TYPE_CONSTANT))
AnswerParam->ParamType = ANSWER_PARAM_TYPE_UNKNOWN;

BracketCnt++;
break;
}
case ')':
{
if ((AnswerParam->ParamType == ANSWER_PARAM_TYPE_INTEGER)
||(AnswerParam->ParamType == ANSWER_PARAM_TYPE_CONSTANT))
AnswerParam->ParamType = ANSWER_PARAM_TYPE_UNKNOWN;
BracketCnt--;
break;
}
case '\"':
{
if (FlagNewParam)
{
AnswerParam->ParamType = ANSWER_PARAM_TYPE_STRING;
AnswerParam->Value = Buf;
}
if ((AnswerParam->ParamType == ANSWER_PARAM_TYPE_INTEGER)
||(AnswerParam->ParamType == ANSWER_PARAM_TYPE_CONSTANT))
AnswerParam->ParamType = ANSWER_PARAM_TYPE_UNKNOWN;
QuotesCnt++;
break;
}
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
{
if (FlagNewParam)
{
AnswerParam->ParamType = ANSWER_PARAM_TYPE_INTEGER;
AnswerParam->Value = Buf;
}
if (AnswerParam->ParamType == ANSWER_PARAM_TYPE_INTEGER)
{
AnswerParam->IntVal = AnswerParam->IntVal * 10;
AnswerParam->IntVal += *Buf-0x30;
}


break;
}
default:
{
if (FlagNewParam)
{
AnswerParam->ParamType = ANSWER_PARAM_TYPE_CONSTANT;
AnswerParam->Value = Buf;
}

if (AnswerParam->ParamType == ANSWER_PARAM_TYPE_INTEGER)
AnswerParam->ParamType = ANSWER_PARAM_TYPE_CONSTANT;
}
}

if (*Buf!=' ') FlagNewParam=0;
if (*Buf!=':') FlagCmd=0;
tmp=*Buf;
if (!((tmp==' ')&&(FlagNewParam)))
Len++;

Buf++;
Buf=ReMapRxBufferPoint(Buf); // Пересчет указателя, т.к буфер кольцевой
}
while (tmp);

return Buf;
}
tdocs.su
Вспомнил. Делал еще какую-то буферизацию для хвоста сообщения, остававшегося неразобранным с прошлого "сеанса". Т.е. то, что приходило и оказывалось неполным, не игнорировалось, а сохранялось в tile, а потом, когда дополнялось, разбиралось полностью. Жаль вот кодов не осталось, а то бы сейчас проблем не было. Но кодам почти 20 лет уже, все хранилось в архивах, а потом винда эти архивы грохнула. Работало все безупречно...
Lotor
Цитата(tdocs.su @ Apr 29 2014, 08:14) *
Вспомнил. Делал еще какую-то буферизацию для хвоста сообщения, остававшегося неразобранным с прошлого "сеанса".

Неразобранное обязательно надо проверять. У меня конечный автомат парсера помимо ожидаемых сообщений еще и проверяет на unsolicted. И если таковые имеются - вызываются их обработчики.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.