OpenSCADA

Документация/Как сделать/Создать модуль

This page is a translated version of the page Documents/How to/Create module and the translation is 100% complete.

English • ‎российский • ‎українська
Constr.png Generic revision and updating

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

At.png Для создания модулей OpenSCADA нужны знания в программировании на языке C/C++, сборочной системы AutoTools, а также базовые знания ОС Linux и используемого дистрибутива Linux.

Для размещения разработанного модуля в основном репозитории дерева исходных текстов OpenSCADA Вы должны выполнить следующее и следовать приведенным требованиям:

Contents

1 Создание нового модуля

Модули в OpenSCADA представляют из себя разделяемые библиотеки, которые подключаются к ядру OpenSCADA динамически, в момент работы программы. Многие модули в процессе работы могут быть отключены, подключены и обновлены из менеджера модулей. Модули также могут быть включены в ядро OpenSCADA при сборке, посредством аргумента --enable-{ModName}=incl к скрипту конфигурации "configure", о чём можно узнать из руководства по сборке. Модули OpenSCADA могут быть семи типов, согласно присутствующим модульным подсистемам. Сейчас модули к системе OpenSCADA пишутся на языке программирования "C++", хотя в дальнейшем возможно появление биндингов на другие языки.

Для облегчения создания новых модулей в дереве исходных текстов, в ветви каждой подсистемы, предусмотрена директория "=Tmpl=" с шаблоном модуля соответствующей подсистемы. Разработчик нового модуля может взять эту директорию и скопировать её с именем своего нового модуля. Предусмотрена возможность создания модулей в дереве исходных текстов проекта OpenSCADA или как независимого проекта внешнего модуля к OpenSCADA.

1.1 Создание в дереве исходных текстов проекта OpenSCADA

Создавать новые модули в дереве исходных текстов проекта OpenSCADA имеет смысл в случае дальнейших планов передачи нового модуля проекту OpenSCADA. Поскольку модуль не должен противоречить духу открытого проекта и лицензии на основе которой разрабатывается и распространяется OpenSCADA то лицензией нового модуля очевидно должна быть одна из свободных лицензий.

В целом процедура создания нового модуля с включением в дерево исходных текстов на основе шаблона является проще процедуры для внешнего модуля и включает в себя шаги:

