This commit is contained in:
svad05 2022-07-29 10:20:49 +03:00
commit b68e6e2a86
67 changed files with 28065 additions and 0 deletions

4
.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
# игнорирование папок build
/src/build
/profinet_test/sample_app/build
*.bin

37
debug_support/gdb_update.sh Executable file
View File

@ -0,0 +1,37 @@
#!/usr/bin/env sh
# Motivation: build gdb 12.1 from source because of this issue: https://github.com/Gallopsled/pwntools/issues/1783
# Optional: remove existing gdb
echo ">>>> remove existing gdb:"
echo ">>>> sudo apt remove gdb gdbserver"
#sudo apt remove gdb gdbserver
# Get latest source from https://ftp.gnu.org/gnu/gdb/
echo ">>>> Get latest source from https://ftp.gnu.org/gnu/gdb/:"
echo ">>>> wget https://ftp.gnu.org/gnu/gdb/gdb-12.1.tar.xz"
wget https://ftp.gnu.org/gnu/gdb/gdb-12.1.tar.xz
echo ">>>> tar xf gdb-12.1.tar.xz"
tar xf gdb-12.1.tar.xz
# Install necessary build tools
echo ">>>> Install necessary build tools:"
echo ">>>> sudo apt install -y build-essential texinfo bison flex"
sudo apt install -y build-essential texinfo bison flex python3-dev libgmp-dev
echo ">>>> cd gdb-12.1"
cd gdb-12.1
echo ">>>> Configure with python3 support"
echo ">>>> ./configure --with-python=/usr/bin/python3"
# Configure with python3 support
./configure --with-python=/usr/bin/python3
# Build (this takes a while)
echo ">>>> Build (this takes a while)"
echo ">>>> make -j8"
make -j8
# Install binary
echo ">>>> Install binary:"
echo ">>>> sudo make install"
sudo make install

View File

@ -13,3 +13,8 @@ sudo chmod -x debug_support/gdb
-i - eth-интерфейс к которому подключен Profinet мастер
-v - уровень логов, чтобы был Debug нужно указать 4 раза
-p - папка для хранения настроечных файлов
Если версия Ubuntu 22.04, то там есть баг с gdb - он падает при переключении на другой поток в отлажеваемом ПО
Для исправления нужно либо установить Ubuntu 22.10, либо собрать gdb 12.1.
Для сборки запустить скрипт gdb_update.sh

40
src/CMakeLists.txt Normal file
View File

@ -0,0 +1,40 @@
#********************************************************************
# _ _ _
# _ __ | |_ _ | | __ _ | |__ ___
# | '__|| __|(_)| | / _` || '_ \ / __|
# | | | |_ _ | || (_| || |_) |\__ \
# |_| \__|(_)|_| \__,_||_.__/ |___/
#
# http://www.rt-labs.com
# Copyright 2017 rt-labs AB, Sweden.
# See LICENSE file in the project root for full license information.
#*******************************************************************/
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})
target_include_directories(pn_dev_test PRIVATE
./
${INC_DIRS}
${LIBS_INSTALL_PATH}/include
${LIBS_INSTALL_PATH}/include/sys
${PNET_PATH}/build/src
${PNET_PATH}/src
${PNET_PATH}/src/ports/linux
)
target_link_directories(pn_dev_test PUBLIC ${LIBS_INSTALL_PATH}/lib)
target_link_libraries (pn_dev_test PUBLIC profinet osal)

8
src/app_settings.hpp Normal file
View File

@ -0,0 +1,8 @@
#pragma once
#include <string>
struct AppSettings
{
std::string EthDevName; /// Имя адаптера
};

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 );

34
src/flags/flags.hpp Normal file
View File

@ -0,0 +1,34 @@
#pragma once
#include <mutex>
#include <cstdint>
class Flags {
public:
Flags() : flags_{0} {}
void set_flag(uint32_t mask) {
std::lock_guard<std::mutex> guard(mutex_);
flags_ |= mask;
}
uint32_t get_flag(uint32_t mask) {
std::lock_guard<std::mutex> guard(mutex_);
return flags_ & mask;
}
uint32_t get_flags() {
std::lock_guard<std::mutex> guard(mutex_);
return flags_;
}
void clear_flags(uint32_t mask)
{
std::lock_guard<std::mutex> guard(mutex_);
flags_ &= ~mask;
}
private:
uint32_t flags_;
mutable std::mutex mutex_;
};

17
src/log/log.hpp Normal file
View File

@ -0,0 +1,17 @@
#pragma once
#include <iostream>
#include <string>
class Log {
public:
Log(int log_level) : log_level_(log_level) {}
void put(std::string info) {
if (log_level_ > 0)
std::cout << "log: " << info << std::endl;
}
private:
const int log_level_;
};

99
src/main.cpp Normal file
View File

@ -0,0 +1,99 @@
#include "app_settings.hpp"
#include "../libs/include/pnet_api.h"
#include "profinet.hpp"
#include <thread>
#include <iostream>
#include "program_config.hpp"
#define ECHO_MODULE_ID 0x00000040 /// Идентификатор тестового модуля
#define ECHO_SUBMOD_ID 0x00000140 /// Идентификатор тестового подмодуля
#define ECHO_INPUT_DATA_SIZE 8
#define ECHO_OUTPUT_DATA_SIZE 8
#define ECHO_PARAMETER_GAIN_IDX 125 /// Индекс параметра Gain для подмодуля ECHO
using namespace std;
uint32_t Echo_Gain = 0;
struct EchoData {
float data_f;
uint32_t data_i;
};
union EchoDataMem
{
EchoData data;
uint8_t mem[sizeof(EchoData)];
};
EchoDataMem Echo_inpCycData;
EchoDataMem Echo_outCycData;
uint8_t be_data_inp[sizeof(EchoData)];
void endian_convert_32(uint8_t * p_data)
{
uint8_t tmp = p_data[1];
p_data[1] = p_data[2];
p_data[2] = tmp;
tmp = p_data[0];
p_data[0] = p_data[3];
p_data[3] = tmp;
}
int main(int argc, char * argv[])
{
Echo_inpCycData.data.data_f = 54321.9f; /// Преобразовать в big endian
Echo_inpCycData.data.data_i = 876; /// Преобразовать в big endian
endian_convert_32((uint8_t*)&Echo_inpCycData.data.data_f);
endian_convert_32((uint8_t*)&Echo_inpCycData.data.data_i);
Echo_outCycData.data.data_f = 0.0f;
Echo_outCycData.data.data_i = 0;
ProfinetSettings profinet_settings;
ProfinetDeviceSettings profinet_dev_settings;
/// Читаем настройки из файла
if (!programconf_getProfinetSettings("program_configure.json", profinet_settings))
{
return 0;
}
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(profinet_settings, profinet_dev_settings, Modules))
{
return 0;
}
/**
* Циклические данные устройства передаются с определенным периодом, сообщения от контроллера при этом не требуются.
*/
/// Запуск потока 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)
{
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
++ix;
std::cout << "tick: " << ix << endl;
}
return 0;
}

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

@ -0,0 +1,26 @@
set(SRC_FILES ${SRC_FILES}
./profinet/profinet.cpp
./profinet/profinet_slot.cpp
./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
./profinet/profinet_cb_ccontrol_ind.cpp
./profinet/profinet_cb_connect_ind.cpp
./profinet/profinet_cb_dcontrol_ind.cpp
./profinet/profinet_cb_exp_module_ind.cpp
./profinet/profinet_cb_exp_submodule_ind.cpp
./profinet/profinet_cb_new_data_status_ind.cpp
./profinet/profinet_cb_read_ind.cpp
./profinet/profinet_cb_release_ind.cpp
./profinet/profinet_cb_reset_ind.cpp
./profinet/profinet_cb_signal_led_ind.cpp
./profinet/profinet_cb_state_ind.cpp
./profinet/profinet_cb_write_ind.cpp
)
set(INC_DIRS ${INC_DIRS} ./profinet)

941
src/profinet/profinet.cpp Normal file
View File

