Commit ff8c1b49 authored by Pavel Vainerman's avatar Pavel Vainerman

(uniset-codegen): Набросал документацию по генератору кода

parent 5c19921d
......@@ -13,10 +13,10 @@ GENERATED=TestGen_SK.h TestGen_SK.cc TestGen-main.cc
GENERATED2=TestGenAlone_SK.h TestGenAlone_SK.cc TestGenAlone-main.cc
$(GENERATED): ../@PACKAGE@-codegen testgen.src.xml ../*.xsl
../@PACKAGE@-codegen -l $(top_builddir)/Utilities/codegen --local-include --ask -n TestGen testgen.src.xml
../@PACKAGE@-codegen -l $(top_builddir)/Utilities/codegen --local-include -n TestGen testgen.src.xml
$(GENERATED2): ../@PACKAGE@-codegen testgen-alone.src.xml ../*.xsl
../@PACKAGE@-codegen -l $(top_builddir)/Utilities/codegen --local-include --ask --alone -n TestGenAlone testgen-alone.src.xml
../@PACKAGE@-codegen -l $(top_builddir)/Utilities/codegen --local-include --alone -n TestGenAlone testgen-alone.src.xml
clean-local:
rm -rf $(GENERATED) $(GENERATED2)
......
<?xml version="1.0" encoding="koi8-r"?>
<?xml version="1.0" encoding="utf-8"?>
<!--
name - название класса
msgcount - сколько сообщений обрабатывается за один раз
......
......@@ -32,11 +32,11 @@
<item name="test_int2" type="int" max="100" default="110" no_range_exception="1"/>
<item name="test_float" type="float" max="100.0" default="50.0" />
<item name="test_bool" type="bool" />
<item name="test_str" type="str" default1="ddd"/>
<item name="test_str" type="str" default="ddd"/>
</variables>
<smap>
<!-- name - название переменной в конф. файле -->
<item name="input1_s" vartype="in" iotype="DI" comment="comment for input1" node="K1Node"/>
<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>
......
......@@ -23,15 +23,19 @@ $PROG - generate source code for control process
xmlfile - source xml-file
Valid options are:
-m, --main filename for main.cc. Default: main.cc
-n, --name filename for *_SK files (base class implementation). Default: xmlfilename_SK
-h, --help display help screen
-h, --help - display help
-m, --main - filename for main.cc. Default: main.cc
-n, --name - filename for *_SK files (base class implementation). Default: xmlfilename_SK
--ask - Use 'ask' templates. See the documentation.
--alone - Use 'alone' templates. See the documentation.
EOF
[ -n "$1" ] && exit "$1" || exit
}
#parse command line options
TEMP=`getopt -n $PROG -o h,n:,m:,a,l: -l help,name:,main:,no-main,topdir:,path:,alone,ask,local:,local-include,add-cc-include,add-hh-include -- "$@"` || exit 1
TEMP=`getopt -n $PROG -o h,n:,m:,a,l:,z -l help,name:,main:,no-main,topdir:,path:,alone,ask,no-ask,local:,local-include,add-cc-include,add-hh-include -- "$@"` || exit 1
eval set -- "$TEMP"
name=
......@@ -41,7 +45,7 @@ uni_main=
no_main=
xmlfile=
alone=
ask=
ask=1
localinc=0
add_cc_inc=
add_hh_inc=
......@@ -76,10 +80,6 @@ while :; do
xls_m="ctl-cpp-main-alone.xsl"
;;
--with-prev-values)
prev_val=1
;;
--local-include)
localinc=1
;;
......@@ -96,6 +96,10 @@ while :; do
ask=1
;;
--no-ask|-z)
ask=
;;
-l|--local)
shift
xsltdir=$1
......
......@@ -143,6 +143,7 @@
<item name="imitator_performance1" precision="6" textname="Производительность танка 1" iotype="AI" rs="mbslave" mbreg="43" mb_vtype="I2"/>
<item name="performance1" precision="6" noprecision="1" textname="Производительность танка 1" iotype="AI" rs="mbmaster" tcp_vtype="I2" tcp_mbtype="rtu"
tcp_mbaddr="1" tcp_mbreg="43" tcp_mbfunc="0x04"/>
<item iotype="DI" name="Message1" priority="Medium" textname="Текст сообщения 1"/>
</sensors>
<thresholds name="thresholds">
<sensor iotype="AI" name="AI_AS">
......
......@@ -8,4 +8,5 @@
- \ref page_Concept
- \ref page_Uniset
- \ref UniSetLibStylePage
- \ref pages
*/
/*! \page page_Codegen Генератор кода uniset-codegen
- \ref pg_Codegen_Common
- \ref pg_Codegen_XmlFile
- \ref pg_Codegen_Settings
- \ref pg_Codegen_SMap
- \ref pg_Codegen_MsgMap
- \ref pg_Codegen_Variables
- \ref pg_Codegen_Configuration
- \ref pg_Codegen_TestMode
- \ref pg_Codegen_Make
- \ref pg_Codegen_Templ_Ask
- \ref pg_Codegen_Templ_Alone
\section pg_Codegen_Common Общее описание утилиты uniset-codegen
Утилита uniset-codegen предназначена для генерирования "скелета" процесса управления на основе
заданного xml-описания. В скелет процесса включается "рутинный" код по получению и проверке индетификаторов,
переменных, обработке сообщений и ряд других вспомогательных функций. Помимо этого определяются базовые виртуальные функции,
участвующие в процессе.
Процесс можно сгенерировать по нескольким шаблонам
- на основе специального xml-файла (см. \ref pg_Codegen_XmlFile)
- на основе кофигурационного файла проекта (см. \ref pg_Codegen_Templ_Alone)
\note Генерирование на основе отдельного xml-файла удобно если вам необходимо создавать несколько объектов вашего класса,
отличающихся только набором датчиков.
\note Генерирование на основе кофигурационного файла проекта (шаблон 'Alone') удобно использовать когда у вас планируется
один единственный объект вашего класса. Т.к. в данном случае привязка производиться непосредственно к конкретным датчикам
в конфигурационном файле.
Генерируемый процесс функционирует по следующему обобщённому алгоритму:
- Обновление входов (в случае работы на основе заказа датчиков, этот шаг отсутствует)
- проверка срабатывания внутренних таймеров
- Обработка сообщений в очереди сообщений
- Выполенение шага алгоритма (функция step)
- обновление датчика "сердцебиения" /см. \ref sec_SM_HeartBeat /
- Обновление выходов
Помимо этого обрабатывается специальный режим: \ref pg_Codegen_TestMode
Сам сгенерированный код представляет из себя класс ("_SK" - skeleton), который необходимо
использовать как базовый класс для своего процесса. Переопределяя виртуальные функции,
и реализуя в них необходимую функциональность.
\section pg_Codegen_XmlFile Файл описания для генерирования базового класса
Исходный xml-файл описания необходимый для генерирования выглядит следующим образом.
\code
<?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" />
<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>
\endcode
\section pg_Codegen_Settings Секция <settings>
В секции \<settings> описываются следующие параметры:
- \b class-name - Имя генерируемого класса. В итоге будет сгенерирован класс с названием \b "ClassName_SK" (\a "_SK" - сокращение от "skeleton").
- \b msg-count - Количество обрабатываемых за один раз(шаг) сообщений.
- \b sleep-msec - пауза между шагами основного цикла процесса
- \b base-class - Имя базового класса. По умолчанию: UniSetObject
\section pg_Codegen_SMap Секция <smap>
В секции \<smap> описываются "входы" и "выходы" процесса связанные с датчиками. При генерировании процесса,
для каждого входа или выхода генерируется ряд свойств:
- \b name - идентификатор датчика связанного с этим входом или выходом (совпадает с именем указанной переменной)
- [\b in |\b out |\b io]\b _node_name - идентификатор узла датчика связанного с этим входом или выходом (по умолчанию локальный узел)
- [\b in |\b out |\b io]\b _name - переменная хранящая текущее состояние датчика (генерируется с префиксом в зависимости от \b vartype)
- \b prev_[\b in |\b out |\b io]\b _name - переменная хранящая состояние датчика на предыдущем шаге (генерируется с префиксом в зависимости от \b vartype)
- \b no_check_id - no_check_id="1" означает игнорировать (не генерировать исключение) при запуске процесса, если идентификатор датчика не найден.
Помимо этого необходимо указывать свойство \b iotype.
\warning Поле \b iotype должно \b ОБЯЗАТЕЛЬНО совпадать с типом датчика к которому
будет привязана данная переменная. Т.к. генерируется код для работы с датчиком в зависимости от его типа.
\section pg_Codegen_MsgMap Секция <msgmap>
В секции \<msgmap> описываются поля связанные с идентификаторами сообщений. По сути, сообщения это тоже датчики, только
используемые специальным образом. Для посылки сообщения датчик выставляется в "1" и через некоторое время должен быть сброшен.
(чтобы можно было опять послать тоже самое сообщение). Т.е. само событие "сообщение" это переход датчика "0" --> "1".
В сгенерированном коде реализован "автоматический" сброс сообщения через \b resetMsgTime миллисекунд.
\b resetMsgTime настраивается через конфигурационную секцию (см. \ref pg_Codegen_Configuration ). Следует иметь ввиду,
что это время должно быть достаточным чтобы датчик (изменение "0"-->"1") успело быть переданным по сети на другие узлы
(зависит от используемого протокола передачи).
Для сообщений генерируется такой же набор "переменных" как и для полей указанных в \<smap> (см. \ref pg_Codegen_SMap). За исключением того,
что генерируется имя с префиксом \b mid_. И "привязка" идентификаторов не является обязательной.
\warning Датчики-сообщений \b ОБЯЗАТЕЛЬНО должны иметь тип \b "DI"
Для работы с сообщениями существует ряд правил:
- сообщения должны посылаться при помощи специальной (сгенерированной) функций
\b setMsg( UniSetTypes::ObjectId code, bool state ) или alarm( UniSetTypes::ObjectId code, bool state ).
Для передачи сообщения необходим вызов c параметром \b state="true".
- Сообщения "автоматически" сбрасываются в "0" через \b resetMsgTime (настраиваемое в конф. секции), поэтому
вызывать функции с \b state="false" нет смысла.
\section pg_Codegen_Variables Секция <variables>
В данной секции можно перечислить \b переменные разных типов, для которых будет сгенерирован код по их "инициализации"
и проверке "диапазона"(если указаны поля min или max). На данный момент поддерживаются переменные следующих типов:
- \b int - int
- \b float - float
- \b bool - bool
- \b str - string
Так же доступны следующие необязательные вспомогательные поля:
- \b min - минимальное разрешенное значение
- \b max - максимальное разрешенное значение
- \b default - значение по умолчанию (при инициализации)
- \b no_range_exception - не генерировать исключение в случае выхода переменной за указанный диапазон (min или max).
Помимо этого в самой секции \<variables> можно указать свойство \b arg_prefix="...", которое используется при инициализации
при помощи аргументов командной строки.
В генерируемом коде для каждой переменной происходит её инициализация по следующему шаблону (псевдокод):
\code
varname = conf->getArgParam("--'arg_prefix'varname'",it.getProp("'varname'"));
if( varname.empty() )
varname = 'default'
\endcode
Где \a it.getProp() - получение значения из соответствующей настроечной секции в конфигурационном файле (см. \ref pg_Codegen_Configuration)
Если указаны поля \b min или \b max происходит проверка значения (после инициализации) на соответсвие указанному диапазону.
По умолчанию, при выходе за диапазон, генериурется исключение. Но если указано \b no_range_exception="1",
то просто выдаётся warning в unideb[Debug::WARN].
\section pg_Codegen_Configuration Конфигурирование
Для режима генерирования на основе отдельного xml-файла (\ref pg_Codegen_XmlFile) необходимо дополнительно производить конфигурирование.
\a Конфигурирование - это привязка конкретных имён датчиков к указанным полям класса. Для этого в конфигурационном файле проекта
(обычно в секции \<settings>) создаётся настроечная секция для вашего объекта.
А в самом классе генерируется специальный конcтруктор, позволяющий указать настроечный xml-узел:
\code
ClassName( UniSetTypes::ObjectId id, xmlNode* node=UniSetTypes::conf->getNode("ClassName") );
\endcode
Ниже приведён пример настроечной секции, для объекта сгенерированного на основе xml-файла указанного в \ref pg_Codegen_XmlFile
\code
...
<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"
/>
...
\endcode
Обычно для каждого объекта класса создаётся своя настроечная секция.
Дополнительно в сгенерированном коде присутствуют следующие настройки:
- \b sleep_msec = conf->getArgPInt("--sleep-msec","150", 150); - пауза между шагами основного цикла процесса
- \b resetMsgTime = conf->getPIntProp(cnode,"resetMsgTime", 2000); - время до автоматического сборса датчиков-сообщений в "0".
- \b smReadyTimeout = conf->getArgInt("--sm-ready-timeout",""); - время ожидания готовности SharedMemory к работе
- \b activateTimeout = conf->getArgPInt("--activate-timeout", 20000); - время отведённое на инициализацию процесса
- \b msec = conf->getArgPInt("--startup-timeout", 10000); - пауза, в течение которой игнорируется сообщение SystemMessage::WatchDog.
В случае если они приходят подряд.
\section pg_Codegen_TestMode Специальный режим "тест" (TestMode)
В генератор кода заложен специальный код для перевода процесса в тестовый режим. В этом режиме отключается вся работа процесса.
Перестают обрабатываться сообщения, обновлятся входы и выходы и т.д.
Для перевода процесса в "тестовый режим" необходимо задать идентификаторы для двух "DI" датчиков:
- \b TestMode_S - глобальный датчик перехода в тестовый режим. \b Обязан быть в конфигурационном файле.
- \b LocalTestMode_S - датчик перевода в тестовый режим для данного процесса. Задаётся в настроечной секции данного процесса.
Переход в тестовый режим осуществляется, только если \b ОБА датчика станут равными \b "1".
\note Два датчика сделаны для защиты от "случайного" перехода.
\section pg_Codegen_Templ_Ask Типа процессов (активный и пассивный)
uniset-codegen поддерживает генерирование двух видов процессов:
- \b пассивный \b процесс - (по умолчанию). Основан на "заказе датчиков".
- \b активный \b процесс - на каждом шаге опрашивает свои "входы".
По умолчанию используется шаблон для "пассивнного процесса".
Т.е. все \b "in" датчики будут "заказаны" при старте процесса и далее работа будет вестись по сообщениям
об изменении (UniSetTypes::SensorMessage).
Для генерирования \b активного \b процесса необходимо использовать параметр \b --no-ask. В таком процессе происходит \a активная работа
с датчиками. Т.е. на каждом шаге основного цикла, происходит "принудительное" обновление значений всех "входов" (getValue)
независимо от того, менялись ли они.
\warning Следует иметь ввиду, что при работе не на основе "заказа датчиков", существует вероятность пропустить(потерять) "изменение"
состояния датчика, в случае если он поменяется и восстановится обратно в течение времени меньше чем \b sleep-time у данного процесса.
\section pg_Codegen_Templ_Alone Шаблон 'Alone'
Шаблон \b "Alone" предназначен для генерирования \b без \b использования специального xml-файла с описанием переменных.
Генерирование происходит непосредственно по конфигурационному файлу проекта. Для этого всё-равно необходимо создать
соответствующую настроечную секцию, в которой будут прописаны параметры необходимые для генерирования "SK"-класса.
При этом используемые "входы" и "выходы" записываются непосредственно у каждого используемого датчика в секции \b \<consumers>.
Ниже приведён пример конфигурирования процесса непосредственно в конфигурационном файле проекта:
\code
...
<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>
\endcode
Как видно из примера, \b vartype переменных записывается непосредственно в свойствах \b \<consumer>.
\warning * Следует иметь ввиду, что при изменении конфигураионного файла, необходимо перегенерировать код.
И в свою очередь, если поставить в Makefile зависимость на конфигурационный файл, то каждый раз при его изменении
(независимо от того, что менялось), код будет перегенерироваться.
\warning * Шаблон 'alone' не расчитан на создание "многих" объектов сгенерированного класса, т.к. они будут работать,
с одним и тем же набором датчиков. Сложно представить себе пример, где бы это могло потребоваться.
Более того, следует иметь ввиду, что создание нескольких объектов класса приведёт к конфликту по выставлению "out"-переменных.
\section pg_Codegen_Make Параметры генерирования кода
Типичное правило для генерирования в Makefile.am выглядит следующим образом:
\code
..._SOURCES= MyClass_SK.cc ...
MyClass_SK.cc: myclass.src.xml
@UNISET_CODEGEN@ --ask -n MyClass --no-main myclass.src.xml
\endcode
В этом примере
- \b myclass.src.xml - это файл с описанием переменных
- \b --no-main - отключает генерирование "запускающего" файла (функция main)
- \b -n - опредеяет название файлов для сгенерированного класса. В данном случае будут сгенерированы \a MyClass_SK.h и \a MyClass_SK.cc
- \b --ask - генерировать код на основе шаблона "заказа датчиков" (см. \ref pg_Codegen_Templ_Ask)
*/
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment