dev(UML-981): Реализована конфигурация из файла и совмкестный доступ к данным модуля из разных потоков.

This commit is contained in:
Vadim Sychev 2022-07-29 09:25:07 +03:00
parent 999208fc87
commit 5e9e6c3133
21 changed files with 24911 additions and 167 deletions

View File

@ -13,14 +13,17 @@ cmake_minimum_required (VERSION 3.14)
project (PN_DEV_TEST VERSION 0.0.1)
set(CMAKE_CXX_STANDARD 20)
set(LIBS_INSTALL_PATH ../libs)
set(PNET_PATH ../profinet_stack/p-net)
include(./utils/utils.cmake)
include(./profinet/profinet.cmake)
include(./nlohmann_json/nlohmann_json.cmake)
include(./file_api/file_api.cmake)
include(./configuration/configuration.cmake)
add_executable(pn_dev_test
./main.cpp ${SRC_FILES})
add_executable(pn_dev_test ./main.cpp ${SRC_FILES})
target_include_directories(pn_dev_test PRIVATE
./
@ -34,4 +37,4 @@ target_include_directories(pn_dev_test PRIVATE
target_link_directories(pn_dev_test PUBLIC ${LIBS_INSTALL_PATH}/lib)
target_link_libraries (pn_dev_test PUBLIC profinet osal)
target_link_libraries (pn_dev_test PUBLIC profinet osal)

View File

@ -0,0 +1,5 @@
set(SRC_FILES ${SRC_FILES}
./configuration/program_config.cpp
)
set(INC_DIRS ${INC_DIRS} ./configuration)

View File

@ -0,0 +1,241 @@
#include "program_config.hpp"
#include <filesystem>
#include <iostream>
#include "file_api.hpp"
#include "json.hpp"
#include <cstdint>
using json = nlohmann::json;
using string = std::string;
static bool programconf_readfile(json& j, std::string& file_path)
{
///string config_str;
try
{
///config_str = fileapi_read_full_stream( fileapi_read_file(file_path) );
j = json::parse(fileapi_read_file(file_path));
}
catch(const std::exception& e)
{
std::cerr << e.what() << '\n';
return false;
}
return true;
}
bool programconf_getProfinetSettings(std::string file_path, ProfinetSettings& pn_sett)
{
json j;
if (!programconf_readfile(j, file_path))
{
return false;
}
pn_sett.ticks_us = j["ProfinetSettings"]["ticks_us"].get<int>();
pn_sett.cyclic_ms = j["ProfinetSettings"]["cyclic_ms"].get<int>();
pn_sett.eth_dev_name = j["ProfinetSettings"]["eth_dev_name"].get<string>();
pn_sett.profinet_device_config = j["ProfinetSettings"]["profinet_device_config"].get<string>();
return true;
}
static uint32_t hexstring_to_int(string s)
{
uint32_t n;
if (s.find("0x") != string::npos)
{
s = s.substr(2); //Игнорируем 0x
std::istringstream(s) >> std::hex >> n;
}
else
{
std::istringstream(s) >> std::dec >> n;
}
return n;
}
static bool programconf_getPnetDeviceSettings(json& j, ProfinetDeviceSettings& pnet_sett)
{
if (!j.contains("ProfinetDeviceSettings"))
return false;
auto& sett = j["ProfinetDeviceSettings"];
pnet_sett.product_name = sett["product_name"].get<string>();
pnet_sett.station_name = sett["station_name"].get<string>();
pnet_sett.station_name = sett["station_name"].get<string>();
if (!sett.contains("im_0"))
return false;
auto& im_0 = sett["im_0"];
pnet_sett.im_0.vendor_id = hexstring_to_int( im_0["vendor_id"].get<string>() );
pnet_sett.im_0.hw_revision = im_0["hw_revision"].get<int>();
if (!im_0.contains("sw_revision"))
return false;
auto& sw_revision = im_0["sw_revision"];
string prefix = sw_revision["prefix"].get<string>();
if (prefix.length())
{
pnet_sett.im_0.sw_revision.prefix = prefix.c_str()[0];
}
else
{
pnet_sett.im_0.sw_revision.prefix = ' ';
}
pnet_sett.im_0.sw_revision.functional_enhancement = sw_revision["functional_enhancement"].get<int>();
pnet_sett.im_0.sw_revision.bug_fix = sw_revision["bug_fix"].get<int>();
pnet_sett.im_0.sw_revision.internal_change = sw_revision["internal_change"].get<int>();
pnet_sett.im_0.revision_counter = im_0["revision_counter"].get<int>();
pnet_sett.im_0.profile_id = hexstring_to_int(im_0["profile_id"].get<string>());
pnet_sett.im_0.profile_specific_type = hexstring_to_int(im_0["profile_specific_type"].get<string>());
pnet_sett.im_0.supported = hexstring_to_int(im_0["supported"].get<string>());
pnet_sett.im_0.order_id = im_0["order_id"].get<string>();
pnet_sett.im_0.serial_number = im_0["serial_number"].get<string>();
if (!sett.contains("im_1"))
return false;
auto& im_1 = sett["im_1"];
pnet_sett.im_1.tag_function = im_1["tag_function"].get<string>();
pnet_sett.im_1.tag_location = im_1["tag_location"].get<string>();
pnet_sett.im_2.date = sett["im_2"]["date"].get<string>();
pnet_sett.im_3.descriptor = sett["im_3"]["descriptor"].get<string>();
pnet_sett.im_4.signature = sett["im_4"]["signature"].get<string>();
pnet_sett.device_id = hexstring_to_int(sett["device_id"].get<string>());
pnet_sett.oem_vendor_id = hexstring_to_int(sett["oem_vendor_id"].get<string>());
pnet_sett.oem_device_id = hexstring_to_int(sett["oem_device_id"].get<string>());
pnet_sett.send_hello = sett["send_hello"].get<bool>();
pnet_sett.min_device_interval = sett["min_device_interval"].get<int>();
return true;
}
static pnet_submodule_dir_t programconf_dataDirConvert(string dir_str)
{
pnet_submodule_dir_t out = PNET_DIR_NO_IO;
if (dir_str == "PNET_DIR_IO")
{
out = PNET_DIR_IO;
}
else if (dir_str == "PNET_DIR_INPUT")
{
out = PNET_DIR_INPUT;
}
else if (dir_str == "PNET_DIR_OUTPUT")
{
out = PNET_DIR_OUTPUT;
}
return out;
}
static bool programconf_getProfinetModulesSettings(json& j, std::vector<std::shared_ptr<ProfinetModule>>& modules_vect)
{
if (!j.contains("ProfinetDeviceModulesSettings"))
return false;
auto& modules_sett = j["ProfinetDeviceModulesSettings"]["modules"];
for (auto& mod_set : modules_sett)
{
uint32_t mod_id = hexstring_to_int(mod_set["id"].get<string>());
string mod_name = mod_set["name"].get<string>();
auto module_ptr = ProfinetModule::Create(mod_id, mod_name);
if (module_ptr == nullptr)
{
return false;
}
auto& submodules_sett = mod_set["submodules"];
for (auto& submod_sett : submodules_sett)
{
uint32_t submod_id = hexstring_to_int(submod_sett["id"].get<string>());
string submod_name = submod_sett["name"].get<string>();
pnet_data_cfg_t data_cfg;
data_cfg.data_dir = programconf_dataDirConvert(submod_sett["cyc_data_dir"].get<string>());
data_cfg.insize = submod_sett["cyc_inp_data_size"].get<int>();
data_cfg.outsize = submod_sett["cyc_out_data_size"].get<int>();
auto submodule_ptr = ProfinetSubmodule::Create(submod_id, submod_name, data_cfg);
if (submodule_ptr == nullptr)
{
return false;
}
auto& params_sett = submod_sett["parameters"];
/// Записываем параметры в подмодуль
for (auto& parm_sett : params_sett)
{
uint32_t index = parm_sett["index"].get<int>();
string parm_name = parm_sett["name"].get<string>();
uint16_t length = parm_sett["data_size"].get<int>();
auto submod_param_ptr = ProfinetParameter::Create(index, parm_name, length);
if (submod_param_ptr == nullptr)
{
return false;
}
submodule_ptr->addParameter(submod_param_ptr);
}
module_ptr->addSubmodule(submodule_ptr);
}
modules_vect.push_back(module_ptr);
}
return true;
}
bool programconf_getProfinetDeviceSettings(std::string file_path, ProfinetDeviceSettings& pn_sett, std::vector<std::shared_ptr<ProfinetModule>>& modules_vect)
{
json j;
if (!programconf_readfile(j, file_path))
{
return false;
}
if (!programconf_getPnetDeviceSettings(j, pn_sett))
{
return false;
}
if (!programconf_getProfinetModulesSettings(j, modules_vect))
{
return false;
}
return true;
}

View File

@ -0,0 +1,13 @@
#pragma once
#include <string>
#include <vector>
#include "json.hpp"
#include "profinet_settings.hpp"
#include "profinet_module.hpp"
bool programconf_getProfinetSettings(std::string file_path, ProfinetSettings& pn_sett);
bool programconf_getProfinetDeviceSettings(std::string file_path, ProfinetDeviceSettings& pn_sett,
std::vector<std::shared_ptr<ProfinetModule>>& modules_vect);

View File

@ -0,0 +1,5 @@
set(SRC_FILES ${SRC_FILES}
./file_api/file_api.cpp
)
set(INC_DIRS ${INC_DIRS} ./file_api)

26
src/file_api/file_api.cpp Normal file
View File

@ -0,0 +1,26 @@
#include "file_api.hpp"
std::ifstream fileapi_read_file( std::filesystem::path filepath, std::ostream & info)
{
if( not std::filesystem::exists( filepath ) )
throw std::invalid_argument{ "File: " + filepath.string() + " does not exist." };
if( not std::filesystem::is_regular_file( filepath ) )
throw std::invalid_argument{"File: " + filepath.string() + " is not a file."};
info << "File: " + filepath.string() + " is valid." << std::endl;
return { filepath };
}
std::string fileapi_read_full_stream( std::ifstream _is )
{
std::string file_;
while( _is ){
std::string file_str;
_is >> file_str;
file_ += file_str;
}
return file_;
}

10
src/file_api/file_api.hpp Normal file
View File

@ -0,0 +1,10 @@
#pragma once
#include <iostream>
#include <fstream>
#include <filesystem>
#include <string>
std::ifstream fileapi_read_file( std::filesystem::path filepath, std::ostream & info = std::cout );
std::string fileapi_read_full_stream( std::ifstream _is );

View File

@ -4,6 +4,8 @@
#include <thread>
#include <iostream>
#include "program_config.hpp"
#define ECHO_MODULE_ID 0x00000040 /// Идентификатор тестового модуля
#define ECHO_SUBMOD_ID 0x00000140 /// Идентификатор тестового подмодуля
#define ECHO_INPUT_DATA_SIZE 8
@ -51,99 +53,40 @@ int main(int argc, char * argv[])
Echo_outCycData.data.data_f = 0.0f;
Echo_outCycData.data.data_i = 0;
AppSettings app_settings = {
.EthDevName = "enp6s1",
};
ProfinetSettings profinet_settings;
ProfinetDeviceSettings profinet_dev_settings;
ProfinetSettings settings = {
.ticks_us = 1000, /// Период таймера в мкс
.EthIface = "enp6s1", /// Имя интерфейса Ethernet к которому будет подключаться
};
/// Читаем настройки из файла
if (!programconf_getProfinetSettings("program_configure.json", profinet_settings))
{
return 0;
}
ProfinetDeviceSettings profinet_settings = {
.product_name = "P-Net Sample Application",
.station_name = "rt-labs-dev",
.im_0 = {
.vendor_id = 0x0493,
.hw_revision = 3,
.sw_revision = {
.prefix = 'V',
.functional_enhancement = 0,
.bug_fix = 2,
.internal_change = 0,
},
.revision_counter = 0,
.profile_id = 0x1234,
.profile_specific_type = 0x5678,
.supported = (PNET_SUPPORTED_IM1 | PNET_SUPPORTED_IM2 | PNET_SUPPORTED_IM3),
.order_id = "12345 Abcdefghijk",
.serial_number = "007",
},
.im_1 = {
.tag_function = "my function",
.tag_location = "my location"
},
.im_2 = {
.date = "2022-03-01 10:03"
},
.im_3 = {
.descriptor = "my descriptor"
},
.im_4 = {
.signature = ""
},
.device_id = 0x0002,
.oem_vendor_id = 0xcafe,
.oem_device_id = 0xee02,
.send_hello = true,
.min_device_interval = 32 //1ms ??
};
std::vector<std::shared_ptr<ProfinetModule>> Modules;
/// Читаем настройки из файла
if (!programconf_getProfinetDeviceSettings(profinet_settings.profinet_device_config, profinet_dev_settings, Modules))
{
return 0;
}
Profinet profinet;
/// Настройка Profinet: Инициализация pnet, добавление DAP слотов и подслотов и подключение к ним модулей и подмодулей
if (!profinet.Config(settings, profinet_settings))
/// Настройка Profinet: Инициализация pnet, добавление DAP слотов и подслотов и подключение к ним модулей и подмодулей,
if (!profinet.Config(profinet_settings, profinet_dev_settings, Modules))
{
return 0;
/**
* @brief Создание тестового модуля ECHO
* От контроллера принимает данные в составе 1 значения uint32_t и 1 значения float
* Умножает их на определенное число Gain и передает получившиеся значения обратно в контроллер.
*
*/
/// 1. Создаем тестовый модуль ECHO: module_id = 0x40 (64)
auto echo_module_ptr = make_shared<ProfinetModule>(ECHO_MODULE_ID, "Echo module");
}
/// 2. Создаем конфигурацию циклических данных подмодуля
pnet_data_cfg_t echo_data_cfg = { PNET_DIR_IO, ECHO_INPUT_DATA_SIZE, ECHO_OUTPUT_DATA_SIZE };
/// 3. Создаем тестовый подмодуль ECHO: submodule_id = 0x140 (320)
auto echo_submodule_ptr = make_shared<ProfinetSubmodule>(ECHO_SUBMOD_ID,
"Echo submodule",
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);
/**
* Циклические данные устройства передаются с определенным периодом, сообщения от контроллера при этом не требуются.
*/
/// Запуск потока Profinet
profinet.Start();
auto echo_mod = profinet.getModule(ECHO_MODULE_ID);
auto echo_submod = echo_mod->getSubmodulePtr(ECHO_SUBMOD_ID);
echo_submod->putDataToPlc(Echo_inpCycData.mem);
uint32_t ix = 0;
while(1)
{

24188
src/nlohmann_json/json.hpp Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1 @@
set(INC_DIRS ${INC_DIRS} ./nlohmann_json)

View File

@ -5,6 +5,7 @@ set(SRC_FILES ${SRC_FILES}
./profinet/profinet_subslot.cpp
./profinet/profinet_module.cpp
./profinet/profinet_submodule.cpp
./profinet/profinet_parameter.cpp
./profinet/profinet_cb_alarm_ack_cnf.cpp
./profinet/profinet_cb_alarm_cnf.cpp
./profinet/profinet_cb_alarm_ind.cpp

View File

@ -166,7 +166,9 @@ Profinet::Profinet() : m_pnet_data({nullptr, 0, UINT32_MAX, 0}),
}
bool Profinet::Config(ProfinetSettings& Settings, ProfinetDeviceSettings& DevSettings)
bool Profinet::Config(ProfinetSettings& Settings,
ProfinetDeviceSettings& DevSettings,
std::vector<std::shared_ptr<ProfinetModule>>& Modules)
{
bool ret = true;
pnet_cfg_t pnet_cfg = {0};
@ -184,7 +186,7 @@ bool Profinet::Config(ProfinetSettings& Settings, ProfinetDeviceSettings& DevSet
///В колбэки pnet передается этот объект
pnet_cfg.cb_arg = static_cast<void*>(this);
utils_pnet_cfg_init_netifs(Settings.EthIface.c_str(),
utils_pnet_cfg_init_netifs(Settings.eth_dev_name.c_str(),
&netif_name_list,
&number_of_ports,
&netif_cfg,
@ -210,13 +212,13 @@ bool Profinet::Config(ProfinetSettings& Settings, ProfinetDeviceSettings& DevSet
///
pnet_data_cfg_t dap_data_cfg = { PNET_DIR_NO_IO, 0, 0 };
/// Создаем обязательный модуль DAP
auto dap_module = make_shared<ProfinetModule>(PNET_MOD_DAP_IDENT, "DAP 1");
auto dap_module = ProfinetModule::Create(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, nullptr, nullptr);
auto dap_submodule_indent_1 = ProfinetSubmodule::Create(PNET_SUBMOD_DAP_IDENT, "DAP Identity 1", dap_data_cfg);
/// Создаем обязательный подмодуль 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, nullptr, nullptr);
auto dap_submodule_iface_1_indent_1 = ProfinetSubmodule::Create(PNET_SUBMOD_DAP_INTERFACE_1_IDENT, "DAP IFACE1 IDENT", dap_data_cfg);
/// Создаем обязательный подмодуль 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, nullptr, nullptr);
auto dap_subslot_iface_1_port1_ident = ProfinetSubmodule::Create(PNET_SUBMOD_DAP_INTERFACE_1_PORT_1_IDENT, "DAP Port 1", dap_data_cfg);
/// Добавляем подмодули к модулю
ret&= dap_module->addSubmodule(dap_submodule_indent_1);
ret&= dap_module->addSubmodule(dap_submodule_iface_1_indent_1);
@ -231,7 +233,23 @@ bool Profinet::Config(ProfinetSettings& Settings, ProfinetDeviceSettings& DevSet
ret&= addSubslotAndPlugSubmodule(PNET_SLOT_DAP_IDENT, PNET_SUBSLOT_DAP_INTERFACE_1_IDENT, dap_submodule_iface_1_indent_1);
/// 3. Подмодуль "DAP Port 1"
ret&= addSubslotAndPlugSubmodule(PNET_SLOT_DAP_IDENT, PNET_SUBSLOT_DAP_INTERFACE_1_PORT_1_IDENT, dap_subslot_iface_1_port1_ident);
///Копируем конфигурацию модулей
for (auto module_ptr : Modules)
{
if (module_ptr == nullptr)
{
ret = false;
break;
}
if (!addModule(module_ptr))
{
ret = false;
break;
}
}
return ret;
}
@ -480,20 +498,13 @@ void Profinet::initDataAndIoxs()
}
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);
}
submodule_ptr->inputSetData( m_pnet_data.pnet_ptr,
m_pnet_data.api,
slot_ptr->m_slot_nbr,
subslot_ptr->m_subslot_nbr,
indata_iops);
/*
* If a submodule is still plugged but not used in current AR,
@ -501,10 +512,6 @@ void Profinet::initDataAndIoxs()
* This is not a problem.
* Log message below will only be printed for active submodules.
*/
if (ret == 0)
{
/// Успешно
}
}
/**
@ -584,42 +591,29 @@ void Profinet::cyclicIoData()
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)
{
/// Что-то не так с данными от контроллера
}
submodule_ptr->outputGetData( m_pnet_data.pnet_ptr,
m_pnet_data.api,
slot_ptr->m_slot_nbr,
subslot_ptr->m_subslot_nbr);
}
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 (
/**
* @brief Отправляем данные подмодуля в ПЛК
*
*/
submodule_ptr->inputSetData(
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);
indata_iops
);
(void)pnet_input_get_iocs (
m_pnet_data.pnet_ptr,
m_pnet_data.api,
@ -754,7 +748,7 @@ int Profinet::callbackDcontrolInd ( uint32_t arep,
pnet_control_command_t control_command,
pnet_result_t * p_result)
{
LOG("callbackDcontrolInd");
LOG("callbackDcontrolInd: command= " + to_string(control_command));
/**
* @brief
* Можно оставить пустым
@ -805,7 +799,12 @@ int Profinet::callbackReadInd ( uint32_t arep,
}
///3. Передаем данные параметра
*pp_read_data = param_ptr->data_ptr;
/**
* По идее тут нужно утановить мьютекс lock, а unlock сделать потом, когда данные будет причитаны стеком pnet
* Проблема в том, что нет ни одного события которое бы сообщало об окончании процесса чтения данных стеком pnet.
* Нужно поисследовать.
*/
*pp_read_data = param_ptr->getDataPointer();
*p_read_length = param_ptr->length;
return 0;
@ -845,14 +844,14 @@ int Profinet::callbackWriteInd ( uint32_t arep,
}
///3. Копируем данные
std::memcpy(param_ptr->data_ptr, p_write_data, param_ptr->length);
param_ptr->writeParameter(p_write_data);
return 0;
}
int Profinet::callbackExpModuleInd ( uint16_t slot, uint32_t module_ident)
{
LOG("callbackExpModuleInd: plug module " + std::to_string(slot) + " -> slot " + std::to_string(module_ident));
LOG("callbackExpModuleInd: plug module id=" + std::to_string(module_ident) + " -> slot num=" + std::to_string(slot));
bool ret = addSlotAndPlugModule(slot, module_ident);

View File

@ -10,6 +10,7 @@
#include <atomic>
#include <thread>
#include <vector>
#include "../log/log.hpp"
#include "../flags/flags.hpp"
@ -19,7 +20,9 @@ class Profinet {
public:
Profinet();
bool Config(ProfinetSettings& Settings, ProfinetDeviceSettings& DevSettings);
bool Config(ProfinetSettings& Settings,
ProfinetDeviceSettings& DevSettings,
std::vector<std::shared_ptr<ProfinetModule>>& Modules);
/**
* @brief Добавляет ранее созданный слот slot_ptr

View File

@ -10,8 +10,12 @@
class ProfinetModule {
public:
ProfinetModule(uint32_t module_id, std::string module_name);
static std::shared_ptr<ProfinetModule> Create(uint32_t module_id, std::string module_name) {
return std::shared_ptr<ProfinetModule>(new ProfinetModule(module_id, module_name));
}
ProfinetModule(uint32_t module_id, std::string module_name);
bool addSubmodule(std::shared_ptr<ProfinetSubmodule>& submodule_ptr);
std::shared_ptr<ProfinetSubmodule> getSubmodulePtr(uint32_t submodule_id);

View File

@ -0,0 +1,50 @@
#include "profinet_parameter.hpp"
#include <cstring>
ProfinetParameter::ProfinetParameter(uint32_t Index, std::string Name, uint16_t Length) :
index(Index),
name(Name),
length(Length),
data_ptr_(new uint8_t[Length])
{
};
bool ProfinetParameter::readParameter(uint8_t * const data_ptr)
{
std::lock_guard<std::mutex> guard(data_mutex_); /// Синхронизация чтения и записи
if (data_ptr == nullptr)
{
return false;
}
memcpy(data_ptr, data_ptr_.get(), length);
return true;
}
bool ProfinetParameter::writeParameter(const uint8_t * data_ptr)
{
std::lock_guard<std::mutex> guard(data_mutex_); /// Синхронизация чтения и записи
if (data_ptr == nullptr)
{
return false;
}
memcpy(data_ptr_.get(), data_ptr, length);
return true;
}
uint8_t * ProfinetParameter::getDataPointer()
{
/**
* @brief Слабое утешение от ситуации когда приложение записывает данные в параметр, а контроллер хочет этот параметр
* прочитать и вызвался коллбэк profinet_cb_read. В этом случае будем тут висеть пока приложение не запишет параметр.
*
*/
std::lock_guard<std::mutex> guard(data_mutex_);
return data_ptr_.get();
}

View File

@ -2,18 +2,54 @@
#include <cstdint>
#include <string>
#include <memory.h>
#include <memory>
#include <mutex>
struct ProfinetParameter
class ProfinetParameter
{
ProfinetParameter(uint32_t Index, std::string Name, uint8_t * pData, uint16_t Length) :
index(Index),
name(Name),
data_ptr(pData),
length(Length) {};
public:
static std::shared_ptr<ProfinetParameter> Create(uint32_t Index, std::string Name, uint16_t Length)
{
return std::shared_ptr<ProfinetParameter>(new ProfinetParameter(Index, Name, Length));
}
ProfinetParameter(uint32_t Index, std::string Name, uint16_t Length);
/**
* @brief Защищенная запись данных параметра
*
* @param data_ptr
* @return true
* @return false
*/
bool writeParameter(const uint8_t * data_ptr);
/**
* @brief Защищенное чтение данных параметра
*
* @param data_ptr
* @return true
* @return false
*/
bool readParameter(uint8_t * const data_ptr);
/**
* @brief Стек pnet сам копирует данные параметра внутрь себя.
* Для этого ему в колбэке нужно предоставить указатель на данные параметра и длину данных.
* Подразумевается, что эта функция будет использоваться только внутри колбэка стека pnet.
* см. profinet_cb_read.cpp
* Не использовать эту функцию в приложении!!!
* @return uint8_t*
*/
uint8_t * getDataPointer();
const std::string name; /// Имя параметра
const uint32_t index; /// Идентфикатор параметра
uint8_t * const data_ptr; /// указатель на данные параметра
const uint16_t length; /// длина параметра в байтах
const std::string name; /// Имя параметра
const uint32_t index; /// Идентфикатор параметра
const uint16_t length; /// длина параметра в байтах
private:
std::shared_ptr<uint8_t[]> data_ptr_; /// указатель на данные параметра
mutable std::mutex data_mutex_; /// Доступ к данным
};

View File

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

View File

@ -1,19 +1,27 @@
#include "profinet_submodule.hpp"
#include <cstring>
ProfinetSubmodule::ProfinetSubmodule(uint32_t submodule_id,
std::string submodule_name,
pnet_data_cfg_t& submodule_data_cfg,
void * const inp_data_ptr,
void * const out_data_ptr) :
pnet_data_cfg_t& submodule_data_cfg) :
m_id(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),
inp_data_ptr_(nullptr),
out_data_ptr_(nullptr),
m_indata_iocs(0),
m_outdata_iops(0)
{
/// Могут быть подмодули без данных, например DAP
if (m_data_cfg.insize > 0)
{
inp_data_ptr_ = std::shared_ptr<uint8_t[]>(new uint8_t[m_data_cfg.insize]);
}
if (m_data_cfg.outsize > 0)
{
out_data_ptr_ = std::shared_ptr<uint8_t[]>(new uint8_t[m_data_cfg.outsize]);
}
}
bool ProfinetSubmodule::addParameter(std::shared_ptr<ProfinetParameter>& param)
@ -31,4 +39,98 @@ std::shared_ptr<ProfinetParameter> ProfinetSubmodule::getParameterPtr(uint32_t i
}
return m_params[index];
}
}
bool ProfinetSubmodule::inputSetData( pnet_t * pnet_ptr,
uint32_t api,
uint16_t slot_nbr,
uint16_t subslot_nbr,
uint8_t iops)
{
std::lock_guard<std::mutex> guard(inp_data_mutex_);
if ((pnet_ptr == nullptr) || (inp_data_ptr_ == nullptr))
{
return false;
}
/// Копируем данные для передачи в ПЛК
pnet_input_set_data_and_iops( pnet_ptr,
api,
slot_nbr,
subslot_nbr,
inp_data_ptr_.get(),
m_data_cfg.insize,
iops);
return true;
}
bool ProfinetSubmodule::outputGetData( pnet_t * pnet_ptr,
uint32_t api,
uint16_t slot_nbr,
uint16_t subslot_nbr )
{
/// Существуют подмодули без данных, например DAP
if ((pnet_ptr == nullptr) || (out_data_ptr_ == nullptr))
{
return false;
}
std::lock_guard<std::mutex> guard(out_data_mutex_);
uint16_t outdata_length = m_data_cfg.outsize;
uint8_t outdata_iops;
bool updated;
/// Копируем данные от полученные от контроллера
pnet_output_get_data_and_iops (pnet_ptr,
api,
slot_nbr,
subslot_nbr,
&updated,
out_data_ptr_.get(),
&outdata_length,
&outdata_iops);
m_outdata_iops = outdata_iops;
data_updated_ = updated;
if (m_data_cfg.outsize != outdata_length)
{
/// Неправильная длина данных
}
else if (outdata_iops == PNET_IOXS_BAD)
{
/// Что-то не так с данными от контроллера
}
return true;
}
bool ProfinetSubmodule::getDataFromPlc(uint8_t * data_ptr)
{
std::lock_guard<std::mutex> guard(out_data_mutex_);
if ((data_ptr == nullptr) || (out_data_ptr_ == nullptr))
{
return false;
}
memcpy(data_ptr, out_data_ptr_.get(), m_data_cfg.outsize);
return true;
}
bool ProfinetSubmodule::putDataToPlc(uint8_t * data_ptr)
{
std::lock_guard<std::mutex> guard(inp_data_mutex_);
if ((data_ptr == nullptr) || (inp_data_ptr_ == nullptr))
{
return false;
}
memcpy(inp_data_ptr_.get(), data_ptr, m_data_cfg.insize);
return true;
}

View File

@ -4,6 +4,8 @@
#include <string>
#include <map>
#include <memory>
#include <mutex>
#include "profinet_parameter.hpp"
#include "../../libs/include/pnet_api.h"
@ -11,17 +13,39 @@
class ProfinetSubmodule {
public:
static std::shared_ptr<ProfinetSubmodule> Create(uint32_t submodule_id,
std::string submodule_name,
pnet_data_cfg_t& submodule_data_cfg)
{
return std::shared_ptr<ProfinetSubmodule>(new ProfinetSubmodule(submodule_id,
submodule_name,
submodule_data_cfg));
}
ProfinetSubmodule(uint32_t submodule_id,
std::string submodule_name,
pnet_data_cfg_t& submodule_data_cfg,
void * const inp_data_ptr,
void * const out_data_ptr);
pnet_data_cfg_t& submodule_data_cfg);
bool addParameter(std::shared_ptr<ProfinetParameter>& param);
std::shared_ptr<ProfinetParameter> getParameterPtr(uint32_t index);
bool inputSetData( pnet_t * pnet_ptr,
uint32_t api,
uint16_t slot_nbr,
uint16_t subslot_nbr,
uint8_t iops);
bool outputGetData(pnet_t * pnet_ptr,
uint32_t api,
uint16_t slot_nbr,
uint16_t subslot_nbr);
bool getDataFromPlc(uint8_t * data_ptr);
bool putDataToPlc (uint8_t * data_ptr);
bool isDataUpdated() { return data_updated_; };
public:
const uint32_t m_id; /// Идентификатор подмодуля
@ -33,11 +57,17 @@ public:
/// 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)
private:
/// Набор параметров подмодуля
std::map<uint32_t, std::shared_ptr<ProfinetParameter>> m_params;
std::shared_ptr<uint8_t[]> inp_data_ptr_; /// Входные циклические данные (DEV->PLC)
std::mutex inp_data_mutex_;
std::shared_ptr<uint8_t[]> out_data_ptr_; /// Выходные циклические данные (PLC->DEV)
std::mutex out_data_mutex_;
bool data_updated_;
};

View File

@ -0,0 +1,74 @@
{
"ProfinetDeviceSettings":
{
"product_name": "P-Net Sample Application",
"station_name": "rt-labs-dev",
"im_0":
{
"vendor_id": "0x0493",
"hw_revision": 3,
"sw_revision":
{
"prefix": "V",
"functional_enhancement": 0,
"bug_fix": 2,
"internal_change": 0
},
"revision_counter": 0,
"profile_id": "0x1234",
"profile_specific_type": "0x5678",
"supported": "0x000E",
"order_id": "12345 Abcdefghijk",
"serial_number": "007"
},
"im_1":
{
"tag_function": "my function",
"tag_location": "my location"
},
"im_2":
{
"date": "2022-03-01 10:03"
},
"im_3":
{
"descriptor": "my descriptor"
},
"im_4":
{
"signature": ""
},
"device_id": "0x0002",
"oem_vendor_id": "0xcafe",
"oem_device_id": "0xee02",
"send_hello": true,
"min_device_interval": 32
},
"ProfinetDeviceModulesSettings":
{
"modules": [
{
"id": "0x00000040",
"name": "Echo module",
"submodules": [
{
"id": "0x00000140",
"name": "Echo submodule",
"cyc_data_dir": "PNET_DIR_IO",
"cyc_inp_data_size": 8,
"cyc_out_data_size": 8,
"parameters": [
{
"index": 125,
"name": "Echo gain setting",
"data_size": 4
}
]
}
]
}
]
}
}

View File

@ -0,0 +1,9 @@
{
"ProfinetSettings":
{
"ticks_us": 1000,
"cyclic_ms": 100,
"eth_dev_name": "enp6s1",
"profinet_device_config": "profinet_device_configure.json"
}
}