@ -0,0 +1,941 @@
#include "profinet.hpp"
#include "profinet_cb_state_ind.hpp"
#include "profinet_cb_connect_ind.hpp"
#include "profinet_cb_release_ind.hpp"
#include "profinet_cb_dcontrol_ind.hpp"
#include "profinet_cb_ccontrol_ind.hpp"
#include "profinet_cb_read_ind.hpp"
#include "profinet_cb_write_ind.hpp"
#include "profinet_cb_exp_module_ind.hpp"
#include "profinet_cb_exp_submodule_ind.hpp"
#include "profinet_cb_new_data_status_ind.hpp"
#include "profinet_cb_alarm_ind.hpp"
#include "profinet_cb_alarm_cnf.hpp"
#include "profinet_cb_alarm_ack_cnf.hpp"
#include "profinet_cb_reset_ind.hpp"
#include "profinet_cb_signal_led_ind.hpp"
#include "utils.h"
#include <cstring>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <chrono>
#include <thread>
using namespace std;
#define GET_HIGH_BYTE(id) ((id >> 8) & 0xFF)
#define GET_LOW_BYTE(id) (id & 0xFF)
#define APP_MAIN_SLEEPTIME_US 5000 * 1000
#define APP_SNMP_THREAD_PRIORITY 1
#define APP_SNMP_THREAD_STACKSIZE 256 * 1024 /* bytes */
#define APP_ETH_THREAD_PRIORITY 10
#define APP_ETH_THREAD_STACKSIZE 4096 /* bytes */
#define APP_BG_WORKER_THREAD_PRIORITY 5
#define APP_BG_WORKER_THREAD_STACKSIZE 4096 /* bytes */
#define FLAGS_AREP_APP_READY 0x00000001
//#define RELEASE
#ifndef RELEASE
#define LOG(STR) log_.put(STR)
#else
#define LOG(STR)
#endif
static void pnetConfigure(pnet_cfg_t& cfg, ProfinetSettings& Settings, ProfinetDeviceSettings& DevSettings)
{
cfg.tick_us = Settings.ticks_us;
cfg.im_0_data.im_vendor_id_hi = GET_HIGH_BYTE(DevSettings.im_0.vendor_id);
cfg.im_0_data.im_vendor_id_lo = GET_LOW_BYTE (DevSettings.im_0.vendor_id);
cfg.im_0_data.im_hardware_revision = DevSettings.im_0.hw_revision;
cfg.im_0_data.im_sw_revision_prefix = DevSettings.im_0.sw_revision.prefix;
cfg.im_0_data.im_sw_revision_functional_enhancement = DevSettings.im_0.sw_revision.functional_enhancement;
cfg.im_0_data.im_sw_revision_bug_fix = DevSettings.im_0.sw_revision.bug_fix;
cfg.im_0_data.im_sw_revision_internal_change = DevSettings.im_0.sw_revision.internal_change;
cfg.im_0_data.im_revision_counter = DevSettings.im_0.revision_counter;
cfg.im_0_data.im_profile_id = DevSettings.im_0.profile_id;
cfg.im_0_data.im_profile_specific_type = DevSettings.im_0.profile_specific_type;
cfg.im_0_data.im_version_major = 1;
cfg.im_0_data.im_version_minor = 1;
cfg.im_0_data.im_supported = DevSettings.im_0.supported;
std::snprintf (
cfg.im_0_data.im_order_id,
sizeof (cfg.im_0_data.im_order_id),
"%s",
DevSettings.im_0.order_id.c_str());
std::snprintf (
cfg.im_0_data.im_serial_number,
sizeof (cfg.im_0_data.im_serial_number),
"%s",
DevSettings.im_0.serial_number.c_str());
std::snprintf (
cfg.im_1_data.im_tag_function,
sizeof (cfg.im_1_data.im_tag_function),
"%s",
DevSettings.im_1.tag_function.c_str());
std::snprintf (
cfg.im_1_data.im_tag_location,
sizeof (cfg.im_1_data.im_tag_location),
"%s",
DevSettings.im_1.tag_location.c_str());
std::snprintf (
cfg.im_2_data.im_date,
sizeof (cfg.im_2_data.im_date),
"%s",
DevSettings.im_2.date.c_str());
std::snprintf (
cfg.im_3_data.im_descriptor,
sizeof (cfg.im_3_data.im_descriptor),
"%s",
DevSettings.im_3.descriptor.c_str());
std::snprintf (
cfg.im_4_data.im_signature,
sizeof (cfg.im_4_data.im_signature),
"%s",
DevSettings.im_4.signature.c_str());
cfg.state_cb = profinet_cb_state_ind;
cfg.connect_cb = profinet_cb_connect_ind;
cfg.release_cb = profinet_cb_release_ind;
cfg.dcontrol_cb = profinet_cb_dcontrol_ind;
cfg.ccontrol_cb = profinet_cb_ccontrol_ind;
cfg.read_cb = profinet_cb_read_ind;
cfg.write_cb = profinet_cb_write_ind;
cfg.exp_module_cb = profinet_cb_exp_module_ind;
cfg.exp_submodule_cb = profinet_cb_exp_submodule_ind;
cfg.new_data_status_cb = profinet_cb_new_data_status_ind;
cfg.alarm_ind_cb = profinet_cb_alarm_ind;
cfg.alarm_cnf_cb = profinet_cb_alarm_cnf;
cfg.alarm_ack_cnf_cb = profinet_cb_alarm_ack_cnf;
cfg.reset_cb = profinet_cb_reset_ind;
cfg.signal_led_cb = profinet_cb_signal_led_ind;
cfg.device_id.vendor_id_hi = GET_HIGH_BYTE (DevSettings.im_0.vendor_id);
cfg.device_id.vendor_id_lo = GET_LOW_BYTE (DevSettings.im_0.vendor_id);
cfg.device_id.device_id_hi = GET_HIGH_BYTE (DevSettings.device_id);
cfg.device_id.device_id_lo = GET_LOW_BYTE (DevSettings.device_id);
cfg.oem_device_id.vendor_id_hi = GET_HIGH_BYTE (DevSettings.oem_vendor_id);
cfg.oem_device_id.vendor_id_lo = GET_LOW_BYTE (DevSettings.oem_vendor_id);
cfg.oem_device_id.device_id_hi = GET_HIGH_BYTE (DevSettings.oem_device_id);
cfg.oem_device_id.device_id_lo = GET_LOW_BYTE (DevSettings.oem_device_id);
snprintf (
cfg.product_name,
sizeof (cfg.product_name),
"%s",
DevSettings.product_name.c_str());
cfg.send_hello = true;
/* Timing */
cfg.min_device_interval = DevSettings.min_device_interval;
/* Should be set by application as part of network configuration. */
cfg.num_physical_ports = 1;
snprintf (
cfg.station_name,
sizeof (cfg.station_name),
"%s",
DevSettings.station_name.c_str());
/* Diagnosis mechanism */
/* We prefer using "Extended channel diagnosis" instead of
* "Qualified channel diagnosis" format on the wire,
* as this is better supported by Wireshark.
*/
cfg.use_qualified_diagnosis = false;
}
Profinet::Profinet() : m_pnet_data({nullptr, 0, UINT32_MAX, 0}),
m_cyclic_io_cnt{0},
m_periodic_stop{false},
log_{1}
{
}
bool Profinet::Config(ProfinetSettings& Settings,
ProfinetDeviceSettings& DevSettings,
std::vector<std::shared_ptr<ProfinetModule>>& Modules)
{
bool ret = true;
pnet_cfg_t pnet_cfg = {0};
utils_netif_namelist_t netif_name_list;
uint16_t number_of_ports = 1;
pnet_if_cfg_t netif_cfg = {0};
std::memset(&pnet_cfg, 0, sizeof(pnet_cfg));
pnetConfigure(pnet_cfg, Settings, DevSettings);
m_run_period_us = Settings.ticks_us;
m_cyclic_io_period_ms = Settings.cyclic_ms;
///В колбэки pnet передается этот объект
pnet_cfg.cb_arg = static_cast<void*>(this);
utils_pnet_cfg_init_netifs(Settings.eth_dev_name.c_str(),
&netif_name_list,
&number_of_ports,
&netif_cfg,
0x10 /* Copper 100 Mbit/s Full duplex */
);
pnet_cfg.if_cfg = netif_cfg;
pnet_cfg.num_physical_ports = number_of_ports;
pnet_cfg.pnal_cfg.snmp_thread.prio = APP_SNMP_THREAD_PRIORITY;
pnet_cfg.pnal_cfg.snmp_thread.stack_size = APP_SNMP_THREAD_STACKSIZE;
pnet_cfg.pnal_cfg.eth_recv_thread.prio = APP_ETH_THREAD_PRIORITY;
pnet_cfg.pnal_cfg.eth_recv_thread.stack_size = APP_ETH_THREAD_STACKSIZE;
pnet_cfg.pnal_cfg.bg_worker_thread.prio = APP_BG_WORKER_THREAD_PRIORITY;
pnet_cfg.pnal_cfg.bg_worker_thread.stack_size = APP_BG_WORKER_THREAD_STACKSIZE;
/// Инициализация библиотеки pnet
m_pnet_data.pnet_ptr = pnet_init (&pnet_cfg);
if (m_pnet_data.pnet_ptr == nullptr)
return false;
///
pnet_data_cfg_t dap_data_cfg = { PNET_DIR_NO_IO, 0, 0 };
/// Создаем обязательный модуль DAP
auto dap_module = ProfinetModule::Create(PNET_MOD_DAP_IDENT, "DAP 1");
/// Создаем обязательный подмодуль DAP Identity 1
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 = ProfinetSubmodule::Create(PNET_SUBMOD_DAP_INTERFACE_1_IDENT, "DAP IFACE1 IDENT", dap_data_cfg);
/// Создаем обязательный подмодуль DAP Port 1
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);
ret&= dap_module->addSubmodule(dap_subslot_iface_1_port1_ident);
/// Создаем слот для модуля DAP и доключаем к нему модуль dap_module
ret&= addSlotAndPlugModule(PNET_SLOT_DAP_IDENT, dap_module);
/// Добавляем подслоты к слоту PNET_SLOT_DAP_IDENT и подключаем к подслотам подмодули
/// 1. Подмодуль "DAP Identity 1"
ret&= addSubslotAndPlugSubmodule(PNET_SLOT_DAP_IDENT, PNET_SUBSLOT_DAP_IDENT, dap_submodule_indent_1);
/// 2. Подмодуль "DAP IFACE1 IDENT"
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;
}
bool Profinet::addSlot(std::shared_ptr<ProfinetSlot>& slot_ptr)
{
if (slot_ptr == nullptr)
return false;
m_slots[slot_ptr->m_slot_nbr] = slot_ptr;
return true;
}
std::shared_ptr<ProfinetSlot> Profinet::addSlot(uint16_t slot_nbr)
{
std::shared_ptr<ProfinetSlot> slot_ptr(nullptr);
if (m_slots.count(slot_nbr))
{
slot_ptr = m_slots[slot_nbr];
}
else
{
slot_ptr = make_shared<ProfinetSlot>(slot_nbr);
}
if (slot_ptr != nullptr)
{
m_slots[slot_nbr] = slot_ptr;
}
return slot_ptr;
}
bool Profinet::addModule(std::shared_ptr<ProfinetModule>& module_ptr)
{
if (module_ptr == nullptr)
return false;
m_modules[module_ptr->m_id] = module_ptr;
return true;
}
std::shared_ptr<ProfinetModule> Profinet::getModule(uint32_t module_id)
{
if (!m_modules.count(module_id))
{
return nullptr;
}
return m_modules[module_id];
}
bool Profinet::addSlotAndPlugModule(uint16_t slot_nbr, std::shared_ptr<ProfinetModule>& module_ptr)
{
auto slot = addSlot(slot_nbr);
if (slot == nullptr)
{
return false;
}
return slot->plugModule(m_pnet_data, module_ptr);
}
bool Profinet::addSlotAndPlugModule(uint16_t slot_nbr, uint32_t module_id)
{
/// 1. Проверяем, что module_id поддерживается
auto module_ptr = getModule(module_id);
if (module_ptr == nullptr)
{
return false;
}
///2. Добавляет слот
auto slot = addSlot(slot_nbr);
if (slot == nullptr)
{
return false;
}
/// 3. Подключаем модуль к слоту
return slot->plugModule(m_pnet_data, module_ptr);
}
bool Profinet::addSubslotAndPlugSubmodule(uint16_t slot_nbr, uint16_t subslot_nbr, std::shared_ptr<ProfinetSubmodule>& submodule_ptr)
{
auto slot_ptr = getSlotPtr(slot_nbr);
if (slot_ptr == nullptr)
{
return false;
}
auto subslot_ptr = slot_ptr->addSubslot(subslot_nbr);
if (subslot_ptr == nullptr)
{
return false;
}
return subslot_ptr->plugSubmodule(m_pnet_data, submodule_ptr);
}
bool Profinet::addSubslotAndPlugSubmodule(uint16_t slot_nbr,
uint16_t subslot_nbr,
uint32_t module_id,
uint32_t submodule_id,
const pnet_data_cfg_t * p_exp_data)
{
///1. Проверяем наличие слота
auto slot_ptr = getSlotPtr(slot_nbr);
if (slot_ptr == nullptr)
{
return false;
}
///2. Проверяем, что к слоту подключен модуль с идентификатором module_id
auto module_ptr = slot_ptr->getModulePtr();
if (module_ptr == nullptr)
{
return false;
}
if (module_ptr->m_id != module_id)
{
return false;
}
///3. Проверяем, что в модуль входит подмодуль с идентфикатором submodule_id
auto submodule_ptr = module_ptr->getSubmodulePtr(submodule_id);
if (submodule_ptr == nullptr)
{
return false;
}
if (submodule_ptr->m_id != submodule_id)
{
return false;
}
///4. Проверяем, что конфигурация данных подмодуля совпадает
if ((submodule_ptr->m_data_cfg.data_dir != p_exp_data->data_dir) ||
(submodule_ptr->m_data_cfg.insize != p_exp_data->insize) ||
(submodule_ptr->m_data_cfg.outsize != p_exp_data->outsize))
{
return false;
}
return addSubslotAndPlugSubmodule(slot_nbr, subslot_nbr, submodule_ptr);
}
std::shared_ptr<ProfinetSlot> Profinet::getSlotPtr(uint16_t slot_nbr)
{
/// 1. Проверить, что слот slot добавлен в конфигурацию
if (!m_slots.count(slot_nbr))
{
return nullptr;
}
return m_slots[slot_nbr];
}
std::shared_ptr<ProfinetSubslot> Profinet::getSubslotPtr(uint16_t slot_nbr, uint16_t subslot_nbr)
{
auto slot_ptr = getSlotPtr(slot_nbr);
if (slot_ptr == nullptr)
{
return nullptr;
}
return slot_ptr->getSubslotPtr(subslot_nbr);
}
std::shared_ptr<ProfinetParameter> Profinet::getSubmoduleParameter(uint16_t slot_nbr, uint16_t subslot_nbr, uint32_t param_index)
{
///1. Проверяем что существует подслот \a subslot_nbr входящий к \a slot_nbr
auto subslot_ptr = getSubslotPtr(slot_nbr, subslot_nbr);
if (subslot_ptr == nullptr)
{
return nullptr;
}
///2. Проверяем что к подслоту \a subslot_nbr подключен подмодуль
auto submodule_ptr = subslot_ptr->getSubmodulePtr();
if (submodule_ptr == nullptr)
{
return nullptr;
}
///3. Проверяем, что у подмодуля есть параметр с индексом idx
auto param_ptr = submodule_ptr->getParameterPtr(param_index);
if (param_ptr == nullptr)
{
return nullptr;
}
return param_ptr;
}
void Profinet::initDataAndIoxs()
{
uint8_t indata_iops;
/**
* Итерируемся по всем слотам.
* Если слоты есть, то и модули к ним уже подключены, т.к. это делает контроллер.
*/
for (auto slot : m_slots)
{
shared_ptr<ProfinetSlot>& slot_ptr = slot.second;
auto& subslots = slot_ptr->getSubslotsPtr();
/// Итерируемся по всем подслотам
for (auto subslot : subslots)
{
shared_ptr<ProfinetSubslot>& subslot_ptr = subslot.second;
auto submodule_ptr = subslot_ptr->getSubmodulePtr();
/// На всякий случай. Вообще если подслоты есть, то и подмодули уже к ним подключены.
if (submodule_ptr == nullptr)
continue;
indata_iops = PNET_IOXS_BAD;
/**
* @brief Если для подмодуля существуют входные данные (данные от устройства к контроллеру) или
* циклических данных нет(DAP), то нужно установить статус IOPS.
* submodule_ptr->m_data_cfg.data_dir == PNET_DIR_NO_IO - вот это условие нужно для установки статуса для DAP
*/
if ( submodule_ptr->m_data_cfg.insize > 0 ||
submodule_ptr->m_data_cfg.data_dir == PNET_DIR_NO_IO )
{
/// Для данных DAP статус всегда ОК
if (slot_ptr->m_slot_nbr == PNET_SLOT_DAP_IDENT)
{
/// Для данных DAP статус ОК
indata_iops = PNET_IOXS_GOOD;
}
else if (submodule_ptr->m_data_cfg.insize > 0)
{
}
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,
* setting the data and IOPS will fail.
* This is not a problem.
* Log message below will only be printed for active submodules.
*/
}
/**
* @brief Если данные подмодуля включаются в себя выходные данные(данные от контроллера к устройству)
* То нужно установить для них статус IOCS в PNET_IOXS_GOOD.
*
*/
if (submodule_ptr->m_data_cfg.outsize > 0)
{
int ret = pnet_output_set_iocs ( m_pnet_data.pnet_ptr,
m_pnet_data.api,
slot_ptr->m_slot_nbr,
subslot_ptr->m_subslot_nbr,
PNET_IOXS_GOOD);
if (ret == 0)
{
/// Успешно
}
}
}
}
}
void Profinet::PeriodicOperations()
{
while(!m_periodic_stop.load(std::memory_order_relaxed))
{
/// Ждем время в мкс: ticks_period_us, по умолчанию 1мс
std::this_thread::sleep_for(std::chrono::microseconds(m_run_period_us));
if (flags_.get_flag(FLAGS_AREP_APP_READY))
{
pnet_application_ready (m_pnet_data.pnet_ptr, m_pnet_data.arep_for_appl_ready);
flags_.clear_flags(FLAGS_AREP_APP_READY);
LOG("FLAGS_AREP_APP_READY");
}
/// Вызов cyclic io
if (++m_cyclic_io_cnt >= m_cyclic_io_period_ms)
{
m_cyclic_io_cnt = 0;
cyclicIoData();
}
/// запускаем pnet
pnet_handle_periodic (m_pnet_data.pnet_ptr);
}
}
void Profinet::cyclicIoData()
{
for (auto slot : m_slots)
{
shared_ptr<ProfinetSlot>& slot_ptr = slot.second;
/// DAP слот игнорируем при циклическом обмене
if (slot_ptr->m_slot_nbr == PNET_SLOT_DAP_IDENT)
continue;
auto& subslots = slot_ptr->getSubslotsPtr();
/// Итерируемся по всем подслотам
for (auto subslot : subslots)
{
shared_ptr<ProfinetSubslot>& subslot_ptr = subslot.second;
auto submodule_ptr = subslot_ptr->getSubmodulePtr();
/// На всякий случай. Вообще если подслоты есть, то и подмодули уже к ним подключены.
if (submodule_ptr == nullptr)
continue;
if (submodule_ptr->m_data_cfg.outsize > 0)
{
bool outdata_updated;
uint16_t outdata_length = submodule_ptr->m_data_cfg.outsize;
uint8_t outdata_iops;
/// Копируем данные от контроллера в m_out_data_ptr
submodule_ptr->outputGetData( m_pnet_data.pnet_ptr,
m_pnet_data.api,
slot_ptr->m_slot_nbr,
subslot_ptr->m_subslot_nbr);
}
if (submodule_ptr->m_data_cfg.insize > 0)
{
uint8_t indata_iocs;
uint8_t indata_iops = PNET_IOXS_GOOD;
/**
* @brief Отправляем данные подмодуля в ПЛК
*
*/
submodule_ptr->inputSetData(
m_pnet_data.pnet_ptr,
m_pnet_data.api,
slot_ptr->m_slot_nbr,
subslot_ptr->m_subslot_nbr,
indata_iops
);
(void)pnet_input_get_iocs (
m_pnet_data.pnet_ptr,
m_pnet_data.api,
slot_ptr->m_slot_nbr,
subslot_ptr->m_subslot_nbr,
&indata_iocs);
submodule_ptr->m_indata_iocs = indata_iocs;
}
}
}
}
void Profinet::Start()
{
LOG("Waiting PLC connection...");
m_periodic_thread = std::move(std::thread(&Profinet::PeriodicOperations, this));
}
Profinet::~Profinet()
{
///Остановка потока
m_periodic_stop.store(true, std::memory_order_relaxed);
if (m_periodic_thread.joinable())
{
m_periodic_thread.join();
}
}
/**\
* =========================================================================================
* Callbacks
* =========================================================================================
*/
int Profinet::callbackStateInd ( uint32_t arep, pnet_event_values_t event)
{
LOG("callbackStateInd: event = " + std::to_string(event));
uint16_t err_cls = 0; /* Error code 1 */
uint16_t err_code = 0; /* Error code 2 */
const char * error_class_description = "";
const char * error_code_description = "";
if (event == PNET_EVENT_ABORT)
{
LOG("callbackStateInd:PNET_EVENT_ABORT");
if (pnet_get_ar_error_codes (m_pnet_data.pnet_ptr, arep, &err_cls, &err_code) == 0)
{
/**
* @brief Коды ошибок в err_cls и err_code
* TODO: Нужно передавать в управляющее приложение
*/
}
else
{
/// Ошибок нет
}
/* Only abort AR with correct session key */
///os_event_set (app->main_events, APP_EVENT_ABORT);
}
else if (event == PNET_EVENT_PRMEND)
{
LOG("callbackStateInd:PNET_EVENT_PRMEND");
if (isConnectedToController())
{
/// Если уже были подключены и пришел запрос на еще одно подключение
}
m_pnet_data.arep = arep;
initDataAndIoxs();
(void)pnet_set_provider_state (m_pnet_data.pnet_ptr, true);
/**
* @brief Костыль библиотеки pnet:
* По правилам Profinet нужно ответить Application ready.
* Однако тут нельзя вызвать pnet_application_ready, т.к. это испортит
* внутреннее состояние стэка pnet.
* Это нужно делать на следующем "тике".
* См. описание pnet_application_ready:
* This function must be called after the application has received the pnet_state_ind()
* user callback with PNET_EVENT_PRMEND, in order for a connection to be established.
*
*/
/* Send application ready at next tick
Do not call pnet_application_ready() here as it will affect
the internal stack states */
m_pnet_data.arep_for_appl_ready = arep;
flags_.set_flag(FLAGS_AREP_APP_READY);
///os_event_set (app->main_events, APP_EVENT_READY_FOR_DATA); ///
}
else if (event == PNET_EVENT_DATA)
{
LOG("callbackStateInd:PNET_EVENT_DATA");
/// Стартовал обмен cyclic io
}
return 0;
}
int Profinet::callbackConnectInd ( uint32_t arep, pnet_result_t * p_result)
{
LOG("callbackConnectInd");
/**
* @brief
* Если будет возвращено 0, то соединение будет установлено.
* Если будет возвращено значение отличное от 0, то не будет, в этом случае нужно указать p_result.
*/
/// Сообщить приложению об установлении связи с контроллером.
return 0;
}
int Profinet::callbackReleaseInd ( uint32_t arep, pnet_result_t * p_result)
{
LOG("callbackReleaseInd");
/**
* @brief
* Cоединение будет разорвано при любом возвращаемом значении
*/
/// Сообщить приложению об разрыве связи с контроллером.
return 0;
}
int Profinet::callbackDcontrolInd ( uint32_t arep,
pnet_control_command_t control_command,
pnet_result_t * p_result)
{
LOG("callbackDcontrolInd: command= " + to_string(control_command));
/**
* @brief
* Можно оставить пустым
*/
return 0;
}
int Profinet::callbackCcontrolInd ( uint32_t arep, pnet_result_t * p_result)
{
LOG("callbackCcontrolInd");
/**
* @brief
* Можно оставить пустым
*/
return 0;
}
int Profinet::callbackReadInd ( uint32_t arep,
uint16_t slot_nbr,
uint16_t subslot_nbr,
uint16_t idx,
uint16_t sequence_number,
uint8_t ** pp_read_data,
uint16_t * p_read_length,
pnet_result_t * p_result)
{
LOG("callbackReadInd");
///1. Проверяем, что у подмодуля есть параметр с индексом idx
auto param_ptr = getSubmoduleParameter(slot_nbr, subslot_nbr, idx);
if (param_ptr == nullptr)
{
p_result->pnio_status.error_code = PNET_ERROR_CODE_READ;
p_result->pnio_status.error_decode = PNET_ERROR_DECODE_PNIORW;
p_result->pnio_status.error_code_1 = PNET_ERROR_CODE_1_APP_READ_ERROR;
p_result->pnio_status.error_code_2 = 0; /* User specific */
return -1;
}
///2. Проверяем корректность длины данных (меньше быть не должно, если больше - это норм)
if (*p_read_length < param_ptr->length)
{
p_result->pnio_status.error_code = PNET_ERROR_CODE_READ;
p_result->pnio_status.error_decode = PNET_ERROR_DECODE_PNIORW;
p_result->pnio_status.error_code_1 = PNET_ERROR_CODE_1_APP_READ_ERROR;
p_result->pnio_status.error_code_2 = 0; /* User specific */
return -1;
}
///3. Передаем данные параметра
/**
* По идее тут нужно утановить мьютекс lock, а unlock сделать потом, когда данные будет причитаны стеком pnet
* Проблема в том, что нет ни одного события которое бы сообщало об окончании процесса чтения данных стеком pnet.
* Нужно поисследовать.
*/
*pp_read_data = param_ptr->getDataPointer();
*p_read_length = param_ptr->length;
return 0;
}
int Profinet::callbackWriteInd ( uint32_t arep,
uint16_t slot_nbr,
uint16_t subslot_nbr,
uint16_t idx,
uint16_t sequence_number,
uint16_t write_length,
const uint8_t * p_write_data,
pnet_result_t * p_result)
{
LOG("callbackWriteInd");
///1. Проверяем, что у подмодуля есть параметр с индексом idx
auto param_ptr = getSubmoduleParameter(slot_nbr, subslot_nbr, idx);
if (param_ptr == nullptr)
{
p_result->pnio_status.error_code = PNET_ERROR_CODE_WRITE;
p_result->pnio_status.error_decode = PNET_ERROR_DECODE_PNIORW;
p_result->pnio_status.error_code_1 = PNET_ERROR_CODE_1_APP_WRITE_ERROR;
p_result->pnio_status.error_code_2 = 0; /* User specific */
LOG("callbackWriteInd: No such parameter with index " + std::to_string(idx));
return -1;
}
///2. Проверяем корректность длины данных
if (write_length != param_ptr->length)
{
p_result->pnio_status.error_code = PNET_ERROR_CODE_WRITE;
p_result->pnio_status.error_decode = PNET_ERROR_DECODE_PNIORW;
p_result->pnio_status.error_code_1 = PNET_ERROR_CODE_1_APP_WRITE_ERROR;
p_result->pnio_status.error_code_2 = 0; /* User specific */
LOG("callbackWriteInd: Wrong data length " + std::to_string(write_length) + ", will be " + std::to_string(param_ptr->length));
return -1;
}
///3. Копируем данные
param_ptr->writeParameter(p_write_data);
return 0;
}
int Profinet::callbackExpModuleInd ( uint16_t slot, uint32_t module_ident)
{
LOG("callbackExpModuleInd: plug module id=" + std::to_string(module_ident) + " -> slot num=" + std::to_string(slot));
bool ret = addSlotAndPlugModule(slot, module_ident);
if (ret == false)
{
return -1;
LOG("Error!");
}
return 0;
}
int Profinet::callbackExpSubmoduleInd ( uint16_t slot,
uint16_t subslot,
uint32_t module_id,
uint32_t submodule_id,
const pnet_data_cfg_t * p_exp_data)
{
LOG("callbackExpSubmoduleInd: plug submodule " + std::to_string(submodule_id) + " -> subslot " + std::to_string(subslot));
bool ret = addSubslotAndPlugSubmodule(slot, subslot, module_id, submodule_id, p_exp_data);
if (ret == false)
{
LOG("Error!");
return -1;
}
return 0;
}
int Profinet::callbackNewDataStatusInd ( uint32_t arep,
uint32_t crep,
uint8_t changes,
uint8_t data_status)
{
LOG("callbackNewDataStatusInd");
/// Пока ничего не делаем
return 0;
}
int Profinet::callbackAlarmInd ( uint32_t arep,
const pnet_alarm_argument_t * p_alarm_arg,
uint16_t data_len,
uint16_t data_usi,
const uint8_t * p_data)
{
LOG("callbackAlarmInd");
/**
* @brief Должен быть ответ pnet_alarm_send_ack
* Пока заглушка
*
*/
pnet_pnio_status_t pnio_status = {0, 0, 0, 0};
pnet_alarm_send_ack (m_pnet_data.pnet_ptr, arep, p_alarm_arg, &pnio_status);
return 0;
}
int Profinet::callbackAlarmCnf ( uint32_t arep, const pnet_pnio_status_t * p_pnio_status)
{
LOG("callbackAlarmCnf");
/// Пока ничего не делаем
return 0;
}
int Profinet::callbackAlarmAckCnf ( uint32_t arep, int res)
{
LOG("callbackAlarmAckCnf");
/// Пока ничего не делаем
return 0;
}
int Profinet::callbackResetInd ( bool should_reset_application, uint16_t reset_mode)
{
LOG("callbackResetInd");
/// Пока ничего не делаем
return 0;
}
int Profinet::callbackSignalLedInd (bool led_state)
{
LOG("callbackSignalLedInd");
/// Пока ничего не делаем
return 0;
}

