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)
|
||||
|
||||
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)
|
||||
|
||||
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 );
|
||||
107
src/main.cpp
107
src/main.cpp
@ -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
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_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
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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);
|
||||
|
||||
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 <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_; /// Доступ к данным
|
||||
};
|
||||
|
||||
@ -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
|
||||
};
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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_;
|
||||
};
|
||||
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