Как стать автором
Обновить

Комментарии 6

> также обработка сообщения не должна занимать слишком много времени

У меня вот такой вопрос — а как это время можно контролировать? Вот, предположим, у меня есть два потока — в одном происходит опрос некоего датчика, а второй управляет, скажем задвижкой. При поступлении сигнала с датчика мы отправим в очередь сигнал и задвижка должна закрыться, но не позднее чем, скажем за двадцать миллисекунд после наступления события. Понятно, что я могу отправить подтверждение (от датчика закрытия задвижки), но как при этом можно проконтролировать время между посылкой сигнала и подтверждением?
В промышленных ПЛК реализуют фиксированное (и определяемое пользователем) время цикла для программы/подпрограммы, таким образом гарантируется, например, что датчик будет опрошен раз в 100 мсек, данные в сеть отправлены раз в 10 мсек, а команда на задвижку будет послана раз в 1 сек. Мы называем это временной дорожкой, но в спец. литературе такого термина не встречал.
Ну и опять же не забываем про инерционность исполнительных механизмов, какой нибудь клапан с редуктором может открываться/закрываться 1 минуту, так что дергать его каждую миллисекунду совсем не обязательно.
C ПЛК понятно — там время цикла контролируется железкой. Если оно будет превышено (или достигнет, скажем 75% от заданной границы), то сразу будет предупреждение (ну у Siemens, по крайней мере это так). Причём программировать для этого вообще ничего не надо — надо просто задать пороговое время. Я подумал, что быть может в FreeRTOS тоже должны быть какие-то встроенные средства контроля времени исполнения. Ну не городить же счётчик и проверку каждый раз когда это нужно. А нужно будет довольно часто.
Контролировать время достаточно просто. Для этого в структуру сообщения нужно добавить «время» и задействовать счетчик, который инкрементирует значение каждую микросекунду. Текущее время записывается в ссобщение при отправке (метод sendMessage), а после обработки вычисляется разница и выводиться в лог. Также можно выводить разницу во времени между началом и концом обработки сообщения.
А вот гарантировать задержку гораздо сложнее. Как я уже написал выше, если слишком много Handler'ов использует один поток, очередь может быть заполнена сообщениями. Если время реакции критическое, то для этого Handler'a нужно создать отдельный поток. Впрочем, 20мс это достаточно большой временной интервал.
Уважаемый (?) автор, я бы на вашем месте не считал любой рабочий кусок кода поводом для написания статьи. Всё-таки, у вас не только должна быть цель, но также должно быть чёткое понимание того, что вы делаете.

Код просто ужасен. Возможно, вы — телепат, и вам всё очевидно, но я из
    char what;
ну никак не могу понять, что обрабатывается в сообщении. Особенно если заглядываю в код спустя пару месяцев после его написания. Может быть, обрабатываются символы? Тип же char :D
Короче, сделайте what хотя бы enum'ом.

    /** First argument */ 
    char arg1; 
    /** Second argument */ 
    char arg2; 
Спасибо, кэп. А что значат эти аргументы? Зависит от сообщения? Тогда почему их два, а не, скажем, пять? И снова char. Ваш любимый тип данных, что ли? Если что, то диапазон его значений — от -128 до 127, не вижу никаких очевидных причин выбирать char для аргументов.

    /** Pointer to the allocated memory. Handler should cast to the proper type, 
     * according to the message.what */ 
    void *ptr; 
О да, название ptr говорит мне о многом. Но только когда я курю грибы, а в обычном состоянии — только то, что поле является указателем, что и так видно по звёздочке (*) перед именем поля. Название data подошло бы гораздо лучше, хотя тоже не слишком информативно.

Организация классов у вас — просто вынос мозга. В структуре MESSAGE (АФФТАРБЛОНДИНКО?) есть handler, в котором есть messageQueue, который был получен от looper.

Чё?!


debugTx->putString(...)
Эвона как, пин TX уже взял на себя обязанности отправки данных по UART. Боюсь представить, что умеет UART в вашем коде (переводит с китайского на клингонский, наверное).

Об идее в целом: велосипед с треугольными колёсами, деревянной рамой и собачьей цепью.

Плюсы
расширить существующий Handler или добавить новый проще, чем создать новый поток
Не проще ли написать просто одну функцию — диспетчер сообщений, как это делается в Win32 API?
каждый Handler описан в отдельном файле
Да просто охренеть, какое преимущество. Ничего не мешает так сделать и без всей вашей кодовой каши.

Минусы
также обработка сообщения не должна занимать слишком много времени
Это очевидно для любой real-time среды. Это не минус именно вашего кода, хотя он и попахивает.
сложнее предсказать время реакции на событие из-за того, что обработка сообщений проиходит по-очереди, а не пседво-одновременно (по time slice)
И RTOS превращается… превращается… в просто OS!

* * *

У меня остался только один вопрос: нах простите, зачем так извращаться? Это просто over-engineering с целью поднятия ЧСВ (йа ымбеддерщег, учитесь) и получить плюсы в кармочку.
Отвечу по порядку.
Автор не телепат. Использовать enum нельзя из-за того, что обработка сообщений происходит в классах наследуемых от Handler. В своих классах вы можете использовать enum, только вот проще просто сделать define. Класс с хендлером содержит и типы сообщений и обработчик — все в одном файле, в одном switch. Так что вы гарантированно не забудете и избежите дублирования кодов сообщений (what). По поводу arg1 arg2 и ptr. Они называються так, потому что в каждом конкретном случае могут означать разные вещи, в зависимости от what. Наконец, я эти названия не сам придумал, а передрал из Android (зачем выдумавать новое, а потом путаться?). Думаю вам стоит написать Дианне Хакборн и сказать, что она пишет «ужасный код».
Автор не блондинка. Очевидно, что код приведен не весь. Если бы вы обратили внимание, то заметили, что MESSAGE есть только в одном месте, в остальных местах написано Message. Это было нужно, чтобы сделать typedef, который я не вставил в статью.
По поводу debugTx. В данном случае Tx не означает пин (с чего вы взяли?). Tx это абстрактный класс, реализацией которого может быть uart, usb-cdc, лог на дисплее или в файле.
По поводу иерархии классов. Не вижу проблемы в том, что 2 класса и структура, которые всегда используются только вместе, сильно связаны. В данном случае по-другому сделать нельзя.
По поводу превращения RTOS в ОS. Не хочу показаться грубым, но прежде чем писать такие комментарии, убедитесь, что вы разбираетесь в предмете. Наличие tick или time-slice это далеко не обязательный аттрибут ОСРВ. Сейчас многие ОСРВ вообще не имеют tick-прерывания. Единственное, что делает ОС ОСРВ, это возможность своевременно реагировать на события, что обеспечивается вытеснением потоков с низким приоритетом. Ну и, наконец, RTOS это подмножество ОС.

Зачем так извращаться? Уж не для плюсов в карму точно. Этот код изначально я писал для себя. Микроконтроллеры для меня это хобби, профессионально я занимаюсь разработкой Android framework. В Android все основано на Handler (вместе с пресловутыми what и arg). Этого мне очень не хватало при разработке для микроконтроллеров. В последствие, этот код стал успешно использоваться в нескольких проектах (в том числе в Rohde & Schwarz и UPS). Поскольку я не видел нигде подобных реализаций, я решил что это можеть полезно для читателей Хабра.
Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

Публикации

Истории