427
src/profinet/profinet.hpp Normal file
View File

@ -0,0 +1,427 @@
#pragma once
#include "../../libs/include/pnet_api.h"
#include "profinet_settings.hpp"
#include <string>
#include <map>
#include <memory>
#include "profinet_slot.hpp"
#include "profinet_serv_data.hpp"
#include <atomic>
#include <thread>
#include <vector>
#include "../log/log.hpp"
#include "../flags/flags.hpp"
class Profinet {
public:
Profinet();
bool Config(ProfinetSettings& Settings,
ProfinetDeviceSettings& DevSettings,
std::vector<std::shared_ptr<ProfinetModule>>& Modules);
/**
* @brief Добавляет ранее созданный слот slot_ptr
*
* @param slot_ptr слот
* @return true слот добавлен
* @return false слот не был добавлен
*/
bool addSlot(std::shared_ptr<ProfinetSlot>& slot_ptr);
/**
* @brief Если слот с номером slot_nbr еще не добавлен, то создает слот и добавляет его
*
* @param slot_nbr номер слота
* @return указатель на слот
*/
std::shared_ptr<ProfinetSlot> addSlot(uint16_t slot_nbr);
/**
* @brief Добавляет слот с номером slot_nbr и подключает к нему модуль module_ptr.
* Если слота с номером slot_nbr еще не добавлено, то он будет создан и добавлен.
* Если слот с номером slot_nbr был добавлен ранее, то это штатная работа данной функции.
*
* @param slot_nbr номер слота
* @param module_ptr указатель на модуль
* @return true модуль успешно подключен
* @return false модуль не подключен
*/
bool addSlotAndPlugModule(uint16_t slot_nbr, std::shared_ptr<ProfinetModule>& module_ptr);
/**
* @brief Добавляет слот с номером slot_nbr и подключает к нему модуль из словаря \a m_modules чей
* идентификатор равен module_id.
* Если слота с номером slot_nbr еще не добавлено, то он будет создан и добавлен.
* Если слот с номером slot_nbr был добавлен ранее, то это штатная работа данной функции.
*
* @param slot_nbr номер слота
* @param module_id идентификатор модуля
* @return true модуль успешно подключен
* @return false модуль не подключен
*/
bool addSlotAndPlugModule(uint16_t slot_nbr, uint32_t module_id);
/**
* @brief Добавляет к слоту с номером \a slot_nbr подслот с номером \a subslot_nbr и подключает к нему подмодуль \a submodule_ptr.
* Если слота с номером \a slot_nbr еще не добавлено, то выполнене функции завершиться со значением \a false.
* Если подслота с номером \a subslot_nbr еще не добавлено в слот \a slot_nbr то он будет создан и добавлен.
*
* @param slot_nbr номер слота
* @param subslot_nbr номер подслота
* @param submodule_ptr указатель на подмодуль
* @return true подмодуль успешно подключен
* @return false подмодуль не подключен
*/
bool addSubslotAndPlugSubmodule(uint16_t slot_nbr, uint16_t subslot_nbr, std::shared_ptr<ProfinetSubmodule>& submodule_ptr);
/**
* @brief Добавляет к слоту с номером \a slot_nbr, к которому подключен модуль с идентификатором \a module_id, подслот
* с номером \a subslot_nbr и подключает к нему подмодуль с идентификатором \a submodule_id.
* Выполнение функции прекратиться с возвратом false, если:
* - слот \a slot_nbr не был добавлен ранее,
* - слот \a slot_nbr был добавлен ранее, но к нему подключен модуль с идентфикатором не равным \a module_id,
* - в модуль с идентификатором \a module_id не входит подмодуль с идентификатором \a submodule_id
* - описание данных подмодуля \a submodule_id не совпарадет с описанием данным в \a p_exp_data
*
* @param slot_nbr номер слота
* @param subslot_nbr номер подслота
* @param module_id идентификатор модуля
* @param submodule_id идентификатор подмодуля
* @param p_exp_data описание данныз подмодуля
* @return true подмодуль подключен
* @return false подмодуль не подключен
*/
bool addSubslotAndPlugSubmodule(uint16_t slot_nbr,
uint16_t subslot_nbr,
uint32_t module_id,
uint32_t submodule_id,
const pnet_data_cfg_t * p_exp_data);
/**
* @brief Добавляет модуль в конфигурацию
*
* @param module_ptr
* @return true
* @return false
*/
bool addModule(std::shared_ptr<ProfinetModule>& module_ptr);
/**
* @brief геттер модуля
*
* @param module_id идентификатор модуля
* @return std::shared_ptr<ProfinetModule> возвращает указатель на модуль или nullptr если такого модуля нет в \a m_modules
*/
std::shared_ptr<ProfinetModule> getModule(uint32_t module_id);
/**
* @brief геттер указателя на структуру данных параметра подмодуля с идексом \a param_index подключенного к подслоту
* \a subslot_nbr входящего в состав \a slot_nbr.
*
* @param slot_nbr номер слота
* @param subslot_nbr номер подслота
* @param param_index индекс параметра
* @return std::shared_ptr<ProfinetParameter> указатель на параметр или nullptr
*/
std::shared_ptr<ProfinetParameter> getSubmoduleParameter(uint16_t slot_nbr, uint16_t subslot_nbr, uint32_t param_index);
/**
* @brief Таймер для циклического обмена данными
*
*/
void PeriodicOperations();
/**
* @brief Запуск потока
*
*/
void Start();
~Profinet();
private:
ProfinetServiceData m_pnet_data;
uint32_t m_run_period_us; /// Период одного тика (цикла) выполнения
uint32_t m_cyclic_io_period_ms; /// Период циклического обмена
uint32_t m_cyclic_io_cnt; /// Счетчик циклического обмена
std::thread m_periodic_thread; /// Поток в котором исполняется PeriodicOperations
std::atomic<bool> m_periodic_stop; /// Флаг завершения потока
/**
* @brief Ключ - номер слота, Значение - указатель на слот
*
*/
std::map<uint16_t, std::shared_ptr<ProfinetSlot>> m_slots;
/**
* @brief Ключ - идентиикатор модуля, Значение - указатель на модуль
*
*/
std::map<uint32_t, std::shared_ptr<ProfinetModule>> m_modules;
Log log_;
Flags flags_;
private:
/**
* @brief Проверяет наличие слота slot_nbr в конфигурации
*
* @param slot_nbr номер слота
* @return ProfinetSlot* - указатель на слот, при успехе или nullptr есои слот в конфигурации отсутсвует
*/
std::shared_ptr<ProfinetSlot> getSlotPtr(uint16_t slot_nbr);
std::shared_ptr<ProfinetSubslot> getSubslotPtr(uint16_t slot_nbr, uint16_t subslot_nbr);
bool isConnectedToController() { return m_pnet_data.arep != UINT32_MAX; };
void initDataAndIoxs();
void cyclicIoData();
public:
/**
*
* Indication to the application that a module is requested by the controller in
* a specific slot.
*
* This application call-back function is called by the Profinet stack to
* indicate that the controller has requested the presence of a specific module,
* ident number \a module_ident, in the slot number \a slot.
*
* The application must react to this by configuring itself accordingly (if
* possible) and call function pnet_plug_module() to configure the stack for
* this module.
*
* If the wrong module ident number is plugged then the stack will accept this,
* but signal to the controller that a substitute module is fitted.
*
* This function should return 0 (zero) if a valid module was plugged. Or return
* -1 if the application cannot handle this request.
*
* @param arep In: The AREP.
* @param event In: The state transition event. See pnet_event_values_t
* @return 0 on success. Other values are ignored.
*/
int callbackStateInd ( uint32_t arep, pnet_event_values_t event);
/**
* @brief This application call-back function is called by the Profinet stack on every
* Connect request from the Profinet controller.
*
* @param arep In: The AREP.
* @param p_result Out: Detailed error information if return != 0.
* @return 0 on success.
* -1 if an error occurred.
*/
int callbackConnectInd ( uint32_t arep, pnet_result_t * p_result);
/**
* @brief This application call-back function is called by the Profinet stack on every
* Release request from the Profinet controller.
*
* @param arep In: The AREP.
* @param p_result Out: Detailed error information if return != 0.
* @return 0 on success.
* -1 if an error occurred.
*/
int callbackReleaseInd ( uint32_t arep, pnet_result_t * p_result);
/**
* @brief This application call-back function is called by the Profinet stack on every
* DControl request from the Profinet controller.
*
* @param arep In: The AREP.
* @param control_command In: The DControl command code.
* @param p_result Out: Detailed error information if return != 0.
* @return 0 on success.
* -1 if an error occurred.
*/
int callbackDcontrolInd ( uint32_t arep,
pnet_control_command_t control_command,
pnet_result_t * p_result);
/**
* @brief * This application call-back function is called by the Profinet stack on every
* CControl confirmation from the Profinet controller.
* @param arep In: The AREP.
* @param p_result Out: Detailed error information.
* @return 0 on success. Other values are ignored.
*/
int callbackCcontrolInd ( uint32_t arep, pnet_result_t * p_result);
/**
* @brief This application call-back function is called by the Profinet stack on every
* IODRead request from the Profinet controller which specify an
* application-specific value of \a idx (0x0000 - 0x7fff). All other values of
* \a idx are handled internally by the Profinet stack.
*
* @param arep In: The AREP.
* @param slot In: The slot number.
* @param subslot In: The sub-slot number.
* @param idx In: The data record index.
* @param sequence_number In: The sequence number.
* @param pp_read_data Out: A pointer to the binary value.
* @param p_read_length InOut: The maximum (in) and actual (out) length in
* bytes of the binary value.
* @param p_result Out: Detailed error information if returning != 0
* @return 0 on success.
* -1 if an error occurred.
*/
int callbackReadInd (
uint32_t arep,
uint16_t slot_nbr,
uint16_t subslot_nbr,
uint16_t idx,
uint16_t sequence_number,
uint8_t ** pp_read_data,
uint16_t * p_read_length,
pnet_result_t * p_result);
/**
* @brief This application call-back function is called by the Profinet stack on every
* IODWrite request from the Profinet controller which specify an
* application-specific value of \a idx (0x0000 - 0x7fff). All other values of
* \a idx are handled internally by the Profinet stack.
*
* @param arep In: The AREP.
* @param slot In: The slot number.
* @param subslot In: The sub-slot number.
* @param idx In: The data record index.
* @param sequence_number In: The sequence number.
* @param write_length In: The length in bytes of the binary value.
* @param p_write_data In: A pointer to the binary value.
* @param p_result Out: Detailed error information if returning != 0
* @return 0 on success.
* -1 if an error occurred.
*/
int callbackWriteInd (
uint32_t arep,
uint16_t slot_nbr,
uint16_t subslot_nbr,
uint16_t idx,
uint16_t sequence_number,
uint16_t write_length,
const uint8_t * p_write_data,
pnet_result_t * p_result);
/**
* @brief * This application call-back function is called by the Profinet stack to
* indicate that the controller has requested the presence of a specific module,
* ident number \a module_ident, in the slot number \a slot.
*
* @param slot
* @param module_ident
* @return int
*/
int callbackExpModuleInd ( uint16_t slot, uint32_t module_ident);
/**
* @brief This application call-back function is called by the Profinet stack to
* indicate that the controller has requested the presence of a specific
* sub-module with ident number \a submodule_ident, in the sub-slot number
* \a subslot, with module ident number \a module_ident in slot \a slot.
*
* @param slot In: The slot number.
* @param subslot In: The sub-slot number.
* @param module_ident In: The module ident number.
* @param submodule_ident In: The sub-module ident number.
* @param p_exp_data In: The expected data configuration (sizes and
* direction)
* @return 0 on success.
* -1 if an error occurred.
*/
int callbackExpSubmoduleInd (
uint16_t slot,
uint16_t subslot,
uint32_t module_id,
uint32_t submodule_id,
const pnet_data_cfg_t * p_exp_data);
/**
* @brief This application call-back function is called by the Profinet stack to
* indicate that the received data status has changed.
*
* @param arep In: The AREP.
* @param crep In: The CREP.
* @param changes In: The changed bits in the received data status.
* See pnet_data_status_bits_t
* @param data_status In: Current received data status (after changes).
* See pnet_data_status_bits_t
* @return 0 on success. Other values are ignored.
*/
int callbackNewDataStatusInd (
uint32_t arep,
uint32_t crep,
uint8_t changes,
uint8_t data_status);
/**
* @brief This functionality is used for alarms triggered by the IO-controller.
*
* @param arep In: The AREP.
* @param p_alarm_argument In: The alarm argument (with slot, subslot,
* alarm_type etc)
* @param data_len In: Data length
* @param data_usi In: Alarm USI
* @param p_data In: Alarm data
* @return 0 on success.
* Other values are ignored.
*/
int callbackAlarmInd (
uint32_t arep,
const pnet_alarm_argument_t * p_alarm_arg,
uint16_t data_len,
uint16_t data_usi,
const uint8_t * p_data);
/**
* @brief This functionality is used for alarms triggered by the IO-device.
*
* @param arep In: The AREP.
* @param p_pnio_status In: Detailed ACK information.
* @return 0 on success. Other values are ignored.
*/
int callbackAlarmCnf ( uint32_t arep, const pnet_pnio_status_t * p_pnio_status);
/**
* @brief This functionality is used for alarms triggered by the IO-controller.
*
* @param arep In: The AREP.
* @param res In: 0 if ACK was received by the remote side.
* This is cnf(+).
* -1 if ACK was not received by the remote
* side. This is cnf(-).
* @return 0 on success. Other values are ignored.
*/
int callbackAlarmAckCnf ( uint32_t arep, int res);
/**
* @brief This application call-back function is called by the Profinet stack on every
* reset request (via the DCP "Set" command) from the Profinet controller.
*
* @param should_reset_application In: True if the user should reset the
* application data.
* @param reset_mode In: Detailed reset information.
* @return 0 on success. Other values are ignored.
*/
int callbackResetInd ( bool should_reset_application, uint16_t reset_mode);
/**
* @brief Use this callback to implement control of the LED.
*
* @param led_state In: True if the signal LED should be on.
* @return 0 on success.
* -1 if an error occurred. Will trigger a log message.
*/
int callbackSignalLedInd (bool led_state);
};

