dev(UML-981): Промежуточный коммит

1.Реализован коллбэк callbackStateInd
2.Добавлены данные для циклического обмена
This commit is contained in:
Vadim Sychev 2022-07-26 13:36:54 +03:00
parent 85eff4f7cd
commit 14c8d5a1eb
9 changed files with 248 additions and 23 deletions

View File

@ -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<ProfinetModule>(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<ProfinetSubmodule>(PNET_SUBMOD_DAP_IDENT,
"DAP Identity 1",
echo_data_cfg,
static_cast<void*>(Echo_inpCycData.mem),
static_cast<void*>(Echo_outCycData.mem));
/// 4. Создаем конфигурационные данные подмодуля (не имеет отношения к echo_data_cfg). Это кастомные настройки подмодуля.
auto echo_parameter = make_shared<ProfinetParameter>(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;
}

View File

@ -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<ProfinetModule>(PNET_MOD_DAP_IDENT, "DAP 1");
/// Создаем обязательный подмодуль DAP Identity 1
auto dap_submodule_indent_1 = make_shared<ProfinetSubmodule>(PNET_SUBMOD_DAP_IDENT, "DAP Identity 1", dap_data_cfg);
auto dap_submodule_indent_1 = make_shared<ProfinetSubmodule>(PNET_SUBMOD_DAP_IDENT, "DAP Identity 1", dap_data_cfg, nullptr, nullptr);
/// Создаем обязательный подмодуль DAP IFACE1 IDENT
auto dap_submodule_iface_1_indent_1 = make_shared<ProfinetSubmodule>(PNET_SUBMOD_DAP_INTERFACE_1_IDENT, "DAP IFACE1 IDENT", dap_data_cfg);
auto dap_submodule_iface_1_indent_1 = make_shared<ProfinetSubmodule>(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<ProfinetSubmodule>(PNET_SUBMOD_DAP_INTERFACE_1_PORT_1_IDENT, "DAP Port 1", dap_data_cfg);
auto dap_subslot_iface_1_port1_ident = make_shared<ProfinetSubmodule>(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<ProfinetParameter> Profinet::getSubmoduleParameter(uint16_t slot
return param_ptr;
}
void Profinet::initDataAndIoxs()
{
uint8_t indata_iops;
/**
* Итерируемся по всем слотам.
* Если слоты есть, то и модули к ним уже подключены, т.к. это делает контроллер.
*/
for (auto slot : m_slots)
{
shared_ptr<ProfinetSlot>& slot_ptr = slot.second;
auto& subslots = slot_ptr->getSubslotsPtr();
/// Итерируемся по всем подслотам
for (auto subslot : subslots)
{
shared_ptr<ProfinetSubslot>& 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<ProfinetParameter> 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;
}

View File

@ -145,6 +145,10 @@ private:
std::shared_ptr<ProfinetSubslot> getSubslotPtr(uint16_t slot_nbr, uint16_t subslot_nbr);
bool isConnectedToController() { return m_pnet_data.arep != UINT32_MAX; };
void initDataAndIoxs();
public:
/**
*

View File

@ -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) {};

View File

@ -5,4 +5,6 @@
struct ProfinetServiceData {
pnet_t * pnet_ptr;
uint32_t api; /// Непонятно что это, обязательно посмотреть в коде либы pnet
uint32_t arep; /// Аналогично
uint32_t arep_for_appl_ready; /// Костыль pnet
};

View File

@ -55,6 +55,8 @@ public:
std::shared_ptr<ProfinetModule> getModulePtr();
std::map<uint16_t, std::shared_ptr<ProfinetSubslot>>& getSubslotsPtr() { return m_subslots; };
public:
/// Номер слота
const uint16_t m_slot_nbr;
@ -65,4 +67,4 @@ private:
/// множество подслотов
std::map<uint16_t, std::shared_ptr<ProfinetSubslot>> m_subslots;
};
};

View File

@ -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)
{
}

View File

@ -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<ProfinetParameter>& param);
std::shared_ptr<ProfinetParameter> 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<uint32_t, std::shared_ptr<ProfinetParameter>> m_params;
};

View File

@ -9,7 +9,6 @@
#include "profinet_serv_data.hpp"
#include "profinet_submodule.hpp"
class ProfinetSubslot
{
public:
@ -46,7 +45,4 @@ private:
/// Подмодуль вставленный в этот подслот
std::shared_ptr<ProfinetSubmodule> m_submodule_ptr;
};
};