Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: State machine
Форум разработчиков электроники ELECTRONIX.ru > Сайт и форум > В помощь начинающему > Программирование
Страницы: 1, 2
Dog Pawlowa
Цитата(TMX @ Mar 11 2009, 17:22) *
А вот насчет вашей реализации - похоже на реализацию Gomez, но event driven, и те же недостатки.

Мне важно иметь механизм, а с недостатками можно смириться. Для сведения - в устройстве более 100 состояний.

Цитата(TMX @ Mar 11 2009, 17:22) *
Простой switch по состояниями имеет меньше источников ошибок, поскольку там нет нумерации и перечисление событий только в одном месте.

Когда "одно место" растягивается на много страниц, меня лично это раздражает, и я делаю больше ошибок.
А со списком я вижу на три строчки, в которых перечислено название состояния (с суффиксами номера и функции) и текст, который выводится в терминал (или на дисплей) для его идентификации.
TMX
Цитата(Dog Pawlowa @ Mar 11 2009, 16:46) *
Мне важно иметь механизм, а с недостатками можно смириться. Для сведения - в устройстве более 100 состояний.

Странно люди относятся к инструментам, которыми пользуются.
К примеру, просто машину (не состояний) они тщательно выбирают, взвешивают все за и против, смотрят на внешний вид и удобство посадки. Тратят на это немало времени, читают журналы, просматривают интернет. Пожалуй, стоит взять на вооружение фразу "мне важно иметь механизм, а с недостатками можно смириться, для сведения - я проезжаю 100 км за раз" a14.gif
В то же время, с помощью методов и средств программирования мы зарабатываем деньги, в том числе, на автомобили. Почему бы не потратить время на поиск удачных решений?

По поводу списка:
в случае switch все состояния тоже переименовываются с помощью enum. Так что, вот и список (1 шт.).
Кстати, для основных автоматов я использую не switch, а возврат функцией состояния указателя на следующую функцию состояния. Там тоже список прототипов функций (1 шт.).

В одном из устройств у меня в фоновом процессе крутилось параллельно около 10 взаимосвязанных автоматов от 10 до 60 состояний в каждом. Плюс протокол обмена - порядка 100 состояний.
Dog Pawlowa
Цитата(TMX @ Mar 12 2009, 15:14) *
К примеру, просто машину (не состояний) они тщательно выбирают, взвешивают все за и против, смотрят на внешний вид и удобство посадки. Тратят на это немало времени, читают журналы, просматривают интернет. Пожалуй, стоит взять на вооружение фразу "мне важно иметь механизм, а с недостатками можно смириться, для сведения - я проезжаю 100 км за раз"

Честно признаюсь - я свою машину купил за 10 минут и нисколько не жалею. Любая машина - кусок железа. Главное, что она выполняет свои функции. Так и с машиной состояний smile.gif Недостатки, которые знаешь, становятся просто фичами.

Цитата(TMX @ Mar 12 2009, 15:14) *
Почему бы не потратить время на поиск удачных решений?

Логично, потратил.

Цитата(TMX @ Mar 12 2009, 15:14) *
в случае switch все состояния тоже переименовываются с помощью enum. Так что, вот и список (1 шт.).

Не понял, как привязываются функции к состоянию. Поясните, плз. Зачем этот список состояний, если ниже :

Цитата(TMX @ Mar 12 2009, 15:14) *
Кстати, для основных автоматов я использую не switch, а возврат функцией состояния указателя на следующую функцию состояния. Там тоже список прототипов функций (1 шт.).


Цитата(TMX @ Mar 12 2009, 15:14) *
одном из устройств у меня в фоновом процессе крутилось параллельно около 10 взаимосвязанных автоматов от 10 до 60 состояний в каждом. Плюс протокол обмена - порядка 100 состояний.
Померялись biggrin.gif
TMX
Цитата(Dog Pawlowa @ Mar 13 2009, 12:21) *
Не понял, как привязываются функции к состоянию. Поясните, плз. Зачем этот список состояний, если ниже :

Просто мы используем несколько реализаций машин состояний:
Для фоновой задачи, где много состояний - контроль оборудования, взаимодействие с пользователем и т.п. используется реализация с возвратом указателя (описана в раннем посте), она удобна для отладки. Поскольку обычно требования меняются по ходу разработки.
В прерываниях или в случаях ограниченных ресурсов (маленький аппаратный стек, например) используется реализация с помощью switch.
Ну а для меню написали специальную библиотеку и приложение под windows, которое генерирует уже инициализированную таблицу меню.

Цитата(Dog Pawlowa @ Mar 13 2009, 12:21) *
Померялись biggrin.gif