View File

@ -0,0 +1,13 @@
#include "./profinet/profinet_cb_alarm_ack_cnf.hpp"
#include "./profinet/profinet.hpp"
int profinet_cb_alarm_ack_cnf (
pnet_t * net,
void * arg,
uint32_t arep,
int res)
{
Profinet * profinet_ptr = static_cast<Profinet*>(arg);
return profinet_ptr->callbackAlarmAckCnf(arep, res);
}

View File

@ -0,0 +1,33 @@
#pragma once
#include "../../libs/include/pnet_api.h"
/**
* The controller acknowledges the alarm ACK sent previously.
*
* This functionality is used for alarms triggered by the IO-controller.
*
* It is optional to implement this callback.
* The return value from this call-back function is ignored by the Profinet
* stack.
*
* Контроллер подтверждает отправленный ранее ACK аварийного сигнала.
*
* Эта функциональность используется для аварийных сигналов, запускаемых IO-контроллером.
*
* Реализовать этот обратный вызов необязательно. Возвращаемое значение этой функции обратного вызова игнорируется стеком Profinet.
*
* @param net InOut: The p-net stack instance
* @param arg InOut: User-defined data (not used by p-net)
* @param arep In: The AREP.
* @param res In: 0 if ACK was received by the remote side.
* This is cnf(+).
* -1 if ACK was not received by the remote
* side. This is cnf(-).
* @return 0 on success. Other values are ignored.
*/
int profinet_cb_alarm_ack_cnf (
pnet_t * net,
void * arg,
uint32_t arep,
int res);

