ВходНаше всё Теги codebook 无线电组件 Поиск Опросы Закон Суббота
13 июля
479404
fk0, легенда (11.01.2014 02:01 - 02:07, просмотров: 1392)
***** тут звонит и спрашивает как мол программировать автоматы. Особенной засадой является перепутывание имён переменных при программировании по методике сходной с методикой Шалыто. Ну там для смены состояния в функции автомата A14 пишем, мол S14=<новое-состояние>. Вообще конечно тут легко перепутать. A14 и A17, S11 и S13... Поэтому от цифр лучше конечно же сразу отказаться в пользу символных обозначений. А то к тому же трудно помнить, что в A14 у нас датчик температуры, например, в A17 электромагнит, в A11 двигатель, а в A13 парсер сообщений из компорта. Особенно если проектов больше одного, тут такая каша в голове начинается... Соответственно, вместо A11() и S11 пишем что-то вроде motor_fsm() и enum e_motor_states {MOTOR_STATE1, MOTOR_STATE2...} motor_S; Не помню как у Шалыто, но состояния лучше сразу через enum обозначить. И нигде и никогда не использовать цифры, только символические обозначения. По той же причине. Присвоить magnet_S=MOTOR_STATE2 теперь в языке C формально тоже можно. Но уже в глаза бросается больше. Кроме того, даже если проект и не собирается C++ компилятором, то можно попробовать его хотя бы частично собирать на ПК именно C++ компилятором (или на целевой платформе, если позволяет). Это позволит враз отловить такие ошибки и некоторые другие (и научиться сразу писать код приемлемый в рамках C++). Хотя это не всегда возможно. Кроме того, раз состояния обозначены типом enum, то при наличии -Wswitch у компилятора gcc (winavr), или также опции -Wall компилятор будет предупреждать, мол xxx_fsm() функция не все состояния в switch рассматривает, что тоже предупреждает некоторые ошибки. Предупреждения (warnings) компилятора нужно всегда анализировать и реагировать! Либо исправлять код, если он заведомо правильный, чтоб компилятор не давал предупреждения, либо код неправильный. Часто warning скрывает за собой действительную ошибку. Mожно написать if (magnet_S=MOTOR_STATE2) blablabla... В последнем случае опция -Wall у gcc даст предупреждение. Почему важны варнинги. Но несмотря на сказанное выше всё-же возможность налажать остаётся. Можно написать magnet_S=MOTOR_STATE2 и без C++ это никто не заметит. Как с этим бороться. Во-первых функционально разные автоматы разносить пор разным *.c файлам. И не для всех автоматов необходимо наблюдение за переменной его состояния в автоматах из других *.c файлов. Тогда такую переменную можно сделать статической static enum s_motor_states motor_S (писать внутри *.c файла). И другие автоматы, из других *.c файлов уже не смогут при всём желании её изменить. Можно даже пойти дальше. Наблюдения за состоянием автомата вообще не требуется. И переменную состояния можно сделать статической внутри фунции. Снаружи функции она вообще видна не будет. Ещё можно научиться работать не непосредственно с переменными, а с getter-функцией. Тут либо переменная может быть static для модуля, а функция motor_state(), например, будет возвращать её значение. Тогда, понятно, прочитать можно, а изменить нельзя. Либо переменная может быть тоже глобальна, а функция вовсе макросом: #define motor_state() ((void)0, motor_S) -- в таком случае испортить можно, но достаточно трудно, если приучить себя никогда не пользоваться переменной motor_S напрямую, а использовать motor_state(). Написать motor_state()=5 можно, но это вызовет ошибку компиляции (результат вычисления оператора "запятая" перестаёт быть L-value). В GCC и C++ можно ещё использовать inline функции. Можно сделать такой трюк, что переменная состояния внутри *.c модуля с автоматом будет обычной переменной, а снаружи она будет видна как const переменная. Тогда невозможно будет написать код её модифицирующий (без DECONST macro, что само по себе ещё тот изврат). Для чего пишем в соответствующем *.h файле такое: EXPORT enum e_motor_states motor_S. А в *.c файле, после включения всех нужных include, кроме нашего .h файла, пишем такое: #define EXPORT extern, потом #include "наш.h", потом enum e_motor_state motor_S /* без EXPORT */ И в настройках компилятора задаём глобальный для всех (из командной строки компилятору) дефайн: -DEXPORT="extern const". Тогда для всех других модулей переменная будет read-only. Метод несколько грязным хаком попахивает и может не работать на некорых платформах... Не знаю как улучшить. Что ещё можно сделать. Можно отказаться от ручного манипулирования переменной состояния и, хуже того, отказаться от хранения состояния собственно в виде кода в этой переменной. Идея заключается примерно в следующем. Что есть набор макросов для описания конечного автомата в C-функции. И есть скрытая за этими макросами переменная в которой состояние (настоящее состояние) и хранится. Сделать это можно через расширение computed goto в GCC. Состояние: адрес (строка) функции, где начинается обработка событий в данном состоянии. Переход между состояниями: натурально goto с запоминанием адреса метки в переменной состояния. Испортить переменную состояния сложно или невозможно, но также невозможно наблюдать за состоянием автомата снаружи. Для этого есть вторая переменная. Которая уже отображает состояние (аналогично, через enum) и каждый раз когда макрос меняет состояние -- то меняется эта переменная и делается goto. Т.е. забыть её обновить сложно. Но и испортить -- можно, но бесполезно, на работу не повлияет. Хотя общая идея, этой системы с макросами, не в этом, а в том, что технология получается более наглядная, чем switch. Подробности опишу позже. Work in Progress. Прошу добавить, кто чего может добавить...
[ZX]