Дело в том, что при с ростом сложности задачи, генерация и реализация автомата становится рутинной операцией. Проблемой становится выбор правил моделирования.
На данный момент меня интересуют следующие вещи:
1. преимущества и недостатки моделей управляемых событиями и по опросу. Пока ясно, что модель по опросу менее требовательна к ресурсам в прерываниях.
2. организация взаимодействия между автоматами. Возможно, в некоторых случаях выгоднее применять иерархическую модель с управлением по событиям.

В принципе, я давно этой темой интересуюсь. Многие иностранные книжки по этой теме у меня в бумажном варианте. Но кроме Samek'а ничего оригинального не предлагается - почти везде switch + UML модель по опросу.
Dog Pawlowa
Цитата(TMX @ Mar 13 2009, 14:07) *
Просто мы используем несколько реализаций машин состояний...
В принципе, я давно этой темой интересуюсь. ...

Понятно ... Где-то рядом ходим, только я меньше теорией интересуюсь, потому как следующий шаг - RTOS- в общем то напрашивается. А я не хочу.
Приложение на PC тоже есть, но уже для ARM с ЖКИ, вместе с графикой, автоматически готовятся данные для SD-карты, и менюшка работает по данным SD-карты. В идеале код править не нужно - просто вставить другую карточку и получилось другое устройство. В форуме АРМов я эту тему затрагивал.
Вопросы взаимодействия автоматов я в каком-то виде решил, обычно сочетаю явно редкие события плюс опросы состояний.
Поскольку быстродействие switch по большому счету непредсказуемо, часто использую массив функций, что в сочетании с pragma inline дает великолепные результаты.
Успехов smile.gif Готов всегда поддержать дискусиию rolleyes.gif
_Pasha
Цитата(Dog Pawlowa @ Mar 13 2009, 17:59) *
что в сочетании с pragma inline дает великолепные результаты.
Ваш аргумент был последним, который перевесил соблазн пользоваться гнутым косвенным goto. Надолго smile.gif
TMX
Цитата(Dog Pawlowa @ Mar 13 2009, 17:59) *
Понятно ... Где-то рядом ходим, только я меньше теорией интересуюсь, потому как следующий шаг - RTOS- в общем то напрашивается. А я не хочу.

Если автоматы по опросу (т.е. циклически вызываются), то можно сделать так:
Код
struct auto_struct
{
  void *auto_ptr (void);
  int priority;
  int cnt;
} automata_list =
{
automaton_1, 1, 0,
automaton_2, 2, 0,
automaton_3, 3, 0,
};

while (1)
{
  for (i = 0; i < sizeof(automata_list) / sizeof(struct auto_struct); i++)
  {
    if (--automata_list.cnt == 0)
    {
      automata_list.auto_ptr();
      automata_cnt = automata.priority;
    }
  }
}

добавляем общие состояния ожидания с возвратом в предыдущее состояние и Voila! получаем кооперативную многозадачность.
_Pasha
Цитата(TMX @ Mar 13 2009, 19:08) *
Если автоматы по опросу (т.е. циклически вызываются), то можно сделать так:

...И в анализе приоритетов пишем


Код
if(automata.priority >= priority_sentinel) automata_list.auto_ptr();


А priority_sentinel ужЕ увеличивается/уменьшается в зависимости от частоты запросов на повышение приоритета.
TMX
это я показал round-robin с постоянным приоритетом, но разным доступом к ресурсам процессора.
т.е. автоматы с низким приоритетом (большим значением priority) вызываются реже.
Можно, конечно, и динамические приоритеты реализовать. Можно смешанные.

Вопрос в организации запросов к ресурсам и ожидания.
_Pasha
Цитата(TMX @ Mar 13 2009, 20:55) *
Вопрос в организации запросов к ресурсам и ожидания.

Организация ожидания. Если нетрудно, осветите проблемы. Интересно. Еще добавлю линк на тему: http://electronix.ru/forum/index.php?showtopic=48243

Имхо, я добился полезного решения.
ReAl
Цитата(Dog Pawlowa @ Mar 11 2009, 09:18) *
LOL : чуть дальше "Но реально не применяю :-), как-то не жмёт пока обычный switch()... "
По крайней мере я не пользуюсь этой возможностью не потому, что я о ней не знаю :-)
Мне просто не нужен эффект от неё, а я с какого-то фига стараюсь не использовать нечто очень специфическое - хотя иногдасамому непонятно почему. Используют же работающие с IAR его всякие красивости и в ус не дуют.
Впрочем, потихоньку прорывает, недавно таки поставил диапазонный case там, где он был удобен, чуть раньше локальную (вложенную) функцию использовал там, где она очень просилась.