View File

@ -0,0 +1,13 @@
#include "./profinet/profinet_cb_alarm_cnf.hpp"
#include "./profinet/profinet.hpp"
int profinet_cb_alarm_cnf (
pnet_t * net,
void * arg,
uint32_t arep,
const pnet_pnio_status_t * p_pnio_status)
{
Profinet * const profinet_ptr = static_cast<Profinet*>(arg);
return profinet_ptr->callbackAlarmCnf(arep, p_pnio_status);
}

View File

@ -0,0 +1,31 @@
#pragma once
#include "../../libs/include/pnet_api.h"
/**
* The controller acknowledges the alarm sent previously.
* It is now possible to send another alarm.
*
* This functionality is used for alarms triggered by the IO-device.
*
* It is optional to implement this callback.
* The return value from this call-back function is ignored by the Profinet
* stack.
*
* Контроллер подтверждает отправленный ранее аварийный сигнал. Теперь можно отправить еще один сигнал тревоги.
*
* Эта функция используется для аварийных сигналов, инициируемых IO-устройством.
*
* Реализовать этот обратный вызов необязательно. Возвращаемое значение этой функции обратного вызова игнорируется стеком Profinet.
*
* @param net InOut: The p-net stack instance
* @param arg InOut: User-defined data (not used by p-net)
* @param arep In: The AREP.
* @param p_pnio_status In: Detailed ACK information.
* @return 0 on success. Other values are ignored.
*/
int profinet_cb_alarm_cnf (
pnet_t * net,
void * arg,
uint32_t arep,
const pnet_pnio_status_t * p_pnio_status);

View File

@ -0,0 +1,16 @@
#include "./profinet/profinet_cb_alarm_ind.hpp"
#include "./profinet/profinet.hpp"
int profinet_cb_alarm_ind (
pnet_t * net,
void * arg,
uint32_t arep,
const pnet_alarm_argument_t * p_alarm_arg,
uint16_t data_len,
uint16_t data_usi,
const uint8_t * p_data)
{
Profinet * const profinet_ptr = static_cast<Profinet*>(arg);
return profinet_ptr->callbackAlarmInd(arep, p_alarm_arg, data_len, data_usi, p_data);
}

View File

@ -0,0 +1,38 @@
#pragma once
#include "../../libs/include/pnet_api.h"
/**
* The IO-controller has sent an alarm to the device.
*
* This functionality is used for alarms triggered by the IO-controller.
*
* When receiving this indication, the application shall
* respond with \a pnet_alarm_send_ack(), which
* may be called in the context of this callback.
*
* IO-контроллер отправил сигнал тревоги на устройство.
*
* Эта функциональность используется для аварийных сигналов, запускаемых IO-контроллером.
* При получении этого указания приложение должно ответить \a pnet_alarm_send_ack(), которое
* может быть вызвано в контексте этого обратного вызова.
*
* @param net InOut: The p-net stack instance
* @param arg InOut: User-defined data (not used by p-net)
* @param arep In: The AREP.
* @param p_alarm_argument In: The alarm argument (with slot, subslot,
* alarm_type etc)
* @param data_len In: Data length
* @param data_usi In: Alarm USI
* @param p_data In: Alarm data
* @return 0 on success.
* Other values are ignored.
*/
int profinet_cb_alarm_ind (
pnet_t * net,
void * arg,
uint32_t arep,
const pnet_alarm_argument_t * p_alarm_arg,
uint16_t data_len,
uint16_t data_usi,
const uint8_t * p_data);

View File

@ -0,0 +1,13 @@
#include "./profinet/profinet_cb_ccontrol_ind.hpp"
#include "./profinet/profinet.hpp"
int profinet_cb_ccontrol_ind (
pnet_t * net,
void * arg,
uint32_t arep,
pnet_result_t * p_result)
{
Profinet * const profinet_ptr = static_cast<Profinet*>(arg);
return profinet_ptr->callbackCcontrolInd(arep, p_result);
}

View File

@ -0,0 +1,42 @@
#pragma once
#include "../../libs/include/pnet_api.h"
/**
* Indication to the application that a CControl confirmation was received from
* the controller. Typically this means that the controller has received our
* "Application ready" message.
*
* This application call-back function is called by the Profinet stack on every
* CControl confirmation from the Profinet controller.
*
* The application is not required to take any action.
* The return value from this call-back function is ignored by the Profinet
* stack. In case of error the application should provide error information in
* \a p_result.
*
* It is optional to implement this callback (assumes success?).
*
* Индикация приложению о том, что от контроллера получено подтверждение CControl. Обычно это означает,
* что контроллер получил наше сообщение «Приложение готово».
*
* Эта функция обратного вызова приложения вызывается стеком Profinet при каждом подтверждении CControl от контроллера Profinet.
*
* Приложение не требует никаких действий. Возвращаемое значение этой функции обратного вызова игнорируется стеком Profinet.
*
* В случае ошибки приложение должно предоставить информацию об ошибке в \a p_result.
*
* Реализовать этот обратный вызов необязательно (предполагается успех?).
*
* @param net InOut: The p-net stack instance
* @param arg InOut: User-defined data (not used by p-net)
* @param arep In: The AREP.
* @param p_result Out: Detailed error information.
* @return 0 on success. Other values are ignored.
*/
int profinet_cb_ccontrol_ind (
pnet_t * net,
void * arg,
uint32_t arep,
pnet_result_t * p_result);

View File

@ -0,0 +1,12 @@
#include "./profinet/profinet_cb_connect_ind.hpp"
#include "./profinet/profinet.hpp"
int profinet_cb_connect_ind( pnet_t * net,
void * arg,
uint32_t arep,
pnet_result_t * p_result)
{
Profinet * const profinet_ptr = static_cast<Profinet*>(arg);
return profinet_ptr->callbackConnectInd(arep, p_result);
}

View File

@ -0,0 +1,39 @@
#pragma once
#include "../../libs/include/pnet_api.h"
/**
* Indication to the application that a Connect request was received from the
* controller.
*
* This application call-back function is called by the Profinet stack on every
* Connect request from the Profinet controller.
*
* The connection will be opened if this function returns 0 (zero) and the stack
* is otherwise able to establish a connection.
*
* If this function returns something other than 0 (zero) then the Connect
* request is refused by the device. In case of error the application should
* provide error information in \a p_result.
*
* It is optional to implement this callback (assumes success if not
* implemented).
*
* Индикация приложению о том, что от контроллера получен запрос на подключение.
* Эта функция обратного вызова приложения вызывается стеком Profinet при каждом запросе Connect от контроллера Profinet.
* Соединение будет открыто, если эта функция вернет 0 (ноль) и стек в противном случае сможет установить соединение.
* Если эта функция возвращает значение, отличное от 0 (нуля), то запрос на подключение отклоняется устройством.
* В случае ошибки приложение должно предоставить информацию об ошибке в p_result.
* Реализовать этот обратный вызов необязательно (предполагается успех, если он не реализован).
*
* @param net InOut: The p-net stack instance
* @param arg InOut: User-defined data (not used by p-net)
* @param arep In: The AREP.
* @param p_result Out: Detailed error information if return != 0.
* @return 0 on success.
* -1 if an error occurred.
*/
int profinet_cb_connect_ind( pnet_t * net,
void * arg,
uint32_t arep,
pnet_result_t * p_result);

View File

@ -0,0 +1,14 @@
#include "./profinet/profinet_cb_dcontrol_ind.hpp"
#include "./profinet/profinet.hpp"
int profinet_cb_dcontrol_ind (
pnet_t * net,
void * arg,
uint32_t arep,
pnet_control_command_t control_command,
pnet_result_t * p_result)
{
Profinet * const profinet_ptr = static_cast<Profinet*>(arg);
return profinet_ptr->callbackDcontrolInd(arep, control_command, p_result);
}

View File

@ -0,0 +1,46 @@
#pragma once
#include "../../libs/include/pnet_api.h"
/**
* Indication to the application that a DControl request was received from the
* controller. Typically this means that the controller is done writing
* parameters.
*
* This application call-back function is called by the Profinet stack on every
* DControl request from the Profinet controller.
*
* The application is not required to take any action but the function must
* return 0 (zero) for proper function of the stack. If this function returns
* something other than 0 (zero) then the DControl request is refused by the
* device. In case of error the application should provide error information in
* \a p_result.
*
* It is optional to implement this callback (assumes success if not
* implemented).
*
* Индикация приложению о том, что от контроллера получен запрос DControl. Обычно это означает, что контроллер закончил запись параметров.
*
* Эта функция обратного вызова приложения вызывается стеком Profinet при каждом запросе DControl от контроллера Profinet.
*
* От приложения не требуется никаких действий, но функция должна возвращать 0 (ноль) для правильной работы стека.
* Если эта функция возвращает значение, отличное от 0 (нуля), то запрос DControl отклоняется устройством.
* В случае ошибки приложение должно предоставить информацию об ошибке в \a p_result.
*
* Реализовать этот обратный вызов необязательно (предполагается успех, если он не реализован).
*
*
* @param net InOut: The p-net stack instance
* @param arg InOut: User-defined data (not used by p-net)
* @param arep In: The AREP.
* @param control_command In: The DControl command code.
* @param p_result Out: Detailed error information if return != 0.
* @return 0 on success.
* -1 if an error occurred.
*/
int profinet_cb_dcontrol_ind (
pnet_t * net,
void * arg,
uint32_t arep,
pnet_control_command_t control_command,
pnet_result_t * p_result);

View File

@ -0,0 +1,14 @@
#include "./profinet/profinet_cb_exp_module_ind.hpp"
#include "./profinet/profinet.hpp"
int profinet_cb_exp_module_ind (
pnet_t * net,
void * arg,
uint32_t api,
uint16_t slot,
uint32_t module_ident)
{
Profinet * const profinet_ptr = static_cast<Profinet*>(arg);
return profinet_ptr->callbackExpModuleInd(slot, module_ident);
}

View File

@ -0,0 +1,50 @@
#pragma once
#include "../../libs/include/pnet_api.h"
/**
* Indication to the application that a module is requested by the controller in
* a specific slot.
*
* This application call-back function is called by the Profinet stack to
* indicate that the controller has requested the presence of a specific module,
* ident number \a module_ident, in the slot number \a slot.
*
* The application must react to this by configuring itself accordingly (if
* possible) and call function pnet_plug_module() to configure the stack for
* this module.
*
* If the wrong module ident number is plugged then the stack will accept this,
* but signal to the controller that a substitute module is fitted.
*
* This function should return 0 (zero) if a valid module was plugged. Or return
* -1 if the application cannot handle this request.
*
* Индикация приложению о том, что модуль запрошен контроллером в определенном слоте.
*
* Эта функция обратного вызова приложения вызывается стеком Profinet, чтобы указать, что
* контроллер запросил наличие определенного модуля с идентификационным номером \a module_ident в слоте с номером \a slot.
*
* Приложение должно отреагировать на это, соответствующим образом сконфигурировав себя (если это возможно) и вызвать
* функцию pnet_plug_module() для настройки стека для этого модуля.
*
* Если установлен неправильный идентификационный номер модуля, стек примет это, но сигнализирует контроллеру, что установлен
* заменяющий модуль.
*
* Эта функция должна возвращать 0 (ноль), если был подключен допустимый модуль.
* Или верните -1, если приложение не может обработать этот запрос.
*
* @param net InOut: The p-net stack instance
* @param arg InOut: User-defined data (not used by p-net)
* @param api In: The AP identifier.
* @param slot In: The slot number.
* @param module_ident In: The module ident number.
* @return 0 on success.
* -1 if an error occurred.
*/
int profinet_cb_exp_module_ind (
pnet_t * net,
void * arg,
uint32_t api,
uint16_t slot,
uint32_t module_ident);

