|
<< Click to Display Table of Contents >> Navigation: API MasterSCADA 4D > Функции, ФБ, Протоколы на С++ > API для ФБ и протоколов > Разработка протоколов > Разработка протокола с группой каналов > Реализация протокола с группой каналов в C++ |
Пример реализации подключен к Examples.sln как mplc_test_protocol_ch_groups:

Для разработки своих собственных протоколов нужно открыть пример и изменить те части проекта, которые отвечают за логику работы. Вспомогательные файлы, служащие для взаимодействия с исполнительной системой, редактировать не требуется.
Для начала по аналогии с простым протоколом создается класс, унаследованный от ScadaProtocol, реализующий требуемый функционал. Необходимы макросы регистрации класса MPLC_OBJECT(ProtocolChannelGroups) в секции public. Также переопределению подлежат чисто виртуальные методы Execute() и метод Create() для модулей.
Для этого в примере был создан заголовочный файл test_protocol_ch_groups.h:

Содержимое файла:
Скриншот |
Текст |
|
#pragma once #include <mplc/api.h>
#include "emulator.h" #include "module_with_ch_groups.h"
class ProtocolChannelGroups final : public mplc::api::ScadaProtocol { private: Emulator *emulator; std::vector<ModuleWithChannelGroups*> modules; bool connected{false}; bool Connect();
public: MPLC_OBJECT(ProtocolChannelGroups); ProtocolChannelGroups(); ~ProtocolChannelGroups(); void Execute() override; mplc::api::ScadaModule* Create(const mplc::vm::IOModule* module) override; void Init() override; }; |
Протокол и его свойства надо зарегистрировать в файлах исходных кодов макроса MPLC_PROTOCOL_TYPE.
Для этого в примере был создан файл реализации для модулей с настройками test_protocol_ch_groups.cpp:

Содержимое файла:
Скриншот |
Текст |
|
#include "test_protocol_ch_groups.h"
ProtocolChannelGroups::ProtocolChannelGroups() { emulator = new Emulator(); }
ProtocolChannelGroups::~ProtocolChannelGroups() { emulator->close(); delete emulator; }
bool ProtocolChannelGroups::Connect() { if (connected) { return true; } if (!emulator->connect()) { return false; } connected = true; SetFaultState(false, ""); return true; }
void ProtocolChannelGroups::Init() { emulator->init(); }
void ProtocolChannelGroups::Execute() { if (!isConnect()) { return; } if (!Connect()) { SetFaultState(true, "Failed to connect"); return; } if (!isExecute()) { return; }
std::string buff; for (auto module: modules) { module->Execute(emulator); } AcknowledgeAllChanges(); }
mplc::api::ScadaModule* ProtocolChannelGroups::Create(const mplc::vm::IOModule* module) { auto mod = new ModuleWithChannelGroups(modules.size()); modules.push_back(mod); mplc::api::ScadaModule& req = *mod; return &req; }
MPLC_PROTOCOL_TYPE(ProtocolChannelGroups, ProtocolChannelGroups);
|
Если при создании модулей необходимо отличать их, то в методе протокола Create() для модулей имеется поле SubType класса vm::IOModule, в котором и содержится значение Подтипа модуля, которое указывалось в редакторе.
Далее идёт создание класса модуля, унаследованного от ScadaModule. Далее создаётся отдельный класс ChannelGroup, что и будет выполнять работу группы каналов. Для создания группы каналов в классе ScadaModule есть перегружаемый метод InitChannelGroup, в котором дополнительно происходит инициализация каналов. Так как работу по созданию каналов выполняет метод InitChannelGroup, а метод Create() чисто виртуальный, необходимо переопределить его кодом с возвращением nullptr.
Для этого в примере был создан файл реализации для протокола с настройками module_with_ch_groups.h:

Содержимое файла:
Скриншот |
Текст |
|
#pragma once #include <mplc/api.h> #include "emulator.h"
struct ChannelGroup { int module_id; int group_id; mplc::api::ScadaChannel TestInput; mplc::api::ScadaChannel TestIncrement; mplc::api::ScadaChannel TestOutput;
ChannelGroup(int nod_num, int group_num): module_id(nod_num), group_id(group_num) {} void Execute(Emulator* emulator); };
class ModuleWithChannelGroups : public mplc::api::ScadaModule { int module_id; std::vector<ChannelGroup*> groups;
public: ModuleWithChannelGroups(int num): module_id(num) {} void InitChannelGroup(const mplc::vm::ChannelGroup* group, LuaDataProvider* provider) override; virtual mplc::api::ScadaChannel* Create(const mplc::vm::Channel* channel, LuaDataProvider* provider) override; void Execute(Emulator* emulator); }; |
В файле исходных кодов работа с макросами регистрации не отличается от обычного протокола.
Для этого в примере был создан файл реализации для протокола module_with_ch_groups.cpp:

Содержимое файла:
Скриншот |
Текст |
|
#include "module_with_ch_groups.h"
void ChannelGroup::Execute(Emulator* emulator) { std::string buff; if (TestIncrement.OutVar.Changed()) { emulator->set_increment(module_id, group_id, TestIncrement.OutVar.Get<int>()); } if (TestInput.OutVar.Changed()) { emulator->write(module_id, group_id, TestInput.OutVar.Get<std::string>()); } buff = emulator->read(module_id, group_id); TestOutput.InVar.Update(buff); }
void ModuleWithChannelGroups::InitChannelGroup(const mplc::vm::ChannelGroup* group, LuaDataProvider* provider) { int group_id = 0; if (auto it = group->settings.find("id"); it != group->settings.end()) { group_id = it->second.Get<int>(0); }
auto ch_group = new ChannelGroup(module_id, group_id);
for (auto channel: group->childs<mplc::vm::Channel>()) { mplc::api::ScadaChannel* ch; if (channel->name.utf8() == "test_input") { ch = &ch_group->TestInput; } else if (channel->name.utf8() == "test_output") { ch = &ch_group->TestOutput; } else if (channel->name.utf8() == "test_increment") { ch = &ch_group->TestIncrement; } else { ch = nullptr; } if (ch) { ch->BaseInit(channel); Register(ch); } } groups.push_back(ch_group); }
mplc::api::ScadaChannel* ModuleWithChannelGroups::Create(const mplc::vm::Channel* channel, LuaDataProvider* provider) { return nullptr; }
void ModuleWithChannelGroups::Execute(Emulator* emulator) { for (auto group: groups) { group->Execute(emulator); } }
|
Ввод и вывод данных из каналов модулей происходит методами Read() и Write() класса ScadaChannel. ch - это указатель на класс ScadaChannel.
Важно! Метод Read выдаёт ошибку и не возвращает значение, пока заменяется на комбинацию методов.
Далее можно компилировать проект.
Для компиляции проекта на ОС Windows нужно выполнить команду Build в контекстном меню проекта:

Скомпилированные файлы появятся в папке проекта: C:\mplc\api\platform\x64\Release\bin\ (адрес действителен, если название папок, которые делал разработчик, совпадают с названиями, которые давались в этой инструкции).
Чтобы произвести сборку под Linux платформы, нужно сохранить проект и перенести все файлы из папки C:\mplc\api (адрес действителен, если название папок, которые делал разработчик, совпадают с названиями, которые давались в этой инструкции) в созданный каталог на компьютере с ОС Linux. Подробнее смотрите в разделе Сборка под Linux платформы.
После сборки можно приступать к тестированию созданного ФБ.