From 14c8d5a1eb728650c747d5372896c4fc2e50b028 Mon Sep 17 00:00:00 2001 From: Vadim Sychev Date: Tue, 26 Jul 2022 13:36:54 +0300 Subject: [PATCH] =?UTF-8?q?dev(UML-981):=20=D0=9F=D1=80=D0=BE=D0=BC=D0=B5?= =?UTF-8?q?=D0=B6=D1=83=D1=82=D0=BE=D1=87=D0=BD=D1=8B=D0=B9=20=D0=BA=D0=BE?= =?UTF-8?q?=D0=BC=D0=BC=D0=B8=D1=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1.Реализован коллбэк callbackStateInd 2.Добавлены данные для циклического обмена --- src/main.cpp | 71 ++++++++++++- src/profinet/profinet.cpp | 156 +++++++++++++++++++++++++++- src/profinet/profinet.hpp | 4 + src/profinet/profinet_parameter.hpp | 4 +- src/profinet/profinet_serv_data.hpp | 2 + src/profinet/profinet_slot.hpp | 4 +- src/profinet/profinet_submodule.cpp | 8 +- src/profinet/profinet_submodule.hpp | 16 ++- src/profinet/profinet_subslot.hpp | 6 +- 9 files changed, 248 insertions(+), 23 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index cce6065..52be0b6 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2,15 +2,42 @@ #include "../libs/include/pnet_api.h" #include "profinet.hpp" +#define ECHO_MODULE_ID 0x00000040 /// Идентификатор тестового модуля +#define ECHO_SUBMOD_ID 0x00000140 /// Идентификатор тестового подмодуля +#define ECHO_INPUT_DATA_SIZE 8 +#define ECHO_OUTPUT_DATA_SIZE 8 +#define ECHO_PARAMETER_GAIN_IDX 125 /// Индекс параметра Gain для подмодуля ECHO + +using namespace std; + +uint32_t Echo_Gain = 0; + +struct EchoData +{ + uint32_t data_i; + float data_f; +}; + +union EchoDataMem { + EchoData data; + uint8_t mem[sizeof(EchoData)]; +}; + +EchoDataMem Echo_inpCycData; +EchoDataMem Echo_outCycData; + + + int main(int argc, char * argv[]) { + AppSettings app_settings = { .EthDevName = "enp6s1", }; ProfinetSettings settings = { - .ticks_us = 1000, - .EthIface = "enp6s1", + .ticks_us = 1000, /// Период таймера в мкс + .EthIface = "enp6s1", /// Имя интерфейса Ethernet к которому будет подключаться }; ProfinetDeviceSettings profinet_settings = { @@ -56,8 +83,46 @@ int main(int argc, char * argv[]) /// Настройка Profinet: Инициализация pnet, добавление DAP слотов и подслотов и подключение к ним модулей и подмодулей if (!profinet.Config(settings, profinet_settings)) return 0; + /** + * @brief Создание тестового модуля ECHO + * От контроллера принимает данные в составе 1 значения uint32_t и 1 значения float + * Умножает их на определенное число Gain и передает получившиеся значения обратно в контроллер. + * + */ + /// 1. Создаем тестовый модуль ECHO: module_id = 0x40 + auto echo_module_ptr = make_shared(ECHO_MODULE_ID, "Echo module"); + + /// 2. Создаем конфигурацию циклических данных подмодуля + pnet_data_cfg_t echo_data_cfg = { PNET_DIR_NO_IO, ECHO_INPUT_DATA_SIZE, ECHO_OUTPUT_DATA_SIZE }; + + /// 3. Создаем тестовый подмодуль для модуля ECHO + auto echo_submodule_ptr = make_shared(PNET_SUBMOD_DAP_IDENT, + "DAP Identity 1", + echo_data_cfg, + static_cast(Echo_inpCycData.mem), + static_cast(Echo_outCycData.mem)); + + /// 4. Создаем конфигурационные данные подмодуля (не имеет отношения к echo_data_cfg). Это кастомные настройки подмодуля. + auto echo_parameter = make_shared(ECHO_PARAMETER_GAIN_IDX, + "Echo gain setting", + (uint8_t *)&Echo_Gain, + sizeof(Echo_Gain)); + + /// 5. Добавляем параметр к подмодулю + echo_submodule_ptr->addParameter(echo_parameter); + + /// 6. Добавляем подмодуль к модулю + echo_module_ptr->addSubmodule(echo_submodule_ptr); + + /// 7. Добавляем модуль к конфигурации. В этом месте модуль еще не активен, т.к. он не вставлен в слот. + /// Это делает profinet controller. + profinet.addModule(echo_module_ptr); - /// Тестовый слот и модуль ECHO: module_id = 0x40 + /// Циклические данные? + + /** + * Циклические данные устройства передаются с определенным периодом, сообщения от контроллера при этом не требуются. + */ return 0; } \ No newline at end of file diff --git a/src/profinet/profinet.cpp b/src/profinet/profinet.cpp index 5f8b4d6..2798489 100644 --- a/src/profinet/profinet.cpp +++ b/src/profinet/profinet.cpp @@ -145,7 +145,7 @@ static void pnetConfigure(pnet_cfg_t& cfg, ProfinetSettings& Settings, ProfinetD cfg.use_qualified_diagnosis = false; } -Profinet::Profinet(void) : m_pnet_data({nullptr, 0}) +Profinet::Profinet(void) : m_pnet_data({nullptr, 0, UINT32_MAX}) { } @@ -192,11 +192,11 @@ bool Profinet::Config(ProfinetSettings& Settings, ProfinetDeviceSettings& DevSet /// Создаем обязательный модуль DAP auto dap_module = make_shared(PNET_MOD_DAP_IDENT, "DAP 1"); /// Создаем обязательный подмодуль DAP Identity 1 - auto dap_submodule_indent_1 = make_shared(PNET_SUBMOD_DAP_IDENT, "DAP Identity 1", dap_data_cfg); + auto dap_submodule_indent_1 = make_shared(PNET_SUBMOD_DAP_IDENT, "DAP Identity 1", dap_data_cfg, nullptr, nullptr); /// Создаем обязательный подмодуль DAP IFACE1 IDENT - auto dap_submodule_iface_1_indent_1 = make_shared(PNET_SUBMOD_DAP_INTERFACE_1_IDENT, "DAP IFACE1 IDENT", dap_data_cfg); + auto dap_submodule_iface_1_indent_1 = make_shared(PNET_SUBMOD_DAP_INTERFACE_1_IDENT, "DAP IFACE1 IDENT", dap_data_cfg, nullptr, nullptr); /// Создаем обязательный подмодуль DAP Port 1 - auto dap_subslot_iface_1_port1_ident = make_shared(PNET_SUBMOD_DAP_INTERFACE_1_PORT_1_IDENT, "DAP Port 1", dap_data_cfg); + auto dap_subslot_iface_1_port1_ident = make_shared(PNET_SUBMOD_DAP_INTERFACE_1_PORT_1_IDENT, "DAP Port 1", dap_data_cfg, nullptr, nullptr); /// Добавляем подмодули к модулю ret&= dap_module->addSubmodule(dap_submodule_indent_1); ret&= dap_module->addSubmodule(dap_submodule_iface_1_indent_1); @@ -204,7 +204,7 @@ bool Profinet::Config(ProfinetSettings& Settings, ProfinetDeviceSettings& DevSet /// Создаем слот для модуля DAP и доключаем к нему модуль dap_module ret&= addSlotAndPlugModule(PNET_SLOT_DAP_IDENT, dap_module); - /// Добавляем подслоты к слоту и подключаем к подслоту подмодуль + /// Добавляем подслоты к слоту PNET_SLOT_DAP_IDENT и подключаем к подслотам подмодули /// 1. Подмодуль "DAP Identity 1" ret&= addSubslotAndPlugSubmodule(PNET_SLOT_DAP_IDENT, PNET_SUBSLOT_DAP_IDENT, dap_submodule_indent_1); /// 2. Подмодуль "DAP IFACE1 IDENT" @@ -415,6 +415,95 @@ std::shared_ptr Profinet::getSubmoduleParameter(uint16_t slot return param_ptr; } +void Profinet::initDataAndIoxs() +{ + uint8_t indata_iops; + /** + * Итерируемся по всем слотам. + * Если слоты есть, то и модули к ним уже подключены, т.к. это делает контроллер. + */ + for (auto slot : m_slots) + { + shared_ptr& slot_ptr = slot.second; + + auto& subslots = slot_ptr->getSubslotsPtr(); + /// Итерируемся по всем подслотам + for (auto subslot : subslots) + { + shared_ptr& subslot_ptr = subslot.second; + + auto submodule_ptr = subslot_ptr->getSubmodulePtr(); + + /// На всякий случай. Вообще если подслоты есть, то и подмодули уже к ним подключены. + if (submodule_ptr == nullptr) + continue; + + indata_iops = PNET_IOXS_BAD; + /** + * @brief Если для подмодуля существуют входные данные (данные от устройства к контроллеру) или + * циклических данных нет(DAP), то нужно установить статус IOPS. + * submodule_ptr->m_data_cfg.data_dir == PNET_DIR_NO_IO - вот это условие нужно для установки статуса для DAP + */ + if ( submodule_ptr->m_data_cfg.insize > 0 || + submodule_ptr->m_data_cfg.data_dir == PNET_DIR_NO_IO ) + { + /// Для данных DAP статус всегда ОК + if (slot_ptr->m_slot_nbr == PNET_SLOT_DAP_IDENT) + { + /// Для данных DAP статус ОК + indata_iops = PNET_IOXS_GOOD; + } + else if (submodule_ptr->m_data_cfg.insize > 0) + { + /// Проверка, что данные есть + if (submodule_ptr->m_inp_data_ptr != nullptr) + { + indata_iops = PNET_IOXS_GOOD; + } + } + + int ret = pnet_input_set_data_and_iops ( m_pnet_data.pnet_ptr, + m_pnet_data.api, + slot_ptr->m_slot_nbr, + subslot_ptr->m_subslot_nbr, + (uint8_t*)submodule_ptr->m_inp_data_ptr, /// Указатель на данные + submodule_ptr->m_data_cfg.insize, /// Размер данных + indata_iops); + + /* + * If a submodule is still plugged but not used in current AR, + * setting the data and IOPS will fail. + * This is not a problem. + * Log message below will only be printed for active submodules. + */ + if (ret == 0) + { + /// Успешно + } + } + + /** + * @brief Если данные подмодуля включаются в себя выходные данные(данные от контроллера к устройству) + * То нужно установить для них статус IOCS в PNET_IOXS_GOOD. + * + */ + if (submodule_ptr->m_data_cfg.outsize > 0) + { + int ret = pnet_output_set_iocs ( m_pnet_data.pnet_ptr, + m_pnet_data.api, + slot_ptr->m_slot_nbr, + subslot_ptr->m_subslot_nbr, + PNET_IOXS_GOOD); + + if (ret == 0) + { + /// Успешно + } + } + } + } +} + /**\ * ========================================================================================= * Callbacks @@ -423,6 +512,63 @@ std::shared_ptr Profinet::getSubmoduleParameter(uint16_t slot int Profinet::callbackStateInd ( uint32_t arep, pnet_event_values_t event) { + uint16_t err_cls = 0; /* Error code 1 */ + uint16_t err_code = 0; /* Error code 2 */ + const char * error_class_description = ""; + const char * error_code_description = ""; + + if (event == PNET_EVENT_ABORT) + { + if (pnet_get_ar_error_codes (m_pnet_data.pnet_ptr, arep, &err_cls, &err_code) == 0) + { + /** + * @brief Коды ошибок в err_cls и err_code + * TODO: Нужно передавать в управляющее приложение + */ + } + else + { + /// Ошибок нет + } + + + /* Only abort AR with correct session key */ + ///os_event_set (app->main_events, APP_EVENT_ABORT); + } + else if (event == PNET_EVENT_PRMEND) + { + if (isConnectedToController()) + { + /// Если уже были подключены и пришел запрос на еще одно подключение + } + + m_pnet_data.arep = arep; + + initDataAndIoxs(); + + (void)pnet_set_provider_state (m_pnet_data.pnet_ptr, true); + /** + * @brief Костыль библиотеки pnet: + * По правилам Profinet нужно ответить Application ready. + * Однако тут нельзя вызвать pnet_application_ready, т.к. это испортит + * внутреннее состояние стэка pnet. + * Это нужно делать на следующем "тике". + * См. описание pnet_application_ready: + * This function must be called after the application has received the pnet_state_ind() + * user callback with PNET_EVENT_PRMEND, in order for a connection to be established. + * + */ + /* Send application ready at next tick + Do not call pnet_application_ready() here as it will affect + the internal stack states */ + m_pnet_data.arep_for_appl_ready = arep; + + ///os_event_set (app->main_events, APP_EVENT_READY_FOR_DATA); /// + } + else if (event == PNET_EVENT_DATA) + { + /// Стартовал обмен cyclic io + } return 0; } diff --git a/src/profinet/profinet.hpp b/src/profinet/profinet.hpp index 2c40b74..a1fe468 100644 --- a/src/profinet/profinet.hpp +++ b/src/profinet/profinet.hpp @@ -145,6 +145,10 @@ private: std::shared_ptr getSubslotPtr(uint16_t slot_nbr, uint16_t subslot_nbr); + bool isConnectedToController() { return m_pnet_data.arep != UINT32_MAX; }; + + void initDataAndIoxs(); + public: /** * diff --git a/src/profinet/profinet_parameter.hpp b/src/profinet/profinet_parameter.hpp index 416cdfc..d108766 100644 --- a/src/profinet/profinet_parameter.hpp +++ b/src/profinet/profinet_parameter.hpp @@ -6,9 +6,9 @@ struct ProfinetParameter { - ProfinetParameter(std::string Name, uint32_t Index, uint8_t * pData, uint16_t Length) : - name(Name), + ProfinetParameter(uint32_t Index, std::string Name, uint8_t * pData, uint16_t Length) : index(Index), + name(Name), data_ptr(pData), length(Length) {}; diff --git a/src/profinet/profinet_serv_data.hpp b/src/profinet/profinet_serv_data.hpp index fd61089..3ad48d5 100644 --- a/src/profinet/profinet_serv_data.hpp +++ b/src/profinet/profinet_serv_data.hpp @@ -5,4 +5,6 @@ struct ProfinetServiceData { pnet_t * pnet_ptr; uint32_t api; /// Непонятно что это, обязательно посмотреть в коде либы pnet + uint32_t arep; /// Аналогично + uint32_t arep_for_appl_ready; /// Костыль pnet }; \ No newline at end of file diff --git a/src/profinet/profinet_slot.hpp b/src/profinet/profinet_slot.hpp index 5791988..c18324e 100644 --- a/src/profinet/profinet_slot.hpp +++ b/src/profinet/profinet_slot.hpp @@ -55,6 +55,8 @@ public: std::shared_ptr getModulePtr(); + std::map>& getSubslotsPtr() { return m_subslots; }; + public: /// Номер слота const uint16_t m_slot_nbr; @@ -65,4 +67,4 @@ private: /// множество подслотов std::map> m_subslots; -}; +}; \ No newline at end of file diff --git a/src/profinet/profinet_submodule.cpp b/src/profinet/profinet_submodule.cpp index 7a6e021..e6c99d4 100644 --- a/src/profinet/profinet_submodule.cpp +++ b/src/profinet/profinet_submodule.cpp @@ -2,10 +2,14 @@ ProfinetSubmodule::ProfinetSubmodule(uint16_t submodule_id, std::string submodule_name, - pnet_data_cfg_t& submodule_data_cfg) : + pnet_data_cfg_t& submodule_data_cfg, + void * const inp_data_ptr, + void * const out_data_ptr) : m_id(submodule_id), m_name(submodule_name), - m_data_cfg(submodule_data_cfg) + m_data_cfg(submodule_data_cfg), + m_inp_data_ptr(inp_data_ptr), + m_out_data_ptr(out_data_ptr) { } diff --git a/src/profinet/profinet_submodule.hpp b/src/profinet/profinet_submodule.hpp index d99a43d..3ccd898 100644 --- a/src/profinet/profinet_submodule.hpp +++ b/src/profinet/profinet_submodule.hpp @@ -13,19 +13,25 @@ class ProfinetSubmodule { public: ProfinetSubmodule(uint16_t submodule_id, std::string submodule_name, - pnet_data_cfg_t& submodule_data_cfg); + pnet_data_cfg_t& submodule_data_cfg, + void * const inp_data_ptr, + void * const out_data_ptr); bool addParameter(std::shared_ptr& param); std::shared_ptr getParameterPtr(uint32_t index); + + public: - const uint16_t m_id; /// Идентификатор подмодуля - const std::string m_name; /// Имя подмодуля - const pnet_data_cfg_t m_data_cfg; /// Конфигурация циклических данных подмодуля + const uint16_t m_id; /// Идентификатор подмодуля + const std::string m_name; /// Имя подмодуля + const pnet_data_cfg_t m_data_cfg; /// Конфигурация циклических данных подмодуля + + void * const m_inp_data_ptr; /// Входные циклические данные (DEV->PLC) + void * const m_out_data_ptr; /// Выходные циклические данные (PLC->DEV) private: /// Набор параметров подмодуля std::map> m_params; - }; \ No newline at end of file diff --git a/src/profinet/profinet_subslot.hpp b/src/profinet/profinet_subslot.hpp index 7b43d84..c78d220 100644 --- a/src/profinet/profinet_subslot.hpp +++ b/src/profinet/profinet_subslot.hpp @@ -9,7 +9,6 @@ #include "profinet_serv_data.hpp" #include "profinet_submodule.hpp" - class ProfinetSubslot { public: @@ -46,7 +45,4 @@ private: /// Подмодуль вставленный в этот подслот std::shared_ptr m_submodule_ptr; -}; - - - +}; \ No newline at end of file