1. Получение дерева исходных текстов проекта OpenSCADA.
Для рабочей ветки:
$ svn co svn://oscada.org/trunk/OpenSCADA
Для ветки стабильного релиза (нежелательно поскольку к стабильным LTS релизам принимаются только исправления и эта инструкция требует версии 0.9 и более):
$ svn co svn://oscada.org/tags/openscada_0.8.0
2. Копирование директории шаблона с именем нового модуля "NewMod" (например, для подсистемы "БД"):
$ cd OpenSCADA/src/moduls/bd
$ cp -r =Tmpl= NewMod; cd NewMod
$ rm -f configure.ac
3. Редактирование файла "module.cpp".
Изменить имена функций включения модуля согласно имени нового модуля:
"TModule::SAt bd_Tmpl_module( int n_mod )" — bd_NewMod_module
"TModule *bd_Tmpl_attach( const TModule::SAt &AtMod, const string &source )" — bd_NewMod_attach
Информация о модуле в файле "module.cpp", а именно участок:
//************************************************
//* Modul info!                                  *
#define MOD_ID          "NewMod"
#define MOD_NAME        _("DB NewMod")
#define MOD_TYPE        SDB_ID
#define VER_TYPE        SDB_VER
#define MOD_VER         "0.0.1"
#define AUTHORS         _("MyName MyFamily")
#define DESCRIPTION     _("BD NewMod description.")
#define MOD_LICENSE     "GPL2"
4. Редактирование конфигурации сборки модуля в файле "Makefile.am" к такому виду:
EXTRA_DIST = *.h po/*

if NewModIncl
noinst_LTLIBRARIES = db_NewMod.la
db_NewMod_la_CXXFLAGS = -DMOD_INCL -fpic
db_NewMod_la_LIBTOOLFLAGS = --tag=disable-shared
db_NewMod_la_LDFLAGS = -module
else
oscd_modul_LTLIBRARIES = db_NewMod.la
db_NewMod_la_CXXFLAGS =
db_NewMod_la_LIBTOOLFLAGS = --tag=disable-static
db_NewMod_la_LDFLAGS = -module -avoid-version $(top_builddir)/src/liboscada.la
endif

db_NewMod_la_CXXFLAGS += $(NewMod_CFLAGS)
db_NewMod_la_LDFLAGS += $(NewMod_LDLAGS)
db_NewMod_la_SOURCES = module.cpp

I18N_mod = $(oscd_modulpref)NewMod
include ../../../../I18N.mk
5. Добавление записи нового модуля в конец секции подсистемы (у нас "> DB modules"), конфигурационного файла (OpenSCADA/configure.ac) сборочной системы OpenSCADA:
AX_MOD_DB_EN(NewMod,[disable or enable[=incl] build module DB.NewMod],disable,incl,
[
    # Код проверки внешних библиотек модуля
])
6. Теперь новый модуль можно собрать в составе OpenSCADA после переформирования сборочной системы:
$ autoreconf -if
$ ./configure --enable-NewMod
$ make
7. Публикация. Формирование патча с вашим модулем и отправка его разработчикам OpenSCADA:
$ cd OpenSCADA; make distclean; rm -f src/moduls/bd/NewMod/Makefile.in
$ svn add src/moduls/bd/NewMod
$ svn diff > NewMod.patch

1.2 Создание внешнего модуля к OpenSCADA

Создание внешнего модуля к OpenSCADA может иметь смысл в случае разработки интерфейса интеграции с коммерческими системами, требующими закрытия кода взаимодействия, а также в случае других реализаций коммерческих интерфейсов, при которых модуль к OpenSCADA приобретает статус отдельного проекта, распространяется и поддерживается независимо, часто в виде бинарных сборок под конкретную платформу и версию OpenSCADA. Лицензия таких модулей соответственно может быть любой.

Процедура создания нового внешнего модуля на основе шаблона во многом похожа на предыдущую процедуру и включает в себя шаги:

1. Получение исходных текстов проекта OpenSCADA. Для внешнего модуля в качестве источника шаблона можно использовать любые исходные файлы OpenSCADA версии более 0.9 поскольку из них нужно скопировать только директорию "=Tmpl=" и несколько файлов для сборки.
2. Копирование директории шаблона с именем нового модуля "NewMod" (например, для подсистемы "БД"). Создание и копирование нужных файлов для внешнего модуля. В дальнейшем информационные файлы проекта "COPYING", "NEWS", "README", "AUTHORS" и "ChangeLog" нужно заполнить согласно сути нового модуля.
$ cp -r OpenSCADA/src/moduls/bd/=Tmpl= NewMod
$ touch NewMod/{NEWS,README,AUTHORS,ChangeLog}
$ cp OpenSCADA/I18N.mk NewMod/
3. Редактирование информации о модуле в файле "module.cpp", аналогично этому пункту предыдущего раздела.
4. Редактирование конфигурации сборки модуля в файле "Makefile.am", аналогично этому пункту предыдущего раздела, кроме:
# вместо "db_NewMod_la_LDFLAGS = -module -avoid-version $(top_builddir)/src/liboscada.la"
db_NewMod_la_LDFLAGS = -module -avoid-version
# Вместо "include ../../../../I18N.mk"
include I18N.mk
5. Редактирование файла конфигурации сборочной системы "configure.ac":
"AC_INIT([DB.Tmpl],[0.0.1],[my@email.org])" — информация о модуле: имя, версия и email проекта.
"AM_CONDITIONAL([TmplIncl],[test])" — AM_CONDITIONAL([NewModIncl],[test])
6. Установка пакета разработки OpenSCADA. Ввиду того, что модуль внешний и исходные файлы OpenSCADA нужны только на первом этапе создания модуля, необходимо установить пакет разработки OpenSCADA (openscada-devel), который содержит заголовочные файлы и библиотеки.
7. Теперь новый модуль можно собрать, после формирования сборочной системы:
$ autoreconf -if
$ ./configure
$ make

2 API модуля

API системы OpenSCADA для разработчика OpenSCADA и модулей к ней исчерпывающе, в формальной форме, описано в соответствующем документе API системы OpenSCADA, который должен быть всегда под рукой при разработке для OpenSCADA. В данном же документе уклон сделан на детальное разъяснение основных моментов модульного API.

Общим для всех модулей является наследование корневого объекта-класса модуля от класса TModule посредством класса модульной подсистемы, а значит есть общая часть интерфейса модуля, которую рассмотрим ниже. В целом, для представления себе архитектуры модулей в контексте общей архитектуры OpenSCADA, настоятельно рекомендуется иметь перед глазами общую диаграмму классов OpenSCADA!

Точкой входа любого модуля являются функции:

Для удобства прямой адресации к корневому объекту модуля из любого объекта модуля ниже по иерархии рекомендуется определять глобальную переменную "mod" в области имён модуля с инициализацией её в конструкторе корневого объекта модуля. Также, для прозрачного перевода текстовых сообщений модуля рекомендуется определять шаблон функции вызова перевода сообщений модуля "_({Сообщение})", как:

#undef _
#define _(mess) mod->I18N(mess)

В конструкторе корневого объекта модуля, наследованного от TModule, необходимо установить основную информацию модуля вызовом функции void modInfoMainSet({Имя}, {Тип}, {Версия}, {Авторы}, {Описание}, {Лицензия}, {Источник}), после инициализации быстрой ссылки "mod" на корневой объект этого модуля.

А также инициировать окружение модуля с помощью функций:

Последующее получение файла шаблона переводов "po/NewMod.pot" текстовых сообщений "_({Сообщение})", а также обновление-актуализация файлов уже существующих переводов "po/{en|ru|uk|de|...}.po" осуществляется командой в директории модуля "$ make messages".

С целью общего управления модулем в классе TModule предусмотрен ряд виртуальных функций, которые могут быть определены в корневом объекте модуля с реализацией нужной реакции на команды ядра OpenSCADA к модулю:

Все интерфейсные объекты модулей наследуют класс узла TCntrNode, который предоставляет механизм интерфейса управления, одной из задач которого является предоставление интерфейса конфигурации объекта в любом конфигураторе OpenSCADA. Для решения задач нового модуля может понадобиться расширение параметров конфигурации, что делается в виртуальной функции void cntrCmdProc( XMLNode *opt );. Содержимое этой функции, добавляющее свойство, в простейшем случае имеет вид:

void MBD::cntrCmdProc( XMLNode *opt )
{
    //> Get page info
    if(opt->name() == "info")
    {
        TBD::cntrCmdProc(opt);
        ctrMkNode("comm",opt,-1,"/prm/st/end_tr",_("Close opened transaction"),RWRWRW,"root",SDB_ID);
        return;
    }
    //> Process command to page
    string a_path = opt->attr("path");
    if(a_path == "/prm/st/end_tr" && ctrChkNode(opt,"set",RWRWRW,"root",SDB_ID,SEC_WR)) transCommit();
    else TBD::cntrCmdProc(opt);
}

Первая половина этой функции обслуживает информационные запросы "info" с перечнем и свойствами полей конфигурации. Вторая половина обслуживает все остальные команды на получение, установку значения и другое. Вызов TBD::cntrCmdProc(opt); используется для получения наследованного интерфейса. Детальнее о назначении использованных функций смотрите в интерфейса управления, а также в исходных текстах существующих модулей.

Кроме функции интерфейса управления объект TCntrNode предоставляет унифицированные механизмы контроля за модификацией конфигурации объекта, загрузки и сохранения конфигурации в хранилище. Для выполнения установки флага модификации данных объекта можно использовать функции modif() и modifG(), а специфические для модуля действия по загрузке и сохранению можно помещать в виртуальные функции:

Типично, работа с конфигурацией осуществляется посредством объекта TConfig, который содержит набор указанных свойств. Для прямого отражения свойств объекта модуля он наследуется от TConfig, а новые свойства добавляются командой:

fldAdd(new TFld("PRM_BD",_("Parameters cache table"),TFld::String,TFld::NoFlag,"30",""));

Загрузка и сохранение свойств, указанных в объекте TConfig, из/в хранилище осуществляется командами:

SYS->db().at().dataGet(fullDB(),owner().nodePath()+"DAQ",*this);
SYS->db().at().dataSet(fullDB(),owner().nodePath()+"DAQ",*this);

Где:

Для помещения отладочных сообщений, в контексте общей концепции отладки, нужно использовать функцию mess_debug() с условием вызова по участку исходного текста программы:

#ifdef OSC_DEBUG
  mess_debug(...);
#endif

2.1 Модуль подсистемы "Базы Данных (БД)"

Модуль данного типа предназначен для интеграции OpenSCADA с СУБД, реализуемой модулем.

Интерфейс OpenSCADA для обслуживания запросов к БД представлен объектами и виртуальными функциями вызовов из ядра OpenSCADA:

2.2 Модуль подсистемы "Транспорты"

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

Программный интерфейс OpenSCADA для обслуживания входящих и исходящих запросов через сетевой интерфейс представлен объектами и виртуальными функциями вызовов из ядра OpenSCADA:

2.3 Модуль подсистемы "Транспортные протоколы"

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

Программный интерфейс OpenSCADA для реализации протокольного слоя представлен объектами и виртуальными функциями вызовов из ядра OpenSCADA:

2.4 Модуль подсистемы "Сбор данных (DAQ)"

Модуль этого типа предназначен для получения данных реального времени внешних систем или их формирования в вычислителях, реализуемых модулем.

Программный интерфейс OpenSCADA для реализации доступа к данным реального времени представлен объектами и виртуальными функциями вызовов из ядра OpenSCADA:

Для специализированной диагностики можно помещать отладочные сообщения для условия выбора уровня диагностики "Отладка (0)": if(messLev() == TMess::Debug) mess_debug_(...);.

2.5 Модуль подсистемы "Архивы"

Модуль этого типа предназначен для архивирования и ведения истории сообщений OpenSCADA и данных реального времени, полученных в подсистеме "Сбор данных" реализуемым модулем способом.

Программный интерфейс OpenSCADA для реализации доступа к архивным данным представлен объектами и виртуальными функциями вызовов из ядра OpenSCADA:

2.6 Модуль подсистемы "Пользовательские интерфейсы (UI)"

Модуль этого типа предназначен для предоставления пользовательского интерфейса реализуемым модулем способом. Корневым объектом модуля данной подсистемы является TUI->TModule, который не содержит специфических интерфейсов, а пользовательский интерфейс формируется согласно с реализуемой концепцией и механизмами, например, библиотеки графических примитивов.

2.7 Модуль подсистемы "Специальные"

Модуль этого типа предназначен для реализации специфических функций, не вошедших ни в одну из вышеперечисленных подсистем, реализуемым модулем способом. Корневым объектом модуля данной подсистемы является TSpecial->TModule, который не содержит специфических интерфейсов, а специфические функции формируется согласно их требованиям с использованием всех возможностей API OpenSCADA.

Documents/How_to/Create_module/ru - GFDLFebruary 2022OpenSCADA 1+r2802