dev(UML-981): Создание и уничтожение потока циклических операций

This commit is contained in:
Vadim Sychev 2022-07-27 09:12:31 +03:00
parent 14c8d5a1eb
commit 8f279814df
9 changed files with 176 additions and 18 deletions

View File

@ -1,6 +1,7 @@
#include "app_settings.hpp"
#include "../libs/include/pnet_api.h"
#include "profinet.hpp"
#include <thread>
#define ECHO_MODULE_ID 0x00000040 /// Идентификатор тестового модуля
#define ECHO_SUBMOD_ID 0x00000140 /// Идентификатор тестового подмодуля
@ -118,11 +119,16 @@ int main(int argc, char * argv[])
/// Это делает profinet controller.
profinet.addModule(echo_module_ptr);
/// Циклические данные?
/**
* Циклические данные устройства передаются с определенным периодом, сообщения от контроллера при этом не требуются.
*/
/// Запуск потока Profinet
profinet.Start();
while(1)
{
}
return 0;
}

View File

@ -23,6 +23,9 @@
#include <cstring>
#include <iostream>
#include <chrono>
#include <thread>
using namespace std;
@ -145,9 +148,10 @@ static void pnetConfigure(pnet_cfg_t& cfg, ProfinetSettings& Settings, ProfinetD
cfg.use_qualified_diagnosis = false;
}
Profinet::Profinet(void) : m_pnet_data({nullptr, 0, UINT32_MAX})
Profinet::Profinet() : m_pnet_data({nullptr, 0, UINT32_MAX, 0}),
m_cyclic_io_cnt{0},
m_periodic_stop{false}
{
}
bool Profinet::Config(ProfinetSettings& Settings, ProfinetDeviceSettings& DevSettings)
@ -161,6 +165,12 @@ bool Profinet::Config(ProfinetSettings& Settings, ProfinetDeviceSettings& DevSet
std::memset(&pnet_cfg, 0, sizeof(pnet_cfg));
pnetConfigure(pnet_cfg, Settings, DevSettings);
m_run_period_us = Settings.ticks_us;
m_cyclic_io_period_ms = Settings.cyclic_ms;
///TODO Определить что передавать в cb_cfg
pnet_cfg.cb_arg = static_cast<void*>(this);
@ -504,12 +514,125 @@ void Profinet::initDataAndIoxs()
}
}
void Profinet::PeriodicOperations()
{
while(!m_periodic_stop.load(std::memory_order_relaxed))
{
/// Ждем время в мкс: ticks_period_us, по умолчанию 1мс
std::this_thread::sleep_for(std::chrono::microseconds(m_run_period_us));
/// Вызов cyclic io
if (++m_cyclic_io_cnt >= m_cyclic_io_period_ms)
{
m_cyclic_io_cnt = 0;
cyclicIoData();
}
/// запускаем pnet
pnet_handle_periodic (m_pnet_data.pnet_ptr);
}
}
void Profinet::cyclicIoData()
{
for (auto slot : m_slots)
{
shared_ptr<ProfinetSlot>& slot_ptr = slot.second;
/// DAP слот игнорируем при циклическом обмене
if (slot_ptr->m_slot_nbr == PNET_SLOT_DAP_IDENT)
continue;
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;
if (submodule_ptr->m_data_cfg.outsize > 0)
{
bool outdata_updated;
uint16_t outdata_length = submodule_ptr->m_data_cfg.outsize;
uint8_t outdata_iops;
/// Копируем данные от контроллера в m_out_data_ptr
(void)pnet_output_get_data_and_iops (
m_pnet_data.pnet_ptr,
m_pnet_data.api,
slot_ptr->m_slot_nbr,
subslot_ptr->m_subslot_nbr,
&outdata_updated,
(uint8_t*)submodule_ptr->m_out_data_ptr,
&outdata_length,
&outdata_iops);
submodule_ptr->m_outdata_iops = outdata_iops;
if (submodule_ptr->m_data_cfg.outsize != outdata_length)
{
/// Неправильная длина данных
}
else if (outdata_iops == PNET_IOXS_BAD)
{
/// Что-то не так с данными от контроллера
}
}
if (submodule_ptr->m_data_cfg.insize > 0)
{
uint8_t indata_iocs;
uint8_t indata_iops = PNET_IOXS_GOOD;
/* Send input data to the PLC */
(void)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);
(void)pnet_input_get_iocs (
m_pnet_data.pnet_ptr,
m_pnet_data.api,
slot_ptr->m_slot_nbr,
subslot_ptr->m_subslot_nbr,
&indata_iocs);
submodule_ptr->m_indata_iocs = indata_iocs;
}
}
}
}
void Profinet::Start()
{
m_periodic_thread = std::move(std::thread(&Profinet::PeriodicOperations, this));
}
Profinet::~Profinet()
{
///Остановка потока
m_periodic_stop.store(true, std::memory_order_relaxed);
if (m_periodic_thread.joinable())
{
m_periodic_thread.join();
}
}
/**\
* =========================================================================================
* Callbacks
* =========================================================================================
*/
int Profinet::callbackStateInd ( uint32_t arep, pnet_event_values_t event)
{
uint16_t err_cls = 0; /* Error code 1 */

View File

@ -8,6 +8,9 @@
#include "profinet_slot.hpp"
#include "profinet_serv_data.hpp"
#include <atomic>
#include <thread>
class Profinet {
public:
Profinet();
@ -121,8 +124,31 @@ public:
*/
std::shared_ptr<ProfinetParameter> getSubmoduleParameter(uint16_t slot_nbr, uint16_t subslot_nbr, uint32_t param_index);
/**
* @brief Таймер для циклического обмена данными
*
*/
void PeriodicOperations();
/**
* @brief Запуск потока
*
*/
void Start();
~Profinet();
private:
ProfinetServiceData m_pnet_data;
uint32_t m_run_period_us; /// Период одного тика (цикла) выполнения
uint32_t m_cyclic_io_period_ms; /// Период циклического обмена
uint32_t m_cyclic_io_cnt; /// Счетчик циклического обмена
std::thread m_periodic_thread; /// Поток в котором исполняется PeriodicOperations
std::atomic<bool> m_periodic_stop; /// Флаг завершения потока
/**
* @brief Ключ - номер слота, Значение - указатель на слот
*
@ -149,6 +175,8 @@ private:
void initDataAndIoxs();
void cyclicIoData();
public:
/**
*

View File

@ -3,8 +3,8 @@
#include "../../libs/include/pnet_api.h"
struct ProfinetServiceData {
pnet_t * pnet_ptr;
pnet_t * pnet_ptr; /// Указатель на pnet
uint32_t api; /// Непонятно что это, обязательно посмотреть в коде либы pnet
uint32_t arep; /// Аналогично
uint32_t arep_for_appl_ready; /// Костыль pnet
uint32_t arep_for_appl_ready; /// Костыль для pnet (нужно реализовать как в примере)
};

View File

@ -83,5 +83,6 @@ struct ProfinetDeviceSettings {
/// Настройки не зависящие от описания данных Profinet
struct ProfinetSettings {
uint32_t ticks_us = 1000; /// Устанавливает время в мкс между вызовами pnet_handle_periodic()
uint32_t cyclic_ms = 100; /// Устанавливает период в мс для циклического обмена данными
std::string EthIface = ""; /// Имя интерфейса ethernet
};

View File

@ -9,7 +9,9 @@ ProfinetSubmodule::ProfinetSubmodule(uint16_t submodule_id,
m_name(submodule_name),
m_data_cfg(submodule_data_cfg),
m_inp_data_ptr(inp_data_ptr),
m_out_data_ptr(out_data_ptr)
m_out_data_ptr(out_data_ptr),
m_indata_iocs(0),
m_outdata_iops(0)
{
}

View File

@ -28,6 +28,12 @@ public:
const std::string m_name; /// Имя подмодуля
const pnet_data_cfg_t m_data_cfg; /// Конфигурация циклических данных подмодуля
/// iocs = I/O consumer status data (формат данных из pnet_ioxs_values_t)
uint8_t m_indata_iocs;
/// iops = I/O provider status data (формат данных из pnet_ioxs_values_t)
uint8_t m_outdata_iops;
void * const m_inp_data_ptr; /// Входные циклические данные (DEV->PLC)
void * const m_out_data_ptr; /// Выходные циклические данные (PLC->DEV)

View File

@ -4,8 +4,6 @@ ProfinetSubslot::ProfinetSubslot(uint16_t subslot_nbr, uint16_t slot_nbr, uint32
m_subslot_nbr(subslot_nbr),
m_slot_nbr(slot_nbr),
m_module_id(module_id),
m_indata_iocs(0),
m_outdata_iops(0),
m_submodule_ptr(nullptr)
{

View File

@ -37,12 +37,6 @@ private:
/// Идентификатор модуля куда входит подключаемый подмодуль
uint32_t m_module_id;
/// iocs = I/O consumer status data (формат данных из pnet_ioxs_values_t)
uint8_t m_indata_iocs;
/// iops = I/O provider status data (формат данных из pnet_ioxs_values_t)
uint8_t m_outdata_iops;
/// Подмодуль вставленный в этот подслот
std::shared_ptr<ProfinetSubmodule> m_submodule_ptr;
};