В подобных же темах часто обсуждается как бы выжать ещё немного объёма и скорости и в таком месте вычисляемый goto вполне в строку. Имеет общие черты как функцй, возвращющих указатели на фунции, так и switch
Diz
Интересно, а как сделать с набором параллельно работающих конечных автоматов (без RTOS) долго работающую задачу, требующую активного участия процессора ? Например, работу с FAT (сектор может читаться значительное время) или рендеринг экрана для UI (тоже небыстро). Разбивать на отдельные этапы некрасиво и неудобно, а иначе задача будет блокировать другие на долгое время.

С подобными вещами не сталкивался, так как у меня обычно много мелких задач и отрабатываются они мгновенно, либо без участия
процессора - большую часть времени процессор спит.
Dog Pawlowa
Цитата(Diz @ Mar 14 2009, 13:13) *
Интересно, а как сделать с набором параллельно работающих конечных автоматов (без RTOS) долго работающую задачу, требующую активного участия процессора ? Например, работу с FAT (сектор может читаться значительное время) или рендеринг экрана для UI (тоже небыстро). Разбивать на отдельные этапы некрасиво и неудобно, а иначе задача будет блокировать другие на долгое время.

Да, есть некоторая проблема. Там, где обращение к УВВ гробит интерфейс пользователя, делаем упреждающее чтение в память при включении устройства (ну типа Windows smile.gif ). Там где это невозможно... ну работали же раньше с дискетами? Часики, песочек...
singlskv
Цитата(Diz @ Mar 14 2009, 12:13) *
Интересно, а как сделать с набором параллельно работающих конечных автоматов (без RTOS) долго работающую задачу, требующую активного участия процессора ? Например, работу с FAT (сектор может читаться значительное время) или рендеринг экрана для UI (тоже небыстро). Разбивать на отдельные этапы некрасиво и неудобно, а иначе задача будет блокировать другие на долгое время.
Разбивать на этапы, и других вариантов особо и нет...
Ну или хитрое разделение с задачами выполняемыми в прерываниях.
Diz
Цитата(singlskv @ Mar 15 2009, 01:23) *
Разбивать на этапы, и других вариантов особо и нет...
Ну или хитрое разделение с задачами выполняемыми в прерываниях.


Вообщем, для таких вещей напрашивается RTOS. Или разделение на несколько процессоров, если задача позволяет.
_Pasha
Цитата(Diz @ Mar 15 2009, 13:26) *
Вообщем, для таких вещей напрашивается RTOS.

ОЗУ надо побольше, чтоб стеки таких процессов не проваливались. И тогда - хоть setjmp, хоть его обрезанные варианты-решат проблему аж бегом.
Diz
Еще один интересный вариант реализации машины состояния на плюсах, использовали в одном проекте.

Каждое состояние представляется отдельным классом, унаследованным от базового. Все обработчики событий заданы в базовом классе как виртуальные функции:
virtual void eventButtonPressed() = 0 ;
virtual void eventTimerExpired() = 0 ;

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

Преимущества:

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

Можно сделать более сложную иерархию классов. Унаследовать класс от базового, определить в нем обработчик (напр. eventPacketReceived). От этого класса наследовать еще несколько подсостояний, но там этот обработчик не определять. В итоге подстостояния унаследуют реакцию на eventPacketReceived от своего суперкласса. Получается простая реализация иерархической машины состояний.

Можно инкапсулировать в класс данные, нужные только для конкретного состояния - например, счетчик принятых пакетов.

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

Хорошие IDE умеют показывать иерархию классов, что приятно для визуализации во время кодирования.


Недостатки:

Много писанины, много однострочных функций. Наглядности без визуализации IDE никакой.

Требуется еще одна таблица или switch для выбора метода по приходу события.

Компилятор может оказаться невменяемым :-)
singlskv
Цитата(Diz @ Mar 15 2009, 12:26) *
Вообщем, для таких вещей напрашивается RTOS. Или разделение на несколько процессоров, если задача позволяет.
Какой нафиг RTOS... smile.gif
вот буквально последняя задачка, опрос 6 дискретов с тактом 50мкс с фильтрацией (это быстрый процесс)
и обмен по уарт на 230400("медленный" процесс), обмен при этом на скорости ~19Кбайт/сек
все решает точное деление на состояния автомата обмена по уарту...
Diz
Цитата(singlskv @ Mar 19 2009, 23:12) *
Какой нафиг RTOS... smile.gif
вот буквально последняя задачка, опрос 6 дискретов с тактом 50мкс с фильтрацией (это быстрый процесс)
и обмен по уарт на 230400("медленный" процесс), обмен при этом на скорости ~19Кбайт/сек
все решает точное деление на состояния автомата обмена по уарту...


Ну, это деление не этапы опять же :-) Иногда хочется использовать готовую библиотеку (для того же FAT) - бить на состояния уже непросто.
Проще даже переписать с нуля.
Dog Pawlowa
Цитата(Diz @ Mar 20 2009, 00:32) *
Ну, это деление не этапы опять же :-) Иногда хочется использовать готовую библиотеку (для того же FAT) - бить на состояния уже непросто.
Проще даже переписать с нуля.