View File

@ -0,0 +1,17 @@
#include "./profinet/profinet_cb_exp_submodule_ind.hpp"
#include "./profinet/profinet.hpp"
int profinet_cb_exp_submodule_ind (
pnet_t * net,
void * arg,
uint32_t api,
uint16_t slot,
uint16_t subslot,
uint32_t module_id,
uint32_t submodule_id,
const pnet_data_cfg_t * p_exp_data)
{
Profinet * const profinet_ptr = static_cast<Profinet*>(arg);
return profinet_ptr->callbackExpSubmoduleInd(slot, subslot, module_id, submodule_id, p_exp_data);
}

View File

@ -0,0 +1,64 @@
#pragma once
#include "../../libs/include/pnet_api.h"
/**
* Indication to the application that a sub-module is requested by the
* controller in a specific sub-slot.
*
* This application call-back function is called by the Profinet stack to
* indicate that the controller has requested the presence of a specific
* sub-module with ident number \a submodule_ident, in the sub-slot number
* \a subslot, with module ident number \a module_ident in slot \a slot.
*
* If a module has not been plugged in the slot \a slot then an automatic plug
* request is issued internally by the stack.
*
* The application must react to this by configuring itself accordingly (if
* possible) and call function \a pnet_plug_submodule() to configure the stack
* with the correct input/output data sizes.
*
* If the wrong sub-module ident number is plugged then the stack will accept
* this, but signal to the controller that a substitute sub-module is fitted.
*
* This function should return 0 (zero) if a valid sub-module was plugged,
* or return -1 if the application cannot handle this request.
*
* Индикация приложению о том, что субмодуль запрошен контроллером в определенном субслоте.
*
* Эта функция обратного вызова приложения вызывается стеком Profinet, чтобы указать, что контроллер
* запросил наличие определенного подмодуля с идентификационным номером \a идентификатор_подмодуля,
* в подслоте с номером подслота \a, с идентификационным номером модуля \a module_ident в слоте \a slot.
*
* Если модуль не был вставлен в слот \a slot, то внутри стека выдается автоматический запрос на вставку.
*
* Приложение должно отреагировать на это, настроив себя соответствующим образом (если это возможно) и
* вызвать функцию \a pnet_plug_submodule() для настройки стека с правильными размерами входных/выходных данных.
*
* Если установлен неправильный идентификационный номер субмодуля, стек примет это, но сигнализирует контроллеру,
* что установлен замещающий субмодуль.
*
* Эта функция должна возвращать 0 (ноль), если был подключен допустимый подмодуль, или возвращать -1,
* если приложение не может обработать этот запрос.
*
* @param net InOut: The p-net stack instance
* @param arg InOut: User-defined data (not used by p-net)
* @param api In: The AP identifier.
* @param slot In: The slot number.
* @param subslot In: The sub-slot number.
* @param module_ident In: The module ident number.
* @param submodule_ident In: The sub-module ident number.
* @param p_exp_data In: The expected data configuration (sizes and
* direction)
* @return 0 on success.
* -1 if an error occurred.
*/
int profinet_cb_exp_submodule_ind (
pnet_t * net,
void * arg,
uint32_t api,
uint16_t slot,
uint16_t subslot,
uint32_t module_id,
uint32_t submodule_id,
const pnet_data_cfg_t * p_exp_data);

View File

@ -0,0 +1,16 @@
#include "./profinet/profinet_cb_new_data_status_ind.hpp"
#include "./profinet/profinet.hpp"
int profinet_cb_new_data_status_ind (
pnet_t * net,
void * arg,
uint32_t arep,
uint32_t crep,
uint8_t changes,
uint8_t data_status)
{
Profinet * const profinet_ptr = static_cast<Profinet*>(arg);
return profinet_ptr->callbackNewDataStatusInd(arep, crep, changes, data_status);
}

View File

@ -0,0 +1,39 @@
#pragma once
#include "../../libs/include/pnet_api.h"
/**
* Indication to the application that the data status received from the
* controller has changed.
*
* This application call-back function is called by the Profinet stack to
* indicate that the received data status has changed.
*
* The application is not required by the Profinet stack to take any action. It
* may use this information as it wishes. The return value from this call-back
* function is ignored by the Profinet stack.
*
* Индикация приложению, что статус данных, полученных от контроллера, изменился.
*
* Эта функция обратного вызова приложения вызывается стеком Profinet, чтобы указать, что статус полученных данных изменился.
*
* Стек Profinet не требует от приложения каких-либо действий. Он может использовать эту информацию по своему усмотрению.
* Возвращаемое значение этой функции обратного вызова игнорируется стеком Profinet.
*
* @param net InOut: The p-net stack instance
* @param arg InOut: User-defined data (not used by p-net)
* @param arep In: The AREP.
* @param crep In: The CREP.
* @param changes In: The changed bits in the received data status.
* See pnet_data_status_bits_t
* @param data_status In: Current received data status (after changes).
* See pnet_data_status_bits_t
* @return 0 on success. Other values are ignored.
*/
int profinet_cb_new_data_status_ind (
pnet_t * net,
void * arg,
uint32_t arep,
uint32_t crep,
uint8_t changes,
uint8_t data_status);

View File

@ -0,0 +1,27 @@
#include "./profinet/profinet_cb_read_ind.hpp"
#include "./profinet/profinet.hpp"
int profinet_cb_read_ind (
pnet_t * net,
void * arg,
uint32_t arep,
uint32_t api,
uint16_t slot_nbr,
uint16_t subslot_nbr,
uint16_t idx,
uint16_t sequence_number,
uint8_t ** pp_read_data,
uint16_t * p_read_length,
pnet_result_t * p_result)
{
Profinet * const profinet_ptr = static_cast<Profinet*>(arg);
return profinet_ptr->callbackReadInd( arep,
slot_nbr,
subslot_nbr,
idx,
sequence_number,
pp_read_data,
p_read_length,
p_result);
}

View File

@ -0,0 +1,65 @@
#pragma once
#include "../../libs/include/pnet_api.h"
/**
* Indication to the application that an IODRead request was received from the
* controller.
*
* This application call-back function is called by the Profinet stack on every
* IODRead request from the Profinet controller which specify an
* application-specific value of \a idx (0x0000 - 0x7fff). All other values of
* \a idx are handled internally by the Profinet stack.
*
* The application must verify the value of \a idx, and that \a p_read_length is
* large enough. Further, the application must provide a
* pointer to the binary value in \a pp_read_data and the size, in bytes, of the
* binary value in \a p_read_length.
*
* The Profinet stack does not perform any endianness conversion on the binary
* value.
*
* In case of error the application should provide error information in \a
* p_result.
*
* Индикация приложению о том, что от контроллера получен запрос IODRead.
*
* Эта функция обратного вызова приложения вызывается стеком Profinet при каждом запросе IODRead от контроллера Profinet,
* в котором указывается специфичное для приложения значение \a idx (0x0000 - 0x7fff). Все остальные значения \a idx
* обрабатываются внутри стека Profinet.
*
* Приложение должно проверить значение \a idx и достаточно ли велико значение \a p_read_length.
* Кроме того, приложение должно предоставить указатель на двоичное значение в \a pp_read_data и размер в байтах
* двоичного значения в \a p_read_length.
*
* Стек Profinet не выполняет никакого преобразования порядка байтов в двоичном значении.
*
* В случае ошибки приложение должно предоставить информацию об ошибке в \a p_result.
*
* @param net InOut: The p-net stack instance
* @param arg InOut: User-defined data (not used by p-net)
* @param arep In: The AREP.
* @param api In: The AP identifier.
* @param slot In: The slot number.
* @param subslot In: The sub-slot number.
* @param idx In: The data record index.
* @param sequence_number In: The sequence number.
* @param pp_read_data Out: A pointer to the binary value.
* @param p_read_length InOut: The maximum (in) and actual (out) length in
* bytes of the binary value.
* @param p_result Out: Detailed error information if returning != 0
* @return 0 on success.
* -1 if an error occurred.
*/
int profinet_cb_read_ind (
pnet_t * net,
void * arg,
uint32_t arep,
uint32_t api,
uint16_t slot_nbr,
uint16_t subslot_nbr,
uint16_t idx,
uint16_t sequence_number,
uint8_t ** pp_read_data,
uint16_t * p_read_length,
pnet_result_t * p_result);

View File

@ -0,0 +1,15 @@
#include "./profinet/profinet_cb_release_ind.hpp"
#include "./profinet/profinet.hpp"
#include <iostream>
using namespace std;
int profinet_cb_release_ind( pnet_t * net,
void * arg,
uint32_t arep,
pnet_result_t * p_result)
{
Profinet * const profinet_ptr = static_cast<Profinet*>(arg);
return profinet_ptr->callbackReleaseInd(arep, p_result);
}

View File

@ -0,0 +1,37 @@
#pragma once
#include "../../libs/include/pnet_api.h"
/**
* Indication to the application that a Release request was received from the
* controller.
*
* This application call-back function is called by the Profinet stack on every
* Release request from the Profinet controller.
*
* The connection will be closed regardless of the return value from this
* function. In case of error the application should provide error information
* in \a p_result.
*
* It is optional to implement this callback (assumes success if not
* implemented).
*
* Индикация приложению о том, что от контроллера получен запрос Release.
*
* Эта функция обратного вызова приложения вызывается стеком Profinet при каждом запросе Release от контроллера Profinet.
*
* Соединение будет закрыто независимо от возвращаемого значения этой функцией. В случае ошибки приложение должно предоставить
* информацию об ошибке в \a p_result.
*
* Реализовать этот обратный вызов необязательно (предполагается успех, если он не реализован).
*
* @param net InOut: The p-net stack instance
* @param arg InOut: User-defined data (not used by p-net)
* @param arep In: The AREP.
* @param p_result Out: Detailed error information if return != 0.
* @return 0 on success.
* -1 if an error occurred.
*/
int profinet_cb_release_ind( pnet_t * net,
void * arg,
uint32_t arep,
pnet_result_t * p_result);

View File

@ -0,0 +1,13 @@
#include "./profinet/profinet_cb_reset_ind.hpp"
#include "./profinet/profinet.hpp"
int profinet_cb_reset_ind (
pnet_t * net,
void * arg,
bool should_reset_application,
uint16_t reset_mode)
{
Profinet * const profinet_ptr = static_cast<Profinet*>(arg);
return profinet_ptr->callbackResetInd(should_reset_application, reset_mode);
}

View File

@ -0,0 +1,81 @@
#pragma once
#include "../../libs/include/pnet_api.h"
/**
* Indication to the application that a reset request was received from the
* IO-controller.
*
* The IO-controller can ask for communication parameters or application
* data to be reset, or to do a factory reset.
*
* This application call-back function is called by the Profinet stack on every
* reset request (via the DCP "Set" command) from the Profinet controller.
*
* The application should reset the application data if
* \a should_reset_application is true. For other cases this callback is
* triggered for diagnostic reasons.
*
* The return value from this call-back function is ignored by the Profinet
* stack.
*
* It is optional to implement this callback (if you do not have any application
* data that could be reset).
*
* Reset modes:
* * 0: (Power-on reset, not from IO-controller. Will not trigger this.)
* * 1: Reset application data
* * 2: Reset communication parameters (done by the stack)
* * 99: Reset all (factory reset).
*
* The reset modes 1-9 (out of which 1 and 2 are supported here) are defined
* by the Profinet standard. Value 99 is used here to indicate that the
* IO-controller has requested a factory reset via another mechanism.
*
* In order to remain responsive to DCP communication and Ethernet switching,
* the device should not do a hard or soft reset for reset mode 1 or 2. It is
* allowed for the factory reset case (but not mandatory).
*
* No \a arep information is available, as this callback typically is triggered
* when there is no active connection.
*
* Индикация приложению о том, что от IO-контроллера получен запрос на сброс.
*
* Контроллер ввода-вывода может запросить сброс параметров связи или данных приложения или выполнить сброс к заводским настройкам.
*
* Эта функция обратного вызова приложения вызывается стеком Profinet при каждом запросе на сброс (через команду «Set» DCP)
* от контроллера Profinet.
*
* Приложение должно сбросить данные приложения, если \a should_reset_application имеет значение true. В других случаях этот
* обратный вызов запускается по диагностическим причинам.
*
* Возвращаемое значение этой функции обратного вызова игнорируется стеком Profinet.
*
* Реализовать этот обратный вызов необязательно (если у вас нет данных приложения, которые можно было бы сбросить).
*
* Режимы сброса:
* 0: (Сброс при включении питания, а не от IO-контроллера. Это не срабатывает.)
* 1: Сбросить данные приложения
* 2: Сброс параметров связи (выполняется стеком)
* 99: Сбросить все (сброс настроек).
*
* Режимы сброса 1-9 (из которых здесь поддерживаются 1 и 2) определены стандартом Profinet. Здесь используется значение 99,
* чтобы указать, что контроллер ввода-вывода запросил сброс к заводским настройкам с помощью другого механизма.
*
* Чтобы оставаться чувствительным к связи DCP и коммутации Ethernet, устройство не должно выполнять аппаратный
* или программный сброс для режима сброса 1 или 2. Это разрешено для случая сброса к заводским настройкам (но не обязательно).
*
* Информация \a arep недоступна, так как этот обратный вызов обычно запускается при отсутствии активного соединения.
*
* @param net InOut: The p-net stack instance
* @param arg InOut: User-defined data (not used by p-net)
* @param should_reset_application In: True if the user should reset the
* application data.
* @param reset_mode In: Detailed reset information.
* @return 0 on success. Other values are ignored.
*/
int profinet_cb_reset_ind (
pnet_t * net,
void * arg,
bool should_reset_application,
uint16_t reset_mode);

