dev(UML-981): Реализована конфигурация из файла и совмкестный доступ к данным модуля из разных потоков.
This commit is contained in:
parent
999208fc87
commit
5e9e6c3133
@ -13,14 +13,17 @@ cmake_minimum_required (VERSION 3.14)
|
|||||||
|
|
||||||
project (PN_DEV_TEST VERSION 0.0.1)
|
project (PN_DEV_TEST VERSION 0.0.1)
|
||||||
|
|
||||||
|
set(CMAKE_CXX_STANDARD 20)
|
||||||
set(LIBS_INSTALL_PATH ../libs)
|
set(LIBS_INSTALL_PATH ../libs)
|
||||||
set(PNET_PATH ../profinet_stack/p-net)
|
set(PNET_PATH ../profinet_stack/p-net)
|
||||||
|
|
||||||
include(./utils/utils.cmake)
|
include(./utils/utils.cmake)
|
||||||
include(./profinet/profinet.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
|
add_executable(pn_dev_test ./main.cpp ${SRC_FILES})
|
||||||
./main.cpp ${SRC_FILES})
|
|
||||||
|
|
||||||
target_include_directories(pn_dev_test PRIVATE
|
target_include_directories(pn_dev_test PRIVATE
|
||||||
./
|
./
|
||||||
|
|||||||
5
src/configuration/configuration.cmake
Normal file
5
src/configuration/configuration.cmake
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
set(SRC_FILES ${SRC_FILES}
|
||||||
|
./configuration/program_config.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
set(INC_DIRS ${INC_DIRS} ./configuration)
|
||||||
241
src/configuration/program_config.cpp
Normal file
241
src/configuration/program_config.cpp
Normal 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;
|
||||||
|
}
|
||||||
|
|
||||||
13
src/configuration/program_config.hpp
Normal file
13
src/configuration/program_config.hpp
Normal 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);
|
||||||
|
|
||||||
5
src/file_api/file_api.cmake
Normal file
5
src/file_api/file_api.cmake
Normal 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
26
src/file_api/file_api.cpp
Normal 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
10
src/file_api/file_api.hpp
Normal 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 );
|
||||||
105
src/main.cpp
105
src/main.cpp
@ -4,6 +4,8 @@
|
|||||||
#include <thread>
|
#include <thread>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
|
#include "program_config.hpp"
|
||||||
|
|
||||||
#define ECHO_MODULE_ID 0x00000040 /// Идентификатор тестового модуля
|
#define ECHO_MODULE_ID 0x00000040 /// Идентификатор тестового модуля
|
||||||
#define ECHO_SUBMOD_ID 0x00000140 /// Идентификатор тестового подмодуля
|
#define ECHO_SUBMOD_ID 0x00000140 /// Идентификатор тестового подмодуля
|
||||||
#define ECHO_INPUT_DATA_SIZE 8
|
#define ECHO_INPUT_DATA_SIZE 8
|
||||||
@ -51,92 +53,29 @@ int main(int argc, char * argv[])
|
|||||||
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;
|
||||||
|
|
||||||
AppSettings app_settings = {
|
ProfinetSettings profinet_settings;
|
||||||
.EthDevName = "enp6s1",
|
ProfinetDeviceSettings profinet_dev_settings;
|
||||||
};
|
|
||||||
|
|
||||||
ProfinetSettings settings = {
|
/// Читаем настройки из файла
|
||||||
.ticks_us = 1000, /// Период таймера в мкс
|
if (!programconf_getProfinetSettings("program_configure.json", profinet_settings))
|
||||||
.EthIface = "enp6s1", /// Имя интерфейса Ethernet к которому будет подключаться
|
{
|
||||||
};
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
ProfinetDeviceSettings profinet_settings = {
|
std::vector<std::shared_ptr<ProfinetModule>> Modules;
|
||||||
.product_name = "P-Net Sample Application",
|
|
||||||
.station_name = "rt-labs-dev",
|
/// Читаем настройки из файла
|
||||||
.im_0 = {
|
if (!programconf_getProfinetDeviceSettings(profinet_settings.profinet_device_config, profinet_dev_settings, Modules))
|
||||||
.vendor_id = 0x0493,
|
{
|
||||||
.hw_revision = 3,
|
return 0;
|
||||||
.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 ??
|
|
||||||
};
|
|
||||||
|
|
||||||
Profinet profinet;
|
Profinet profinet;
|
||||||
/// Настройка Profinet: Инициализация pnet, добавление DAP слотов и подслотов и подключение к ним модулей и подмодулей
|
/// Настройка Profinet: Инициализация pnet, добавление DAP слотов и подслотов и подключение к ним модулей и подмодулей,
|
||||||
if (!profinet.Config(settings, profinet_settings))
|
if (!profinet.Config(profinet_settings, profinet_dev_settings, Modules))
|
||||||
|
{
|
||||||
return 0;
|
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);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Циклические данные устройства передаются с определенным периодом, сообщения от контроллера при этом не требуются.
|
* Циклические данные устройства передаются с определенным периодом, сообщения от контроллера при этом не требуются.
|
||||||
@ -144,6 +83,10 @@ int main(int argc, char * argv[])
|
|||||||
/// Запуск потока Profinet
|
/// Запуск потока Profinet
|
||||||
profinet.Start();
|
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;
|
uint32_t ix = 0;
|
||||||
while(1)
|
while(1)
|
||||||
{
|
{
|
||||||
|
|||||||
24188
src/nlohmann_json/json.hpp
Normal file
24188
src/nlohmann_json/json.hpp
Normal file
File diff suppressed because it is too large
Load Diff
1
src/nlohmann_json/nlohmann_json.cmake
Normal file
1
src/nlohmann_json/nlohmann_json.cmake
Normal file
@ -0,0 +1 @@
|
|||||||
|
set(INC_DIRS ${INC_DIRS} ./nlohmann_json)
|
||||||
@ -5,6 +5,7 @@ set(SRC_FILES ${SRC_FILES}
|
|||||||
./profinet/profinet_subslot.cpp
|
./profinet/profinet_subslot.cpp
|
||||||
./profinet/profinet_module.cpp
|
./profinet/profinet_module.cpp
|
||||||
./profinet/profinet_submodule.cpp
|
./profinet/profinet_submodule.cpp
|
||||||
|
./profinet/profinet_parameter.cpp
|
||||||
./profinet/profinet_cb_alarm_ack_cnf.cpp
|
./profinet/profinet_cb_alarm_ack_cnf.cpp
|
||||||
./profinet/profinet_cb_alarm_cnf.cpp
|
./profinet/profinet_cb_alarm_cnf.cpp
|
||||||
./profinet/profinet_cb_alarm_ind.cpp
|
./profinet/profinet_cb_alarm_ind.cpp
|
||||||
|
|||||||
@ -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;
|
bool ret = true;
|
||||||
pnet_cfg_t pnet_cfg = {0};
|
pnet_cfg_t pnet_cfg = {0};
|
||||||
@ -184,7 +186,7 @@ bool Profinet::Config(ProfinetSettings& Settings, ProfinetDeviceSettings& DevSet
|
|||||||
///В колбэки pnet передается этот объект
|
///В колбэки pnet передается этот объект
|
||||||
pnet_cfg.cb_arg = static_cast<void*>(this);
|
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,
|
&netif_name_list,
|
||||||
&number_of_ports,
|
&number_of_ports,
|
||||||
&netif_cfg,
|
&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 };
|
pnet_data_cfg_t dap_data_cfg = { PNET_DIR_NO_IO, 0, 0 };
|
||||||
/// Создаем обязательный модуль DAP
|
/// Создаем обязательный модуль 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
|
/// Создаем обязательный подмодуль 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
|
/// Создаем обязательный подмодуль 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
|
/// Создаем обязательный подмодуль 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_indent_1);
|
||||||
ret&= dap_module->addSubmodule(dap_submodule_iface_1_indent_1);
|
ret&= dap_module->addSubmodule(dap_submodule_iface_1_indent_1);
|
||||||
@ -232,6 +234,22 @@ bool Profinet::Config(ProfinetSettings& Settings, ProfinetDeviceSettings& DevSet
|
|||||||
/// 3. Подмодуль "DAP Port 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);
|
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;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -480,20 +498,13 @@ void Profinet::initDataAndIoxs()
|
|||||||
}
|
}
|
||||||
else if (submodule_ptr->m_data_cfg.insize > 0)
|
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,
|
submodule_ptr->inputSetData( m_pnet_data.pnet_ptr,
|
||||||
slot_ptr->m_slot_nbr,
|
m_pnet_data.api,
|
||||||
subslot_ptr->m_subslot_nbr,
|
slot_ptr->m_slot_nbr,
|
||||||
(uint8_t*)submodule_ptr->m_inp_data_ptr, /// Указатель на данные
|
subslot_ptr->m_subslot_nbr,
|
||||||
submodule_ptr->m_data_cfg.insize, /// Размер данных
|
indata_iops);
|
||||||
indata_iops);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If a submodule is still plugged but not used in current AR,
|
* If a submodule is still plugged but not used in current AR,
|
||||||
@ -501,10 +512,6 @@ void Profinet::initDataAndIoxs()
|
|||||||
* This is not a problem.
|
* This is not a problem.
|
||||||
* Log message below will only be printed for active submodules.
|
* Log message below will only be printed for active submodules.
|
||||||
*/
|
*/
|
||||||
if (ret == 0)
|
|
||||||
{
|
|
||||||
/// Успешно
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -584,41 +591,28 @@ void Profinet::cyclicIoData()
|
|||||||
uint8_t outdata_iops;
|
uint8_t outdata_iops;
|
||||||
|
|
||||||
/// Копируем данные от контроллера в m_out_data_ptr
|
/// Копируем данные от контроллера в m_out_data_ptr
|
||||||
(void)pnet_output_get_data_and_iops (
|
submodule_ptr->outputGetData( m_pnet_data.pnet_ptr,
|
||||||
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,
|
|
||||||
&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)
|
if (submodule_ptr->m_data_cfg.insize > 0)
|
||||||
{
|
{
|
||||||
uint8_t indata_iocs;
|
uint8_t indata_iocs;
|
||||||
uint8_t indata_iops = PNET_IOXS_GOOD;
|
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.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,
|
||||||
(uint8_t*)submodule_ptr->m_inp_data_ptr,
|
indata_iops
|
||||||
submodule_ptr->m_data_cfg.insize,
|
);
|
||||||
indata_iops);
|
|
||||||
|
|
||||||
(void)pnet_input_get_iocs (
|
(void)pnet_input_get_iocs (
|
||||||
m_pnet_data.pnet_ptr,
|
m_pnet_data.pnet_ptr,
|
||||||
@ -754,7 +748,7 @@ int Profinet::callbackDcontrolInd ( uint32_t arep,
|
|||||||
pnet_control_command_t control_command,
|
pnet_control_command_t control_command,
|
||||||
pnet_result_t * p_result)
|
pnet_result_t * p_result)
|
||||||
{
|
{
|
||||||
LOG("callbackDcontrolInd");
|
LOG("callbackDcontrolInd: command= " + to_string(control_command));
|
||||||
/**
|
/**
|
||||||
* @brief
|
* @brief
|
||||||
* Можно оставить пустым
|
* Можно оставить пустым
|
||||||
@ -805,7 +799,12 @@ int Profinet::callbackReadInd ( uint32_t arep,
|
|||||||
}
|
}
|
||||||
|
|
||||||
///3. Передаем данные параметра
|
///3. Передаем данные параметра
|
||||||
*pp_read_data = param_ptr->data_ptr;
|
/**
|
||||||
|
* По идее тут нужно утановить мьютекс lock, а unlock сделать потом, когда данные будет причитаны стеком pnet
|
||||||
|
* Проблема в том, что нет ни одного события которое бы сообщало об окончании процесса чтения данных стеком pnet.
|
||||||
|
* Нужно поисследовать.
|
||||||
|
*/
|
||||||
|
*pp_read_data = param_ptr->getDataPointer();
|
||||||
*p_read_length = param_ptr->length;
|
*p_read_length = param_ptr->length;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@ -845,14 +844,14 @@ int Profinet::callbackWriteInd ( uint32_t arep,
|
|||||||
}
|
}
|
||||||
|
|
||||||
///3. Копируем данные
|
///3. Копируем данные
|
||||||
std::memcpy(param_ptr->data_ptr, p_write_data, param_ptr->length);
|
param_ptr->writeParameter(p_write_data);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Profinet::callbackExpModuleInd ( uint16_t slot, uint32_t module_ident)
|
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);
|
bool ret = addSlotAndPlugModule(slot, module_ident);
|
||||||
|
|
||||||
|
|||||||
@ -10,6 +10,7 @@
|
|||||||
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include "../log/log.hpp"
|
#include "../log/log.hpp"
|
||||||
#include "../flags/flags.hpp"
|
#include "../flags/flags.hpp"
|
||||||
@ -19,7 +20,9 @@ class Profinet {
|
|||||||
public:
|
public:
|
||||||
Profinet();
|
Profinet();
|
||||||
|
|
||||||
bool Config(ProfinetSettings& Settings, ProfinetDeviceSettings& DevSettings);
|
bool Config(ProfinetSettings& Settings,
|
||||||
|
ProfinetDeviceSettings& DevSettings,
|
||||||
|
std::vector<std::shared_ptr<ProfinetModule>>& Modules);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Добавляет ранее созданный слот slot_ptr
|
* @brief Добавляет ранее созданный слот slot_ptr
|
||||||
|
|||||||
@ -10,6 +10,10 @@
|
|||||||
|
|
||||||
class ProfinetModule {
|
class ProfinetModule {
|
||||||
public:
|
public:
|
||||||
|
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);
|
ProfinetModule(uint32_t module_id, std::string module_name);
|
||||||
|
|
||||||
bool addSubmodule(std::shared_ptr<ProfinetSubmodule>& submodule_ptr);
|
bool addSubmodule(std::shared_ptr<ProfinetSubmodule>& submodule_ptr);
|
||||||
|
|||||||
50
src/profinet/profinet_parameter.cpp
Normal file
50
src/profinet/profinet_parameter.cpp
Normal 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();
|
||||||
|
}
|
||||||
@ -2,18 +2,54 @@
|
|||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <string>
|
#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) :
|
public:
|
||||||
index(Index),
|
static std::shared_ptr<ProfinetParameter> Create(uint32_t Index, std::string Name, uint16_t Length)
|
||||||
name(Name),
|
{
|
||||||
data_ptr(pData),
|
return std::shared_ptr<ProfinetParameter>(new ProfinetParameter(Index, Name, Length));
|
||||||
length(Length) {};
|
}
|
||||||
|
|
||||||
const std::string name; /// Имя параметра
|
ProfinetParameter(uint32_t Index, std::string Name, uint16_t Length);
|
||||||
const uint32_t index; /// Идентфикатор параметра
|
|
||||||
uint8_t * const data_ptr; /// указатель на данные параметра
|
/**
|
||||||
const 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; /// Идентфикатор параметра
|
||||||
|
const uint16_t length; /// длина параметра в байтах
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::shared_ptr<uint8_t[]> data_ptr_; /// указатель на данные параметра
|
||||||
|
mutable std::mutex data_mutex_; /// Доступ к данным
|
||||||
};
|
};
|
||||||
|
|||||||
@ -82,7 +82,8 @@ struct ProfinetDeviceSettings {
|
|||||||
|
|
||||||
/// Настройки не зависящие от описания данных Profinet
|
/// Настройки не зависящие от описания данных Profinet
|
||||||
struct ProfinetSettings {
|
struct ProfinetSettings {
|
||||||
uint32_t ticks_us = 1000; /// Устанавливает время в мкс между вызовами pnet_handle_periodic()
|
uint32_t ticks_us = 1000; /// Устанавливает время в мкс между вызовами pnet_handle_periodic()
|
||||||
uint32_t cyclic_ms = 100; /// Устанавливает период в мс для циклического обмена данными
|
uint32_t cyclic_ms = 100; /// Устанавливает период в мс для циклического обмена данными
|
||||||
std::string EthIface = ""; /// Имя интерфейса ethernet
|
std::string eth_dev_name = ""; /// Имя интерфейса ethernet
|
||||||
|
std::string profinet_device_config = ""; ///Файл конфигурации profinet device
|
||||||
};
|
};
|
||||||
@ -1,19 +1,27 @@
|
|||||||
#include "profinet_submodule.hpp"
|
#include "profinet_submodule.hpp"
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
ProfinetSubmodule::ProfinetSubmodule(uint32_t submodule_id,
|
ProfinetSubmodule::ProfinetSubmodule(uint32_t submodule_id,
|
||||||
std::string submodule_name,
|
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_id(submodule_id),
|
||||||
m_name(submodule_name),
|
m_name(submodule_name),
|
||||||
m_data_cfg(submodule_data_cfg),
|
m_data_cfg(submodule_data_cfg),
|
||||||
m_inp_data_ptr(inp_data_ptr),
|
inp_data_ptr_(nullptr),
|
||||||
m_out_data_ptr(out_data_ptr),
|
out_data_ptr_(nullptr),
|
||||||
m_indata_iocs(0),
|
m_indata_iocs(0),
|
||||||
m_outdata_iops(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)
|
bool ProfinetSubmodule::addParameter(std::shared_ptr<ProfinetParameter>& param)
|
||||||
@ -32,3 +40,97 @@ std::shared_ptr<ProfinetParameter> ProfinetSubmodule::getParameterPtr(uint32_t i
|
|||||||
|
|
||||||
return m_params[index];
|
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;
|
||||||
|
}
|
||||||
|
|||||||
@ -4,6 +4,8 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
#include "profinet_parameter.hpp"
|
#include "profinet_parameter.hpp"
|
||||||
|
|
||||||
#include "../../libs/include/pnet_api.h"
|
#include "../../libs/include/pnet_api.h"
|
||||||
@ -11,17 +13,39 @@
|
|||||||
|
|
||||||
class ProfinetSubmodule {
|
class ProfinetSubmodule {
|
||||||
public:
|
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,
|
ProfinetSubmodule(uint32_t submodule_id,
|
||||||
std::string submodule_name,
|
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);
|
bool addParameter(std::shared_ptr<ProfinetParameter>& param);
|
||||||
|
|
||||||
std::shared_ptr<ProfinetParameter> getParameterPtr(uint32_t index);
|
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:
|
public:
|
||||||
const uint32_t m_id; /// Идентификатор подмодуля
|
const uint32_t m_id; /// Идентификатор подмодуля
|
||||||
@ -34,10 +58,16 @@ public:
|
|||||||
/// iops = I/O provider status data (формат данных из pnet_ioxs_values_t)
|
/// iops = I/O provider status data (формат данных из pnet_ioxs_values_t)
|
||||||
uint8_t m_outdata_iops;
|
uint8_t m_outdata_iops;
|
||||||
|
|
||||||
void * const m_inp_data_ptr; /// Входные циклические данные (DEV->PLC)
|
|
||||||
void * const m_out_data_ptr; /// Выходные циклические данные (PLC->DEV)
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/// Набор параметров подмодуля
|
/// Набор параметров подмодуля
|
||||||
std::map<uint32_t, std::shared_ptr<ProfinetParameter>> m_params;
|
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_;
|
||||||
};
|
};
|
||||||
74
src/profinet_device_configure.json
Normal file
74
src/profinet_device_configure.json
Normal 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
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
9
src/program_configure.json
Normal file
9
src/program_configure.json
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"ProfinetSettings":
|
||||||
|
{
|
||||||
|
"ticks_us": 1000,
|
||||||
|
"cyclic_ms": 100,
|
||||||
|
"eth_dev_name": "enp6s1",
|
||||||
|
"profinet_device_config": "profinet_device_configure.json"
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user