dev(UML-981): Реализован механизм евентов от профинета до приложения

This commit is contained in:
Vadim Sychev 2022-07-29 14:15:25 +03:00
parent 86e4f454c2
commit d5c127d911
5 changed files with 115 additions and 38 deletions

View File

@ -2,6 +2,7 @@
#include <mutex>
#include <cstdint>
#include <condition_variable>
class Flags {
public:
@ -10,6 +11,7 @@ public:
void set_flag(uint32_t mask) {
std::lock_guard<std::mutex> guard(mutex_);
flags_ |= mask;
flag_cond_.notify_one();
}
uint32_t get_flag(uint32_t mask) {
@ -28,7 +30,16 @@ public:
flags_ &= ~mask;
}
uint32_t wait_flags(uint32_t mask)
{
std::unique_lock<std::mutex> lk(mutex_);
flag_cond_.wait(lk, [&]{return flags_ & mask;});
lk.unlock();
return flags_;
}
private:
uint32_t flags_;
mutable std::mutex mutex_;
std::condition_variable flag_cond_;
};

View File

@ -44,11 +44,8 @@ void endian_convert_32(uint8_t * p_data)
int main(int argc, char * argv[])
{
Echo_inpCycData.data.data_f = 54321.9f; /// Преобразовать в big endian
Echo_inpCycData.data.data_i = 876; /// Преобразовать в big endian
endian_convert_32((uint8_t*)&Echo_inpCycData.data.data_f);
endian_convert_32((uint8_t*)&Echo_inpCycData.data.data_i);
Echo_inpCycData.data.data_f = 0.0f; /// Преобразовать в big endian
Echo_inpCycData.data.data_i = 0; /// Преобразовать в big endian
Echo_outCycData.data.data_f = 0.0f;
Echo_outCycData.data.data_i = 0;
@ -83,6 +80,11 @@ int main(int argc, char * argv[])
/// Запуск потока Profinet
profinet.Start();
/**
* @brief Ниже тестовый функционал
*
*/
auto echo_mod = profinet.getModule(ECHO_MODULE_ID);
auto echo_submod = echo_mod->getSubmodulePtr(ECHO_SUBMOD_ID);
echo_submod->putDataToPlc(Echo_inpCycData.mem);
@ -90,24 +92,31 @@ int main(int argc, char * argv[])
auto param = echo_submod->getParameterPtr(ECHO_PARAMETER_GAIN_IDX);
uint32_t ix = 0;
while(1)
{
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
++ix;
std::cout << "tick: " << ix << endl;
if (echo_submod->isDataUpdated())
uint32_t event_mask = (Profinet::EVENT_CONNECTION_ESTABLISHED |
Profinet::EVENT_NEW_CYCLIC_DATA |
Profinet::EVENT_NEW_PARAM_DATA );
for (;;)
{
uint32_t events = profinet.EventWait(event_mask);
//std::this_thread::sleep_for(std::chrono::milliseconds(1000));
//++ix;
//std::cout << "tick: " << ix << endl;
if (events & Profinet::EVENT_CONNECTION_ESTABLISHED)
{
std::cout << "New data from Plc" << endl;
profinet.EventClear(Profinet::EVENT_CONNECTION_ESTABLISHED);
std::cout << "Event: PLC connection established" << endl;
}
else if (events & Profinet::EVENT_NEW_CYCLIC_DATA)
{
profinet.EventClear(Profinet::EVENT_NEW_CYCLIC_DATA);
///Читаем данные от ПЛК
echo_submod->getDataFromPlc(Echo_outCycData.mem);
//Читаем параметр записанный ПЛК при установлении связи
param->readParameter((uint8_t*)&Echo_Gain);
/// Конвертируем в литл эндиан
endian_convert_32((uint8_t*)&Echo_Gain);
endian_convert_32((uint8_t*)&Echo_outCycData.data.data_i);
endian_convert_32((uint8_t*)&Echo_outCycData.data.data_f);
///Подготавливаем данные для ПЛК
Echo_inpCycData.data.data_i = Echo_Gain * Echo_outCycData.data.data_i;
Echo_inpCycData.data.data_f = Echo_Gain * Echo_outCycData.data.data_f;
@ -116,6 +125,18 @@ int main(int argc, char * argv[])
endian_convert_32((uint8_t*)&Echo_inpCycData.data.data_f);
///Отправляем данные для ПЛК
echo_submod->putDataToPlc(Echo_inpCycData.mem);
}
else if (events & Profinet::EVENT_NEW_PARAM_DATA)
{
profinet.EventClear(Profinet::EVENT_NEW_PARAM_DATA);
std::cout << "Event: New parameter data: ";
//Читаем параметр записанный ПЛК при установлении связи
param->readParameter((uint8_t*)&Echo_Gain);
/// Конвертируем в литл эндиан
endian_convert_32((uint8_t*)&Echo_Gain);
std::cout << "Echo_Gain = " << std::to_string(Echo_Gain) << endl;
}
}

View File

@ -33,7 +33,6 @@ using namespace std;
#define GET_HIGH_BYTE(id) ((id >> 8) & 0xFF)
#define GET_LOW_BYTE(id) (id & 0xFF)
#define APP_MAIN_SLEEPTIME_US 5000 * 1000
#define APP_SNMP_THREAD_PRIORITY 1
#define APP_SNMP_THREAD_STACKSIZE 256 * 1024 /* bytes */
#define APP_ETH_THREAD_PRIORITY 10
@ -43,9 +42,9 @@ using namespace std;
#define FLAGS_AREP_APP_READY 0x00000001
//#define RELEASE
#define LOG_ENABLE
#ifndef RELEASE
#ifdef LOG_ENABLE
#define LOG(STR) log_.put(STR)
#else
#define LOG(STR)
@ -588,15 +587,16 @@ void Profinet::cyclicIoData()
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
submodule_ptr->outputGetData( m_pnet_data.pnet_ptr,
m_pnet_data.api,
slot_ptr->m_slot_nbr,
subslot_ptr->m_subslot_nbr);
subslot_ptr->m_subslot_nbr);
if (submodule_ptr->isDataUpdated())
{
events_.set_flag(EVENT_NEW_CYCLIC_DATA);
}
}
if (submodule_ptr->m_data_cfg.insize > 0)
@ -672,12 +672,17 @@ int Profinet::callbackStateInd ( uint32_t arep, pnet_event_values_t event)
* @brief Коды ошибок в err_cls и err_code
* TODO: Нужно передавать в управляющее приложение
*/
}
else
{
/// Ошибок нет
}
events_.clear_flags(EVENT_CONNECTION_ESTABLISHED);
m_pnet_data.arep = UINT32_MAX;
/* Only abort AR with correct session key */
///os_event_set (app->main_events, APP_EVENT_ABORT);
}
@ -719,6 +724,7 @@ int Profinet::callbackStateInd ( uint32_t arep, pnet_event_values_t event)
else if (event == PNET_EVENT_STARTUP)
{
LOG("callbackStateInd: PNET_EVENT_STARTUP: A connection has been initiated.");
events_.set_flag(EVENT_CONNECTION_ESTABLISHED);
}
else if (event == PNET_EVENT_APPLRDY)
{
@ -769,10 +775,7 @@ int Profinet::callbackDcontrolInd ( uint32_t arep,
int Profinet::callbackCcontrolInd ( uint32_t arep, pnet_result_t * p_result)
{
LOG("callbackCcontrolInd");
/**
* @brief
* Можно оставить пустым
*/
return 0;
}
@ -854,7 +857,15 @@ int Profinet::callbackWriteInd ( uint32_t arep,
}
///3. Копируем данные
return param_ptr->writeParameter(p_write_data);
if (!param_ptr->writeParameter(p_write_data))
{
return -1;
}
/// Устанавливаем событие
events_.set_flag(EVENT_NEW_PARAM_DATA);
return 0;
}
int Profinet::callbackExpModuleInd ( uint16_t slot, uint32_t module_ident)

View File

@ -18,6 +18,13 @@
class Profinet {
public:
enum Events
{
EVENT_CONNECTION_ESTABLISHED = 0x00000001, /// Установлено соединение с ПЛК
EVENT_NEW_CYCLIC_DATA = 0x00000002, /// Получение новых циклических данных от ПЛК
EVENT_NEW_PARAM_DATA = 0x00000004, /// Новая запись параметра от ПЛК
};
Profinet();
bool Config(ProfinetSettings& Settings,
@ -143,6 +150,11 @@ public:
*/
void Start();
uint32_t EventWait(uint32_t mask) { return events_.wait_flags(mask); }
void EventClear(uint32_t mask) { events_.clear_flags(mask); }
~Profinet();
private:
@ -168,22 +180,36 @@ private:
std::map<uint32_t, std::shared_ptr<ProfinetModule>> m_modules;
Log log_;
Flags flags_;
Flags flags_; /// Внутренние флаги: взаимодействиe между потоком Profinet и потоком стека Pnet
Flags events_; /// Флаги для внешних событий. Это более общие флаги, например флаг получения данных от ПЛК, а в каком подмодуле, уже не уточняется.
private:
/**
* @brief Проверяет наличие слота slot_nbr в конфигурации
* @brief Проверяет наличие слота slot_nbr в конфигурации и возвращает указатель на него
*
* @param slot_nbr номер слота
* @return ProfinetSlot* - указатель на слот, при успехе или nullptr есои слот в конфигурации отсутсвует
* @return std::shared_ptr<ProfinetSlot> указатель на слот, при успехе или nullptr если слот в конфигурации отсутсвует
*/
std::shared_ptr<ProfinetSlot> getSlotPtr(uint16_t slot_nbr);
/**
* @brief Проверяет наличие подслота в слоте и возвращает указатель на него
*
* @param slot_nbr номер слота
* @param subslot_nbr номер подслота
* @return std::shared_ptr<ProfinetSubslot> указатель на подслот, при успехе или nullptr если подслот в конфигурации отсутсвует
*/
std::shared_ptr<ProfinetSubslot> getSubslotPtr(uint16_t slot_nbr, uint16_t subslot_nbr);
/**
* @brief Флаг подключение к контроллеру(ПЛК).
* Реализация взята с примера стека pnet. На самом деле не понятна роль AREP.
* Открытой информации недостаточно, нужно покупать и смотреть спецификацию. *
*
* @return true подключение есть
* @return false
*/
bool isConnectedToController() { return m_pnet_data.arep != UINT32_MAX; };
void initDataAndIoxs();

View File

@ -78,6 +78,8 @@ bool ProfinetSubmodule::outputGetData( pnet_t * pnet_ptr,
uint16_t slot_nbr,
uint16_t subslot_nbr )
{
bool out = true;
/// Существуют подмодули без данных, например DAP
if ((pnet_ptr == nullptr) || (out_data_ptr_ == nullptr))
{
@ -104,19 +106,25 @@ bool ProfinetSubmodule::outputGetData( pnet_t * pnet_ptr,
if ((!data_updated_) && (updated == true))
{
data_updated_ = updated; /// Сбрасывается после прочтения данных устройством
/**
* @brief Устанавливаем флаг новых данных (данные смогут быть такие-же, но флаг все равно установится)
* Флаг сбрасывается в функции getDataFromPlc после прочтения данных устройством
*/
data_updated_ = updated;
}
if (m_data_cfg.outsize != outdata_length)
{
/// Неправильная длина данных
out = false;
}
else if (outdata_iops == PNET_IOXS_BAD)
{
/// Что-то не так с данными от контроллера
out = false;
}
return true;
return out;
}
bool ProfinetSubmodule::getDataFromPlc(uint8_t * data_ptr)