Да, большие готовые библиотеки лишают автоматы всяческих преимуществ...
Однако тупик с автоматами...
singlskv
Цитата(Dog Pawlowa @ Mar 20 2009, 12:05) *
Да, большие готовые библиотеки лишают автоматы всяческих преимуществ...
Однако тупик с автоматами...
Не, не всегда так все печально...,
если есть "жирная"(библиотечная в том числе) функция,
то она работает в основном цикле проги,
автоматы просто выносятся в прерывания,
например в прерывания системного тика (10мс, 1мс, 100мкс, 25мкс...)

Конечно при этом нужно "вручную" рулить приоритетами и занимаемым автоматами временем,
но как бонус, 100% загрузка проца и максимальные параметры по автоматам(при удачном ручном распределении)

Вобщем особых ограничений к использованию автоматов все равно не просматривается....
есть только частные ограничения реализации.
defunct
Цитата(Diz @ Mar 14 2009, 11:13) *
Интересно, а как сделать с набором параллельно работающих конечных автоматов (без RTOS) долго работающую задачу, требующую активного участия процессора ? Например, работу с FAT (сектор может читаться значительное время) или рендеринг экрана для UI (тоже небыстро). Разбивать на отдельные этапы некрасиво и неудобно, а иначе задача будет блокировать другие на долгое время.

.....
С FAT пример неудачен. Сектор то читается драйверком SPI, и пока он читается задача отдыхает (не требует активного участия CPU).

Рендеринг экрана - подходящий пример. Посмотрим по аналогии. Есть Borland'овский VCL, где вся графика обслуживается исключительно в основном потоке приложения, и при запуске какой-то длительной задачки в том же основном потоке прорисовка приложения намертво тормозится. Чтобы пользователь не подумал, что программа зависла, можно и нужно было либо стартовать доп поток, либо, что значительно проще - иногда вызвать функцию:

Application->ProcessMessages();

Вот так же можно поступить и тут. Что мешает реализовать некую функцию yield / idle - которую любой затяжной процесс сможет вызвать сам, чтобы дать поработать всем остальным процессам? Для того чтобы такую функцию можно было реализовать, необходимо чтобы был список задач(автоматов) с возможностью модификации в Run-Time (чтобы процесс вызывающий эту функцию мог быть удален из списка процессов на время своего испонения). Сосбно вот:

CODE
typedef void (* p_cb)(void);

#define COUNT(arr) (sizeof(arr) / sizeof((arr)[0]))

p_cb list[4];

void add_task( p_cb Callback)
{
for(int i = 0; i < COUNT(list); i++)
if (!list[ i ])
{
list[ i ] = Callback;
break;
}
}

void idle(void)
{
for(int i = 0; i < COUNT(list); i++)
if (list[ i ])
{
p_cb cb = list[ i ];
list[ i ] = NULL;
cb();
list[ i ] = cb;
}
}

void foo_1(void)
{
}

void foo_2(void)
{
}

// это долгий процесс делающий рендеринг
void foo_long(void)
{
volatile unsigned long x = -1UL;
while( x--)
{
// ... do complex stuff

if ( (x & 0xFF) == 0 ) // 255 iterations passed
{
// дадим поработать другим задачам
idle();
}
}
}

void main(void)
{
add_task( foo_1 );
add_task( foo_2 );
add_task( foo_long );

for(;;)
idle();
}



Цитата
Да, большие готовые библиотеки лишают автоматы всяческих преимуществ...
Однако тупик с автоматами...

Раскидать idle()'ов по ключевым точкам и автоматы получают вторую жизнь.
Dog Pawlowa
Цитата(defunct @ Mar 22 2009, 07:53) *
Раскидать idle()'ов по ключевым точкам и автоматы получают вторую жизнь.

Конкретный пример. Готовая библиотека efsl. Вставка готового порта заняла час.
Как по быстрому применить автомат для разделения процессов?
Потратить несколько дней на разбиение на куски?
Запихнуть процессы в прерывания? Да у меня под отладкой вообще прерывания глючат, все разобраться некогда.

Тупик может не с автоматами, а в голове. Завтра выставка smile.gif
_Pasha
Цитата(Dog Pawlowa @ Mar 23 2009, 13:57) *
Готовая библиотека efsl.  
Как по быстрому применить автомат для разделения процессов?

Покромсать те части, которые выходят на уровень железа, Yield() вызывать оттуда . Остальное трогать не надо имхо. 
Dog Pawlowa
Не нашел, чтобы эта ссылка была упомянута в теме.

http://www.state-machine.com/
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.