View File

@ -0,0 +1,9 @@
#include "./profinet/profinet_cb_signal_led_ind.hpp"
#include "./profinet/profinet.hpp"
int profinet_cb_signal_led_ind (pnet_t * net, void * arg, bool led_state)
{
Profinet * const profinet_ptr = static_cast<Profinet*>(arg);
return profinet_ptr->callbackSignalLedInd(led_state);
}

View File

@ -0,0 +1,31 @@
#pragma once
#include "../../libs/include/pnet_api.h"
/**
* Indication to the application that the Profinet signal LED should change
* state.
*
* Use this callback to implement control of the LED.
*
* It is optional to implement this callback (but a compliant Profinet device
* must have a signal LED)
*
* No \a arep information is available, as this callback typically is triggered
* when there is no active connection.
*
* Индикация приложению о том, что сигнальный светодиод Profinet должен изменить состояние.
*
* Используйте этот обратный вызов для реализации управления светодиодом.
*
* Реализовать этот обратный вызов необязательно (но совместимое с Profinet устройство должно иметь сигнальный светодиод).
*
* Информация \a arep недоступна, так как этот обратный вызов обычно запускается при отсутствии активного соединения.
*
* @param net InOut: The p-net stack instance
* @param arg InOut: User-defined data (not used by p-net)
* @param led_state In: True if the signal LED should be on.
* @return 0 on success.
* -1 if an error occurred. Will trigger a log message.
*/
int profinet_cb_signal_led_ind (pnet_t * net, void * arg, bool led_state);

View File

@ -0,0 +1,13 @@
#include "./profinet/profinet_cb_state_ind.hpp"
#include "./profinet/profinet.hpp"
int profinet_cb_state_ind (
pnet_t * net,
void * arg,
uint32_t arep,
pnet_event_values_t event)
{
Profinet * const profinet_ptr = static_cast<Profinet*>(arg);
return profinet_ptr->callbackStateInd(arep, event);
}

View File

@ -0,0 +1,43 @@
#pragma once
#include "../../libs/include/pnet_api.h"
/**
* Indication to the application that a state transition has occurred within the
* Profinet stack.
*
* This application call-back function is called by the Profinet stack on
* specific state transitions within the Profinet stack.
*
* At the very least the application must react to the PNET_EVENT_PRMEND state
* transition. After this event the application must call \a
* pnet_application_ready(), when it has finished its setup and it is ready to
* exchange data.
*
* The return value from this call-back function is ignored by the Profinet
* stack.
*
* It is optional to implement this callback (but then it would be difficult
* to know when to call the \a pnet_application_ready() function).
*
* Указание приложению о том, что в стеке Profinet произошел переход состояния.
*
* Эта функция обратного вызова приложения вызывается стеком Profinet при определенных переходах состояний в стеке Profinet.
*
* По крайней мере, приложение должно реагировать на переход состояния PNET_EVENT_PRMEND. После этого события приложение
* должно вызвать \a pnet_application_ready(), когда оно закончит настройку и будет готово к обмену данными.
*
* Возвращаемое значение этой функции обратного вызова игнорируется стеком Profinet.
*
* Реализовать этот обратный вызов необязательно (но тогда будет трудно понять, когда вызывать функцию \a pnet_application_ready()).
*
* @param net InOut: The p-net stack instance
* @param arg InOut: User-defined data (not used by p-net)
* @param arep In: The AREP.
* @param state In: The state transition event. See
* pnet_event_values_t.
* @return 0 on success. Other values are ignored.
*/
int profinet_cb_state_ind( pnet_t * net,
void * arg,
uint32_t arep,
pnet_event_values_t event);

View File

@ -0,0 +1,27 @@
#include "./profinet/profinet_cb_write_ind.hpp"
#include "./profinet/profinet.hpp"
int profinet_cb_write_ind (
pnet_t * net,
void * arg,
uint32_t arep,
uint32_t api,
uint16_t slot_nbr,
uint16_t subslot_nbr,
uint16_t idx,
uint16_t sequence_number,
uint16_t write_length,
const uint8_t * p_write_data,
pnet_result_t * p_result)
{
Profinet * const profinet_ptr = static_cast<Profinet*>(arg);
return profinet_ptr->callbackWriteInd( arep,
slot_nbr,
subslot_nbr,
idx,
sequence_number,
write_length,
p_write_data,
p_result);
}

View File

@ -0,0 +1,62 @@
#pragma once
#include "../../libs/include/pnet_api.h"
/**
* Indication to the application that an IODWrite request was received from the
* controller.
*
* This application call-back function is called by the Profinet stack on every
* IODWrite request from the Profinet controller which specify an
* application-specific value of \a idx (0x0000 - 0x7fff). All other values of
* \a idx are handled internally by the Profinet stack.
*
* The application must verify the values of \a idx and \a write_length and save
* (copy) the binary value in \a p_write_data. A future IODRead must return the
* latest written value.
*
* The Profinet stack does not perform any endianness conversion on the binary
* value.
*
* In case of error the application should provide error information in \a
* p_result.
*
* Индикация приложению о том, что от контроллера получен запрос IODWrite.
*
* Эта функция обратного вызова приложения вызывается стеком Profinet при каждом запросе IODWrite от контроллера Profinet,
* в котором указывается специфичное для приложения значение \a idx (0x0000 - 0x7fff). Все остальные значения \a idx
* обрабатываются внутри стека Profinet.
*
* Приложение должно проверить значения \a idx и \a write_length и сохранить (скопировать) двоичное значение в \a p_write_data.
* Будущий IODRead должен возвращать самое последнее записанное значение.
*
* Стек Profinet не выполняет никакого преобразования порядка байтов в двоичном значении.
*
* В случае ошибки приложение должно предоставить информацию об ошибке в \a p_result.
*
* @param net InOut: The p-net stack instance
* @param arg InOut: User-defined data (not used by p-net)
* @param arep In: The AREP.
* @param api In: The API identifier.
* @param slot In: The slot number.
* @param subslot In: The sub-slot number.
* @param idx In: The data record index.
* @param sequence_number In: The sequence number.
* @param write_length In: The length in bytes of the binary value.
* @param p_write_data In: A pointer to the binary value.
* @param p_result Out: Detailed error information if returning != 0
* @return 0 on success.
* -1 if an error occurred.
*/
int profinet_cb_write_ind (
pnet_t * net,
void * arg,
uint32_t arep,
uint32_t api,
uint16_t slot_nbr,
uint16_t subslot_nbr,
uint16_t idx,
uint16_t sequence_number,
uint16_t write_length,
const uint8_t * p_write_data,
pnet_result_t * p_result);

View File

@ -0,0 +1,7 @@
AREP - Application Relationship End Point (uint32) - конечная точка отношения приложений (uint32),
в значительной степени индекс в массиве AR.
AR - Application Relation. Отношения приложений. Состоит из нескольких коммуникативных отношений (CR).
Обычно это AR IO, который представляет собой соединение с ПЛК, но также может быть AR супервизора.
DAP - Device Access Point

View File

@ -0,0 +1,25 @@
#include "profinet_module.hpp"
ProfinetModule::ProfinetModule(uint32_t module_id, std::string module_name) :
m_id(module_id),
m_name(module_name)
{
};
bool ProfinetModule::addSubmodule(std::shared_ptr<ProfinetSubmodule>& submodule_ptr)
{
auto ret = m_submodules.emplace(submodule_ptr->m_id, submodule_ptr);
return ret.second;
}
std::shared_ptr<ProfinetSubmodule> ProfinetModule::getSubmodulePtr(uint32_t submodule_id)
{
if (!m_submodules.count(submodule_id))
{
return nullptr;
}
return m_submodules[submodule_id];
}

View File

@ -0,0 +1,30 @@
#pragma once
#include <cstdint>
#include <string>
#include <map>
#include <memory>
#include "profinet_submodule.hpp"
class ProfinetModule {
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);
bool addSubmodule(std::shared_ptr<ProfinetSubmodule>& submodule_ptr);
std::shared_ptr<ProfinetSubmodule> getSubmodulePtr(uint32_t submodule_id);
public:
const uint32_t m_id; /// module id
const std::string m_name; /// module name
private:
/// Набор поддерживаемых подмодулей
std::map<uint32_t, std::shared_ptr<ProfinetSubmodule>> m_submodules;
};

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

@ -0,0 +1,55 @@
#pragma once
#include <cstdint>
#include <string>
#include <memory>
#include <mutex>
class ProfinetParameter
{
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; /// Идентфикатор параметра
const uint16_t length; /// длина параметра в байтах
private:
std::shared_ptr<uint8_t[]> data_ptr_; /// указатель на данные параметра
mutable std::mutex data_mutex_; /// Доступ к данным
};

View File

@ -0,0 +1,10 @@
#pragma once
#include "../../libs/include/pnet_api.h"
struct ProfinetServiceData {
pnet_t * pnet_ptr; /// Указатель на pnet
uint32_t api; /// Непонятно что это, обязательно посмотреть в коде либы pnet
uint32_t arep; /// Аналогично
uint32_t arep_for_appl_ready; /// Костыль для pnet (нужно реализовать как в примере)
};

View File

