dev(UML-981): Реализован механизм евентов от профинета до приложения
This commit is contained in:
parent
86e4f454c2
commit
d5c127d911
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
#include <condition_variable>
|
||||||
|
|
||||||
class Flags {
|
class Flags {
|
||||||
public:
|
public:
|
||||||
@ -10,6 +11,7 @@ public:
|
|||||||
void set_flag(uint32_t mask) {
|
void set_flag(uint32_t mask) {
|
||||||
std::lock_guard<std::mutex> guard(mutex_);
|
std::lock_guard<std::mutex> guard(mutex_);
|
||||||
flags_ |= mask;
|
flags_ |= mask;
|
||||||
|
flag_cond_.notify_one();
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t get_flag(uint32_t mask) {
|
uint32_t get_flag(uint32_t mask) {
|
||||||
@ -28,7 +30,16 @@ public:
|
|||||||
flags_ &= ~mask;
|
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:
|
private:
|
||||||
uint32_t flags_;
|
uint32_t flags_;
|
||||||
mutable std::mutex mutex_;
|
mutable std::mutex mutex_;
|
||||||
|
std::condition_variable flag_cond_;
|
||||||
};
|
};
|
||||||
53
src/main.cpp
53
src/main.cpp
@ -44,11 +44,8 @@ void endian_convert_32(uint8_t * p_data)
|
|||||||
|
|
||||||
int main(int argc, char * argv[])
|
int main(int argc, char * argv[])
|
||||||
{
|
{
|
||||||
Echo_inpCycData.data.data_f = 54321.9f; /// Преобразовать в big endian
|
Echo_inpCycData.data.data_f = 0.0f; /// Преобразовать в big endian
|
||||||
Echo_inpCycData.data.data_i = 876; /// Преобразовать в big endian
|
Echo_inpCycData.data.data_i = 0; /// Преобразовать в big endian
|
||||||
|
|
||||||
endian_convert_32((uint8_t*)&Echo_inpCycData.data.data_f);
|
|
||||||
endian_convert_32((uint8_t*)&Echo_inpCycData.data.data_i);
|
|
||||||
|
|
||||||
Echo_outCycData.data.data_f = 0.0f;
|
Echo_outCycData.data.data_f = 0.0f;
|
||||||
Echo_outCycData.data.data_i = 0;
|
Echo_outCycData.data.data_i = 0;
|
||||||
@ -83,6 +80,11 @@ int main(int argc, char * argv[])
|
|||||||
/// Запуск потока Profinet
|
/// Запуск потока Profinet
|
||||||
profinet.Start();
|
profinet.Start();
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Ниже тестовый функционал
|
||||||
|
*
|
||||||
|
*/
|
||||||
auto echo_mod = profinet.getModule(ECHO_MODULE_ID);
|
auto echo_mod = profinet.getModule(ECHO_MODULE_ID);
|
||||||
auto echo_submod = echo_mod->getSubmodulePtr(ECHO_SUBMOD_ID);
|
auto echo_submod = echo_mod->getSubmodulePtr(ECHO_SUBMOD_ID);
|
||||||
echo_submod->putDataToPlc(Echo_inpCycData.mem);
|
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);
|
auto param = echo_submod->getParameterPtr(ECHO_PARAMETER_GAIN_IDX);
|
||||||
|
|
||||||
|
|
||||||
uint32_t ix = 0;
|
uint32_t event_mask = (Profinet::EVENT_CONNECTION_ESTABLISHED |
|
||||||
while(1)
|
Profinet::EVENT_NEW_CYCLIC_DATA |
|
||||||
|
Profinet::EVENT_NEW_PARAM_DATA );
|
||||||
|
for (;;)
|
||||||
{
|
{
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
|
uint32_t events = profinet.EventWait(event_mask);
|
||||||
++ix;
|
//std::this_thread::sleep_for(std::chrono::milliseconds(1000));
|
||||||
std::cout << "tick: " << ix << endl;
|
//++ix;
|
||||||
if (echo_submod->isDataUpdated())
|
//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);
|
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_i);
|
||||||
endian_convert_32((uint8_t*)&Echo_outCycData.data.data_f);
|
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_i = Echo_Gain * Echo_outCycData.data.data_i;
|
||||||
Echo_inpCycData.data.data_f = Echo_Gain * Echo_outCycData.data.data_f;
|
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);
|
endian_convert_32((uint8_t*)&Echo_inpCycData.data.data_f);
|
||||||
///Отправляем данные для ПЛК
|
///Отправляем данные для ПЛК
|
||||||
echo_submod->putDataToPlc(Echo_inpCycData.mem);
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -33,7 +33,6 @@ using namespace std;
|
|||||||
#define GET_HIGH_BYTE(id) ((id >> 8) & 0xFF)
|
#define GET_HIGH_BYTE(id) ((id >> 8) & 0xFF)
|
||||||
#define GET_LOW_BYTE(id) (id & 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_PRIORITY 1
|
||||||
#define APP_SNMP_THREAD_STACKSIZE 256 * 1024 /* bytes */
|
#define APP_SNMP_THREAD_STACKSIZE 256 * 1024 /* bytes */
|
||||||
#define APP_ETH_THREAD_PRIORITY 10
|
#define APP_ETH_THREAD_PRIORITY 10
|
||||||
@ -43,9 +42,9 @@ using namespace std;
|
|||||||
|
|
||||||
#define FLAGS_AREP_APP_READY 0x00000001
|
#define FLAGS_AREP_APP_READY 0x00000001
|
||||||
|
|
||||||
//#define RELEASE
|
#define LOG_ENABLE
|
||||||
|
|
||||||
#ifndef RELEASE
|
#ifdef LOG_ENABLE
|
||||||
#define LOG(STR) log_.put(STR)
|
#define LOG(STR) log_.put(STR)
|
||||||
#else
|
#else
|
||||||
#define LOG(STR)
|
#define LOG(STR)
|
||||||
@ -588,15 +587,16 @@ void Profinet::cyclicIoData()
|
|||||||
|
|
||||||
if (submodule_ptr->m_data_cfg.outsize > 0)
|
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
|
/// Копируем данные от контроллера в m_out_data_ptr
|
||||||
submodule_ptr->outputGetData( m_pnet_data.pnet_ptr,
|
submodule_ptr->outputGetData( m_pnet_data.pnet_ptr,
|
||||||
m_pnet_data.api,
|
m_pnet_data.api,
|
||||||
slot_ptr->m_slot_nbr,
|
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)
|
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
|
* @brief Коды ошибок в err_cls и err_code
|
||||||
* TODO: Нужно передавать в управляющее приложение
|
* TODO: Нужно передавать в управляющее приложение
|
||||||
*/
|
*/
|
||||||
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
/// Ошибок нет
|
/// Ошибок нет
|
||||||
}
|
}
|
||||||
|
|
||||||
|
events_.clear_flags(EVENT_CONNECTION_ESTABLISHED);
|
||||||
|
|
||||||
|
m_pnet_data.arep = UINT32_MAX;
|
||||||
|
|
||||||
/* Only abort AR with correct session key */
|
/* Only abort AR with correct session key */
|
||||||
///os_event_set (app->main_events, APP_EVENT_ABORT);
|
///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)
|
else if (event == PNET_EVENT_STARTUP)
|
||||||
{
|
{
|
||||||
LOG("callbackStateInd: PNET_EVENT_STARTUP: A connection has been initiated.");
|
LOG("callbackStateInd: PNET_EVENT_STARTUP: A connection has been initiated.");
|
||||||
|
events_.set_flag(EVENT_CONNECTION_ESTABLISHED);
|
||||||
}
|
}
|
||||||
else if (event == PNET_EVENT_APPLRDY)
|
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)
|
int Profinet::callbackCcontrolInd ( uint32_t arep, pnet_result_t * p_result)
|
||||||
{
|
{
|
||||||
LOG("callbackCcontrolInd");
|
LOG("callbackCcontrolInd");
|
||||||
/**
|
|
||||||
* @brief
|
|
||||||
* Можно оставить пустым
|
|
||||||
*/
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -854,7 +857,15 @@ int Profinet::callbackWriteInd ( uint32_t arep,
|
|||||||
}
|
}
|
||||||
|
|
||||||
///3. Копируем данные
|
///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)
|
int Profinet::callbackExpModuleInd ( uint16_t slot, uint32_t module_ident)
|
||||||
|
|||||||
@ -18,6 +18,13 @@
|
|||||||
|
|
||||||
class Profinet {
|
class Profinet {
|
||||||
public:
|
public:
|
||||||
|
enum Events
|
||||||
|
{
|
||||||
|
EVENT_CONNECTION_ESTABLISHED = 0x00000001, /// Установлено соединение с ПЛК
|
||||||
|
EVENT_NEW_CYCLIC_DATA = 0x00000002, /// Получение новых циклических данных от ПЛК
|
||||||
|
EVENT_NEW_PARAM_DATA = 0x00000004, /// Новая запись параметра от ПЛК
|
||||||
|
};
|
||||||
|
|
||||||
Profinet();
|
Profinet();
|
||||||
|
|
||||||
bool Config(ProfinetSettings& Settings,
|
bool Config(ProfinetSettings& Settings,
|
||||||
@ -143,6 +150,11 @@ public:
|
|||||||
*/
|
*/
|
||||||
void Start();
|
void Start();
|
||||||
|
|
||||||
|
uint32_t EventWait(uint32_t mask) { return events_.wait_flags(mask); }
|
||||||
|
|
||||||
|
void EventClear(uint32_t mask) { events_.clear_flags(mask); }
|
||||||
|
|
||||||
|
|
||||||
~Profinet();
|
~Profinet();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -169,21 +181,35 @@ private:
|
|||||||
|
|
||||||
Log log_;
|
Log log_;
|
||||||
|
|
||||||
Flags flags_;
|
Flags flags_; /// Внутренние флаги: взаимодействиe между потоком Profinet и потоком стека Pnet
|
||||||
|
|
||||||
|
|
||||||
|
Flags events_; /// Флаги для внешних событий. Это более общие флаги, например флаг получения данных от ПЛК, а в каком подмодуле, уже не уточняется.
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/**
|
/**
|
||||||
* @brief Проверяет наличие слота slot_nbr в конфигурации
|
* @brief Проверяет наличие слота slot_nbr в конфигурации и возвращает указатель на него
|
||||||
*
|
*
|
||||||
* @param slot_nbr номер слота
|
* @param slot_nbr номер слота
|
||||||
* @return ProfinetSlot* - указатель на слот, при успехе или nullptr есои слот в конфигурации отсутсвует
|
* @return std::shared_ptr<ProfinetSlot> указатель на слот, при успехе или nullptr если слот в конфигурации отсутсвует
|
||||||
*/
|
*/
|
||||||
std::shared_ptr<ProfinetSlot> getSlotPtr(uint16_t slot_nbr);
|
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);
|
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; };
|
bool isConnectedToController() { return m_pnet_data.arep != UINT32_MAX; };
|
||||||
|
|
||||||
void initDataAndIoxs();
|
void initDataAndIoxs();
|
||||||
|
|||||||
@ -78,6 +78,8 @@ bool ProfinetSubmodule::outputGetData( pnet_t * pnet_ptr,
|
|||||||
uint16_t slot_nbr,
|
uint16_t slot_nbr,
|
||||||
uint16_t subslot_nbr )
|
uint16_t subslot_nbr )
|
||||||
{
|
{
|
||||||
|
bool out = true;
|
||||||
|
|
||||||
/// Существуют подмодули без данных, например DAP
|
/// Существуют подмодули без данных, например DAP
|
||||||
if ((pnet_ptr == nullptr) || (out_data_ptr_ == nullptr))
|
if ((pnet_ptr == nullptr) || (out_data_ptr_ == nullptr))
|
||||||
{
|
{
|
||||||
@ -104,19 +106,25 @@ bool ProfinetSubmodule::outputGetData( pnet_t * pnet_ptr,
|
|||||||
|
|
||||||
if ((!data_updated_) && (updated == true))
|
if ((!data_updated_) && (updated == true))
|
||||||
{
|
{
|
||||||
data_updated_ = updated; /// Сбрасывается после прочтения данных устройством
|
/**
|
||||||
|
* @brief Устанавливаем флаг новых данных (данные смогут быть такие-же, но флаг все равно установится)
|
||||||
|
* Флаг сбрасывается в функции getDataFromPlc после прочтения данных устройством
|
||||||
|
*/
|
||||||
|
data_updated_ = updated;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_data_cfg.outsize != outdata_length)
|
if (m_data_cfg.outsize != outdata_length)
|
||||||
{
|
{
|
||||||
/// Неправильная длина данных
|
/// Неправильная длина данных
|
||||||
|
out = false;
|
||||||
}
|
}
|
||||||
else if (outdata_iops == PNET_IOXS_BAD)
|
else if (outdata_iops == PNET_IOXS_BAD)
|
||||||
{
|
{
|
||||||
/// Что-то не так с данными от контроллера
|
/// Что-то не так с данными от контроллера
|
||||||
|
out = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ProfinetSubmodule::getDataFromPlc(uint8_t * data_ptr)
|
bool ProfinetSubmodule::getDataFromPlc(uint8_t * data_ptr)
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user