UniSet
1.4.0
|
Утилита uniset-codegen предназначена для генерирования "скелета" процесса управления на основе заданного xml-описания. В скелет процесса включается "рутинный" код по получению и проверке индетификаторов, переменных, обработке сообщений и ряд других вспомогательных функций. Помимо этого определяются базовые виртуальные функции, участвующие в процессе.
Процесс можно сгенерировать по нескольким шаблонам
Генерируемый процесс функционирует по следующему обобщённому алгоритму:
Помимо этого обрабатывается специальный режим: Специальный режим "тест" (TestMode)
Сам сгенерированный код представляет из себя класс ("_SK" - skeleton), который необходимо использовать как базовый класс для своего процесса. Переопределяя виртуальные функции, и реализуя в них необходимую функциональность.
Исходный xml-файл описания необходимый для генерирования выглядит следующим образом.
<?xml version="1.0" encoding="utf-8"?> <!-- name - название класса msgcount - сколько сообщений обрабатывается за один раз sleep_msec - пауза между итерациями в работе процесса type ==== in - входные регистры (только для чтения) out - выходные регистры (запись) io - запись и чтение --> <Test> <settings> <set name="class-name" val="TestGen"/> <set name="msg-count" val="20"/> <set name="sleep-msec" val="150"/> </settings> <variables arg_prefix="test-"> <!-- type = [int,str,bool,float] int: max,min,no_range_exception=[0,1] str: float: max,min,no_range_exception=[0,1] bool: min - минимальное значение (может быть не задано) max - максимальное значение (может быть не задано) default - значение по умолчанию (может быть не задано) no_range_exception=1 - при выходе за границы min или max только писать unideb[WARN]. --> <item name="startTimeout" type="int" min="0" comment="test int variable"/> <item name="stopTimeout" type="int" max="100" default="110" no_range_exception="1"/> <item name="test_float" type="float" max="100.0" default="50.0" public="1" const="1" /> <item name="test_bool" type="bool" /> <item name="test_str" type="str" default="ddd"/> </variables> <smap> <!-- name - название переменной в конф. файле --> <item name="input1_s" vartype="in" iotype="DI" comment="comment for input1"/> <item name="input2_s" vartype="in" iotype="DI" comment="comment for input2" /> <item name="output1_c" vartype="out" iotype="DO" omment="comment for output1" no_check_id="1"/> </smap> <msgmap> <item name="msg1" comment="comment for Message 1" /> </msgmap> </Test>
В секции <settings> описываются следующие параметры:
В секции <smap> описываются "входы" и "выходы" процесса связанные с датчиками. При генерировании процесса, для каждого входа или выхода генерируется ряд свойств:
Помимо этого необходимо указывать свойство iotype.
В секции <msgmap> описываются поля связанные с идентификаторами сообщений. По сути, сообщения это тоже датчики, только используемые специальным образом. Для посылки сообщения датчик выставляется в "1" и через некоторое время должен быть сброшен. (чтобы можно было опять послать тоже самое сообщение). Т.е. само событие "сообщение" это переход датчика "0" --> "1". В сгенерированном коде реализован "автоматический" сброс сообщения через resetMsgTime миллисекунд. resetMsgTime настраивается через конфигурационную секцию (см. Конфигурирование ). Следует иметь ввиду, что это время должно быть достаточным чтобы датчик (изменение "0"-->"1") успел быть переданным по сети на другие узлы (зависит от используемого протокола передачи). Для сообщений генерируется такой же набор "переменных" как и для полей указанных в <smap> (см. Секция <smap>). За исключением того, что генерируется имя с префиксом mid_. И "привязка" идентификаторов не является обязательной.
Для работы с сообщениями существует ряд правил:
В данной секции можно перечислить переменные разных типов, для которых будет сгенерирован код по их "инициализации" и проверке "диапазона"(если указаны поля min или max). На данный момент поддерживаются переменные следующих типов:
Так же доступны следующие необязательные вспомогательные поля:
Помимо этого в самой секции <variables> можно указать свойство arg_prefix="...", которое используется при инициализации при помощи аргументов командной строки.
В генерируемом коде для каждой переменной происходит её инициализация по следующему шаблону (псевдокод):
varname = conf->getArgParam("--'arg_prefix'varname'",it.getProp("'varname'")); if( varname.empty() ) varname = 'default'
Где it.getProp() - получение значения из соответствующей настроечной секции в конфигурационном файле (см. Конфигурирование). Из кода можно видеть, что приоритетным является аргумент командной строки, потом значение из конф. файла и только потом default.
Если указаны поля min или max происходит проверка значения (после инициализации) на соответствие указанному диапазону. По умолчанию, при выходе за диапазон, генериурется исключение. Но если указано no_range_exception="1", то просто выдаётся warning в unideb[Debug::WARN].
По умолчанию эти поля генерируются как protected. Но если есть необходимость, то можно указать свойство public="1" или private="1" и тогда они будут иметь соответствующую область видимости.
Для режима генерирования на основе отдельного xml-файла (Файл описания для генерирования базового класса) необходимо дополнительно производить конфигурирование. Конфигурирование - это привязка конкретных имён датчиков к указанным полям класса. Для этого в конфигурационном файле проекта (обычно в секции <settings>) создаётся настроечная секция для вашего объекта. А в самом классе генерируется специальный конcтруктор, позволяющий указать настроечный xml-узел:
ClassName( UniSetTypes::ObjectId id, xmlNode* node=UniSetTypes::conf->getNode("ClassName") );
Ниже приведён пример настроечной секции, для объекта сгенерированного на основе xml-файла указанного в Файл описания для генерирования базового класса
... <TestGen name="TestGen" startTimeout="4000" stopTimeout="2000" input1_s="Input1_S" node_input1_s="Node2" input2_s="DumpSensor1_S" output1_c="DO_C" msg1="Message1" /> ...
Обычно для каждого объекта класса создаётся своя настроечная секция.
Дополнительно в сгенерированном коде присутствуют следующие настройки:
В генератор кода заложен специальный код для перевода процесса в тестовый режим. В этом режиме отключается вся работа процесса. Перестают обрабатываться сообщения, обновлятся входы и выходы и т.д.
Для перевода процесса в "тестовый режим" необходимо задать идентификаторы для двух "DI" датчиков:
Переход в тестовый режим осуществляется, только если ОБА датчика станут равными "1".
uniset-codegen поддерживает генерирование двух видов процессов:
По умолчанию используется шаблон для "пассивнного процесса". Т.е. все "in" датчики будут "заказаны" при старте процесса и далее работа будет вестись по сообщениям об изменении (UniSetTypes::SensorMessage).
Для генерирования активного процесса необходимо использовать параметр --no-ask. В таком процессе происходит активная работа с датчиками. Т.е. на каждом шаге основного цикла, происходит "принудительное" обновление значений всех "входов" (getValue) независимо от того, менялись ли они.
Шаблон "Alone" предназначен для генерирования без использования специального xml-файла с описанием переменных. Генерирование происходит непосредственно по конфигурационному файлу проекта. Для этого всё-равно необходимо создать соответствующую настроечную секцию, в которой будут прописаны параметры необходимые для генерирования "SK"-класса. При этом используемые "входы" и "выходы" записываются непосредственно у каждого используемого датчика в секции <consumers>. Ниже приведён пример конфигурирования процесса файле проекта:
... <settings> ... <TestGenAlone name="TestGenAlone"> <set name="ID" val="TestGenAlone"/> <set name="class-name" val="TestGenAlone"/> <set name="msg-count" val="20"/> <set name="sleep-msec" val="150"/> </TestGenAlone> ... </settings> ... <sensors> <item id="1" name="input1_s" iotype="DI" textname="xxx"> <consumers> <consumer name="TestGenAlone" vartype="in" type="objects"/> </consumers> </item> <item id="23" name="input2_s" iotype="DI" textname="xxx"> <consumers> <consumer name="TestGenAlone" vartype="in" type="objects"/> </consumers> </item> <item id="31" name="output1_c" iotype="DO" textname="xxx" node="Test1Node"> <consumers> <consumer name="TestGenAlone" vartype="out" type="objects"/> </consumers> </item> </sensors> <objects> <item id="2000" name="TestGenAlone" /> </objects>
Как видно из примера, vartype переменных записывается непосредственно в свойствах <consumer>.
Типичное правило для генерирования в Makefile.am выглядит следующим образом:
..._SOURCES= MyClass_SK.cc ... MyClass_SK.cc: myclass.src.xml @UNISET_CODEGEN@ -n MyClass --no-main myclass.src.xml
В этом примере