@ -0,0 +1,89 @@
#pragma once
#include <stdint.h>
#include <string>
struct ProfinetSettingsSwRevision
{
/* Allowed: 'V', 'R', 'P', 'U', 'T' */
char prefix;
uint8_t functional_enhancement;
uint8_t bug_fix;
uint8_t internal_change;
};
struct ProfinetSettingsIm0
{
uint16_t vendor_id;
uint16_t hw_revision;
ProfinetSettingsSwRevision sw_revision;
uint16_t revision_counter;
uint16_t profile_id;
uint16_t profile_specific_type;
/** One bit for each supported I&M1..15. I&M0 is always supported.
Use pnet_im_supported_values_t. */
uint16_t supported;
std::string order_id;
std::string serial_number;
};
struct ProfinetSettingsIm1
{
std::string tag_function;
std::string tag_location;
};
struct ProfinetSettingsIm2
{
std::string date;
};
struct ProfinetSettingsIm3
{
std::string descriptor;
};
struct ProfinetSettingsIm4
{
std::string signature;
};
struct ProfinetDeviceSettings {
/**
* Product name
*
* This is known as DeviceVendorValue and DeviceType in the Profinet
* specification. It constitutes the first part of SystemIdentification
* (sysDescr in SNMP). It may also be used to construct the Chassis ID.
* See IEC CDV 61158-6-10 ch. 4.10.3.3.1.
*
* Terminated string.
*/
std::string product_name;
/** Default station name.*/
std::string station_name;
ProfinetSettingsIm0 im_0;
ProfinetSettingsIm1 im_1;
ProfinetSettingsIm2 im_2;
ProfinetSettingsIm3 im_3;
ProfinetSettingsIm4 im_4;
uint16_t device_id;
uint16_t oem_vendor_id;
uint16_t oem_device_id;
bool send_hello;
uint16_t min_device_interval;
};
/// Настройки не зависящие от описания данных Profinet
struct ProfinetSettings {
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

@ -0,0 +1,88 @@
#include "profinet_slot.hpp"
ProfinetSlot::ProfinetSlot ( uint16_t slot_nbr ) :
m_slot_nbr(slot_nbr),
m_module_ptr(nullptr)
{
}
bool ProfinetSlot::pullModule(ProfinetServiceData& pnet_data)
{
int result = pnet_pull_module (pnet_data.pnet_ptr, pnet_data.api, m_slot_nbr);
m_module_ptr = nullptr;
return (result == 0);
}
bool ProfinetSlot::plugModule(ProfinetServiceData& pnet_data, std::shared_ptr<ProfinetModule>& module_ptr)
{
/// Сначала отключаем модуль
pullModule(pnet_data);
/// Затем подключаем модуль
int result = pnet_plug_module (pnet_data.pnet_ptr, pnet_data.api, m_slot_nbr, module_ptr->m_id);
if (result == 0)
{
m_module_ptr = module_ptr;
}
return (m_module_ptr != nullptr);
}
bool ProfinetSlot::addSubslot(std::shared_ptr<ProfinetSubslot>& subslot_ptr)
{
if (subslot_ptr == nullptr)
return false;
if (m_module_ptr == nullptr)
return false;
subslot_ptr->setSlotNumber(m_slot_nbr);
subslot_ptr->setModuleId(m_module_ptr->m_id);
m_subslots[subslot_ptr->m_subslot_nbr] = subslot_ptr;
return true;
}
std::shared_ptr<ProfinetSubslot> ProfinetSlot::addSubslot(uint16_t subslot_nbr)
{
std::shared_ptr<ProfinetSubslot> subslot_ptr(nullptr);
if (m_subslots.count(subslot_nbr))
{
subslot_ptr = m_subslots[subslot_nbr];
}
else
{
subslot_ptr = std::make_shared<ProfinetSubslot>(subslot_nbr, m_slot_nbr, m_module_ptr->m_id);
}
if (subslot_ptr != nullptr)
{
m_subslots[subslot_nbr] = subslot_ptr;
}
return subslot_ptr;
}
std::shared_ptr<ProfinetSubslot> ProfinetSlot::getSubslotPtr(uint16_t subslot_nbr)
{
/// Проверка наличия подслота в конфигурации
if (!m_subslots.count(subslot_nbr))
{
return nullptr;
}
return m_subslots[subslot_nbr];
}
std::shared_ptr<ProfinetModule> ProfinetSlot::getModulePtr()
{
return m_module_ptr;
}

View File

@ -0,0 +1,70 @@
#pragma once
#include <cstdint>
#include <map>
#include <memory>
#include "profinet_serv_data.hpp"
#include "profinet_subslot.hpp"
#include "profinet_module.hpp"
class ProfinetSlot
{
public:
/**
* @brief Construct a new Profinet Slot object
*
* @param slot_nbr номер слота
*/
ProfinetSlot(uint16_t slot_nbr);
/**
* @brief Подключает модуль к слоту
*
* @param pnet_data Данные pnet
* @return true модуль подключен
* @return false модуль не подключен
*/
bool plugModule(ProfinetServiceData& pnet_data, std::shared_ptr<ProfinetModule>& module_ptr);
/**
* @brief Отключает модуль от слота
*
* @param pnet_data Данные pnet
* @return true модуль был поключен и теперь отключен
* @return false модуль не был подключен
*/
bool pullModule(ProfinetServiceData& pnet_data);
/**
* @brief Добавляет ранее созданный подслот subslot_ptr
*
* @param subslot_ptr подслот
* @return true подслот добавлен
* @return false подслот был добавлен ранее
*/
bool addSubslot(std::shared_ptr<ProfinetSubslot>& subslot_ptr);
/**
* @brief Если подслот с номером subslot_nbr еще не добавлен, то создает подслот и добавляет его
*
* @param subslot_nbr номер подслота
* @return std::shared_ptr<ProfinetSubslot> указатель на подслот
*/
std::shared_ptr<ProfinetSubslot> addSubslot(uint16_t subslot_nbr);
std::shared_ptr<ProfinetSubslot> getSubslotPtr(uint16_t subslot_nbr);
std::shared_ptr<ProfinetModule> getModulePtr();
std::map<uint16_t, std::shared_ptr<ProfinetSubslot>>& getSubslotsPtr() { return m_subslots; };
public:
/// Номер слота
const uint16_t m_slot_nbr;
private:
/// Модуль вставленный в этот слот
std::shared_ptr<ProfinetModule> m_module_ptr;
/// множество подслотов
std::map<uint16_t, std::shared_ptr<ProfinetSubslot>> m_subslots;
};

View File

@ -0,0 +1,136 @@
#include "profinet_submodule.hpp"
#include <cstring>
ProfinetSubmodule::ProfinetSubmodule(uint32_t submodule_id,
std::string submodule_name,
pnet_data_cfg_t& submodule_data_cfg) :
m_id(submodule_id),
m_name(submodule_name),
m_data_cfg(submodule_data_cfg),
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)
{
auto ret = m_params.emplace(param->index, param);
return ret.second;
}
std::shared_ptr<ProfinetParameter> ProfinetSubmodule::getParameterPtr(uint32_t index)
{
if (!m_params.count(index))
{
return nullptr;
}
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

@ -0,0 +1,73 @@
#pragma once
#include <cstdint>
#include <string>
#include <map>
#include <memory>
#include <mutex>
#include "profinet_parameter.hpp"
#include "../../libs/include/pnet_api.h"
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);
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; /// Идентификатор подмодуля
const std::string m_name; /// Имя подмодуля
const pnet_data_cfg_t m_data_cfg; /// Конфигурация циклических данных подмодуля
/// iocs = I/O consumer status data (формат данных из pnet_ioxs_values_t)
uint8_t m_indata_iocs;
/// iops = I/O provider status data (формат данных из pnet_ioxs_values_t)
uint8_t m_outdata_iops;
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,45 @@
#include "profinet_subslot.hpp"
ProfinetSubslot::ProfinetSubslot(uint16_t subslot_nbr, uint16_t slot_nbr, uint32_t module_id) :
m_subslot_nbr(subslot_nbr),
m_slot_nbr(slot_nbr),
m_module_id(module_id),
m_submodule_ptr(nullptr)
{
}
bool ProfinetSubslot::pullSubmodule(ProfinetServiceData& pnet_data)
{
int result = pnet_pull_submodule (pnet_data.pnet_ptr, pnet_data.api, m_slot_nbr, m_subslot_nbr);
/// Очищаем указатель на подмодуль
m_submodule_ptr = nullptr;
return (result == 0);
}
bool ProfinetSubslot::plugSubmodule(ProfinetServiceData& pnet_data, std::shared_ptr<ProfinetSubmodule>& submodule_ptr)
{
/// Сначала отключаем подмодуль
pullSubmodule(pnet_data);
/// Подключаем подмодуль
int result = pnet_plug_submodule (
pnet_data.pnet_ptr,
pnet_data.api,
m_slot_nbr,
m_subslot_nbr,
m_module_id,
submodule_ptr->m_id,
submodule_ptr->m_data_cfg.data_dir,
submodule_ptr->m_data_cfg.insize,
submodule_ptr->m_data_cfg.outsize);
if (result == 0)
{
m_submodule_ptr = submodule_ptr;
}
return (m_submodule_ptr != nullptr);
}

View File

@ -0,0 +1,42 @@
#pragma once
#include <cstdint>
#include <map>
#include <memory>
#include "../../libs/include/pnet_api.h"
#include "profinet_serv_data.hpp"
#include "profinet_submodule.hpp"
class ProfinetSubslot
{
public:
//ProfinetSubslot(uint16_t subslot_nbr) : m_subslot_nbr(subslot_nbr) {};
ProfinetSubslot(uint16_t subslot_nbr, uint16_t slot_nbr, uint32_t module_id);
bool plugSubmodule(ProfinetServiceData& pnet_data, std::shared_ptr<ProfinetSubmodule>& submodule_ptr);
bool pullSubmodule(ProfinetServiceData& pnet_data);
void setSlotNumber(uint16_t slot_nbr) { m_slot_nbr = slot_nbr; };
void setModuleId(uint16_t module_id) { m_module_id = module_id; };
std::shared_ptr<ProfinetSubmodule> getSubmodulePtr() { return m_submodule_ptr; };
public:
/// Номер подслота
const uint16_t m_subslot_nbr;
private:
/// Номер слота куда входит данный подслот
uint16_t m_slot_nbr;
/// Идентификатор модуля куда входит подключаемый подмодуль
uint32_t m_module_id;
/// Подмодуль вставленный в этот подслот
std::shared_ptr<ProfinetSubmodule> m_submodule_ptr;
};

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"
}
}

153
src/utils/utils.c Normal file
View File

@ -0,0 +1,153 @@
#include "utils.h"
#include "string.h"
#include "pnal.h"
static void utils_copy_ip_to_struct (
pnet_cfg_ip_addr_t * destination_struct,
uint32_t ip)
{
destination_struct->a = ((ip >> 24) & 0xFF);
destination_struct->b = ((ip >> 16) & 0xFF);
destination_struct->c = ((ip >> 8) & 0xFF);
destination_struct->d = (ip & 0xFF);
}
static int utils_get_netif_namelist (
const char * arg_str,
uint16_t max_port,
utils_netif_namelist_t * p_if_list,
uint16_t * p_num_ports)
{
int ret = 0;
uint16_t i = 0;
uint16_t j = 0;
uint16_t if_index = 0;
uint16_t number_of_given_names = 1;
uint16_t if_list_size = max_port + 1;
char c;
if (max_port == 0)
{
printf ("Error: max_port is 0.\n");
return -1;
}
memset (p_if_list, 0, sizeof (*p_if_list));
c = arg_str[i++];
while (c != '\0')
{
if (c != ',')
{
if (if_index < if_list_size)
{
p_if_list->netif[if_index].name[j++] = c;
}
}
else
{
if (if_index < if_list_size)
{
p_if_list->netif[if_index].name[j++] = '\0';
j = 0;
if_index++;
}
number_of_given_names++;
}
c = arg_str[i++];
}
if (max_port == 1 && number_of_given_names > 1)
{
printf ("Error: Only 1 network interface expected as max_port is 1.\n");
return -1;
}
if (number_of_given_names == 2)
{
printf ("Error: It is illegal to give 2 interface names. Use 1, or one "
"more than the number of physical interfaces.\n");
return -1;
}
if (number_of_given_names > max_port + 1)
{
printf (
"Error: You have given %u interface names, but max is %u as "
"PNET_MAX_PHYSICAL_PORTS is %u.\n",
number_of_given_names,
max_port + 1,
max_port);
return -1;
}
if (number_of_given_names == 1)
{
if (strlen (p_if_list->netif[0].name) == 0)
{
printf ("Error: Zero length network interface name.\n");
return -1;
}
else
{
p_if_list->netif[1] = p_if_list->netif[0];
*p_num_ports = 1;
}
}
else
{
for (i = 0; i < number_of_given_names; i++)
{
if (strlen (p_if_list->netif[i].name) == 0)
{
printf ("Error: Zero length network interface name (%d).\n", i);
return -1;
}
}
*p_num_ports = number_of_given_names - 1;
}
return ret;
}
int utils_pnet_cfg_init_netifs (
const char * netif_list_str,
utils_netif_namelist_t * if_list,
uint16_t * number_of_ports,
pnet_if_cfg_t * if_cfg,
uint16_t default_mau_type)
{
int ret = 0;
int i = 0;
uint32_t ip;
uint32_t netmask;
uint32_t gateway;
ret = utils_get_netif_namelist (
netif_list_str,
PNET_MAX_PHYSICAL_PORTS,
if_list,
number_of_ports);
if (ret != 0)
{
return ret;
}
if_cfg->main_netif_name = if_list->netif[0].name;
for (i = 1; i <= *number_of_ports; i++)
{
if_cfg->physical_ports[i - 1].netif_name = if_list->netif[i].name;
if_cfg->physical_ports[i - 1].default_mau_type = default_mau_type;
}
/* Read IP, netmask, gateway from operating system */
ip = pnal_get_ip_address (if_cfg->main_netif_name);
netmask = pnal_get_netmask (if_cfg->main_netif_name);
gateway = pnal_get_gateway (if_cfg->main_netif_name);
utils_copy_ip_to_struct (&if_cfg->ip_cfg.ip_addr, ip);
utils_copy_ip_to_struct (&if_cfg->ip_cfg.ip_gateway, gateway);
utils_copy_ip_to_struct (&if_cfg->ip_cfg.ip_mask, netmask);
return ret;
}

3
src/utils/utils.cmake Normal file
View File

@ -0,0 +1,3 @@
set(SRC_FILES ${SRC_FILES} ./utils/utils.c)
set(INC_DIRS ${INC_DIRS} ./utils)

46
src/utils/utils.h Normal file
View File

@ -0,0 +1,46 @@
#ifndef UTILS_H
#define UTILS_H
#ifdef __cplusplus
extern "C" {
#endif
#include "pnet_api.h"
#include "osal.h"
typedef struct utils_netif_name
{
char name[PNET_INTERFACE_NAME_MAX_SIZE];
} utils_netif_name_t;
typedef struct utils_netif_namelist
{
utils_netif_name_t netif[PNET_MAX_PHYSICAL_PORTS + 1];
} utils_netif_namelist_t;
/**
* Update network configuration from a string
* defining a list of network interfaces examples:
* "eth0" or "br0,eth0,eth1"
*
* Read IP, netmask etc from operating system.
*
* @param netif_list_str In: Comma separated string of network ifs
* @param if_list Out: Array of network ifs
* @param number_of_ports Out: Number of ports
* @param if_cfg Out: P-Net network configuration to be updated
* @param default_mau_type
* @return 0 on success, -1 on error
*/
int utils_pnet_cfg_init_netifs (
const char * netif_list_str,
utils_netif_namelist_t * if_list,
uint16_t * number_of_ports,
pnet_if_cfg_t * if_cfg,
uint16_t default_mau_type);
#ifdef __cplusplus
}
#endif
#endif